feat(docs): add developer guide
@ -0,0 +1,26 @@
|
||||
# Build deliveries locally
|
||||
In the project root:
|
||||
|
||||
<figure class="table"><table><thead><tr><th>Platform</th><th>Architecture</th><th>Application</th><th>Build command</th></tr></thead><tbody><tr><th>macOS</th><td>x86_64</td><td>Desktop / Electron app</td><td><code>./bin/build-mac-x64.sh</code></td></tr><tr><td>ARM 64</td><td>Desktop / Electron app</td><td><code>./bin/build-mac-arm64.sh</code></td></tr><tr><th>Linux</th><td>x86_64</td><td>Desktop / Electron app</td><td><code>./bin/build-linux-x64.sh</code></td></tr><tr><td>Server</td><td><code>./bin/build-server.sh</code></td></tr><tr><th>Windows</th><td>x86_64</td><td>Desktop / Electron app</td><td><code>./bin/build-win-x64.sh</code></td></tr></tbody></table></figure>
|
||||
|
||||
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
|
||||
|
||||
<figure class="table"><table><thead><tr><th>Desktop client</th><th>Server</th></tr></thead><tbody><tr><td><pre><code class="language-text-plain">$ NIXPKGS_ALLOW_UNFREE=1 nix-shell -p steam-run
|
||||
[nix-shell] cd dist/trilium-linux-x64
|
||||
[nix-shell] steam-run ./trilium</code></pre></td><td><pre><code class="language-text-plain">$ NIXPKGS_ALLOW_UNFREE=1 nix-shell -p steam-run
|
||||
[nix-shell] cd dist/trilium-linux-x64-server
|
||||
[nix-shell] steam-run ./trilium.sh</code></pre></td></tr></tbody></table></figure>
|
||||
|
After Width: | Height: | Size: 61 KiB |
@ -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:
|
||||
|
||||
<figure class="image"><img src="Main_image.png"></figure>
|
||||
|
||||
Press the green checkmark (or red cross if something went bad). Then look at the list of jobs and their status:
|
||||
|
||||
<figure class="image"><img src="1_Main_image.png"></figure>
|
||||
|
||||
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.
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
@ -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.
|
||||
@ -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.
|
||||
@ -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
|
||||
```
|
||||
@ -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,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,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).
|
||||
@ -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).
|
||||
@ -0,0 +1,2 @@
|
||||
# attachments
|
||||
<figure class="table" style="width:100%"><table class="ck-table-resized"><colgroup><col> <col> <col> <col> <col></colgroup><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>attachmentId</code></th><td>Text</td><td>Non-null</td><td> </td><td>Unique ID (e.g. <code>qhC1vzU4nwSE</code>)</td></tr><tr><th><code>ownerId</code></th><td>Text</td><td>Non-null</td><td> </td><td>The unique ID of a row in <a class="reference-link" href="notes.md">notes</a>.</td></tr><tr><th><code>role</code></th><td>Text</td><td>Non-null</td><td> </td><td>The role of the attachment: <code>image</code> for images that are attached to a note.</td></tr><tr><th><code>mime</code></th><td>Text</td><td>Non-null</td><td> </td><td>The MIME type of the attachment (e.g. <code>image/png</code>)</td></tr><tr><th><code>title</code></th><td>Text</td><td>Non-null</td><td> </td><td>The title of the attachment.</td></tr><tr><th><code>isProtected</code></th><td>Integer</td><td>Non-null</td><td>0</td><td><code>1</code> if the entity is <a href="../Protected%20entities.md">protected</a>, <code>0</code> otherwise.</td></tr><tr><th><code>position</code></th><td>Integer</td><td>Non-null</td><td>0</td><td>Not sure where the position is relevant for attachments (saw it with values of 10 and 0).</td></tr><tr><th><code>blobId</code></th><td>Text</td><td>Nullable</td><td><code>null</code></td><td>The corresponding <code>blobId</code> from the <a class="reference-link" href="blobs.md">blobs</a> table.</td></tr><tr><th><code>dateModified</code></th><td>Text</td><td>Non-null</td><td> </td><td>Localized modification date (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>Modification date in UTC format (e.g. <code>2023-11-08 16:43:44.204Z</code>)</td></tr><tr><th><code>utcDateScheduledForErasure</code></th><td>Text</td><td>Nullable</td><td><code>null</code></td><td> </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></tbody></table></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# attributes
|
||||
<figure class="table"><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="../Special%20notes.md">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></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# blobs
|
||||
<figure class="table"><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>The unique ID of the blob (e.g. <code>XXbfAJXqWrYnSXcelLFA</code>).</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>text (for plain text notes or HTML notes).</li><li>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>Localized modification date (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>Modification date in UTC format (e.g. <code>2023-11-08 16:43:44.204Z</code>)</td></tr></tbody></table></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# branches
|
||||
<figure class="table"><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>branchId</code></th><td>Text</td><td>Non-null</td><td> </td><td>The ID of the branch, in the form of <code>a_b</code> where <code>a</code> is the <code>parentNoteId</code> and <code>b</code> is the <code>noteId</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>.</td></tr><tr><th><code>parentNoteId</code></th><td>Text</td><td>Non-null</td><td> </td><td>The ID of the parent <a href="notes.md">note</a> the note belongs to.</td></tr><tr><th><code>notePosition</code></th><td>Integer</td><td>Non-null</td><td> </td><td>The position of the branch within the same level of hierarchy, the value is usually a multiple of 10.</td></tr><tr><th><code>prefix</code></th><td>Text</td><td>Nullable</td><td> </td><td>The <a href="../Branch%20prefixes.md">branch prefix</a> if any, or <code>NULL</code> otherwise.</td></tr><tr><th><code>isExpanded</code></th><td>Integer</td><td>Non-null</td><td>0</td><td>Whether the branch should appear expanded (its children shown) to the user.</td></tr><tr><th><code>isDeleted</code></th><td>Integer</td><td>Non-null</td><td>0</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>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></tbody></table></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# entity_changes
|
||||
<figure class="table"><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>id</code></th><td>Integer</td><td>Nullable</td><td> </td><td>A sequential numeric index of the entity change.</td></tr><tr><th><code>entityName</code></th><td>Text</td><td>Nullable</td><td> </td><td>The type of entity being changed (<code>attributes</code>, <code>branches</code>, <code>note_reordering</code>, etc.)</td></tr><tr><th><code>entityId</code></th><td>Text</td><td>Nullable</td><td> </td><td>The ID of the entity being changed.</td></tr><tr><th><code>hash</code></th><td>Text</td><td>Nullable</td><td> </td><td>TODO: Describe how the hash is calculated</td></tr><tr><th><code>isErased</code></th><td>Integer</td><td>Nullable</td><td> </td><td>TODO: What does this do?</td></tr><tr><th><code>changeId</code></th><td>Text</td><td>Nullable</td><td> </td><td>TODO: What does this do?</td></tr><tr><th><code>componentId</code></th><td>Text</td><td>Nullable</td><td> </td><td>TODO: What does this do?</td></tr><tr><th><code>instanceId</code></th><td>Text</td><td>Nullable</td><td> </td><td>TODO: What does this do?</td></tr><tr><th><code>isSynced</code></th><td>Integer</td><td>Nullable</td><td> </td><td>TODO: What does this do?</td></tr><tr><th><code>utcDateChanged</code></th><td>Text</td><td>Nullable</td><td> </td><td>Date of the entity change in UTC format (e.g. <code>2023-11-08 16:43:44.204Z</code>)</td></tr></tbody></table></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# etapi_tokens
|
||||
<figure class="table"><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>etapiTokenId</code></th><td>Text</td><td>Non-null</td><td> </td><td>A unique ID of the token (e.g. <code>aHmLr5BywvfJ</code>).</td></tr><tr><th><code>name</code></th><td>Text</td><td>Non-null</td><td> </td><td>The name of the token, as is set by the user.</td></tr><tr><th><code>tokenHash</code></th><td>Text</td><td>Non-null</td><td> </td><td>The token itself.</td></tr><tr><th><code>utcDateCreated</code></th><td>Text</td><td>Non-null</td><td> </td><td>Creation date in UTC format (e.g. <code>2023-11-08 16:43:44.204Z</code>)</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>0</td><td><code>1</code> if the entity is <a href="../Deleted%20notes.md">deleted</a>, <code>0</code> otherwise.</td></tr></tbody></table></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# notes
|
||||
<figure class="table"><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>noteId</code></th><td>Text</td><td>Non-null</td><td> </td><td>The unique ID of the note (e.g. <code>2LJrKqIhr0Pe</code>).</td></tr><tr><th><code>title</code></th><td>Text</td><td>Non-null</td><td><code>"note"</code></td><td>The title of the note, as defined by the user.</td></tr><tr><th><code>isProtected</code></th><td>Integer</td><td>Non-null</td><td>0</td><td><code>1</code> if the entity is <a href="../Protected%20entities.md">protected</a>, <code>0</code> otherwise.</td></tr><tr><th><code>type</code></th><td>Text</td><td>Non-null</td><td><code>"text"</code></td><td>The type of note (i.e. <code>text</code>, <code>file</code>, <code>code</code>, <code>relationMap</code>, <code>mermaid</code>, <code>canvas</code>).</td></tr><tr><th><code>mime</code></th><td>Text</td><td>Non-null</td><td><code>"text/html"</code></td><td>The MIME type of the note (e.g. <code>text/html</code>).. Note that it can be an empty string in some circumstances, but not null.</td></tr><tr><th><code>isDeleted</code></th><td>Integer</td><td>Nullable</td><td>0</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>Non-null</td><td><code>null</code></td><td> </td></tr><tr><th><code>dateCreated</code></th><td>Text</td><td>Non-null</td><td> </td><td>Localized creation date (e.g. <code>2023-11-08 18:43:44.204+0200</code>)</td></tr><tr><th><code>dateModified</code></th><td>Text</td><td>Non-null</td><td> </td><td>Localized modification date (e.g. <code>2023-11-08 18:43:44.204+0200</code>)</td></tr><tr><th><code>utcDateCreated</code></th><td>Text</td><td>Non-null</td><td> </td><td>Creation date in UTC format (e.g. <code>2023-11-08 16:43:44.204Z</code>)</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>blobId</code></th><td>Text</td><td>Nullable</td><td><code>null</code></td><td>The corresponding ID from <a class="reference-link" href="blobs.md">blobs</a>. Although it can theoretically be <code>NULL</code>, haven't found any such note yet.</td></tr></tbody></table></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# options
|
||||
<figure class="table"><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>name</code></th><td>Text</td><td>Non-null</td><td> </td><td>The name of option (e.g. <code>maxContentWidth</code>)</td></tr><tr><th><code>value</code></th><td>Text</td><td>Non-null</td><td> </td><td>The value of the option.</td></tr><tr><th><code>isSynced</code></th><td>Integer</td><td>Non-null</td><td>0</td><td><code>0</code> if the option is not synchronized and thus can differ between clients, <code>1</code> if the option is synchronized.</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></tbody></table></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# recent_notes
|
||||
<figure class="table"><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>noteId</code></th><td>Text</td><td>Non-null</td><td> </td><td>Unique ID of the note (e.g. <code>yRRTLlqTbGoZ</code>).</td></tr><tr><th><code>notePath</code></th><td>Text</td><td>Non-null</td><td> </td><td>The path (IDs) to the <a href="notes.md">note</a> from root to the note itself, separated by slashes.</td></tr><tr><th><code>utcDateCreated</code></th><td>Text</td><td>Non-null</td><td> </td><td>Creation date in UTC format (e.g. <code>2023-11-08 16:43:44.204Z</code>)</td></tr></tbody></table></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# revisions
|
||||
<figure class="table"><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>revisionId</code></th><td>TextText</td><td>Non-null</td><td> </td><td>Unique ID of the revision (e.g. <code>0GjgUqnEudI8</code>).</td></tr><tr><th><code>noteId</code></th><td>Text</td><td>Non-null</td><td> </td><td>ID of the <a href="notes.md">note</a> this revision belongs to.</td></tr><tr><th><code>type</code></th><td>Text</td><td>Non-null</td><td><code>""</code></td><td>The type of note (i.e. <code>text</code>, <code>file</code>, <code>code</code>, <code>relationMap</code>, <code>mermaid</code>, <code>canvas</code>).</td></tr><tr><th><code>mime</code></th><td>Text</td><td>Non-null</td><td><code>""</code></td><td>The MIME type of the note (e.g. <code>text/html</code>).</td></tr><tr><th><code>title</code></th><td>Text</td><td>Non-null</td><td> </td><td>The title of the note, as defined by the user.</td></tr><tr><th><code>isProtected</code></th><td>Integer</td><td>Non-null</td><td>0</td><td><code>1</code> if the entity is <a href="../Protected%20entities.md">protected</a>, <code>0</code> otherwise.</td></tr><tr><th><code>blobId</code></th><td>Text</td><td>Nullable</td><td><code>null</code></td><td>The corresponding ID from <a class="reference-link" href="blobs.md">blobs</a>. Although it can theoretically be <code>NULL</code>, haven't found any such note yet.</td></tr><tr><th><code>utcDateLastEdited</code></th><td>Text</td><td>Non-null</td><td> </td><td><strong>Not sure how it differs from modification date.</strong></td></tr><tr><th><code>utcDateCreated</code></th><td>Text</td><td>Non-null</td><td> </td><td>Creation date in UTC format (e.g. <code>2023-11-08 16:43:44.204Z</code>)</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>dateLastEdited</code></th><td>Text</td><td>Non-null</td><td> </td><td><strong>Not sure how it differs from modification date.</strong></td></tr><tr><th><code>dateCreated</code></th><td>Text</td><td>Non-null</td><td> </td><td>Localized creatino date (e.g. <code>2023-08-12 15:10:04.045+0300</code>)</td></tr></tbody></table></figure>
|
||||
@ -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.
|
||||
@ -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
|
||||
```
|
||||
|
After Width: | Height: | Size: 233 KiB |
@ -0,0 +1,8 @@
|
||||
# Icons on Mac
|
||||
Looks great in Finder:
|
||||
|
||||
<figure class="image"><img src="Icons on Mac_image.png"></figure>
|
||||
|
||||
Looks great in Dock:
|
||||
|
||||
<figure class="image"><img src="1_Icons on Mac_image.png"></figure>
|
||||
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 748 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 251 KiB |
@ -0,0 +1,6 @@
|
||||
# Adaptive icon
|
||||
<figure class="table" style="width:100%"><table class="ck-table-resized"><colgroup><col> <col></colgroup><tbody><tr><th>Before</th><td><figure class="image"><img src="1_Adaptive icon_image.png"></figure></td></tr><tr><th>After</th><td><figure class="image"><img src="6_Adaptive icon_image.png"></figure></td></tr><tr><th>With new scale</th><td><figure class="image"><img src="4_Adaptive icon_image.png"></figure></td></tr></tbody></table></figure>
|
||||
|
||||
## Scale
|
||||
|
||||
<figure class="table" style="width:300px"><table><tbody><tr><th>0.9</th><td style="background-color:hsl(0, 0%, 90%)"><figure class="image"><img src="2_Adaptive icon_image.png"></figure></td></tr><tr><th>0.85</th><td style="background-color:hsl(0, 0%, 90%)"><figure class="image"><img src="5_Adaptive icon_image.png"></figure></td></tr><tr><th>0.8</th><td style="background-color:hsl(0, 0%, 90%)"><figure class="image"><img src="Adaptive icon_image.png"></figure></td></tr><tr><th>0.75</th><td style="background-color:hsl(0, 0%, 90%)"><figure class="image"><img src="3_Adaptive icon_image.png"></figure></td></tr></tbody></table></figure>
|
||||
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 716 KiB |
|
After Width: | Height: | Size: 336 KiB |
@ -0,0 +1,12 @@
|
||||
# Removed icons
|
||||
The following icons were removed:
|
||||
|
||||
## Main images
|
||||
|
||||
These are stored in `images`:
|
||||
|
||||
<figure class="table"><table><thead><tr><th>Name</th><th>Resolution</th><th>Description</th></tr></thead><tbody><tr><td><code>icon-black.png</code></td><td>36x36</td><td>Does not appear to be used.</td></tr><tr><td><code>icon-color.png</code></td><td>36x36</td><td>Used only by some tests in <code>test-etapi</code>.</td></tr><tr><td><code>icon-grey.png</code></td><td>36x36</td><td>Does not appear to be used.</td></tr><tr><td><code>icon.svg</code></td><td>210x297</td><td>Does not appear to be used.</td></tr></tbody></table></figure>
|
||||
|
||||
## App icons
|
||||
|
||||
<figure class="table"><table><thead><tr><th>Name</th><th>Resolution</th><th>Description</th></tr></thead><tbody><tr><td><code>png/16x16-bw.png</code></td><td>16x16</td><td>Do not appear to be used.</td></tr><tr><td><code>png/16x16.png</code></td></tr><tr><td><code>png/24x24.png</code></td><td>24x24</td></tr><tr><td><code>png/32x32.png</code></td><td>32x32</td></tr><tr><td><code>png/48x48.png</code></td><td>48x48</td></tr><tr><td><code>png/64x64.png</code></td><td>64x64</td></tr><tr><td><code>png/96x96.png</code></td><td>96x96</td></tr><tr><td><code>png/512x512.png</code></td><td>512x512</td><td>Does not appear to be used.</td></tr><tr><td><code>win/setup-banner.xcf</code></td><td> </td><td>GIMP source for <code>win/setup-banner.gif</code>. Provided only for future editing.</td></tr></tbody></table></figure>
|
||||
@ -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.
|
||||
@ -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,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");
|
||||
```
|
||||
@ -0,0 +1,37 @@
|
||||
# Check box option
|
||||
In the TPL:
|
||||
|
||||
```html
|
||||
<div class="options-section">
|
||||
<h4>Background effects</h4>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<div class="col-6 side-checkbox">
|
||||
<label class="form-check">
|
||||
<input type="checkbox" class="background-effects form-check-input" />
|
||||
Enable background effects (Windows 11 only)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
```
|
||||
@ -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`.
|
||||
@ -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
|
||||
<div class="col-6">
|
||||
<label>First day of the week</label>
|
||||
<select class="first-day-of-week-select form-control">
|
||||
<option value="0">Sunday</option>
|
||||
<option value="1">Monday</option>
|
||||
</select>
|
||||
</div>
|
||||
```
|
||||
|
||||
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());
|
||||
});
|
||||
```
|
||||
@ -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.
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -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");
|
||||
|
||||
});
|
||||
```
|
||||
@ -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.
|
||||
@ -0,0 +1,6 @@
|
||||
# Protected entities
|
||||
The following entities can be made protected, via their `isProtected` flag:
|
||||
|
||||
* <a class="reference-link" href="Database/attachments.md">attachments</a>
|
||||
* <a class="reference-link" href="Database/notes.md">notes</a>
|
||||
* <a class="reference-link" href="Database/revisions.md">revisions</a>
|
||||
@ -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.
|
||||
@ -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.
|
||||
@ -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" }
|
||||
```
|
||||
@ -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.
|
||||
@ -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="`
|
||||
@ -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
|
||||
```
|
||||
@ -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).
|
||||
@ -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).
|
||||
@ -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).
|
||||
@ -0,0 +1,2 @@
|
||||
# Updating dependencies
|
||||
<figure class="table" style="width:100%"><table class="ck-table-resized"><colgroup><col> <col> <col> <col> <col></colgroup><thead><tr><th>Dependency</th><th>Name in <code>library_loader</code></th><th>Things to check for a basic sanity check</th><th> </th><th>Protected by unit tests</th></tr></thead><tbody><tr><td><code>better-sqlite3</code></td><td> </td><td>See <a class="reference-link" href="Updating%20dependencies/bettersqlite%20binaries.md">bettersqlite binaries</a>.</td><td> </td><td> </td></tr><tr><td><code>jsdom</code></td><td> </td><td><ul><li>Note map</li><li>Clipper</li><li>Note similarity</li></ul></td><td>Protected by typings, should catch any potential changes in API.</td><td>Yes</td></tr><tr><td><code>async-mutex</code></td><td> </td><td><ul><li>Sync</li></ul></td><td> </td><td> </td></tr><tr><td><code>axios</code></td><td> </td><td><ul><li>Can't be directly tested, as it's exposed only via the backend script API.</li></ul></td><td> </td><td> </td></tr><tr><td><code>sax</code></td><td> </td><td><ul><li>EverNote imports</li></ul></td><td> </td><td> </td></tr><tr><td><ul><li><code>ws</code></li><li><code>debounce</code></li></ul></td><td> </td><td><ul><li>Check any action is reported from server to client (e.g. delete a note).</li></ul></td><td> </td><td> </td></tr><tr><td><code>ejs</code></td><td> </td><td><ul><li>Onboarding / first setup</li></ul></td><td> </td><td> </td></tr><tr><td><code>dayjs</code></td><td> </td><td><ul><li>Day notes</li></ul></td><td> </td><td> </td></tr><tr><td><code>semver</code></td><td> </td><td><ul><li>Application should start.</li></ul></td><td> </td><td> </td></tr><tr><td><code>https-proxy-agent</code></td><td> </td><td>???</td><td> </td><td> </td></tr><tr><td><code>sax</code></td><td> </td><td><ul><li>EverNote import</li></ul></td><td> </td><td> </td></tr><tr><td><code>ini</code></td><td> </td><td><ul><li>Affects config, generally if the application starts then it should be OK.</li></ul></td><td> </td><td> </td></tr><tr><td><code>jsplumb</code></td><td><code>RELATION_MAP</code></td><td><ul><li>Relation map note type</li></ul></td><td> </td><td> </td></tr><tr><td><code>jquery.mark.es6</code></td><td><code>MARKJS</code></td><td><ul><li>In search, when highlighting the text that matched.</li><li>In search in HTML, which might not actually be used since it seems to have been replaced by CKEditor's own find & replace dialog.</li></ul></td><td> </td><td> </td></tr><tr><td><code>knockout.js</code></td><td> </td><td><ul><li>Used in rendering the login and main layout of the application.</li></ul></td><td> </td><td> </td></tr><tr><td><code>normalize.min.css</code></td><td> </td><td><ul><li>Used in shared notes.</li></ul></td><td> </td><td> </td></tr><tr><td><code>wheel-zoom.min.js</code></td><td><code>WHEEL_ZOOM</code></td><td><ul><li>When opening a image that is in attachment.</li><li>When opening a stand-alone image note.</li><li>When zooming in a mermaid chart.</li></ul></td><td> </td><td> </td></tr><tr><td><code>fancytree</code></td><td> </td><td><ul><li>The note tree should be fully functional.</li></ul></td><td> </td><td> </td></tr><tr><td><code>bootstrap</code></td><td> </td><td><ul><li>Check mostly the on-boarding pages, when there is no database.</li></ul></td><td> </td><td> </td></tr><tr><td><code>electron-debug</code></td><td> </td><td><ul><li>Run electron using <code>npm run start-electron</code> 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).</li></ul></td><td> </td><td> </td></tr><tr><td><code>electron-dl</code></td><td> </td><td> </td><td> </td><td> </td></tr><tr><td><code>eslint</code></td><td> </td><td> </td><td> </td><td> </td></tr><tr><td><code>marked</code></td><td> </td><td><ul><li>Importing a markdown note.</li></ul></td><td> </td><td>Yes</td></tr><tr><td><code>force-graph</code></td><td> </td><td><ul><li>Note map</li></ul></td><td> </td><td> </td></tr></tbody></table></figure>
|
||||
@ -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`
|
||||
|
||||
<figure class="table" style="width:100%"><table class="ck-table-resized"><colgroup><col> <col> <col> <col></colgroup><tbody><tr><td><code>better-sqlite3</code> version</td><td>SQLite version</td><td>Node.js prebuilds</td><td>Electron.js prebuilds</td></tr><tr><td>8.4.0</td><td><3.43.0</td><td>v20</td><td>???</td></tr><tr><td>8.5.0</td><td>v20</td><td>v25</td></tr><tr><td>8.5.1</td><td> </td><td>v26</td></tr><tr><td>8.5.2</td><td>v20 (macOS + arm64)</td></tr><tr><td>8.6.0</td><td>3.43.0</td><td> </td></tr><tr><td>8.7.0</td><td>3.43.1</td><td> </td></tr><tr><td>9.0.0</td><td>3.43.2</td><td> </td><td>v27</td></tr><tr><td>9.1.0</td><td>3.44.0</td><td> </td></tr><tr><td>9.1.1</td><td>macOS + Alpine</td></tr><tr><td>9.2.0</td><td>3.44.2</td><td> </td></tr><tr><td>9.2.1 / 9.2.2</td><td> </td><td>v28</td></tr><tr><td>9.3.0</td><td>3.45.0</td><td> </td></tr><tr><td>9.4.0</td><td>3.45.1</td><td> </td></tr><tr><td>9.4.1</td><td>Windows arm, arm64</td></tr><tr><td>9.4.2</td><td> </td><td><v29</td></tr><tr><td>9.4.3</td><td> </td><td><v29</td></tr><tr><td>9.4.4</td><td> </td><td>v29</td></tr><tr><td>9.4.5</td><td>Better prebuilds</td></tr><tr><td>9.5.0</td><td>3.45.2</td><td> </td></tr><tr><td>9.6.0</td><td>3.45.3</td><td> </td><td>v30</td></tr><tr><td>10.0.0</td><td>v22</td></tr><tr><td>10.1.0</td><td>3.46.0</td><td> </td></tr><tr><td>11.0.0</td><td>>21</td></tr><tr><td>11.1.0 (prerelease)</td><td> </td><td> </td><td>v31</td></tr><tr><td>11.1.1</td><td> </td><td> </td></tr><tr><td>11.1.2</td><td> </td><td> </td></tr></tbody></table></figure>
|
||||
@ -0,0 +1,2 @@
|
||||
# Testing compatibility
|
||||
<figure class="table" style="width:100%"><table class="ck-table-resized"><colgroup><col> <col> <col></colgroup><thead><tr><th><code>better-sqlite3</code> version<br><a href="https://github.com/WiseLibs/better-sqlite3/releases">Change log</a></th><th>SQLite version<br><a href="https://www.sqlite.org/changes.html">Change log</a></th><th>Compatibility with upstream Trilium</th></tr></thead><tbody><tr><td>8.4.0</td><td><3.43.0</td><td>Compatible, same version.</td></tr><tr><td>8.6.0</td><td>3.43.0</td><td> </td></tr><tr><td>8.7.0</td><td>3.43.1</td><td> </td></tr><tr><td>9.0.0</td><td>3.43.2</td><td> </td></tr><tr><td>9.1.0 + 9.1.1</td><td>3.44.0</td><td> </td></tr><tr><td>9.2.0 + 9.2.1 + 9.2.2</td><td>3.44.2</td><td> </td></tr><tr><td>9.3.0</td><td>3.45.0</td><td> </td></tr><tr><td>9.4.0, 9.4.1, 9.4.2, 9.4.3, 9.4.4, 9.4.5</td><td>3.45.1</td><td> </td></tr><tr><td>9.5.0</td><td>3.45.2</td><td> </td></tr><tr><td>9.6.0 / 10.0.0</td><td>3.45.3</td><td> </td></tr><tr><td>10.1.0 / 11.0.0 / 11.1.1 / 11.1.2 / 11.2.0 / 11.2.1</td><td>3.46.0</td><td> </td></tr><tr><td>11.3.0</td><td>3.46.1</td><td> </td></tr></tbody></table></figure>
|
||||
@ -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`.
|
||||
@ -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)
|
||||
@ -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 = `<div>Hi</div>`;
|
||||
|
||||
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`.
|
||||
@ -0,0 +1,4 @@
|
||||
# Versions and external plugins
|
||||
## External plugins
|
||||
|
||||
<figure class="table" style="width:100%"><table class="ck-table-resized"><colgroup><col> <col> <col></colgroup><tbody><tr><td>trilium-ckeditor5</td><td>43.2.0</td><td> </td></tr><tr><td><code>ckeditor5-math</code></td><td> </td><td>See <a class="reference-link" href="../ckeditor5-math.md">ckeditor5-math</a>.</td></tr><tr><td> </td><td> </td><td> </td></tr></tbody></table></figure>
|
||||
@ -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.
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
@ -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
|
||||
```
|
||||