mirror of
https://github.com/zadam/trilium.git
synced 2026-02-05 22:19:19 +01:00
docs(dev): update recent technical documentation
This commit is contained in:
0
docs/Developer Guide/Developer Guide/Architecture/Backlinks.md
vendored
Normal file
0
docs/Developer Guide/Developer Guide/Architecture/Backlinks.md
vendored
Normal file
0
docs/Developer Guide/Developer Guide/Architecture/Branch prefixes.md
vendored
Normal file
0
docs/Developer Guide/Developer Guide/Architecture/Branch prefixes.md
vendored
Normal file
16
docs/Developer Guide/Developer Guide/Architecture/Database structure/attachments.md
vendored
Normal file
16
docs/Developer Guide/Developer Guide/Architecture/Database structure/attachments.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# attachments
|
||||
| Column Name | Data Type | Nullity | Default value | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `attachmentId` | Text | Non-null | | Unique ID (e.g. `qhC1vzU4nwSE`) |
|
||||
| `ownerId` | Text | Non-null | | The unique ID of a row in <a class="reference-link" href="notes.md">notes</a>. |
|
||||
| `role` | Text | Non-null | | The role of the attachment: `image` for images that are attached to a note, `file` for uploaded files. |
|
||||
| `mime` | Text | Non-null | | The MIME type of the attachment (e.g. `image/png`) |
|
||||
| `title` | Text | Non-null | | The title of the attachment. |
|
||||
| `isProtected` | Integer | Non-null | 0 | `1` if the entity is [protected](../Protected%20entities.md), `0` otherwise. |
|
||||
| `position` | Integer | Non-null | 0 | Not sure where the position is relevant for attachments (saw it with values of 10 and 0). |
|
||||
| `blobId` | Text | Nullable | `null` | The corresponding `blobId` from the <a class="reference-link" href="blobs.md">blobs</a> table. |
|
||||
| `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`) |
|
||||
| `utcDateScheduledForErasure` | Text | Nullable | `null` | |
|
||||
| `isDeleted` | Integer | Non-null | | `1` if the entity is [deleted](../Deleted%20notes.md), `0` otherwise. |
|
||||
| `deleteId` | Text | Nullable | `null` | |
|
||||
2
docs/Developer Guide/Developer Guide/Architecture/Database structure/attributes.md
vendored
Normal file
2
docs/Developer Guide/Developer Guide/Architecture/Database structure/attributes.md
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# attributes
|
||||
<table><thead><tr><th>Column Name</th><th>Data Type</th><th>Nullity</th><th>Default value</th><th>Description</th></tr></thead><tbody><tr><th><code>attributeId</code></th><td>Text</td><td>Non-null</td><td> </td><td>Unique Id of the attribute (e.g. <code>qhC1vzU4nwSE</code>), can also have a special unique ID for <a class="reference-link" href="#root/r11Bh3uxFGRj">Special notes</a> (e.g. <code>_lbToday_liconClass</code>).</td></tr><tr><th><code>noteId</code></th><td>Text</td><td>Non-null</td><td> </td><td>The ID of the <a href="notes.md">note</a> this atttribute belongs to</td></tr><tr><th><code>type</code></th><td>Text</td><td>Non-null</td><td> </td><td>The type of attribute (<code>label</code> or <code>relation</code>).</td></tr><tr><th><code>name</code></th><td>Text</td><td>Non-null</td><td> </td><td>The name/key of the attribute.</td></tr><tr><th><code>value</code></th><td>Text</td><td>Non-null</td><td><code>""</code></td><td><ul><li>For <code>label</code> attributes, a free-form value of the attribute.</li><li>For <code>relation</code> attributes, the ID of the <a href="notes.md">note</a> the relation is pointing to.</li></ul></td></tr><tr><th><code>position</code></th><td>Integer</td><td>Non-null</td><td>0</td><td>The position of the attribute compared to the other attributes. Some predefined attributes such as <code>originalFileName</code> have a value of 1000.</td></tr><tr><th><code>utcDateModified</code></th><td>Text</td><td>Non-null</td><td> </td><td>Modification date in UTC format (e.g. <code>2023-11-08 16:43:44.204Z</code>)</td></tr><tr><th><code>isDeleted</code></th><td>Integer</td><td>Non-null</td><td> </td><td><code>1</code> if the entity is <a href="../Deleted%20notes.md">deleted</a>, <code>0</code> otherwise.</td></tr><tr><th><code>deleteId</code></th><td>Text</td><td>Nullable</td><td><code>null</code></td><td> </td></tr><tr><th><code>isInheritable</code></th><td>Integer</td><td>Nullable</td><td>0</td><td> </td></tr></tbody></table>
|
||||
2
docs/Developer Guide/Developer Guide/Architecture/Database structure/blobs.md
vendored
Normal file
2
docs/Developer Guide/Developer Guide/Architecture/Database structure/blobs.md
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# blobs
|
||||
<table><thead><tr><th>Column Name</th><th>Data Type</th><th>Nullity</th><th>Default value</th><th>Description</th></tr></thead><tbody><tr><th><code>blobId</code></th><td>Text</td><td>Non-null</td><td> </td><td><p>The unique ID of the blob (e.g. <code>XXbfAJXqWrYnSXcelLFA</code>).</p><aside class="admonition important"><p>The ID is actually a hash of the content, see <code>AbstractBeccaEntity#saveBlob</code>! It is a logic error to modify an existing blob.</p></aside></td></tr><tr><th><code>content</code></th><td>Text</td><td>Nullable</td><td><code>null</code></td><td><p>The content of the blob, can be either:</p><ul><li data-list-item-id="e1f1b8623c64f95f19751bbe494063d0f">text (for plain text notes or HTML notes).</li><li data-list-item-id="e5bbdee6fe5e5b12361fc74e339c9f1db">binary (for images and other types of attachments)</li></ul></td></tr><tr><th><code>dateModified</code></th><td>Text</td><td>Non-null</td><td> </td><td>Creation date with timezone offset (e.g. <code>2023-11-08 18:43:44.204+0200</code>)</td></tr><tr><th><code>utcDateModified</code></th><td>Text</td><td>Non-null</td><td> </td><td><p>Creation date in UTC format (e.g. <code>2023-11-08 16:43:44.204Z</code>).</p><p>Blobs cannot be modified, so this timestamp specifies when the blob was created.</p></td></tr></tbody></table>
|
||||
12
docs/Developer Guide/Developer Guide/Architecture/Database structure/branches.md
vendored
Normal file
12
docs/Developer Guide/Developer Guide/Architecture/Database structure/branches.md
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# 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`. |
|
||||
| `noteId` | Text | Non-null | | The ID of the [note](notes.md). |
|
||||
| `parentNoteId` | Text | Non-null | | The ID of the parent [note](notes.md) the note belongs to. |
|
||||
| `notePosition` | Integer | Non-null | | The position of the branch within the same level of hierarchy, the value is usually a multiple of 10. |
|
||||
| `prefix` | Text | Nullable | | The [branch prefix](../Branch%20prefixes.md) if any, or `NULL` otherwise. |
|
||||
| `isExpanded` | Integer | Non-null | 0 | Whether the branch should appear expanded (its children shown) to the user. |
|
||||
| `isDeleted` | Integer | Non-null | 0 | `1` if the entity is [deleted](../Deleted%20notes.md), `0` otherwise. |
|
||||
| `deleteId` | Text | Nullable | `null` | |
|
||||
| `utcDateModified` | Text | Non-null | | Modification date in UTC format (e.g. `2023-11-08 16:43:44.204Z`) |
|
||||
15
docs/Developer Guide/Developer Guide/Architecture/Database structure/entity_changes.md
vendored
Normal file
15
docs/Developer Guide/Developer Guide/Architecture/Database structure/entity_changes.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# entity_changes
|
||||
| Column Name | Data Type | Nullity | Default value | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `id` | Integer | Non-null | | A sequential numeric index of the entity change. |
|
||||
| `entityName` | Text | Non-null | | The type of entity being changed (`attributes`, `branches`, `note_reordering`, etc.) |
|
||||
| `entityId` | Text | Non-null | | The ID of the entity being changed. |
|
||||
| `hash` | Text | Nullable (\*) | | TODO: Describe how the hash is calculated |
|
||||
| `isErased` | Integer (1 or 0) | Nullable (\*) | | TODO: What does this do? |
|
||||
| `changeId` | Text | Nullable (\*) | | TODO: What does this do? |
|
||||
| `componentId` | Text | Nullable (\*) | | The ID of the UI component that caused this change. <br> <br>Examples: `date-note`, `F-PoZMI0vc`, `NA` (catch all) |
|
||||
| `instanceId` | Text | Nullable (\*) | | The ID of the [instance](#root/pOsGYCXsbNQG/tC7s2alapj8V/Gzjqa934BdH4/c5xB8m4g2IY6) that created this change. |
|
||||
| `isSynced` | Integer (1 or 0) | Non-null | | TODO: What does this do? |
|
||||
| `utcDateChanged` | Text | Non-null | | Date of the entity change in UTC format (e.g. `2023-11-08 16:43:44.204Z`) |
|
||||
|
||||
Nullable (\*) means all new values are non-null, old rows may contain null values.
|
||||
9
docs/Developer Guide/Developer Guide/Architecture/Database structure/etapi_tokens.md
vendored
Normal file
9
docs/Developer Guide/Developer Guide/Architecture/Database structure/etapi_tokens.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# 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`) |
|
||||
| `isDeleted` | Integer | Non-null | 0 | `1` if the entity is [deleted](../Deleted%20notes.md), `0` otherwise. |
|
||||
15
docs/Developer Guide/Developer Guide/Architecture/Database structure/notes.md
vendored
Normal file
15
docs/Developer Guide/Developer Guide/Architecture/Database structure/notes.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# notes
|
||||
| Column Name | Data Type | Nullity | Default value | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `noteId` | Text | Non-null | | The unique ID of the note (e.g. `2LJrKqIhr0Pe`). |
|
||||
| `title` | Text | Non-null | `"note"` | The title of the note, as defined by the user. |
|
||||
| `isProtected` | Integer | Non-null | 0 | `1` if the entity is [protected](../Protected%20entities.md), `0` otherwise. |
|
||||
| `type` | Text | Non-null | `"text"` | The type of note (i.e. `text`, `file`, `code`, `relationMap`, `mermaid`, `canvas`). |
|
||||
| `mime` | Text | Non-null | `"text/html"` | The MIME type of the note (e.g. `text/html`).. Note that it can be an empty string in some circumstances, but not null. |
|
||||
| `isDeleted` | Integer | Nullable | 0 | `1` if the entity is [deleted](../Deleted%20notes.md), `0` otherwise. |
|
||||
| `deleteId` | Text | Non-null | `null` | |
|
||||
| `dateCreated` | Text | Non-null | | 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 <a class="reference-link" href="blobs.md">blobs</a>. Although it can theoretically be `NULL`, haven't found any such note yet. |
|
||||
7
docs/Developer Guide/Developer Guide/Architecture/Database structure/options.md
vendored
Normal file
7
docs/Developer Guide/Developer Guide/Architecture/Database structure/options.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# 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`) |
|
||||
6
docs/Developer Guide/Developer Guide/Architecture/Database structure/recent_notes.md
vendored
Normal file
6
docs/Developer Guide/Developer Guide/Architecture/Database structure/recent_notes.md
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# 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](notes.md) 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`) |
|
||||
15
docs/Developer Guide/Developer Guide/Architecture/Database structure/revisions.md
vendored
Normal file
15
docs/Developer Guide/Developer Guide/Architecture/Database structure/revisions.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# revisions
|
||||
| Column Name | Data Type | Nullity | Default value | Description |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `revisionId` | TextText | Non-null | | Unique ID of the revision (e.g. `0GjgUqnEudI8`). |
|
||||
| `noteId` | Text | Non-null | | ID of the [note](notes.md) this revision belongs to. |
|
||||
| `type` | Text | Non-null | `""` | The type of note (i.e. `text`, `file`, `code`, `relationMap`, `mermaid`, `canvas`). |
|
||||
| `mime` | Text | Non-null | `""` | The MIME type of the note (e.g. `text/html`). |
|
||||
| `title` | Text | Non-null | | The title of the note, as defined by the user. |
|
||||
| `isProtected` | Integer | Non-null | 0 | `1` if the entity is [protected](../Protected%20entities.md), `0` otherwise. |
|
||||
| `blobId` | Text | Nullable | `null` | The corresponding ID from <a class="reference-link" href="blobs.md">blobs</a>. 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`) |
|
||||
0
docs/Developer Guide/Developer Guide/Architecture/Deleted notes.md
vendored
Normal file
0
docs/Developer Guide/Developer Guide/Architecture/Deleted notes.md
vendored
Normal file
22
docs/Developer Guide/Developer Guide/Architecture/Demo document.md
vendored
Normal file
22
docs/Developer Guide/Developer Guide/Architecture/Demo document.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
# Demo document
|
||||
The demo document is an exported .zip that resides in `apps/server/src/assets/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
|
||||
|
||||
1. In the Git root, run `pnpm edit-docs:edit-demo`.
|
||||
2. Wait for the desktop application to show up with the docs.
|
||||
3. Simply make the needed modifications.
|
||||
4. Wait for a few seconds for the change to be processed in the background.
|
||||
5. Commit the change in Git.
|
||||
|
||||
## Testing the changes
|
||||
|
||||
1. Run:
|
||||
|
||||
```
|
||||
rm -r data
|
||||
pnpm server:start
|
||||
```
|
||||
2. And then do the on-boarding again.
|
||||
6
docs/Developer Guide/Developer Guide/Architecture/Hidden notes.md
vendored
Normal file
6
docs/Developer Guide/Developer Guide/Architecture/Hidden notes.md
vendored
Normal file
@@ -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.
|
||||
30
docs/Developer Guide/Developer Guide/Architecture/Icons.md
vendored
Normal file
30
docs/Developer Guide/Developer Guide/Architecture/Icons.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# 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
|
||||
|
||||
<table><thead><tr><th>Name</th><th>Resolution</th><th>Description</th></tr></thead><tbody><tr><td><code>ios/apple-touch-icon.png</code></td><td>180x180</td><td>Used as <code>apple-touch-icon</code>, but only in <code>login.ejs</code> and <code>set_password.ejs</code> for some reason.</td></tr><tr><td><code>mac/icon.icns</code></td><td>512x512</td><td>Provided as <code>--icon</code> to <code>electron-packager</code> for <code>mac-arm64</code> and <code>mac-x64</code> <a href="../Old%20documentation/Build%20deliveries%20locally.md">builds</a>.</td></tr><tr><td><code>png/128x128.png</code></td><td>128x128</td><td>Used in <code>linux-x64</code> <a href="../Old%20documentation/Build%20deliveries%20locally.md">build</a>, to provide an <code>icon.png</code>.</td></tr><tr><td><code>png/256x256-dev.png</code></td><td>256x256</td><td>Used by the Electron window icon, if in dev mode.</td></tr><tr><td><code>png/256x256.png</code></td><td>Used by the Electron window icon, if not in dev mode.</td></tr><tr><td><code>win/icon.ico</code></td><td><ul><li>ICO 16x16</li><li>ICO 32x32</li><li>ICO 48x48</li><li>ICO 64x64</li><li>ICO 128x128</li><li>PNG 256x256</li></ul></td><td><ul><li>Used by the <code>win-x64</code> <a href="../Old%20documentation/Build%20deliveries%20locally.md">build</a>.</li><li>Used by Squirrel Windows installer for: setup icon, app icon, control panel icon</li><li>Used as the favicon.</li></ul></td></tr><tr><td><code>win/setup-banner.gif</code></td><td>640x480</td><td>Used by the Squirrel Windows installer during the installation process. Has only one frame.</td></tr></tbody></table>
|
||||
|
||||
## 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 <a class="reference-link" href="Demo%20document.md">Demo document</a>, as an attachment.
|
||||
* In the <a class="reference-link" href="#root/OeKBfN6JbMIq/MF99QFRe1gVy/xkj1bqW7zJwQ/t6mT72MfEzb2">CKEditor</a> 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.
|
||||
94
docs/Developer Guide/Developer Guide/Architecture/Internationalisation Translat.md
vendored
Normal file
94
docs/Developer Guide/Developer Guide/Architecture/Internationalisation Translat.md
vendored
Normal file
@@ -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 Trilium Notes"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Follow the <a class="reference-link" href="Internationalisation%20%20Translations/Guidelines.md">Guidelines</a> 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 `}}`:
|
||||
|
||||
```
|
||||
{
|
||||
"key": "{{what}} is {{how}}"
|
||||
}
|
||||
```
|
||||
|
||||
Then pass the arguments when reading the translation:
|
||||
|
||||
```
|
||||
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).
|
||||
@@ -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}}.`
|
||||
@@ -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.
|
||||
15
docs/Developer Guide/Developer Guide/Architecture/Internationalisation Translations/i18n-ally.md
vendored
Normal file
15
docs/Developer Guide/Developer Guide/Architecture/Internationalisation Translations/i18n-ally.md
vendored
Normal file
@@ -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 <a class="reference-link" href="Guidelines.md">Guidelines</a> (e.g. `jump_to_note.search-for-note-by-its-name`).
|
||||
13
docs/Developer Guide/Developer Guide/Architecture/Launchers.md
vendored
Normal file
13
docs/Developer Guide/Developer Guide/Architecture/Launchers.md
vendored
Normal file
@@ -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.
|
||||
0
docs/Developer Guide/Developer Guide/Architecture/Note Revisions.md
vendored
Normal file
0
docs/Developer Guide/Developer Guide/Architecture/Note Revisions.md
vendored
Normal file
@@ -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.
|
||||
@@ -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 <a class="reference-link" href="SVG%20rendering.md">SVG rendering</a>. Make sure to deduplicate the code if the SVG rendering is already handled.
|
||||
|
||||
```
|
||||
async renderSvg() {
|
||||
return await this.mind.exportSvg().text();
|
||||
}
|
||||
```
|
||||
|
||||
Then create an event handler to manage the SVG export:
|
||||
|
||||
```
|
||||
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.
|
||||
@@ -0,0 +1,54 @@
|
||||
# First steps
|
||||
> **Note**: When adding or updating step titles/order, don't forget to update the corresponding list in <a class="reference-link" href="Note%20type%20checklist.md">Note type checklist</a>.
|
||||
|
||||
## 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 <a class="reference-link" href="../../Demo%20document.md">Demo document</a> 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):
|
||||
|
||||
```
|
||||
static getType() { return "foo"; }
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
import TypeWidget from "./type_widget.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="note-detail-mind-map note-detail-printable">
|
||||
</div>
|
||||
`;
|
||||
|
||||
export default class MindMapWidget extends TypeWidget {
|
||||
static getType() { return "mindMap"; }
|
||||
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
|
||||
super.doRender();
|
||||
}
|
||||
|
||||
async doRefresh(note) {
|
||||
this.$widget.html("<p>Hello</p>");
|
||||
this.$widget.show();
|
||||
}
|
||||
|
||||
async entitiesReloadedEvent({loadResults}) {
|
||||
if (loadResults.isNoteReloaded(this.noteId)) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Loading data
|
||||
Data loading can be done in `doRefresh()` since it gets a reference to the note:
|
||||
|
||||
```
|
||||
const blob = await note.getBlob();
|
||||
const content = blob.getJsonContent();
|
||||
```
|
||||
|
||||
Note that `doRefresh` can sometimes be called by <a class="reference-link" href="Saving%20data%20via%20spaced%20update.md">Saving data via spaced update</a> when the user makes a changes, this has to be accounted for.
|
||||
@@ -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 <a class="reference-link" href="First%20steps.md">First steps</a>:
|
||||
|
||||
* 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 <a class="reference-link" href="#root/XPSyrTI07rbd/DrIXfwu9CVJP">Mindmap gets turned as file</a>).
|
||||
|
||||
## 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 <a class="reference-link" href="SVG%20rendering.md">SVG rendering</a>.
|
||||
|
||||
* 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).
|
||||
@@ -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()`:
|
||||
|
||||
```
|
||||
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:
|
||||
|
||||
```
|
||||
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).
|
||||
@@ -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:
|
||||
|
||||
```
|
||||
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 <a class="reference-link" href="SVG%20rendering.md">SVG rendering</a> 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:
|
||||
|
||||
```
|
||||
mind.bus.addListener("operation", (operation) => {
|
||||
this.spacedUpdate.scheduleUpdate();
|
||||
});
|
||||
```
|
||||
43
docs/Developer Guide/Developer Guide/Architecture/Options.md
vendored
Normal file
43
docs/Developer Guide/Developer Guide/Architecture/Options.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# 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");
|
||||
```
|
||||
|
||||
## Adding new options
|
||||
|
||||
### Checkbox option
|
||||
|
||||
Refer to this example in `backup.tsx`:
|
||||
|
||||
```javascript
|
||||
export function AutomaticBackup() {
|
||||
const [ dailyBackupEnabled, setDailyBackupEnabled ] = useTriliumOptionBool("dailyBackupEnabled");
|
||||
|
||||
return (
|
||||
<OptionsSection title={t("backup.automatic_backup")}>
|
||||
<FormMultiGroup label={t("backup.automatic_backup_description")}>
|
||||
<FormCheckbox
|
||||
name="daily-backup-enabled"
|
||||
label={t("backup.enable_daily_backup")}
|
||||
currentValue={dailyBackupEnabled} onChange={setDailyBackupEnabled}
|
||||
/>
|
||||
</FormMultiGroup>
|
||||
|
||||
<FormText>{t("backup.backup_recommendation")}</FormText>
|
||||
</OptionsSection>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> To trigger a UI refresh (e.g. `utils#reloadFrontendApp`), simply pass a `true` as the second argument to `useTriliumOption` methods.
|
||||
5
docs/Developer Guide/Developer Guide/Architecture/Options/Creating a new option.md
vendored
Normal file
5
docs/Developer Guide/Developer Guide/Architecture/Options/Creating a new option.md
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# 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`.
|
||||
18
docs/Developer Guide/Developer Guide/Architecture/Printing and exporting to PDF.md
vendored
Normal file
18
docs/Developer Guide/Developer Guide/Architecture/Printing and exporting to PDF.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# Printing and exporting to PDF
|
||||
Note printing is handled by `note_detail.js`, in the `printActiveNoteEvent` method. Exporting to PDF works similarly.
|
||||
|
||||
## How it works
|
||||
|
||||
Both printing and exporting as PDF use the same mechanism: a note is rendered individually in a separate webpage that is then sent to the browser or the Electron application either for printing or exporting as PDF.
|
||||
|
||||
The webpage that renders a single note can actually be accessed in a web browser. For example `http://localhost:8080/#root/WWRGzqHUfRln/RRZsE9Al8AIZ?ntxId=0o4fzk` becomes `http://localhost:8080/?print#root/WWRGzqHUfRln/RRZsE9Al8AIZ`.
|
||||
|
||||
Accessing the print note in a web browser allows for easy debugging to understand why a particular note doesn't render well. The mechanism for rendering is similar to the one used in <a class="reference-link" href="#root/0ESUbbAxVnoK">Note List</a>.
|
||||
|
||||
## 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.
|
||||
* <a class="reference-link" href="Syntax%20highlighting.md">Syntax highlighting</a> is handled by the content renderer.
|
||||
6
docs/Developer Guide/Developer Guide/Architecture/Protected entities.md
vendored
Normal file
6
docs/Developer Guide/Developer Guide/Architecture/Protected entities.md
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Protected entities
|
||||
The following entities can be made protected, via their `isProtected` flag:
|
||||
|
||||
* <a class="reference-link" href="Database%20structure/attachments.md">attachments</a>
|
||||
* <a class="reference-link" href="Database%20structure/notes.md">notes</a>
|
||||
* <a class="reference-link" href="Database%20structure/revisions.md">revisions</a>
|
||||
30
docs/Developer Guide/Developer Guide/Architecture/Share.md
vendored
Normal file
30
docs/Developer Guide/Developer Guide/Architecture/Share.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# Share
|
||||
## Share theme
|
||||
|
||||
The share theme represents the layout, styles and scripts behind the Share notes functionality. The current implementation is a heavy adaptation of [trilium.rocks](https://trilium.rocks/) which is a third-party share theme.
|
||||
|
||||
* The theme resides in `packages/share-theme`.
|
||||
* The HTML is defined in `src/templates` using EJS templating.
|
||||
* The `src/scripts` and `src/styles` subdirectories house the rest of the theme.
|
||||
|
||||
## Building the share theme
|
||||
|
||||
* In `packages/share-theme`, run `pnpm build` to trigger a build. This will generate `dist` which will then be used by the server.
|
||||
* Alternatively, use `pnpm dev` to watch for changes.
|
||||
|
||||
## Integration with the server for the share functionality
|
||||
|
||||
The server renders the templates using EJS templating from the share theme and hosts the assets.
|
||||
|
||||
* In dev mode, the templates and assets are served directly from `packages/share-theme/dist`.
|
||||
* Modifications to the assets (scripts or styles) will reflect without having to restart the server. However the share theme needs to be built first (see previous section).
|
||||
* Changes to the template will require a restart of the server, since they are cached. Simply press Enter in the console with `pnpm server:start` to quickly trigger a restart.
|
||||
* In production mode, the share theme is automatically built by the server build script and copied to `dist/share-theme`.
|
||||
|
||||
The server route handling this functionality is in `src/share/routes.ts`.
|
||||
|
||||
## Exporting to static HTML files
|
||||
|
||||
This functionality is also handled by the server, but in `src/services/export/zip/share_theme` instead. It works quite similar to the normal sharing functionality, but it uses `BNote` instead of `SNote` (and so on for other entity types), in order to work regardless of whether a note is shared or not.
|
||||
|
||||
The same templates are used and rendered by the server, except that they are stored in a file instead of served to web clients.
|
||||
17
docs/Developer Guide/Developer Guide/Architecture/Synchronisation/Content hashing.md
vendored
Normal file
17
docs/Developer Guide/Developer Guide/Architecture/Synchronisation/Content hashing.md
vendored
Normal file
@@ -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.
|
||||
84
docs/Developer Guide/Developer Guide/Architecture/Syntax highlighting.md
vendored
Normal file
84
docs/Developer Guide/Developer Guide/Architecture/Syntax highlighting.md
vendored
Normal file
@@ -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" }
|
||||
```
|
||||
17
docs/Developer Guide/Developer Guide/Architecture/Themes.md
vendored
Normal file
17
docs/Developer Guide/Developer Guide/Architecture/Themes.md
vendored
Normal file
@@ -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.
|
||||
Reference in New Issue
Block a user