+
+Under NixOS the following `nix-shell` is needed:
+
+```plain
+nix-shell -p jq
+```
+
+For Linux builds:
+
+```plain
+nix-shell -p jq fakeroot dpkg
+```
+
+The resulting build will be in the `dist` directory under the project root.
+
+### Testing the Linux builds under NixOS
+
+
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/CI/1_Main_image.png b/docs/Developer Guide/Developer Guide/Building and deployment/CI/1_Main_image.png
new file mode 100644
index 000000000..5eacee569
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Building and deployment/CI/1_Main_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main.md b/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main.md
new file mode 100644
index 000000000..667969496
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main.md
@@ -0,0 +1,24 @@
+# Main
+The main workflow of the CI:
+
+* Builds the Docker image and publishes in the GitHub Docker registry.
+* Builds using a portion of the [delivery script](../Build%20deliveries%20locally.md) artifacts for the following platforms:
+ * Windows `x86_64` as .zip file
+ * Windows `x86_64` installer (using Squirrel)
+ * macOS `x86_64` and `aarch64`.
+ * Linux `x86_64`
+ * Linux server `x86_64`.
+
+The main workflow of the CI runs on `develop` branches as well as any branch that starts with `feature/update_`.
+
+## Downloading the artifacts from the main branch
+
+Simply go to the [`develop` branch on GitHub](https://github.com/TriliumNext/Notes) and look at the commit bar:
+
+
+
+Press the green checkmark (or red cross if something went bad). Then look at the list of jobs and their status:
+
+
+
+Then look for any of the entires that starts with “Main” and press the “Details” link next to it. It doesn't really matter which platform you'll choose as the artifacts are available on the same page.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main_image.png b/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main_image.png
new file mode 100644
index 000000000..2f13f1647
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Building and deployment/CI/Main_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/Documentation.md b/docs/Developer Guide/Developer Guide/Building and deployment/Documentation.md
new file mode 100644
index 000000000..a883b2dfe
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Building and deployment/Documentation.md
@@ -0,0 +1,34 @@
+# Documentation
+Development notes are published on [triliumnext.github.io/Notes](https://triliumnext.github.io/Notes) by the CI using GitHub Pages.
+
+The GitHub Pages deployment works by taking the files from the Notes repository, in the `docs` directory.
+
+## How it works
+
+There is a script that uses `wget` to download all the files from a share, that means:
+
+1. You must have a local instance of Trilium Notes server.
+2. You must have the documentation imported, up to date and shared.
+
+Note that currently the documentation source file is not distributed (the note export), until a way is found to automate this process. Contact `eliandoran` should you require to obtain a copy of the documentation.
+
+## Setting up `.env` file
+
+Go to `bin/docs` and copy `.env.example` to `.env` and edit it:
+
+1. Change the `SHARE_PROTOCOL` to either `http` or `https` depending on your setup.
+2. Change `SHARE_HOST` to match the domain name or the URL to the host (without the protocol or any slashes).
+
+Generally `ROOT_NOTE_ID` should not be changed since the note ID must match if the files were imported correctly.
+
+## Triggering a build
+
+Run:
+
+```plain
+./bin/docs/prepare.sh
+```
+
+This will attempt to download all the notes from the share URL and put them in `docs`, rewritten for GitHub Pages.
+
+Commit the results and follow the normal development process to push them.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/Releasing a version.md b/docs/Developer Guide/Developer Guide/Building and deployment/Releasing a version.md
new file mode 100644
index 000000000..af0e08fbb
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Building and deployment/Releasing a version.md
@@ -0,0 +1,22 @@
+# Releasing a version
+On NixOS:
+
+```plain
+nix-shell -p dpkg fakeroot jq nodejs_20
+```
+
+Then simply run from project root:
+
+```plain
+./bin/release.sh 1.2.3
+```
+
+where `1.2.3` is the desired release version.
+
+If a version ends with `-beta`, it will automatically be marked as pre-release in GitHub.
+
+This will automatically generate a release in GitHub if everything goes according to plan.
+
+Note that the Windows installer is not automatically uploaded yet, it has to be taken from the [main workflow of the CI from the `develop` branch](CI/Main.md).
+
+Make sure to check test the artifacts of the release.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Building and deployment/Running a development build.md b/docs/Developer Guide/Developer Guide/Building and deployment/Running a development build.md
new file mode 100644
index 000000000..e88cf3842
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Building and deployment/Running a development build.md
@@ -0,0 +1,89 @@
+# Running a development build
+As always, install the dependencies for the first time (and re-run whenever there are errors about missing dependencies):
+
+```sh
+npm install
+```
+
+## Run server
+
+Run with default settings:
+
+```sh
+npm run start-server
+```
+
+Run with custom port:
+
+```sh
+TRILIUM_PORT=8082 npm run start-server
+```
+
+## Run Electron
+
+Rebuild `better-sqlite3` dependency:
+
+```sh
+npm run switch-electron
+```
+
+Then run Electron:
+
+```sh
+npm run start-electron
+```
+
+To run Electron using the same data directory as the production version:
+
+```sh
+npm run start-electron-no-dir
+```
+
+When done, switch back the `better-sqlite3` dependency:
+
+```sh
+npm run switch-server
+```
+
+## Quick switch
+
+To start Electron without running `switch-electron` first:
+
+```sh
+npm run qstart-electron
+```
+
+Similarly, to start the server without running `switch-server` first:
+
+```sh
+npm run qstart-server
+```
+
+## Safe mode
+
+Safe mode is off by default, to enable it temporarily on a Unix shell, prepend the environment variable setting:
+
+```sh
+TRILIUM_SAFE_MODE=1 npm run start-server
+```
+
+To have the same behaviour on Windows, we would need to alter `package.json`:
+
+```diff
+-"start-electron": "npm run prepare-dist && cross-env TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron ./dist/electron-main.js --inspect=5858 .",
++"start-electron": "npm run prepare-dist && cross-env TRILIUM_SAFE_MODE=1 TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron ./dist/electron-main.js --inspect=5858 .",
+```
+
+## Running on NixOS
+
+When doing development, the Electron binary retrieved from NPM is not going to be compatible with NixOS, resulting in errors when trying to run it. To bypass this, there is a special command to run electron using `nix-shell`:
+
+```sh
+npm run start-electron-nix
+```
+
+Similarly to the original command, to use the same data directory as the production version:
+
+```sh
+npm run start-electron-no-dir-nix
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Dependency Management/Adding a new client library.md b/docs/Developer Guide/Developer Guide/Dependency Management/Adding a new client library.md
new file mode 100644
index 000000000..e413d99d6
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Dependency Management/Adding a new client library.md
@@ -0,0 +1,116 @@
+# Adding a new client library
+In the past some libraries have been copy-pasted (and adapted if needed) to the repository. However, new libraries must be obtained exclusively through npm.
+
+The first step is to install the desired library. As an example we are going to install `i18next`:
+
+```plain
+npm i i18next
+```
+
+### Step 1. Understanding the structure of the import
+
+After installing the dependency, it's important to know how it's structured. You can do this by looking at the directory structure of the newly imported dependency:
+
+```plain
+$ tree node_modules/i18next
+node_modules/i18next
+├── dist
+│ ├── cjs
+│ │ └── i18next.js
+│ ├── esm
+│ │ ├── i18next.bundled.js
+│ │ ├── i18next.js
+│ │ └── package.json
+│ └── umd
+│ ├── i18next.js
+│ └── i18next.min.js
+├── i18next.js
+├── i18next.min.js
+├── index.d.mts
+├── index.d.ts
+├── index.js
+├── index.v4.d.ts
+├── LICENSE
+├── package.json
+├── README.md
+└── typescript
+ ├── helpers.d.ts
+ ├── options.d.ts
+ ├── t.d.ts
+ └── t.v4.d.ts
+```
+
+Generally you should be looking for a `.min.js` file. Note that the `esm` and `cjs` variants generally don't work, we are looking for the classic, no module dependency.
+
+### Step 2. Exposing the library from the server
+
+The library must be delivered by the server and this is done via `src/routes/assets.ts`. In the `register` function, add a new entry near the bottom of the function:
+
+```javascript
+app.use(`/${assetPath}/node_modules/i18next/`, persistentCacheStatic(path.join(srcRoot, "..", 'node_modules/i18next/')));
+```
+
+### Step 3. Adding it to the library loader
+
+The library loader is a client module which is in charge of downloading the library from the server and importing it. The loader is located in `src/public/app/services/library_loader.js`.
+
+To add a new library, start by creating a constant for it, with the value pointing to the minified JS identified at the first step:
+
+```javascript
+const I18NEXT = {
+ js: [
+ "node_modules/i18next/i18next.min.js"
+ ]
+};
+```
+
+Then add it to the `export default` section:
+
+```diff
+ export default {
+ requireCss,
+ requireLibrary,
+ CKEDITOR,
+ CODE_MIRROR,
+ ESLINT,
+ RELATION_MAP,
+ PRINT_THIS,
+ CALENDAR_WIDGET,
+ KATEX,
+ WHEEL_ZOOM,
+ FORCE_GRAPH,
+ MERMAID,
+ EXCALIDRAW,
+- MARKJS
++ MARKJS,
++ I18NEXT
+ }
+```
+
+### Step 4. Using the library
+
+To import the library, simply use the following mechanism:
+
+```diff
+import library_loader from "./library_loader.js";
+
+await library_loader.requireLibrary(library_loader.I18NEXT);
+```
+
+Make sure to replace `I18NEXT` with the library that was created at the previous steps.
+
+Note that because we are not using a module management mechanism such as ES Modules or Common.js modules, the `requireLibrary` method does not actually return anything.
+
+To benefit from the library, it must export on its own an object in `window`.
+
+In the case of `i18next`, it sets `window.i18next` and that can be used directly:
+
+```diff
+i18next.init({});
+```
+
+### Step 5. Adding Electron support
+
+For Electron, the `node_modules` are copied as a separate step by `bin/copy-dist.ts`.
+
+Scroll all the way down to the `nodeModulesFolder` and append an entry for the newly added libraries.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Dependency Management/Having a simpler packaging sys.md b/docs/Developer Guide/Developer Guide/Dependency Management/Having a simpler packaging sys.md
new file mode 100644
index 000000000..98702fa11
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Dependency Management/Having a simpler packaging sys.md
@@ -0,0 +1,8 @@
+# Having a simpler packaging system
+The current build scripts are a bit complicated and maintaining them is not easy.
+
+[Electron Forge](https://www.electronforge.io/) seems more mature and has a boatload of features, including Flatpak, snaps, Windows installers & more.
+
+Have a look also at the [Plugins](https://www.electronforge.io/config/plugins) section since there are quite a few interesting things there as well.
+
+Afterwards consider running a new round of Reducing binary size, especially taking into consideration removing of the unnecessary locales.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Copy image reference to the cl.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Copy image reference to the cl.md
new file mode 100644
index 000000000..d8282bb3f
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Copy image reference to the cl.md
@@ -0,0 +1,6 @@
+# Copy image reference to the clipboard
+This function is handled by `src/public/app/widgets/floating_buttons/copy_image_reference_button.js` and it supports multiple note types out of the box.
+
+To enable the display of the button, simply modify `isEnabled` to add support for the new note type.
+
+No other modifications should be necessary.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Export diagram as SVG.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Export diagram as SVG.md
new file mode 100644
index 000000000..23bb6649a
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Export diagram as SVG.md
@@ -0,0 +1,33 @@
+# Export diagram as SVG
+This mechanism is handled by `src/public/app/widgets/floating_buttons/svg_export_button.js`.
+
+## Step 1. Enable the button
+
+Modify the `isEnabled` method in `svg_export_button.js` to add support for the new note type.
+
+## Step 2. Add support for exporting the image
+
+The SVG export needs to be handled inside the note type implementation.
+
+The first goal is to create a method to handle the SVG rendering. Make sure to deduplicate the code if the SVG rendering is already handled.
+
+```plain
+async renderSvg() {
+ return await this.mind.exportSvg().text();
+}
+```
+
+Then create an event handler to manage the SVG export:
+
+```plain
+async exportSvgEvent({ntxId}) {
+ if (!this.isNoteContext(ntxId) || this.note.type !== "mindMap") {
+ return;
+ }
+
+ const svg = await this.renderSvg();
+ utils.downloadSvg(this.note.title, svg);
+}
+```
+
+Make sure to modify the note type assertion at the beginning of the method. This is very important, otherwise there can be errors when navigating through multiple note types that support this button.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps.md
new file mode 100644
index 000000000..de0f74b3b
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps.md
@@ -0,0 +1,54 @@
+# First steps
+> **Note**: When adding or updating step titles/order, don't forget to update the corresponding list in Note type checklist.
+
+## Step 1. Register the note type in the server
+
+Go to `src\services\note_types.ts` and add a new entry in `noteTypes` with the type ID and the default MIME type.
+
+## Step 2. Register the note type in the client context menu
+
+The client lists the available note types in `src\public\app\services\note_types.ts`.
+
+## Step 3. Create a type widget
+
+Go to `src\public\app\widgets\type_widgets` directory and create a new file corresponding to the new note type.
+
+A blank implementation looks something like this:
+
+## Step 4. Register the type widget
+
+The type widget needs to go in `src\public\app\widgets\note_detail.ts`where there is a `typeWidgetClasses` map, mapping the type IDs with the corresponding type widget that was created at the previous step.
+
+## Step 5. Add the default icon mapping
+
+To set a default icon for this note type, go to `src\public\app\entities\fnote.ts` and add it to `NOTE_TYPE_ICONS`.
+
+## Step 6. Add to note type selector
+
+Go to `src/public/app/widgets/note_type.ts` and register the new note type in `NOTE_TYPES`.
+
+## Step 7. Add the note to server allowed note types
+
+This is required in order to make imports possible, otherwise they will be imported as plain text.
+
+Go to `src/becca/entities/rows.ts` and add the new note type to `ALLOWED_NOTE_TYPES`.
+
+## Additional changes
+
+* If the widget requires a full height, it must be configured in `src\public\app\widgets\note_detail.ts` (look for `checkFullHeight`) then a `height: 100%` style can be applied to the containers to make them fit.
+* To make the note always full width (ignoring the user's content width), go to `note_wrapper` and look for the `refresh` method. There is a `toggleClass` for `full-content-width` based on the note type.
+* To allow the note source to be viewed, go to `src/public/app/widgets/buttons/note_actions.ts` and look for `this.toggleDisabled(this.$showSourceButton` in `refreshVisibility`.
+
+## Final steps
+
+* Update the Demo document to showcase the new note type.
+
+## Troubleshooting
+
+### Content does not appear, however it appears as hidden in the DOM
+
+Type widgets do a check whenever a note is selected to determine whether the widget needs to be displayed or not, based on the note type. Make sure `getType()` is well implemented in the newly added type widget (take great care that the value is returned but also that the note type ID matches the ones registered in the previous steps):
+
+```plain
+static getType() { return "foo"; }
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps/mind_map.js b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps/mind_map.js
new file mode 100644
index 000000000..9e33e54d9
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/First steps/mind_map.js
@@ -0,0 +1,27 @@
+import TypeWidget from "./type_widget.js";
+
+const TPL = `
+
");
+ this.$widget.show();
+ }
+
+ async entitiesReloadedEvent({loadResults}) {
+ if (loadResults.isNoteReloaded(this.noteId)) {
+ this.refresh();
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Loading data.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Loading data.md
new file mode 100644
index 000000000..a29207b15
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Loading data.md
@@ -0,0 +1,9 @@
+# Loading data
+Data loading can be done in `doRefresh()` since it gets a reference to the note:
+
+```plain
+const blob = await note.getBlob();
+const content = blob.getJsonContent();
+```
+
+Note that `doRefresh` can sometimes be called by Saving data via spaced update when the user makes a changes, this has to be accounted for.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Note type checklist.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Note type checklist.md
new file mode 100644
index 000000000..9b81ae39c
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Note type checklist.md
@@ -0,0 +1,49 @@
+# Note type checklist
+The goal of this checklist is to ensure a good implementation or re-test of a note type.
+
+## Implementation checklist
+
+The note type widget must be created according to First steps:
+
+* Register the note type in the server
+* Register the note type in the client context menu
+* Create a type widget
+* Register the type widget
+* Add the default icon mapping
+* Add to note type selector
+* Add the note to server allowed note types
+* Update demo document to include this new note type
+* Increase server sync version (see Mindmap gets turned as file).
+
+## Validation checklist
+
+### Ensure that the note renders properly
+
+* When refreshing to a note that is already displayed
+* When going to another note and then going back
+* When creating a new note of the given type
+* Have two tabs of the same note type and switch between them
+
+### Ensure data persistence
+
+* Save data when modifying changes via spaced update
+
+### Ensure data retrieval
+
+* Go on a note of this type and refresh the page
+* Create a new note of this type while on another note of this type and ensure that the content is set properly.
+
+### Set up a note preview
+
+For an implementation reference, see SVG rendering.
+
+* Note preview rendering (go to parent and see note list).
+* Include note
+* Share
+* Note revisions
+
+### Import/export
+
+* Export & Import, making sure no data is lost in the process.
+* Remove the data folder entirely to test that the demo document is well imported on first setup.
+ * Ensure that the preview also works (check the preview in the root note).
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/SVG rendering.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/SVG rendering.md
new file mode 100644
index 000000000..3deb8f2d1
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/SVG rendering.md
@@ -0,0 +1,72 @@
+# SVG rendering
+For diagrams and similar note types, it makes sense to cache an SVG rendering of the content so that it can be used for:
+
+* Content preview in note lists (when viewing the list of notes from the parent note).
+* Note inclusion
+* Share
+
+## Step 1. Save the SVG content as an attachment
+
+The first step is to obtain the SVG from the custom widget used. For example, for Mind Elixir there is an `exportSvg` method.
+
+If the returned value is a `Blob`, then the underlying text can be obtained via `await blob.text()`.
+
+To save the SVG as an attachment alongside the content, simply modify `getData()`:
+
+```plain
+async getData() {
+ const mind = this.mind;
+ if (!mind) {
+ return;
+ }
+
+ const svgContent = await this.mind.exportSvg().text();
+ return {
+ content: mind.getDataString(),
+ attachments: [
+ {
+ role: "image",
+ title: "mindmap-export.svg",
+ mime: "image/svg+xml",
+ content: svgContent,
+ position: 0
+ }
+ ]
+ };
+}
+```
+
+You can test this step by making a change to the note and then using the “Note attachments” option from the note menu.
+
+## Step 2. Adapting the server to serve SVG attachment
+
+The `src/routes/api/image.ts` route is in charge for serving the image previews of image notes, but also of custom note types such as canvases.
+
+Alter the `returnImageInt` method as follows:
+
+1. Add the image type to the guard condition which returns 400 for unsupported note types.
+2. Add an `if` statement to render the attachment using the correct name:
+
+```plain
+if (image.type === "mindMap") {
+ renderSvgAttachment(image, res, 'mindmap-export.svg');
+}
+```
+
+## Step 3. Serve the SVG attachment for note preview
+
+The client also needs tweaking to allow it to render SVG attachments by calling the previously modified server route.
+
+The `src/public/app/services/content_renderer.js` file is in charge of handling the previews. To render using the image route, modify `getRenderedContent` to add the new note type to the `if` which calls `renderImage`.
+
+## Step 4. Serve SVG for share
+
+By default, `Note type cannot be displayed` will be displayed when trying to access the given note via a share.
+
+To serve the SVG, open `src/share/content_renderer.ts` and look for `getContent`. Then add to the `if` containing `renderImage` the new note type.
+
+This is not enough, as attempting to access the shared note will result in a broken image that fails with `Requested note is not a shareable image`. To solve this one, go to `src/share/routes.ts` and add a `renderImageAttachment` statement to `router.get('/share/api/images/[…])`.
+
+## Step 5. Serve SVG for revisions
+
+In the revisions list, to display the SVG, go to `src/public/app/widgets/dialogs/revisions.js` and look for the `renderContent` method. Simply add the note type to one of the already existing `if`s, such as the one for `canvas` and `mindMap` or `mermaid` (if the text content of the diagram should also be displayed).
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Saving data via spaced update.md b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Saving data via spaced update.md
new file mode 100644
index 000000000..b6344cb78
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Adding a new note type/Saving data via spaced update.md
@@ -0,0 +1,27 @@
+# Saving data via spaced update
+The data persistence is achieved via the spaced update mechanism which is already present and needs to be integrated within the newly created type widgets.
+
+First, the class must implement `getData`, in order to retrieve the data from the custom widget in a serialized form. As an example from the mind map implementation:
+
+```plain
+async getData() {
+ const mind = this.mind;
+ if (!mind) {
+ return;
+ }
+
+ return {
+ content: mind.getDataString()
+ };
+}
+```
+
+Here the content is a string containing a JSON. It is also possible to provide attachments here as well, such as SVG rendering to provide a preview of the content.
+
+Then to trigger an update, register a listener within the custom widget that calls the spaced update, for example:
+
+```plain
+mind.bus.addListener("operation", (operation) => {
+ this.spacedUpdate.scheduleUpdate();
+});
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Backlinks.md b/docs/Developer Guide/Developer Guide/Development and architecture/Backlinks.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Branch prefixes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Branch prefixes.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Build information.md b/docs/Developer Guide/Developer Guide/Development and architecture/Build information.md
new file mode 100644
index 000000000..509386e08
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Build information.md
@@ -0,0 +1,4 @@
+# Build information
+* Provides context about when the build was made and the corresponding Git revision.
+* The information is displayed to the client when going in the about dialog.
+* The build information is hard-coded in `src/services/build.ts`. This file is generated automatically via `npm run update-build-info` which itself is run automatically whenever making a build in the CI, or a [local delivery](../Building%20and%20deployment/Build%20deliveries%20locally.md).
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/attachments.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/attachments.md
new file mode 100644
index 000000000..cbe7e6735
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/attachments.md
@@ -0,0 +1,2 @@
+# attachments
+
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/attributes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/attributes.md
new file mode 100644
index 000000000..5b2604430
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/attributes.md
@@ -0,0 +1,2 @@
+# attributes
+
Column Name
Data Type
Nullity
Default value
Description
attributeId
Text
Non-null
Unique Id of the attribute (e.g. qhC1vzU4nwSE), can also have a special unique ID for Special notes (e.g. _lbToday_liconClass).
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/blobs.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/blobs.md
new file mode 100644
index 000000000..64a5fdc28
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/blobs.md
@@ -0,0 +1,2 @@
+# blobs
+
Column Name
Data Type
Nullity
Default value
Description
blobId
Text
Non-null
The unique ID of the blob (e.g. XXbfAJXqWrYnSXcelLFA).
content
Text
Nullable
null
The content of the blob, can be either:
text (for plain text notes or HTML notes).
binary (for images and other types of attachments)
dateModified
Text
Non-null
Localized modification date (e.g. 2023-11-08 18:43:44.204+0200)
utcDateModified
Text
Non-null
Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/branches.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/branches.md
new file mode 100644
index 000000000..16be8a81c
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/branches.md
@@ -0,0 +1,2 @@
+# branches
+
Column Name
Data Type
Nullity
Default value
Description
branchId
Text
Non-null
The ID of the branch, in the form of a_b where a is the parentNoteId and b is the noteId.
Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/entity_changes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/entity_changes.md
new file mode 100644
index 000000000..85a0b134b
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/entity_changes.md
@@ -0,0 +1,2 @@
+# entity_changes
+
Column Name
Data Type
Nullity
Default value
Description
id
Integer
Nullable
A sequential numeric index of the entity change.
entityName
Text
Nullable
The type of entity being changed (attributes, branches, note_reordering, etc.)
entityId
Text
Nullable
The ID of the entity being changed.
hash
Text
Nullable
TODO: Describe how the hash is calculated
isErased
Integer
Nullable
TODO: What does this do?
changeId
Text
Nullable
TODO: What does this do?
componentId
Text
Nullable
TODO: What does this do?
instanceId
Text
Nullable
TODO: What does this do?
isSynced
Integer
Nullable
TODO: What does this do?
utcDateChanged
Text
Nullable
Date of the entity change in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/etapi_tokens.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/etapi_tokens.md
new file mode 100644
index 000000000..66e64fb8c
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/etapi_tokens.md
@@ -0,0 +1,2 @@
+# etapi_tokens
+
Column Name
Data Type
Nullity
Default value
Description
etapiTokenId
Text
Non-null
A unique ID of the token (e.g. aHmLr5BywvfJ).
name
Text
Non-null
The name of the token, as is set by the user.
tokenHash
Text
Non-null
The token itself.
utcDateCreated
Text
Non-null
Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
utcDateModified
Text
Non-null
Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/notes.md
new file mode 100644
index 000000000..2a21fdcba
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/notes.md
@@ -0,0 +1,2 @@
+# notes
+
Localized creation date (e.g. 2023-11-08 18:43:44.204+0200)
dateModified
Text
Non-null
Localized modification date (e.g. 2023-11-08 18:43:44.204+0200)
utcDateCreated
Text
Non-null
Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
utcDateModified
Text
Non-null
Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
blobId
Text
Nullable
null
The corresponding ID from blobs. Although it can theoretically be NULL, haven't found any such note yet.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/options.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/options.md
new file mode 100644
index 000000000..ec70482c9
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/options.md
@@ -0,0 +1,2 @@
+# options
+
Column Name
Data Type
Nullity
Default value
Description
name
Text
Non-null
The name of option (e.g. maxContentWidth)
value
Text
Non-null
The value of the option.
isSynced
Integer
Non-null
0
0 if the option is not synchronized and thus can differ between clients, 1 if the option is synchronized.
utcDateModified
Text
Non-null
Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/recent_notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/recent_notes.md
new file mode 100644
index 000000000..f1897cc1d
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/recent_notes.md
@@ -0,0 +1,2 @@
+# recent_notes
+
Column Name
Data Type
Nullity
Default value
Description
noteId
Text
Non-null
Unique ID of the note (e.g. yRRTLlqTbGoZ).
notePath
Text
Non-null
The path (IDs) to the note from root to the note itself, separated by slashes.
utcDateCreated
Text
Non-null
Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Database/revisions.md b/docs/Developer Guide/Developer Guide/Development and architecture/Database/revisions.md
new file mode 100644
index 000000000..4ea9c893d
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Database/revisions.md
@@ -0,0 +1,2 @@
+# revisions
+
The corresponding ID from blobs. Although it can theoretically be NULL, haven't found any such note yet.
utcDateLastEdited
Text
Non-null
Not sure how it differs from modification date.
utcDateCreated
Text
Non-null
Creation date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
utcDateModified
Text
Non-null
Modification date in UTC format (e.g. 2023-11-08 16:43:44.204Z)
dateLastEdited
Text
Non-null
Not sure how it differs from modification date.
dateCreated
Text
Non-null
Localized creatino date (e.g. 2023-08-12 15:10:04.045+0300)
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Deleted notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Deleted notes.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Demo document.md b/docs/Developer Guide/Developer Guide/Development and architecture/Demo document.md
new file mode 100644
index 000000000..9a768bb99
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Demo document.md
@@ -0,0 +1,19 @@
+# Demo document
+The demo document is an exported .zip that resides in `db/demo.zip`.
+
+During on-boarding, if the user selects that they are a new user then the `demo.zip` is imported into the root note.
+
+## Modifying the document
+
+On a dev server, remove all your existing notes in order to ensure a clean setup. Right click → Import to note and select the .zip file in `db/demo.zip`. Make sure to disable “Safe import”.
+
+After making the necessary modifications, simply export the “Trilium Demo” note as “HTML in ZIP archive” and replace `db/demo.zip` with the newly exported one.
+
+## Testing the changes
+
+```plain
+rm -r data
+npm run start-server
+```
+
+And then do the on-boarding again.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Docker.md b/docs/Developer Guide/Developer Guide/Development and architecture/Docker.md
new file mode 100644
index 000000000..d615b6355
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Docker.md
@@ -0,0 +1,18 @@
+# Docker
+To run a Docker build:
+
+```plain
+./bin/builder-docker.sh
+```
+
+To run the built Docker image:
+
+```plain
+sudo docker run -p 8081:8080 triliumnext/notes:v0.90.6-beta
+```
+
+To enter a shell in the Docker container:
+
+```plain
+sudo docker run -it --entrypoint=/bin/sh zadam/trilium:0.63-latest
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Hidden notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Hidden notes.md
new file mode 100644
index 000000000..01f1773f6
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Hidden notes.md
@@ -0,0 +1,6 @@
+# Hidden notes
+## Disallow adding child notes
+
+1. To enforce at server level go to `services/notes.ts` and look for the `getAndValidateParent` method. Look for the `params.ignoreForbiddenParents` if statement and add it there.
+2. To hide the plus button in the note tree, go to `widgets/note_tree` in the client and look for `enhanceTitle`. Look for the if statement which starts with `!["search", "launcher"].includes(note.type)`.
+3. To disable it from the contextual menu, go to `tree_context_menu` and look for the `getMenuItems` method. There look for the `insertNoteAfter` and `insertChildNote` actions and look at their `enabled` conditions. If adding a big note type with lots of child notes, see the pattern of optinos & help (rename and augment the `notOptionsOrHelp` variable.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons.md
new file mode 100644
index 000000000..fe48406a7
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons.md
@@ -0,0 +1,26 @@
+# Icons
+Icons are stored in `images` and in `images/app-icons`.
+
+## Favicon
+
+The favicon is served dynamically via `serve-favicon`, using the icon in `images/app-icons/win/icon.ico`.
+
+## Declarative generation of icons
+
+All the icons are now built off of the SVGs in the `images` directory using the `bin/create-icons.sh` script.
+
+## Main images
+
+These are stored in `images`:
+
+
Name
Resolution
Description
icon-black.svg
53x40
Used by the global menu button when not hovered.
icon-color.svg
53x40
Used by the global menu when hovered.
icon-grey.svg
53x40
Used by the dark theme, in place of icon-black.svg.
+
+## App icons
+
+
Name
Resolution
Description
ios/apple-touch-icon.png
180x180
Used as apple-touch-icon, but only in login.ejs and set_password.ejs for some reason.
mac/icon.icns
512x512
Provided as --icon to electron-packager for mac-arm64 and mac-x64builds.
Used by Squirrel Windows installer for: setup icon, app icon, control panel icon
Used as the favicon.
win/setup-banner.gif
640x480
Used by the Squirrel Windows installer during the installation process. Has only one frame.
+
+## Additional locations where the branding is used
+
+* In the client, more specifically in `src/public/app/widgets/buttons/global_menu.js`, where the SVG content of the icon is directly embedded to allow styling via CSS.
+* In the Demo document, as an attachment.
+* In the CKEditor build, look for `packages/ckeditor5-build-balloon-block/src/icons/trilium.svg`. Make sure not to have any `fill` overrides in the SVG as the wrong color will be used.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/1_Icons on Mac_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/1_Icons on Mac_image.png
new file mode 100644
index 000000000..4fa4deed1
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/1_Icons on Mac_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac.md
new file mode 100644
index 000000000..ed0a29475
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac.md
@@ -0,0 +1,8 @@
+# Icons on Mac
+Looks great in Finder:
+
+
+
+Looks great in Dock:
+
+
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Adaptive icon_image.png
new file mode 100644
index 000000000..4fa4deed1
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Adaptive icon_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Slightly blurry icon on Ma.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Slightly blurry icon on Ma.png
new file mode 100644
index 000000000..1a0232adf
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/1_Slightly blurry icon on Ma.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Adaptive icon_image.png
new file mode 100644
index 000000000..e42926852
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Adaptive icon_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Slightly blurry icon on Ma.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Slightly blurry icon on Ma.png
new file mode 100644
index 000000000..c30743ece
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/2_Slightly blurry icon on Ma.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/3_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/3_Adaptive icon_image.png
new file mode 100644
index 000000000..99b0ce3d4
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/3_Adaptive icon_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/4_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/4_Adaptive icon_image.png
new file mode 100644
index 000000000..afcfacc11
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/4_Adaptive icon_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/5_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/5_Adaptive icon_image.png
new file mode 100644
index 000000000..e99523df8
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/5_Adaptive icon_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/6_Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/6_Adaptive icon_image.png
new file mode 100644
index 000000000..eacecb265
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/6_Adaptive icon_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon.md
new file mode 100644
index 000000000..a9d68331f
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon.md
@@ -0,0 +1,6 @@
+# Adaptive icon
+
Before
After
With new scale
+
+## Scale
+
+
0.9
0.85
0.8
0.75
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon_image.png
new file mode 100644
index 000000000..eeec79a01
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Adaptive icon_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Ma.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Ma.png
new file mode 100644
index 000000000..c32a45801
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Ma.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Mac.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Mac.md
new file mode 100644
index 000000000..8d0599797
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac/Slightly blurry icon on Mac.md
@@ -0,0 +1,50 @@
+# Slightly blurry icon on Mac
+Slightly blurry in extended preview on Mac
+
+
+
+In the screenshot, the icon is around 650px whereas the closest image we have is 512px, so that might explain the blur. Adding an `ic10` (`1024x1024`, aka `512x512@2x` to see what happens).
+
+Before:
+
+```plain
+File: ../images/app-icons/mac/icon.icns
+ ic09: 62069 bytes, png: 512x512
+```
+
+After:
+
+```plain
+File: ../images/app-icons/mac/icon.icns
+ icp4: 1140 bytes, png: 16x16
+ icp5: 1868 bytes, png: 32x32
+ ic07: 9520 bytes, png: 128x128
+ ic09: 62069 bytes, png: 512x512
+ ic10: 180442 bytes, png: 512x512@2x
+```
+
+Even with a 1024x1024 icon, the image is still blurry.
+
+Comparing the `.icns` file from the Electron build reveals that the `.icns` file has been tampered with:
+
+
+
+The bluriness might come from the image itself: [https://stackoverflow.com/questions/54030521/convert-svg-to-png-with-sharp-edges](https://stackoverflow.com/questions/54030521/convert-svg-to-png-with-sharp-edges)
+
+Rendering with Inkscape (left) vs ImageMagick (right):
+
+
+
+Now in macOS it's also rendering quite nicely:
+
+
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac_image.png b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac_image.png
new file mode 100644
index 000000000..f587c31cf
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Icons on Mac_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Removed icons.md b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Removed icons.md
new file mode 100644
index 000000000..6ec6a45eb
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Icons/Removed icons.md
@@ -0,0 +1,12 @@
+# Removed icons
+The following icons were removed:
+
+## Main images
+
+These are stored in `images`:
+
+
Name
Resolution
Description
icon-black.png
36x36
Does not appear to be used.
icon-color.png
36x36
Used only by some tests in test-etapi.
icon-grey.png
36x36
Does not appear to be used.
icon.svg
210x297
Does not appear to be used.
+
+## App icons
+
+
Name
Resolution
Description
png/16x16-bw.png
16x16
Do not appear to be used.
png/16x16.png
png/24x24.png
24x24
png/32x32.png
32x32
png/48x48.png
48x48
png/64x64.png
64x64
png/96x96.png
96x96
png/512x512.png
512x512
Does not appear to be used.
win/setup-banner.xcf
GIMP source for win/setup-banner.gif. Provided only for future editing.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translat.md b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translat.md
new file mode 100644
index 000000000..9d9f543bb
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translat.md
@@ -0,0 +1,94 @@
+# Internationalisation / Translations
+During the initial development of Trilium Notes, internationalisation was not considered as it was meant to be an English-only product.
+
+As the application and the user base grows, it makes sense to be able to reach out as many people as possible by providing translations in their native language.
+
+The library used is [i18next](https://www.i18next.com/).
+
+## Where are the translations?
+
+The translations are formatted as JSON files and they are located in `src/public/translations`. For every supported locale, there is a subdirectory in which there is a `translation.json` file (e.g. `src/public/translations/en/translation.json`).
+
+### Message keys
+
+One important aspect is the fact that we are using a key-based approach. This means that each message is identified by an ID rather than a natural-language message (such as the default approach in gettext).
+
+The key-based approach allows a hierarchical structure. For example, a key of `about.title` would be added in `translation.json` as follows:
+
+```json
+{
+ "about": {
+ "title": "About TriliumNext Notes"
+ }
+}
+```
+
+Follow the Guidelines when creating a new message.
+
+### Adding a new locale
+
+To add a new locale, go to `src/public/translations` with your favorite text editor and copy the `en` directory.
+
+Rename the copy to the ISO code (e.g. `fr`, `ro`) of the language being translated.
+
+Translations with a country-language combination, using their corresponding ISO code (e.g. `fr_FR`, `fr_BE`), has not been tested yet.
+
+### Changing the language
+
+Since the internationalisation process is in its early stages, there is no user-facing way to switch the language.
+
+To change the language manually, edit `src/public/app/services/i18n.js` and look for the line containing `lng: "en"`. Replace `en` with the desired language code (from the ones available in `src/public/translations`).
+
+## Client-side translations
+
+### Component-level translations
+
+Most of the client translations are present in the various widgets and layouts.
+
+Translation support has to be added manually for every file.
+
+The first step is to add the translation import with a relative import. For example, if we are in the `src/public/app/widgets/dialogs` directory, the import would look as follows:
+
+```javascript
+import { t } from "../../services/i18n.js";
+```
+
+Afterwards, simply replace the hard-coded message with:
+
+```javascript
+${t("msgid")}
+```
+
+where `msgid` is the key of the message being translated.
+
+### Variables
+
+In the translation, enclose the variables with `{{` and `}}`:
+
+```plain
+{
+ "key": "{{what}} is {{how}}"
+}
+```
+
+Then pass the arguments when reading the translation:
+
+```plain
+t('key', { what: 'i18next', how: 'great' })
+```
+
+### Template-level translations
+
+Templates are `.ejs` files present in `src/views`, these are used to prepare the root layout for desktop, mobile applications as well as setup (onboarding) and the shared notes view.
+
+Due to using a different approach, it is not possible yet to translate those files.
+
+## Server-side translations
+
+Currently the server-side messages are not translatable. They will be added as a separate step.
+
+## Locale/language selection
+
+The language is stored as an option which is synchronized across all devices and the user is able to adjust it via Options → Appearance → Locale.
+
+The options shown to the user are currently hard-coded in `src/routes/api/options.ts`, where there is a `getSupportedLocales()` function. The `id` field must match the corresponding directory in `src/public/translations` and the `name` must be the localized name of the language (so the name must be in that language, not in English).
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Guidelines.md b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Guidelines.md
new file mode 100644
index 000000000..65bdf9673
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Guidelines.md
@@ -0,0 +1,9 @@
+# Guidelines
+* Use hierarchy whenever appropriate, try to group the messages by:
+ * Modals (e.g. `about.foo`, `jump_to_note.foo`)
+* Don't duplicate messages that are very widely used.
+ * One such example is `aria-label="Close"` which should go to a single message such as `modal.close` instead of being duplicated in every modal.
+* On the other hand, don't overly generalise messages. A `close` message that is used whenever the “Close” word is encountered is not a good approach since it can potentially cause issues due to lack of context.
+* Use [variable interpolation](https://www.i18next.com/translation-function/interpolation) whenever appropriate.
+ * If you see multiple messages joined together only to apply add a variable such as a user-inputted value, try to join those messages together into a single message containing a variable.
+ * So instead of `“Number of updates: “ + numUpdates + “.”` use `$(t("number_updates", { numUpdates }))` where the message translation would appear as `Number of updates: {{numUpdates}}.`
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Server translations.md b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Server translations.md
new file mode 100644
index 000000000..f055fce95
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/Server translations.md
@@ -0,0 +1,19 @@
+# Server translations
+* Server-side translations are managed by the same library as the client, i18next.
+* The translation files reside in the `/translations` directory, following the same convention as the client (`translations/{{lng}}/{{ns}}.json`), where the namespace is `server.json`. So for the Spanish translations we have `translations/es/server.json`.
+* Loading of translations is managed by [i18next-fs-backend](https://github.com/i18next/i18next-fs-backend) which loads the translations directly from the file system (unlike HTTP requests like the client), at the path mentioned previously (relative to `package.json`).
+
+## How to translate a string
+
+Unlike the client which uses a dedicated client service, the i18next library on the server is used directly, as such:
+
+```javascript
+import { t } from "i18next";
+
+const translatedString = t("message.id");
+```
+
+## What should be translated
+
+* Avoid translating server-side logs, as those are supposed to be for debugging and as such there is no benefit in translating them.
+* Translate any user-facing message that comes from the server, such as error messages shown in the Electron application, or information such as keyboard shortcuts, note titles, etc.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/i18n-ally.md b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/i18n-ally.md
new file mode 100644
index 000000000..75a8b0cd6
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Internationalisation Translations/i18n-ally.md
@@ -0,0 +1,15 @@
+# i18n-ally
+[`i18n-ally`](https://github.com/lokalise/i18n-ally) is a VS Code extension that aids in internationalization.
+
+It is currently integrated in the project and offers features such as:
+
+* Highlight, autocomplete translations.
+* Display translations inline.
+* Extract messages into translation.
+
+### Extracting messages into translation
+
+1. Open any .js file and select an untranslated string inside a template (`TPL`).
+2. Press Ctrl+P and look for “i18n Ally: Extract text into i18n messages”
+3. Select the first template.
+4. Select the path of translation, taking into consideration the Guidelines (e.g. `jump_to_note.search-for-note-by-its-name`).
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Launchers.md b/docs/Developer Guide/Developer Guide/Development and architecture/Launchers.md
new file mode 100644
index 000000000..f7e5f429e
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Launchers.md
@@ -0,0 +1,13 @@
+# Launchers
+Launchers are items that are displayed in the launcher bar (left side of the screen). They are of two different types:
+
+* Visible launchers: are displayed by default to the user, can be moved to the available launchers section to hide them.
+* Available launchers: can be manually added by the user from settings into the list of visible launchers.
+
+## Adding a new launcher
+
+Regardless of the type, new launchers are added at server level, inside `hidden_subtree.ts` file.
+
+* To add a new available launcher, look for `_lbAvailableLaunchers` and add a new item to its `children`.
+* Similarly, to add a visible launcher, look for `_lbVisibleLaunchers`.
+ * If you add a visible launcher, it will be available for both new and old users, since the application will identify that there is a new launcher to be added regardless of the user preference.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Live reload.md b/docs/Developer Guide/Developer Guide/Development and architecture/Live reload.md
new file mode 100644
index 000000000..1933f5aa2
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Live reload.md
@@ -0,0 +1,20 @@
+# Live reload
+## Server live reload
+
+If running the server using `npm run start-server`, the server will watch for changes in `src/public` and trigger a frontend reload if that occurs.
+
+## Electron live reload
+
+Similarly, `npm run start-electron` supports live refresh as well.
+
+However, a core difference is that Electron watches `dist/src/public` instead of `src/public` since Electron runs on its own copy of the files.
+
+To ameliorate that, a separate watch script has been implemented which automatically copies files from `src/public` to `dist/src/public` whenever a change is detected. To run it:
+
+```plain
+npm run
+```
+
+## Technical details
+
+* This mechanism is managed at server level by watching for changes in`services/ws.ts`.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Note types.md b/docs/Developer Guide/Developer Guide/Development and architecture/Note types.md
new file mode 100644
index 000000000..3c712ad35
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Note types.md
@@ -0,0 +1,30 @@
+# Note types
+The note type is defined by the `type` column in notes.
+
+Possible types:
+
+
both options are shown to the user via the “Book Properties” ribbon widget.
Web View
webView
blank
An empty blob.
#webViewSrc pointing to an URL to render.
Code
code
Depends on the language (e.g. text/plain, text/x-markdown, text/x-c++src).
The plain text content.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options.md
new file mode 100644
index 000000000..2cfff155d
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options.md
@@ -0,0 +1,14 @@
+# Options
+## Read an option
+
+Add the import to the service (make sure the relative path is correct):
+
+```javascript
+import options from "../../services/options.js";
+```
+
+Them simply read the option:
+
+```javascript
+this.firstDayOfWeek = options.getInt("firstDayOfWeek");
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Check box option.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Check box option.md
new file mode 100644
index 000000000..9ca4bf51f
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Check box option.md
@@ -0,0 +1,37 @@
+# Check box option
+In the TPL:
+
+```html
+
+
Background effects
+
+
On the desktop application, it's possible to use a semi-transparent background tinted in the colors of the user's wallpaper to add a touch of color.
+
+
+
+
+
+```
+
+In `doRender()`:
+
+```
+doRender() {
+ this.$backgroundEffects = this.$widget.find("input.background-effects");
+
+ this.$backgroundEffects.on("change", () => this.updateCheckboxOption("backgroundEffects", this.$backgroundEffects));
+}
+```
+
+In `optionsLoaded(options)`:
+
+```
+async optionsLoaded(options) {
+
+ this.setCheckboxState(this.$backgroundEffects, options.backgroundEffects);
+
+}
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Creating a new option.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Creating a new option.md
new file mode 100644
index 000000000..8b49a44d7
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Creating a new option.md
@@ -0,0 +1,7 @@
+# Creating a new option
+1. Go to `options_interface.ts` and add the option to `OptionDefinitions`, specifying its intended data type (boolean, string, number). Note that in the end the option will still be stored as a string, but this aids in type safety across the application.
+
+2. To add a new option with a set default, go to `options_init.ts` in the server and add a new entry in the `defaultOptions`.
+
+3. **Make the option adjustable by the client**
+ By default options are not adjustable or visible to the client. To do so, modify `routes/api/options.ts` to add the newly added option to `ALLOWED_OPTIONS`.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Displaying the option in setti.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Displaying the option in setti.md
new file mode 100644
index 000000000..0cc3f414e
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Displaying the option in setti.md
@@ -0,0 +1,36 @@
+# Displaying the option in settings
+Go to `src/public/app/widgets/type_widgets/options` and select a corresponding category, such as `appearance` and edit one of the JS files.
+
+For example, to create a select:
+
+First, modify the template (`TPL`), to add the new widget:
+
+```plain
+
+
+
+
+```
+
+Secondly, create a reference to the new element in `doRender()`:
+
+```plain
+this.$firstDayOfWeek = this.$widget.find(".first-day-of-week-select");
+```
+
+Then in `optionsLoaded` adjust the value to the one set in the database:
+
+```plain
+this.$firstDayOfWeek.val(options.firstDayOfWeek);
+```
+
+To actually update the option, add a listener in `doRender`:
+
+```plain
+this.$firstDayOfWeek.on("change", () => {
+ this.updateOption("firstDayOfWeek", this.$firstDayOfWeek.val());
+});
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Refresh widget with option cha.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Refresh widget with option cha.md
new file mode 100644
index 000000000..1650f399d
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Refresh widget with option cha.md
@@ -0,0 +1,10 @@
+# Refresh widget with option change
+To make a widget react to a change of a given option, simply add the following to the widget:
+
+```javascript
+async entitiesReloadedEvent({loadResults}) {
+ if (loadResults.getOptionNames().includes("firstDayOfWeek")) {
+ // Do something.
+ }
+}
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Options/Trigger UI refresh.md b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Trigger UI refresh.md
new file mode 100644
index 000000000..7d85a175c
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Options/Trigger UI refresh.md
@@ -0,0 +1,12 @@
+# Trigger UI refresh
+Call `utils.reloadFrontendApp`, but make sure to wait for the option to be saved first.
+
+```
+this.$backgroundEffects.on("change", async () => {
+
+ await this.updateCheckboxOption("backgroundEffects", this.$backgroundEffects);
+
+ utils.reloadFrontendApp("background effect change");
+
+});
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Printing.md b/docs/Developer Guide/Developer Guide/Development and architecture/Printing.md
new file mode 100644
index 000000000..29c081bcb
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Printing.md
@@ -0,0 +1,15 @@
+# Printing
+Note printing is handled by `note_detail.js`, in the `printActiveNoteEvent` method.
+
+The application uses the [`print-this`](https://www.npmjs.com/package/print-this) library to isolate `.note-detail-printable:visible` and prepare it for printing.
+
+Some scripts like KaTeX are manually injected in the footer, and the CSS to be used is manually defined. The most important one is `print.css`.
+
+## Syntax highlighting
+
+Syntax highlighting for code blocks is supported as well:
+
+* It works by injecting a Highlight.js stylesheet into the print.
+* The theme used is hard-coded (the _Visual Studio Light theme_, at the time of writing) in order not to have a dark background in print.
+* The Highlight.js library is not needed since the `.note-detail-printable` which is rendered already has the `.hljs` classes added to it in order to achieve the syntax highlighting.
+* The user's choice of whether to enable syntax highlighting is also respected.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Protected entities.md b/docs/Developer Guide/Developer Guide/Development and architecture/Protected entities.md
new file mode 100644
index 000000000..d6042d6fb
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Protected entities.md
@@ -0,0 +1,6 @@
+# Protected entities
+The following entities can be made protected, via their `isProtected` flag:
+
+* attachments
+* notes
+* revisions
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Revisions.md b/docs/Developer Guide/Developer Guide/Development and architecture/Revisions.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Safe mode.md b/docs/Developer Guide/Developer Guide/Development and architecture/Safe mode.md
new file mode 100644
index 000000000..5522f0062
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Safe mode.md
@@ -0,0 +1,11 @@
+# Safe mode
+Safe mode is triggered by setting the `TRILIUM_SAFE_MODE` environment variable to a truthy value, usually `1`.
+
+In each artifact there is a `trilium-safe-mode.sh` (or `.bat`) script to enable it.
+
+What it does:
+
+* Disables `customWidget` launcher types in `app/widgets/containers/launcher.js`.
+* Disables the running of `mobileStartup` or `frontendStartup` scripts.
+* Displays the root note instead of the previously saved session.
+* Disables the running of `backendStartup`, `hourly`, `daily` scripts and checks for the hidden subtree.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Special notes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Special notes.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Synchronisation/Content hashing.md b/docs/Developer Guide/Developer Guide/Development and architecture/Synchronisation/Content hashing.md
new file mode 100644
index 000000000..4b628a5e3
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Synchronisation/Content hashing.md
@@ -0,0 +1,17 @@
+# Content hashing
+Entity hashing is done in `content_hash#getEntityHashes`.
+
+* It works by looking at the `entity_changes` table and going through each of the entity names/types:
+ * `blobs`
+ * `attributes`
+ * `revisions`
+ * `attachments`
+ * `notes`
+ * `branches`
+ * `etapi_tokens`
+ * `options`
+* For some reason `note_reordering` entities are ignored specifically.
+* All the rows in `entity_changes` are then ordered alphabetically, based on their `entityId`.
+* Every entity row is then grouped by `entityName` and then by sector. The sector is defined as the first character of the `id`.
+* The hash is altered to add the `isErased` value as well since the hash of deleted entries is not updated.
+* For each sector, the hash is calculated using `utils.hash`, using SHA1 encoded as Base64.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Syntax highlighting.md b/docs/Developer Guide/Developer Guide/Development and architecture/Syntax highlighting.md
new file mode 100644
index 000000000..b04131299
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Syntax highlighting.md
@@ -0,0 +1,84 @@
+# Syntax highlighting
+## Defining the MIME type
+
+The first step to supporting a new language for either code blocks or code notes is to define the MIME type. Go to `mime_types.ts` and add a corresponding entry:
+
+```
+{ title: "Batch file (DOS)", mime: "application/x-bat" }
+```
+
+## Syntax highlighting for Highlight.js
+
+### Built-in languages
+
+Highlight.js supports a lot of languages out of the box, for some of them we just need to enable them by specifying one of the language aliases in the `highlightJs` field in the `mime_types` definition:
+
+```
+{ title: "Batch file (DOS)", mime: "application/x-bat", highlightJs: "dos" }
+```
+
+For the full list of supported languages, see [Supported Languages — highlight.js 11.9.0 documentation](https://highlightjs.readthedocs.io/en/latest/supported-languages.html). Look for the “Package” column to see if another library needs to be installed to support it.
+
+Note that we are using the CDN build which may or may not have all the languages listed as predefined in the “Supported languages” list. To view the real list of supported files, see the `node_modules/@highlightjs/cdn-assets/languages` directory.
+
+### Custom language
+
+When the source code for a language is available, one way is to simply copy it to `libraries/highlightjs/{id}.js` where `id` matches the name for `highlightJs`.
+
+Make sure in the script that the language is registered:
+
+```
+hljs.registerLanguage('terraform', hljsDefineTerraform);
+```
+
+Then in `mime_types.ts` make sure to set `highlightJsSource` to `libraries` to load it.
+
+```
+{ title: "Terraform (HCL)", mime: "text/x-hcl", highlightJs: "terraform", highlightJsSource: "libraries", codeMirrorSource: "libraries/codemirror/hcl.js" },
+```
+
+## Syntax highlighting for CodeMirror
+
+### Custom language
+
+Generally new languages are not added in the base installation and need to be separately registered. For CodeMirror 5 it seems that (at least for simple languages), the modes are distributed as _simple modes_ and can generally be copy-pasted in `libraries/codemirror`. An example would be:
+
+```
+(() => {
+
+ CodeMirror.defineSimpleMode("batch", {
+
+ start: [],
+
+ echo: []
+
+ });
+
+
+
+ CodeMirror.defineMIME("application/x-bat", "batch");
+
+ CodeMirror.modeInfo.push({
+
+ ext: [ "bat", "cmd" ],
+
+ mime: "application/x-bat",
+
+ mode: "batch",
+
+ name: "Batch file"
+
+ });
+
+})();
+
+
+```
+
+Note that changing `modeInfo` is crucial, otherwise syntax highlighting will not work. The `mime` field is mandatory, even if `mimes` is used instead.
+
+Afterwards, register it in `mime_types.ts`, specifying `codeMirrorSource` to point to the newly created file:
+
+```
+{ title: "Batch file (DOS)", mime: "application/x-bat", highlightJs: "dos", codeMirrorSource: "libraries/codemirror/batch.js" }
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Development and architecture/Themes.md b/docs/Developer Guide/Developer Guide/Development and architecture/Themes.md
new file mode 100644
index 000000000..5d89c2c2e
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Development and architecture/Themes.md
@@ -0,0 +1,17 @@
+# Themes
+## Server-side
+
+* There are three themes embedded in the application:
+ * `light`, located in `src\public\stylesheets\theme-light.css`
+ * `dark`, located in `src\public\stylesheets\theme-dark.css`
+ * `next`, located in `src\public\stylesheets\theme-next.css`.
+* The default theme is set only once, when the database is created and is managed by `options_init#initNotSyncedOptions`.
+ * In the original implementation: On Electron, the choice between `light` and `dark` is done based on the OS preference. Otherwise, the theme is always `dark`.
+ * Now, we always choose `next` as the default theme.
+* The theme is served via `src\routes\index.ts`, in the `getThemeCssUrl` method.
+
+## Client-side
+
+* The predefined themes are hard-coded in the client in `src\public\app\widgets\type_widgets\options\appearance\theme.js`.
+* The user-defined themes are obtained via a call to the server: `options/user-themes`.
+* The theme retrieval is done via a request.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md
new file mode 100644
index 000000000..009c99c45
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Documentation.md
@@ -0,0 +1,23 @@
+# Documentation
+## Hard-coded links
+
+Hard-coded links are present throughout the application, either in dialogs or in the source code as comments.
+
+You can identify these links by searching for:
+
+```plain
+https://triliumnext.github.io/Docs/Wiki/
+```
+
+## Help buttons
+
+There is a pattern of “?” buttons throughout the application which make use of the `data-help-page` attribute. Whenever these buttons are pressed, the user is redirected to the corresponding wiki page by prepending the wiki root URL to the `data-help-page` attribute.
+
+Since the current wiki has a different structure than the original, for example to link to [https://github.com/TriliumNext/Docs/blob/main/Wiki/tree-concepts.md](https://github.com/TriliumNext/Docs/blob/main/Wiki/tree-concepts.md) the `data-help-page` attribute must be set to `tree-concepts.md`.
+
+For links to headings, simply add the heading after the `.md`: `tree-concepts.md#prefix`
+
+You can identify those by looking for:
+
+* `.attr("data-help-page"`
+* `data-help-page="`
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Documentation/User-facing documentation.md b/docs/Developer Guide/Developer Guide/Documentation/User-facing documentation.md
new file mode 100644
index 000000000..e8bdd64e3
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Documentation/User-facing documentation.md
@@ -0,0 +1,14 @@
+# User-facing documentation
+The user-facing documentation is available on a dedicated repository inside the organization: [https://github.com/TriliumNext/Docs](https://github.com/TriliumNext/Docs)
+
+It is currently organized as a flat tree of MarkDown notes.
+
+The documentation started as an import of the existing upstream documentation in [https://github.com/zadam/trilium/wiki](https://github.com/zadam/trilium/wiki).
+
+The public documentation is available at [https://triliumnext.github.io/Docs/Wiki](https://triliumnext.github.io/Docs/Wiki).
+
+The repository is here: [https://github.com/TriliumNext/Docs](https://github.com/TriliumNext/Docs)
+
+The documentation is stored as Markdown files and is meant to be imported into Trilium Notes and then exported back in, as per the README. However, it's also possible to modify the Markdown files manually and push the changes.
+
+The documentation is deployed automatically to GitHub Pages on every push on the `main` branch.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Installation/Download latest nightly and in.md b/docs/Developer Guide/Developer Guide/Installation/Download latest nightly and in.md
new file mode 100644
index 000000000..4d6b313ac
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Installation/Download latest nightly and in.md
@@ -0,0 +1,12 @@
+# Download latest nightly and install it
+On Ubuntu:
+
+```
+#!/usr/bin/env bash
+
+name=TriliumNextNotes-linux-x64-nightly.deb
+rm -f $name*
+wget https://github.com/TriliumNext/Notes/releases/download/nightly/$name
+sudo apt-get install ./$name
+rm $name
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Notes for old development/Build deliveries locally.clone.md b/docs/Developer Guide/Developer Guide/Notes for old development/Build deliveries locally.clone.md
new file mode 100644
index 000000000..72ed24768
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Notes for old development/Build deliveries locally.clone.md
@@ -0,0 +1,2 @@
+# Build deliveries locally
+This is a clone of a note. Go to its [primary location](../Building%20and%20deployment/Build%20deliveries%20locally.md).
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Notes for old development/Releasing a version.clone.md b/docs/Developer Guide/Developer Guide/Notes for old development/Releasing a version.clone.md
new file mode 100644
index 000000000..148f6c404
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Notes for old development/Releasing a version.clone.md
@@ -0,0 +1,2 @@
+# Releasing a version
+This is a clone of a note. Go to its [primary location](../Building%20and%20deployment/Releasing%20a%20version.md).
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Notes for old development/Running a development build.clone.md b/docs/Developer Guide/Developer Guide/Notes for old development/Running a development build.clone.md
new file mode 100644
index 000000000..ca867a1d7
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Notes for old development/Running a development build.clone.md
@@ -0,0 +1,2 @@
+# Running a development build
+This is a clone of a note. Go to its [primary location](../Building%20and%20deployment/Running%20a%20development%20build.md).
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies.md b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies.md
new file mode 100644
index 000000000..d6a62bc44
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies.md
@@ -0,0 +1,2 @@
+# Updating dependencies
+
Protected by typings, should catch any potential changes in API.
Yes
async-mutex
Sync
axios
Can't be directly tested, as it's exposed only via the backend script API.
sax
EverNote imports
ws
debounce
Check any action is reported from server to client (e.g. delete a note).
ejs
Onboarding / first setup
dayjs
Day notes
semver
Application should start.
https-proxy-agent
???
sax
EverNote import
ini
Affects config, generally if the application starts then it should be OK.
jsplumb
RELATION_MAP
Relation map note type
jquery.mark.es6
MARKJS
In search, when highlighting the text that matched.
In search in HTML, which might not actually be used since it seems to have been replaced by CKEditor's own find & replace dialog.
knockout.js
Used in rendering the login and main layout of the application.
normalize.min.css
Used in shared notes.
wheel-zoom.min.js
WHEEL_ZOOM
When opening a image that is in attachment.
When opening a stand-alone image note.
When zooming in a mermaid chart.
fancytree
The note tree should be fully functional.
bootstrap
Check mostly the on-boarding pages, when there is no database.
electron-debug
Run electron using npm run start-electron and check that the debug hotkeys are still working (Ctrl+Shift+I on Windows/Linux, Cmd+Alt+I for dev tools, Cmd/Ctrl+R for reload).
electron-dl
eslint
marked
Importing a markdown note.
Yes
force-graph
Note map
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Node.js, Electron and `better-.md b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Node.js, Electron and `better-.md
new file mode 100644
index 000000000..2b1161238
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Node.js, Electron and `better-.md
@@ -0,0 +1,6 @@
+# Node.js, Electron and `better-sqlite3`
+`better-sqlite3` requires a native module in order to work. In order to ease the installation process, prebuilt binaries are provided by the library developers.
+
+Trilium Next started with version [8.4.0](https://github.com/WiseLibs/better-sqlite3/releases/tag/v8.4.0) for `better-sqlite3`
+
+
better-sqlite3 version
SQLite version
Node.js prebuilds
Electron.js prebuilds
8.4.0
<3.43.0
v20
???
8.5.0
v20
v25
8.5.1
v26
8.5.2
v20 (macOS + arm64)
8.6.0
3.43.0
8.7.0
3.43.1
9.0.0
3.43.2
v27
9.1.0
3.44.0
9.1.1
macOS + Alpine
9.2.0
3.44.2
9.2.1 / 9.2.2
v28
9.3.0
3.45.0
9.4.0
3.45.1
9.4.1
Windows arm, arm64
9.4.2
<v29
9.4.3
<v29
9.4.4
v29
9.4.5
Better prebuilds
9.5.0
3.45.2
9.6.0
3.45.3
v30
10.0.0
v22
10.1.0
3.46.0
11.0.0
>21
11.1.0 (prerelease)
v31
11.1.1
11.1.2
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Testing compatibility.md b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Testing compatibility.md
new file mode 100644
index 000000000..c97956f1f
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/Testing compatibility.md
@@ -0,0 +1,2 @@
+# Testing compatibility
+
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/bettersqlite binaries.md b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/bettersqlite binaries.md
new file mode 100644
index 000000000..b7aa21ed0
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Project maintenance/Updating dependencies/bettersqlite binaries.md
@@ -0,0 +1,41 @@
+# bettersqlite binaries
+### The native node bindings
+
+`better-sqlite3` has native Node bindings. With updates of `better-sqlite3`, but also of Electron and Node.js versions, these bindings need to be updated.
+
+Note that Electron and Node.js versions need different versions of these bindings, since Electron usually packs a different version of Node.js.
+
+During development, `npm install` tries to build or reuse prebuilt natives for the current Node.js version. This makes `npm run start-server` work out of the box. Trying to run `npm run start-electron` with these versions generally causes an error such as this:
+
+```plain
+Uncaught Exception:
+Error: The module '/Users/elian/Projects/Notes/node_modules/better-sqlite3/build/Release/better_sqlite3.node'
+was compiled against a different Node.js version using
+NODE_MODULE_VERSION 108. This version of Node.js requires
+NODE_MODULE_VERSION 116. Please try re-compiling or re-installing
+the module (for instance, using `npm rebuild` or `npm install`).
+```
+
+### How the natives are handled
+
+Locally, this can be fixed by rebuilding the binaries, which is what `npm run switch-electron` does, which uses `electron-rebuild` under the hood.
+
+When the deliveries are built (see Build deliveries locally), it is not feasible to rebuild the dependencies since we are building for multiple platforms. Luckily, `better-sqlite3` provides these prebuilt binaries from us, available as artifacts on [their GitHub releases page](https://github.com/WiseLibs/better-sqlite3/releases/).
+
+The build script manages the natives for `better-sqlite3` by keeping a copy of the `.node` file for every platform in `bin/better-sqlite3`.
+
+Whenever the version of `better-sqlite3` changes, the `.node` files must also be renewed based on their releases page. To simplify this process, a script was created in `bin/better-sqlite3/update.sh`.
+
+## How to update the natives
+
+The update script needs to know the version of Electron or Node.js for which to download the prebuilt binaries.
+
+If you get errors during download, check on the [releases page](https://github.com/WiseLibs/better-sqlite3/releases/) to ensure that this particular combination of Electron/Node actually exists for the given release.
+
+To determine the `NODE_MODULE_VERSION` that is required, look for `This version of Node.js requires`
+`NODE_MODULE_VERSION` in the error when starting Trilium via:
+
+* `npm run start-electron` (or run any Electron [delivery](../../Building%20and%20deployment/Build%20deliveries%20locally.md)), case in which the `ELECTRON_VERSION` variable needs to be changed.
+* `npm run start-server` (or run the Linux server delivery), case in which the `NODE_VERSION` variable needs to be changed.
+
+Check which files got changed after running the update script and for each platform that got changed, test it locally via Build deliveries locally or via the CI.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Scripting/Server-side imports.md b/docs/Developer Guide/Developer Guide/Scripting/Server-side imports.md
new file mode 100644
index 000000000..af7ccc11a
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Scripting/Server-side imports.md
@@ -0,0 +1,16 @@
+# Server-side imports
+Trilium Notes allowed the use of Common.js module imports inside backend scripts, such as:
+
+```plain
+const isBetween = require('dayjs/plugin/isBetween')
+api.dayjs.extend(isBetween)
+```
+
+For TriliumNext, the backend has been switched to use ESM which has a slightly more complicated syntax. Instead of `require` we now have `import` but which is asynchronous so it will require an `await`:
+
+```plain
+const isBetween = (await import("dayjs/plugin/isBetween")).default;
+api.dayjs.extend(isBetween);
+```
+
+Note that `.default` is also usually needed to obtain the same behaviour as a CJS import. When in doubt, use `console.log` to see the output of the value returned by `await import`.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Scripting/Widgets.md b/docs/Developer Guide/Developer Guide/Scripting/Widgets.md
new file mode 100644
index 000000000..0c26f2518
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Scripting/Widgets.md
@@ -0,0 +1,33 @@
+# Widgets
+To create a basic widget, simply create a code note with type “JS frontend”. Add the `#widget` label in order for it to be loaded at startup.
+
+```plain
+const template = ``;
+
+class MyWidget extends api.BasicWidget {
+ get position() { return 1; }
+ get parentWidget() { return "left-pane" }
+
+ doRender() {
+ this.$widget = $(template);
+ return this.$widget;
+ }
+}
+
+module.exports = new MyWidget();
+```
+
+`parentWidget()` can be given the following values:
+
+* `left-pane` - This renders the widget on the left side of the screen where the note tree lives.
+* `center-pane` - This renders the widget in the center of the layout in the same location that notes and splits appear.
+* `note-detail-pane` - This renders the widget _with_ the note in the center pane. This means it can appear multiple times with splits.
+* `right-pane` - This renders the widget to the right of any opened notes.
+
+* * *
+
+Reference:
+
+* [https://trilium.rocks/X7pxYpiu0lgU](https://trilium.rocks/X7pxYpiu0lgU)
+* [https://github.com/zadam/trilium/wiki/Widget-Basics](https://github.com/zadam/trilium/wiki/Widget-Basics)
+* [https://github.com/zadam/trilium/wiki/Frontend-Basics](https://github.com/zadam/trilium/wiki/Frontend-Basics)
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Scripting/Widgets/CSS.md b/docs/Developer Guide/Developer Guide/Scripting/Widgets/CSS.md
new file mode 100644
index 000000000..52e7298f4
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Scripting/Widgets/CSS.md
@@ -0,0 +1,15 @@
+# CSS
+In `doRender()`:
+
+```plain
+this.cssBlock(`#my-widget {
+ position: absolute;
+ bottom: 40px;
+ left: 60px;
+ z-index: 1;
+}`)
+```
+
+* * *
+
+Reference: [https://trilium.rocks/X7pxYpiu0lgU](https://trilium.rocks/X7pxYpiu0lgU)
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Scripting/Widgets/Right pane widget.md b/docs/Developer Guide/Developer Guide/Scripting/Widgets/Right pane widget.md
new file mode 100644
index 000000000..4299b47f8
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Scripting/Widgets/Right pane widget.md
@@ -0,0 +1,32 @@
+# Right pane widget
+* `doRender` must not be overridden, instead `doRenderBody()` has to be overridden.
+* `parentWidget()` must be set to `“rightPane”`.
+* `widgetTitle()` getter can optionally be overriden, otherwise the widget will be displayed as “Untitled widget”.
+
+```plain
+const template = `
Hi
`;
+
+class ToDoListWidget extends api.RightPanelWidget {
+
+ get widgetTitle() {
+ return "Title goes here";
+ }
+
+ get parentWidget() { return "right-pane" }
+
+ doRenderBody() {
+ this.$body.empty().append($(template));
+ }
+
+ async refreshWithNote(note) {
+ this.toggleInt(false);
+ this.triggerCommand("reEvaluateRightPaneVisibility");
+ this.toggleInt(true);
+ this.triggerCommand("reEvaluateRightPaneVisibility");
+ }
+}
+
+module.exports = new ToDoListWidget();
+```
+
+The implementation is in `src/public/app/widgets/right_panel_widget.js`.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Building the editor.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Building the editor.md
new file mode 100644
index 000000000..700095cb5
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Building the editor.md
@@ -0,0 +1,21 @@
+# Building the editor
+First, make sure Environment setup is set up.
+
+## Trigger the build
+
+```plain
+cd packages/ckeditor5-build-trilium
+yarn build
+```
+
+This will trigger a change in the `build` directory.
+
+## Copy the build artifact to the main repo
+
+Go to `packages/ckeditor5-build-balloon-trilium/build` and copy `ckeditor.js` and `ckeditor.js.map` to `libraries/ckeditor` in the `Notes` repository.
+
+An example shell command to copy it:
+
+```plain
+cp build/ckeditor.* ~/Projects/TriliumNext/Notes/libraries/ckeditor/
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Differences from upstream.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Differences from upstream.md
new file mode 100644
index 000000000..1894d96d9
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Differences from upstream.md
@@ -0,0 +1,22 @@
+# Differences from upstream
+* Embeds [`~~isaul32/ckeditor5-math~~`](https://github.com/isaul32/ckeditor5-math) ckeditor5-math, which is a third-party plugin for adding math support. CKEditor itself also has a [math plugin](https://ckeditor.com/docs/ckeditor5/latest/features/math-equations.html) with MathType and ChemType but it's premium-only.
+* Zadam left a TODO in `findandreplaceUI`: `// FIXME: keyboard shortcut doesn't work:` [`https://github.com/ckeditor/ckeditor5/issues/10645`](https://github.com/ckeditor/ckeditor5/issues/10645)
+* `packages\ckeditor5-build-balloon-block\src\mention_customization.js` introduces note insertion via `@` character.
+
+
Allows triggering the autocomplete for labels and attributes in the attribute editor.
init()
55a63a1934efb9a520fcc2d69f3ce55ac22aca39
Allows dismissing @-mention permanently after pressing ESC, otherwise it would automatically show up as soon as a space was entered.
+
+## Checking the old repo
+
+Use the following command to identify commits from Zadam:
+
+```
+git log --oneline --author="adam" --all
+```
+
+It's best to run the command from zadam's fork of `trilium-ckeditor5` instead of the TriliumNext once since it might not contain all the unmerged branches.
+
+To show a filtered diff of a commit:
+
+```
+git show d42e772783 -- ':!*yarn.lock' ':!*packages/ckeditor5-build-balloon-block/build/*' ':!*package.json'
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Environment setup.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Environment setup.md
new file mode 100644
index 000000000..b262761a8
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Environment setup.md
@@ -0,0 +1,26 @@
+# Environment setup
+## Clone the repository
+
+To set up the repository:
+
+```plain
+git clone https://github.com/TriliumNext/trilium-ckeditor5.git
+```
+
+## Install dependencies
+
+First, install root dependencies:
+
+```
+cd trilium-ckeditor5
+yarn install
+```
+
+Secondly, install the Trilium build dependencies:
+
+```
+cd packages/ckeditor5-build-trilium
+yarn install
+```
+
+To trigger the build, see Building the editor.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Updating to a newer version of.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Updating to a newer version of.md
new file mode 100644
index 000000000..ac2ea361d
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Updating to a newer version of.md
@@ -0,0 +1,65 @@
+# Updating to a newer version of CKEditor
+## Before updating
+
+Make sure that all the plugins are compatible with this version: Versions and external plugins. If not, they will need to be updated to the same version as the one you are updating, by altering their `package.json`.
+
+If the plugin is external to the Trilium organisation, it needs to be forked first.
+
+## Environment setup
+
+The first step is to add the CKEditor source as a remote. This only needs to be done once.
+
+```
+git remote add upstream ssh://git@github.com/ckeditor/ckeditor5.git
+git fetch upstream
+```
+
+## Update steps
+
+Due to how the repository is structured, updates to the CKEditor are a bit difficult.
+
+1. `git fetch upstream`
+2. Pick a version and merge with it: `git merge -X theirs v99.2.0`
+3. When there are complicated conflicts, sometimes it's easier to take everything from the target version instead, for a given path: `git checkout v99.2.0 -- "packages/ckeditor5-list/**"`.
+4. Go in `packages/ckeditor5-build-trilium/package.json` and run `node sync-version.js` to update the `package.json` with the new versions. Review and commit the change.
+5. Follow again the dependency setup in Environment setup, as they have changed.
+6. [Run the build](Building%20the%20editor.md) and check that it works.
+
+## Final steps
+
+1. Start the TriliumNext server
+2. If updated to a newer version of CKEditor, check type `CKEDITOR_VERSION` in the browser/Electron console to ensure that the correct version is used.
+3. Do a basic sanity check as well.
+4. Commit and push the change on both sides (in the `trilium-ckeditor5` repo and in the `Notes` repo).
+
+## Troubleshooting client side errors
+
+These errors might show up when testing the Trilium app:
+
+```
+ReferenceError: CKEditor is not defined
+```
+
+Usually this is a side effect of another error, check the logs carefully to see if there is any other related error (perhaps a `CKEditorError`).
+
+* * *
+
+```
+Uncaught error: Message: CKEditorError: ckeditor-duplicated-modules
+```
+
+Most likely cause is one of the external plugins is incompatible with this version.
+
+For example, to disable the Math plugin, go to `packages/ckeditor5-build-trilium/src/config.ts` and modify:
+
+```diff
+-import Math from '@triliumnext/ckeditor5-math/src/math';
+-import AutoformatMath from '@triliumnext/ckeditor5-math/src/autoformatmath';
+
+export const COMMON_PLUGINS = [
+- Math,
+- AutoformatMath,
+]
+```
+
+In this case, make sure to align the version of all the external plugins with the one you are updating to, usually by forking the external plugin and updating its versions.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Versions and external plugins.md b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Versions and external plugins.md
new file mode 100644
index 000000000..76da10d6e
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Sub-projects/CKEditor/Versions and external plugins.md
@@ -0,0 +1,4 @@
+# Versions and external plugins
+## External plugins
+
+
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math.md b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math.md
new file mode 100644
index 000000000..598042ceb
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math.md
@@ -0,0 +1,30 @@
+# ckeditor5-math
+ckeditor5-math in action.
+
+A fork of [isaul32/ckeditor5-math](https://github.com/isaul32/ckeditor5-math), which is the CKEditor5 plugin which adds the math functionality. The fork was created to handle #297: Insert Math appears to be broken.
+
+## Development environment
+
+* Tested on Node.js 20.
+* The package manager is yarn 1 (v1.22.22 is known to be working fine for it at the time of writing).
+* Committing is protected by `husky` which runs `eslint` to ensure that the code is clean.
+
+Important commands:
+
+* To check if the code has any formatting issues: `yarn lint`
+* To start a live preview: `yarn start`
+* To run the tests: `yarn test`
+ * Note that this requires Chromium, on NixOS this can be achieved by running a `nix-shell -p chromium`, and running `CHROME_BIN=$(which chromium) yarn test` inside it.
+
+## 📦 Packages
+
+The built artifact of the plugin is released by the CI and available on the [GitHub NPM registry](https://github.com/TriliumNext/ckeditor5-math/pkgs/npm/ckeditor5-math).
+
+Note that due to limitations on GitHub's registry, it is not possible to install this package without setting up a personal access token (even though the package itself is public). See [missing note] for more information.
+
+## ⬆️ Integrating with CKEditor
+
+1. Release a new version: Release management & continuous integration
+2. In `trilium-ckeditor5`, go to `packages/ckeditor5-build-trilium/package.json` in the CKEditor repository and change the dependency of `@triliumnext/ckeditor5-math` to the newly released version.
+3. Run `yarn install`.
+4. Proceed with Building the editor to integrate everything into TriliumNext and then commit the change.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Release management & continuou.md b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Release management & continuou.md
new file mode 100644
index 000000000..e0603cf84
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Release management & continuou.md
@@ -0,0 +1,16 @@
+# Release management & continuous integration
+To automate the release process, a GitHub workflow has been added which builds the package and releases it over to GitHub NPM registry.
+
+The workflow publishes a release whenever a tag with the correct format is pushed.
+
+The steps are as follows:
+
+1. Ensure that the source code is clean and ready for a release.
+2. Go to `package.json` and bump the `version` field.
+3. Commit the changes.
+4. Tag the commit with `v1.2.3`, with the correct version number.
+5. Push the changes.
+
+Then follow the CI and it should indicate success. Afterwards, check the [package](https://github.com/TriliumNext/ckeditor5-math/pkgs/npm/ckeditor5-math)section to ensure that the package is in the “Recent Versions” section.
+
+If the changes could benefit upstream, consider opening a pull request with the changes there as well.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Updating with upstream.md b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Updating with upstream.md
new file mode 100644
index 000000000..ff322a427
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math/Updating with upstream.md
@@ -0,0 +1,21 @@
+# Updating with upstream
+If there was a change in the upstream repository ([isaul32/ckeditor5-math](https://github.com/isaul32/ckeditor5-math)), it can be integrated as follows:
+
+1. Add the upstream as remote (`git remote add upstream ssh://git@github.com/isaul32/ckeditor5-math.git`).
+2. Fetch the changes: `git fetch upstream`
+3. Merge with a tag: `git merge v43.1.2`
+4. Solve the conflict in `package.json` by:
+ 1. Taking the same version as the upcoming one and appending `-hotfix1`.
+ 2. Keeping the `@triliumnext/ckeditor5-math` name.
+5. Install dependencies: `yarn install`
+6. Check that the build works via `yarn prepublishOnly`.
+7. Commit the changes, push them.
+8. Release a version with Release management & continuous integration.
+
+## CI job not triggered after pushing all the upstream tags
+
+If the CI job was not triggered, you might have accidentally pushed a lot of tags using `git push --tags`. Manually delete the tag and push it again:
+
+```diff
+git push -d origin v43.1.2-hotfix1 && git push --tags
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math_image.png b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math_image.png
new file mode 100644
index 000000000..51184b130
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Sub-projects/ckeditor5-math_image.png differ
diff --git a/docs/Developer Guide/Developer Guide/Testing.md b/docs/Developer Guide/Developer Guide/Testing.md
new file mode 100644
index 000000000..6963f0207
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Testing.md
@@ -0,0 +1,33 @@
+# Testing
+## Unit testing and integration testing
+
+Using `vitest`, there are some unit and integration tests done for both the client and the server.
+
+These tests can be found by looking for the corresponding `.spec.ts` in the same directory as the source file.
+
+
To run the server-side tests:
npm run server:test
To view the code coverage for the server:
npm run server:coverage
Afterwards, a friendly HTML report can be found in /coverage/index.html.
To run the client-side tests:
npm run client:test
To view the code coverage for the client:
npm run client:coverage
Afterwards, a friendly HTML report can be found in /src/public/app/coverage/index.html.
+
+To run both client and server-side tests:
+
+```
+npm run test
+```
+
+Note that some integration tests rely on an in-memory database in order to function.
+
+### REST API testing for the server
+
+Some original work was done by Zadam in `/test-etapi`, using `.http` files.
+
+New effort using `vitest` and `supertest` to initialize the Express server and run assertions without having to make actual requests to the server.
+
+An important aspect is that we have access to the Express `app` which allows for interesting assertions such as checking the state of the server, registering debug middleware and so on.
+
+One example is `src/share/routes.spec.ts`.
+
+These integration tests are run alongside unit tests.
+
+## End-to-end testing
+
+* This tests both the client and the server, by running the server and then using Playwright to query the state of the page.
+* These can be found in `/e2e`.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/1_Setting up authentication_.png b/docs/Developer Guide/Developer Guide/Testing/Integration testing/1_Setting up authentication_.png
new file mode 100644
index 000000000..020472c6d
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Testing/Integration testing/1_Setting up authentication_.png differ
diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/Running tests.md b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Running tests.md
new file mode 100644
index 000000000..aab19e94e
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Running tests.md
@@ -0,0 +1,38 @@
+# Running tests
+## First-time run
+
+Before starting Playwright, it has to be installed locally via:
+
+```plain
+npx playwright install
+```
+
+## Starting the integration test server
+
+There are two types of integration test servers:
+
+* `npm run integration-mem-db` will run a server with dev mode disabled.
+ * This is usually what the end user will see when accessing a server instance.
+ * It will not test the Electron/desktop side of the application.
+ * Changes to the public scripts will not take effect until running `npm run webpack`.
+* `npm run integration-mem-db-dev` will run a server with dev mode enabled.
+ * This is usually what a dev sees when running `npm run start-server`.
+ * The difference with the production one is that the assets are loaded directly from files and as such it does not require `npm run webpack` to see changes.
+
+Either options will open up a server on [localhost:8082](http://localhost:8082) that can be accessed either manually via the browser or via Playwright.
+
+When asked for a password, the password is `demo1234`.
+
+## Starting the interactive test runner
+
+After starting the integration test server, to run the Playwright UI, run in the terminal:
+
+```plain
+npx playwright test --ui
+```
+
+It is also possible to run the interactive code generator instead:
+
+```plain
+npx playwright codegen
+```
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication.md b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication.md
new file mode 100644
index 000000000..fa140503a
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication.md
@@ -0,0 +1,12 @@
+# Setting up authentication
+There is a setup test that stores the authentication token so that it can be reused throughout all the tests.
+
+If tests fail due to being stuck on login, then it must be run.
+
+To run it manually press “all” near the “Status:” text on top-left of the window
+
+
+
+Then check “setup” and look for `auth.setup.ts` and press its corresponding Run button:
+
+
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication_.png b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication_.png
new file mode 100644
index 000000000..4b260dfbc
Binary files /dev/null and b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Setting up authentication_.png differ
diff --git a/docs/Developer Guide/Developer Guide/Testing/Integration testing/Test database.md b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Test database.md
new file mode 100644
index 000000000..ac7df0b4c
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Testing/Integration testing/Test database.md
@@ -0,0 +1,34 @@
+# Test database
+The integration tests do not use the same database as `npm run start-server`. Instead, the database is located `integration-tests/db/document.db`.
+
+## In-memory database
+
+Even though we are running our own database, there is still the problem of one test affecting the content for the others or accidentally removing important test notes.
+
+To avoid this, the integration test server (run via `integration-mem-db`) loads the database from `integration-test` into memory and operates from there. That means that any changes done on the integration test server (port 8082) will not be persisted between restarts of the server.
+
+Another benefit of having the database in memory for tests is that they can run in parallel without the risk on interfering with each other.
+
+## How to make changes to the database
+
+As mentioned previously, the database itself can be edited manually in order to add content that is relevant to the tests.
+
+In order to do so, run a separate integration test server with:
+
+```plain
+npm run integration-edit-db
+```
+
+This will open up a server on port 8081 which can be used to alter the integration test DB. After finishing the desired changes, it's ideal to close the server to prevent any interferences with further running of tests.
+
+## The database is tracked by Git
+
+This is intentional, meaning that any change to the database will mark the file as changed in Git as well. Some tests require a specific note and it would be too wasteful to have to recreate it via Playwright each time. Instead the content is added manually and then the tests operate directly on the said notes.
+
+In order to make the database easier to track, SQLite WAL was disabled but only for the integration database. This means that only the `.db` file is present and needs to be committed.
+
+## Cleaning up the database
+
+It's recommended to clean up any deleted notes to avoid unnecessary changes being committed. To do so go to Recent Changes in the launcher and select “Erase deleted notes now”.
+
+It's also a good idea to go to Options → Advanced → Vacuum database to clean up it.
\ No newline at end of file
diff --git a/docs/Developer Guide/Developer Guide/Troubleshooting/Error [TransformError] The pac.md b/docs/Developer Guide/Developer Guide/Troubleshooting/Error [TransformError] The pac.md
new file mode 100644
index 000000000..82158440d
--- /dev/null
+++ b/docs/Developer Guide/Developer Guide/Troubleshooting/Error [TransformError] The pac.md
@@ -0,0 +1,40 @@
+# Error [TransformError]: The package "@esbuild/linux-x64" could not be found, and is needed by esbuild.
+Full log:
+
+```
+> trilium@0.91.1-beta start-server
+> cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon src/main.ts
+
+[nodemon] 3.1.9
+[nodemon] to restart at any time, enter `rs`
+[nodemon] watching path(s): src/**/* translations/**/*
+[nodemon] watching extensions: ts,js,json
+[nodemon] starting `tsx src/main.ts`
+
+node:internal/process/promises:391
+ triggerUncaughtException(err, true /* fromPromise */);
+ ^
+Error [TransformError]: The package "@esbuild/linux-x64" could not be found, and is needed by esbuild.
+
+If you are installing esbuild with npm, make sure that you don't specify the
+"--no-optional" or "--omit=optional" flags. The "optionalDependencies" feature
+of "package.json" is used by esbuild to install the correct binary executable
+for your current platform.
+ at generateBinPath (/home/elian/Projects/TriliumNext/Notes/node_modules/esbuild/lib/main.js:1752:15)
+ at esbuildCommandAndArgs (/home/elian/Projects/TriliumNext/Notes/node_modules/esbuild/lib/main.js:1822:33)
+ at ensureServiceIsRunning (/home/elian/Projects/TriliumNext/Notes/node_modules/esbuild/lib/main.js:1979:25)
+ at transform (/home/elian/Projects/TriliumNext/Notes/node_modules/esbuild/lib/main.js:1880:37)
+ at file:///home/elian/Projects/TriliumNext/Notes/node_modules/tsx/dist/index-DlKgSVBb.mjs:16:2755
+ at applyTransformers (file:///home/elian/Projects/TriliumNext/Notes/node_modules/tsx/dist/index-DlKgSVBb.mjs:16:1266)
+ at transform (file:///home/elian/Projects/TriliumNext/Notes/node_modules/tsx/dist/index-DlKgSVBb.mjs:16:2702)
+ at load (file:///home/elian/Projects/TriliumNext/Notes/node_modules/tsx/dist/esm/index.mjs?1734213798404:2:2245)
+ at async nextLoad (node:internal/modules/esm/hooks:868:22)
+ at async Hooks.load (node:internal/modules/esm/hooks:451:20)
+```
+
+The solution is to remove `node_modules` and reinstall all dependencies:
+
+```
+rm -r node_modules
+npm install
+```
\ No newline at end of file
diff --git a/electron-docs-main.ts b/electron-docs-main.ts
index 625f5dff9..916886ed3 100644
--- a/electron-docs-main.ts
+++ b/electron-docs-main.ts
@@ -14,11 +14,38 @@ import TaskContext from "./src/services/task_context.js";
import { deferred } from "./src/services/utils.js";
import { parseNoteMetaFile } from "./src/services/in_app_help.js";
-const NOTE_ID_USER_GUIDE = "pOsGYCXsbNQG";
-const NOTE_ID_RELEASE_NOTES = "hD3V4hiu2VW4";
-const markdownPath = path.join("docs", "User Guide");
-const releaseNotesPath = path.join("docs", "Release Notes");
-const htmlPath = path.join("src", "public", "app", "doc_notes", "en", "User Guide");
+interface NoteMapping {
+ rootNoteId: string;
+ path: string;
+ format: "markdown" | "html";
+ ignoredFiles?: string[];
+ exportOnly?: boolean;
+}
+
+const NOTE_MAPPINGS: NoteMapping[] = [
+ {
+ rootNoteId: "pOsGYCXsbNQG",
+ path: path.join("docs", "User Guide"),
+ format: "markdown"
+ },
+ {
+ rootNoteId: "pOsGYCXsbNQG",
+ path: path.join("src", "public", "app", "doc_notes", "en", "User Guide"),
+ format: "html",
+ ignoredFiles: ["index.html", "navigation.html", "style.css", "User Guide.html"],
+ exportOnly: true
+ },
+ {
+ rootNoteId: "jdjRLhLV3TtI",
+ path: path.join("docs", "Developer Guide"),
+ format: "markdown"
+ },
+ {
+ rootNoteId: "hD3V4hiu2VW4",
+ path: path.join("docs", "Release Notes"),
+ format: "markdown"
+ }
+];
async function main() {
await initializeTranslations();
@@ -26,8 +53,11 @@ async function main() {
const initializedPromise = deferred();
cls.init(async () => {
- await importData(markdownPath);
- await importData(releaseNotesPath);
+ for (const mapping of NOTE_MAPPINGS) {
+ if (!mapping.exportOnly) {
+ await importData(mapping.path);
+ }
+ }
setOptions();
initializedPromise.resolve();
});
@@ -187,11 +217,11 @@ async function registerHandlers() {
const eraseService = (await import("./src/services/erase.js")).default;
const debouncer = debounce(async () => {
eraseService.eraseUnusedAttachmentsNow();
- await exportData(NOTE_ID_USER_GUIDE, "markdown", markdownPath);
- await exportData(NOTE_ID_RELEASE_NOTES, "markdown", releaseNotesPath);
- const ignoredFiles = new Set(["index.html", "navigation.html", "style.css", "User Guide.html"]);
- await exportData(NOTE_ID_USER_GUIDE, "html", htmlPath, ignoredFiles);
+ for (const mapping of NOTE_MAPPINGS) {
+ const ignoredFiles = mapping.ignoredFiles ? new Set(mapping.ignoredFiles) : undefined;
+ await exportData(mapping.rootNoteId, mapping.format, mapping.path, ignoredFiles);
+ }
}, 10_000);
events.subscribe(events.ENTITY_CHANGED, async (e) => {
if (e.entityName === "options") {