docs(dev): reorganize and clean up technical documentation

This commit is contained in:
Elian Doran
2025-11-04 10:55:48 +02:00
parent db644f20ed
commit 0ae4defc6d
71 changed files with 1144 additions and 1757 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,35 @@
<p>Nightly releases are versions built every day, containing the latest improvements
and bugfixes, directly from the main development branch. These versions
are generally useful in preparation for a release, to ensure that there
are no significant bugs that need to be addressed first, or they can be
used to confirm whether a particular bug is fixed or feature is well implemented.</p>
<h2>Regarding the stability</h2>
<p>Despite being on a development branch, generally the main branch is pretty
stable since PRs are tested before they are merged. If you notice any issues,
feel free to report them either via a ticket or via the Matrix.</p>
<h2>Downloading the nightly release manually</h2>
<p>Go to <a href="https://github.com/TriliumNext/Trilium/releases/tag/nightly">github.com/TriliumNext/Trilium/releases/tag/nightly</a> and
look for the artifacts starting with <code>TriliumNotes-main</code>. Choose
the appropriate one for your platform (e.g. <code>windows-x64.zip</code>).</p>
<p>Depending on your use case, you can either test the portable version or
even use the installer.</p>
<aside class="admonition note">
<p>If you choose the installable version (e.g. the .exe on Windows), it will
replace your stable installation.</p>
</aside>
<aside class="admonition important">
<p>By default, the nightly uses the same database as the production version.
Generally you could easily downgrade if needed. However, if there are changes
to the database or sync version, it will not be possible to downgrade without
having to restore from a backup.</p>
</aside>
<h2>Automatically download and install the latest nightly</h2>
<p>This is pretty useful if you are a beta tester that wants to periodically
update their version:</p>
<p>On Ubuntu:</p><pre><code class="language-text-x-trilium-auto">#!/usr/bin/env bash
name=TriliumNotes-linux-x64-nightly.deb
rm -f $name*
wget https://github.com/TriliumNext/Trilium/releases/download/nightly/$name
sudo apt-get install ./$name
rm $name</code></pre>

View File

@@ -0,0 +1,7 @@
<p>Older versions of Trilium Notes allowed the use of Common.js module imports
inside backend scripts, such as:</p><pre><code class="language-text-x-trilium-auto">const isBetween = require('dayjs/plugin/isBetween')
api.dayjs.extend(isBetween)</code></pre>
<p>For newer versions, Node.js imports are <strong>not officially supported anymore</strong>,
since we've added a bundler which makes it more difficult to reuse dependencies.</p>
<p>Theoretically it's still possible to use imports by manually setting up
a <code>node_modules</code> in the server directory via <code>npm</code> or <code>pnpm</code>.</p>

View File

@@ -0,0 +1,9 @@
<p>In <code>doRender()</code>:</p><pre><code class="language-text-x-trilium-auto">this.cssBlock(`#my-widget {
position: absolute;
bottom: 40px;
left: 60px;
z-index: 1;
}`)</code></pre>
<hr>
<p>Reference: <a href="https://trilium.rocks/X7pxYpiu0lgU">https://trilium.rocks/X7pxYpiu0lgU</a>
</p>

View File

@@ -0,0 +1,30 @@
<ul>
<li><code>doRender</code> must not be overridden, instead <code>doRenderBody()</code> has
to be overridden.</li>
<li><code>parentWidget()</code> must be set to <code>“rightPane”</code>.</li>
<li><code>widgetTitle()</code> getter can optionally be overriden, otherwise
the widget will be displayed as “Untitled widget”.</li>
</ul><pre><code class="language-text-x-trilium-auto">const template = `&lt;div&gt;Hi&lt;/div&gt;`;
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();</code></pre>
<p>The implementation is in <code>src/public/app/widgets/right_panel_widget.js</code>.</p>

View File

@@ -16,11 +16,11 @@
module.exports = new MyWidget();</code></pre>
<p>To implement this widget:</p>
<ol>
<li>Create a new <code>JS Frontend</code> note in Trilium and paste in the code
<li data-list-item-id="ee416db068caeb5aebc3edf8d313cdbbf">Create a new <code>JS Frontend</code> note in Trilium and paste in the code
above.</li>
<li>Assign the <code>#widget</code> <a href="#root/_help_zEY4DaJG4YT5">attribute</a> to
<li data-list-item-id="ec82d101a82bd1bdbbc180294534aecaa">Assign the <code>#widget</code> <a href="#root/_help_zEY4DaJG4YT5">attribute</a> to
the <a href="#root/_help_BFs8mudNFgCS">note</a>.</li>
<li>Restart Trilium or reload the window.</li>
<li data-list-item-id="ee1c3b9d316a04a986cc0be3b57d4c569">Restart Trilium or reload the window.</li>
</ol>
<p>To verify that the widget is working, open the developer tools (<code>Cmd</code> + <code>Shift</code> + <code>I</code>)
and run <code>document.querySelector("#my-widget")</code>. If the element
@@ -87,5 +87,18 @@ module.exports = new MyWidget();</code></pre>
}
module.exports = new MyWidget();</code></pre>
<p>Reload the application one last time. When you click the button, a "Hello
World!" message should appear, confirming that your widget is fully functional.</p>
<p><code>parentWidget()</code> can be given the following values:</p>
<ul>
<li data-list-item-id="ebf9bc7b43e420c012d4c72b3b95a8cf0"><code>left-pane</code> - This renders the widget on the left side of the
screen where the note tree lives.</li>
<li data-list-item-id="ec6ca6cd1ed1b9157edc99a61e4b9f336"><code>center-pane</code> - This renders the widget in the center of the
layout in the same location that notes and splits appear.</li>
<li data-list-item-id="e8575696a825b1dce0a62a3ffb6b59ae8"><code>note-detail-pane</code> - This renders the widget <em>with</em> the
note in the center pane. This means it can appear multiple times with splits.</li>
<li
data-list-item-id="e064dfaa93f31a16d42c10c4c45d903be"><code>right-pane</code> - This renders the widget to the right of any opened
notes.</li>
</ul>
<p><a href="#root/pOsGYCXsbNQG/BgmBlOIl72jZ/_help_s8alTXmpFR61">Reload</a> the application
one last time. When you click the button, a "Hello World!" message should
appear, confirming that your widget is fully functional.</p>

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +0,0 @@
# Documentation
## Editing the documentation
To edit the documentation run `pnpm edit-docs:edit-docs`. This will spin up a custom Trilium desktop instance which automatically imports the documentation into memory. Any changes will update in the background the files which can then be committed.
## Automation
The documentation is built via `apps/build-docs`:
1. The output directory is cleared.
2. The User Guide and the Developer Guide are built.
1. The documentation from the repo is archived and imported into an in-memory instance.
2. The documentation is exported using the shared theme.
3. The API docs (internal and ETAPI) are statically rendered via Redocly.
4. The script API is generated via `typedoc`
The `deploy-docs` workflow triggers the documentation build and uploads it to CloudFlare Pages.
## Building locally
In the Git root:
* Run `pnpm docs:build`. The built documentation will be available in `site` at Git root.
* To also run a webserver to test it, run `pnpm docs:preview` (this will not build the documentation) and navigate to `localhost:9000`.

View File

@@ -8,7 +8,7 @@ Releases are usually made directly from the `main` branch.
The process is as follows:
1. Edit the <a class="reference-link" href="Documentation.md">Documentation</a> to add a corresponding entry in the _Release notes_ section.
1. Edit the <a class="reference-link" href="../Documentation.md">Documentation</a> to add a corresponding entry in the _Release notes_ section.
2. In the root `package.json`, set `version` to the new version to be released.
3. Run `chore:update-version` to automatically update the version of the rest of the `package.json` files.
4. Run `pnpm i` to update the package lock as well.

View File

@@ -1,5 +1,5 @@
# Differences from upstream
* Embeds [`~~isaul32/ckeditor5-math~~`](https://github.com/isaul32/ckeditor5-math)  <a class="reference-link" href="../ckeditor5-math.md">ckeditor5-math</a>, which is a third-party plugin for adding math support. CKEditor itself also has a [math plugin](https://ckeditor.com/docs/ckeditor5/latest/features/math-equations.html) with MathType and ChemType but it's premium-only.
* Embeds [`~~isaul32/ckeditor5-math~~`](https://github.com/isaul32/ckeditor5-math)  <a class="reference-link" href="ckeditor5-math.md">ckeditor5-math</a>, which is a third-party plugin for adding math support. CKEditor itself also has a [math plugin](https://ckeditor.com/docs/ckeditor5/latest/features/math-equations.html) with MathType and ChemType but it's premium-only.
* Zadam left a TODO in `findandreplaceUI`: `// FIXME: keyboard shortcut doesn't work:` [`https://github.com/ckeditor/ckeditor5/issues/10645`](https://github.com/ckeditor/ckeditor5/issues/10645)
* `packages\ckeditor5-build-balloon-block\src\mention_customization.js` introduces note insertion via `@` character.

View File

@@ -0,0 +1,16 @@
# ckeditor5-math
<figure class="image image-style-align-right"><img src="ckeditor5-math_image.png"><figcaption><code>ckeditor5-math</code> in action.</figcaption></figure>
A fork of [isaul32/ckeditor5-math](https://github.com/isaul32/ckeditor5-math), which is the CKEditor5 plugin which adds the math functionality. We keep our own version to be able to use it on the latest version of CKEditor, alongside some custom improvements.
## Development environment
* Tested on Node.js 20.
* The package manager is yarn 1 (v1.22.22 is known to be working fine for it at the time of writing).
Important commands:
* To check if the code has any formatting issues: `yarn lint`
* To start a live preview: `yarn start`
* To run the tests: `yarn test`
* Note that this requires Chromium, on NixOS this can be achieved by running a `nix-shell -p chromium`, and running `CHROME_BIN=$(which chromium) yarn test` inside it.

View File

@@ -1,2 +1,2 @@
# Updating dependencies
<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>&nbsp;</th><th>Protected by unit tests</th></tr></thead><tbody><tr><td><code>better-sqlite3</code></td><td>&nbsp;</td><td>See&nbsp;<a class="reference-link" href="Updating%20dependencies/bettersqlite%20binaries.md">bettersqlite binaries</a>.</td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>jsdom</code></td><td>&nbsp;</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>&nbsp;</td><td><ul><li>Sync</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>axios</code></td><td>&nbsp;</td><td><ul><li>Can't be directly tested, as it's exposed only via the backend script API.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>sax</code></td><td>&nbsp;</td><td><ul><li>EverNote imports</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><ul><li><code>ws</code></li><li><code>debounce</code></li></ul></td><td>&nbsp;</td><td><ul><li>Check any action is reported from server to client (e.g. delete a note).</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>ejs</code></td><td>&nbsp;</td><td><ul><li>Onboarding / first setup</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>dayjs</code></td><td>&nbsp;</td><td><ul><li>Day notes</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>semver</code></td><td>&nbsp;</td><td><ul><li>Application should start.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>https-proxy-agent</code></td><td>&nbsp;</td><td>???</td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>sax</code></td><td>&nbsp;</td><td><ul><li>EverNote import</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>ini</code></td><td>&nbsp;</td><td><ul><li>Affects config, generally if the application starts then it should be OK.</li></ul></td><td>&nbsp;</td><td>&nbsp;</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>&nbsp;</td><td>&nbsp;</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 &amp; replace dialog.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>knockout.js</code></td><td>&nbsp;</td><td><ul><li>Used in rendering the login and main layout of the application.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>normalize.min.css</code></td><td>&nbsp;</td><td><ul><li>Used in shared notes.</li></ul></td><td>&nbsp;</td><td>&nbsp;</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>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>fancytree</code></td><td>&nbsp;</td><td><ul><li>The note tree should be fully functional.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>bootstrap</code></td><td>&nbsp;</td><td><ul><li>Check mostly the on-boarding pages, when there is no database.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>electron-debug</code></td><td>&nbsp;</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>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>electron-dl</code></td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>eslint</code></td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>marked</code></td><td>&nbsp;</td><td><ul><li>Importing a markdown note.</li></ul></td><td>&nbsp;</td><td>Yes</td></tr><tr><td><code>force-graph</code></td><td>&nbsp;</td><td><ul><li>Note map</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table>
# Per-dependency checks
<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>&nbsp;</th><th>Protected by unit tests</th></tr></thead><tbody><tr><td><code>better-sqlite3</code></td><td>&nbsp;</td><td>See&nbsp;<a class="reference-link" href="Per-dependency%20checks/bettersqlite%20binaries.md">bettersqlite binaries</a>.</td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>jsdom</code></td><td>&nbsp;</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>&nbsp;</td><td><ul><li>Sync</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>axios</code></td><td>&nbsp;</td><td><ul><li>Can't be directly tested, as it's exposed only via the backend script API.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>sax</code></td><td>&nbsp;</td><td><ul><li>EverNote imports</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><ul><li><code>ws</code></li><li><code>debounce</code></li></ul></td><td>&nbsp;</td><td><ul><li>Check any action is reported from server to client (e.g. delete a note).</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>ejs</code></td><td>&nbsp;</td><td><ul><li>Onboarding / first setup</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>dayjs</code></td><td>&nbsp;</td><td><ul><li>Day notes</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>semver</code></td><td>&nbsp;</td><td><ul><li>Application should start.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>https-proxy-agent</code></td><td>&nbsp;</td><td>???</td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>sax</code></td><td>&nbsp;</td><td><ul><li>EverNote import</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>ini</code></td><td>&nbsp;</td><td><ul><li>Affects config, generally if the application starts then it should be OK.</li></ul></td><td>&nbsp;</td><td>&nbsp;</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>&nbsp;</td><td>&nbsp;</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 &amp; replace dialog.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>knockout.js</code></td><td>&nbsp;</td><td><ul><li>Used in rendering the login and main layout of the application.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>normalize.min.css</code></td><td>&nbsp;</td><td><ul><li>Used in shared notes.</li></ul></td><td>&nbsp;</td><td>&nbsp;</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>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>fancytree</code></td><td>&nbsp;</td><td><ul><li>The note tree should be fully functional.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>bootstrap</code></td><td>&nbsp;</td><td><ul><li>Check mostly the on-boarding pages, when there is no database.</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>electron-debug</code></td><td>&nbsp;</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>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>electron-dl</code></td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>eslint</code></td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr><tr><td><code>marked</code></td><td>&nbsp;</td><td><ul><li>Importing a markdown note.</li></ul></td><td>&nbsp;</td><td>Yes</td></tr><tr><td><code>force-graph</code></td><td>&nbsp;</td><td><ul><li>Note map</li></ul></td><td>&nbsp;</td><td>&nbsp;</td></tr></tbody></table>

View File

@@ -0,0 +1,25 @@
# bettersqlite binaries
### The native node bindings
`better-sqlite3` has native Node bindings. With updates of `better-sqlite3`, but also of Electron and Node.js versions, these bindings need to be updated.
Note that Electron and Node.js versions need different versions of these bindings, since Electron usually packs a different version of Node.js.
During development, `pnpm install` tries to build or reuse prebuilt natives for the current Node.js version. This makes `npm run start-server` work out of the box. Trying to run `npm run start-electron` with these versions generally causes an error such as this:
```
Uncaught Exception:
Error: The module '/Users/elian/Projects/Notes/node_modules/better-sqlite3/build/Release/better_sqlite3.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 108. This version of Node.js requires
NODE_MODULE_VERSION 116. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
```
### How the natives are handled
To avoid issues between the `server` and the `desktop`, the `desktop` build gets its own copy of the `bettersqlite3` dependency in its `node_module`. This copy is then rebuilt automatically to match the Electron version.
This process of rebuilding is handled by `scripts/electron-rebuild.mts` which runs automatically after `pnpm install` (via `postinstall`).
If needed, the script can be run manually again via `pnpm postinstall`.

View File

@@ -1,29 +1,32 @@
# Documentation
<figure class="image image-style-align-right"><img style="aspect-ratio:205/162;" src="Documentation_image.png" width="205" height="162"></figure>
There are multiple types of documentation for Trilium:
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/attachments/2bUrJyt2yfsd/image/Documentation_image.png" width="205" height="162">
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.
* _Release Notes_, this contains the change log for each released or soon-to-be-released version. The release notes are used automatically by the CI when releasing a version.
* The _Script API_, which is an automatically generated documentation for the front-end and back-end APIs for scripts.
## Editing documentation
## Location of the documentation
All documentation is stored in the [Trilium](https://github.com/TriliumNext/Trilium) repository:
* `docs/Developer Guide` contains Markdown documentation that can be modified either externally (using a Markdown editor, or internally using Trilium).
* `docs/Release Notes` is also stored in Markdown format and can be freely edited.
* `docs/Script API` contains auto-generated files and thus must not be modified.
* `docs/User Guide` contains also Markdown-only documentation but must generally not be edited externally.
* The reason is that the `pnpm edit-docs:edit-docs` feature will not only import/export this documentation, but also generate the corresponding HTML documentation and meta structure in `src/public/app/doc_notes/en/User Guide`.
* It's theoretically possible to edit the Markdown files externally and then run `docs:edit` and trigger a change in order to build the documentation, but that would not be a very productive workflow.
## Editing the documentation
There are two ways to modify documentation:
* Using a special mode of Trilium.
* By manually editing the files.
### Using `docs:edit`
### Using the `edit-docs` app
To edit the documentation using Trilium, set up a working development environment and run the following commands:
* On most operating systems, `npm run electron:switch` followed by `npm run docs:edit`
* On NixOS, `npm run docs:edit-nix`.
> [!NOTE]
> `npm run docs:edit` acts very similar to `npm run electron:start` in the sense that you cannot both be editing documentation and starting a server. Using both `npm run electron:start` and `docs:edit` is possible, since they are using the same Electron instance.
To edit the documentation using Trilium, set up a working development environment via <a class="reference-link" href="Environment%20Setup.md">Environment Setup</a> and run the following command: `pnpm edit-docs:edit-docs`.
How it works:
@@ -50,24 +53,30 @@ Important aspects to consider:
* The Trilium import/export mechanism is not perfect, so if you make some modifications to the documentation using `docs:edit`, at the next import/export/import cycle some whitespace might get thrown in. It's generally safe to commit the changes as-is.
* Since we are importing Markdown, editing HTML and then exporting the HTML back to Markdown there might be some edge cases where the formatting is not properly preserved. Try to identify such cases and report them in order to get them fixed (this will benefit also the users).
## Location of the documentation
## Automation
All documentation is stored in the [Notes](https://github.com/TriliumNext/Trilium) repository:
The documentation is built via `apps/build-docs`:
* `docs/Developer Guide` contains Markdown documentation that can be modified either externally (using a Markdown editor, or internally using Trilium).
* `docs/Release Notes` is also stored in Markdown format and can be freely edited.
* `docs/Script API` contains auto-generated files and thus must not be modified.
* `docs/User Guide` contains also Markdown-only documentation but must generally not be edited externally.
* The reason is that the `docs:edit` feature will not only import/export this documentation, but also generate the corresponding HTML documentation and meta structure in `src/public/app/doc_notes/en/User Guide`.
* It's theoretically possible to edit the Markdown files externally and then run `docs:edit` and trigger a change in order to build the documentation, but that would not be a very productive workflow.
1. The output directory is cleared.
2. The User Guide and the Developer Guide are built.
1. The documentation from the repo is archived and imported into an in-memory instance.
2. The documentation is exported using the shared theme.
3. The API docs (internal and ETAPI) are statically rendered via Redocly.
4. The script API is generated via `typedoc`
The `deploy-docs` workflow triggers the documentation build and uploads it to CloudFlare Pages.
## Updating the Script API
As mentioned previously, the Script API is not manually editable since it is auto-generated using TypeDoc.
To update the API documentation, simply run `npm run docs:build`. Compare the changes (if any) and commit them.
To update the API documentation, simply run `pnpm docs:build`. Compare the changes (if any) and commit them.
Note that in order to simulate the environment a script would have, some fake source files (in the sense that they are only used for documentation) are being used as entrypoints for the documentation:
Note that in order to simulate the environment a script would have, some fake source files (in the sense that they are only used for documentation) are being used as entrypoints for the documentation. Look for `backend_script_entrypoint` and `frontend_script_entrypoint` in `apps/build-docs/src`.
* For back-end scripts, the script is located in `src/services/backend_script_entrypoint.ts`.
* For front-end scripts, the script is located in `src/public/app/services/frontend_script_entrypoint.ts`.
## Building locally
In the Git root:
* Run `pnpm docs:build`. The built documentation will be available in `site` at Git root.
* To also run a webserver to test it, run `pnpm docs:preview` (this will not build the documentation) and navigate to `localhost:9000`.

View File

@@ -13,6 +13,8 @@ https://triliumnext.github.io/Docs/Wiki/
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.
### Deprecated `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`
@@ -20,4 +22,12 @@ For links to headings, simply add the heading after the `.md`: `tree-concepts.md
You can identify those by looking for:
* `.attr("data-help-page"`
* `data-help-page="`
* `data-help-page="`
### More modern `data-in-app-help` attribute
Instead of opening in a web browser, this opens the help directly in the application in a split view. This is handled via the `data-in-app-help` attribute, where the value is the note ID of the help page without the `_help_` prefix.
### React
Use the `HelpButton` component in the same fashion as the `data-in-app-help` attribute.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -1,12 +0,0 @@
# Download latest nightly and install it
On Ubuntu:
```
#!/usr/bin/env bash
name=TriliumNotes-linux-x64-nightly.deb
rm -f $name*
wget https://github.com/TriliumNext/Trilium/releases/download/nightly/$name
sudo apt-get install ./$name
rm $name
```

View File

@@ -1,41 +0,0 @@
# bettersqlite binaries
### The native node bindings
`better-sqlite3` has native Node bindings. With updates of `better-sqlite3`, but also of Electron and Node.js versions, these bindings need to be updated.
Note that Electron and Node.js versions need different versions of these bindings, since Electron usually packs a different version of Node.js.
During development, `npm install` tries to build or reuse prebuilt natives for the current Node.js version. This makes `npm run start-server` work out of the box. Trying to run `npm run start-electron` with these versions generally causes an error such as this:
```
Uncaught Exception:
Error: The module '/Users/elian/Projects/Notes/node_modules/better-sqlite3/build/Release/better_sqlite3.node'
was compiled against a different Node.js version using
NODE_MODULE_VERSION 108. This version of Node.js requires
NODE_MODULE_VERSION 116. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
```
### How the natives are handled
Locally, this can be fixed by rebuilding the binaries, which is what `npm run switch-electron` does, which uses `electron-rebuild` under the hood.
When the deliveries are built (see <a class="reference-link" href="../../../Building/Build%20deliveries%20locally.md">Build deliveries locally</a>), it is not feasible to rebuild the dependencies since we are building for multiple platforms. Luckily, `better-sqlite3` provides these prebuilt binaries from us, available as artifacts on [their GitHub releases page](https://github.com/WiseLibs/better-sqlite3/releases/). 
The build script manages the natives for `better-sqlite3` by keeping a copy of the `.node` file for every platform in `bin/better-sqlite3`.
Whenever the version of `better-sqlite3` changes, the `.node` files must also be renewed based on their releases page. To simplify this process, a script was created in `bin/better-sqlite3/update.sh`.
## How to update the natives
The update script needs to know the version of Electron or Node.js for which to download the prebuilt binaries.
If you get errors during download, check on the [releases page](https://github.com/WiseLibs/better-sqlite3/releases/) to ensure that this particular combination of Electron/Node actually exists for the given release.
To determine the `NODE_MODULE_VERSION` that is required, look for `This version of Node.js requires`
`NODE_MODULE_VERSION` in the error when starting Trilium via:
* `npm run start-electron` (or run any Electron [delivery](../../../Building/Build%20deliveries%20locally.md)), case in which the `ELECTRON_VERSION` variable needs to be changed.
* `npm run start-server` (or run the Linux server delivery), case in which the `NODE_VERSION` variable needs to be changed.
Check which files got changed after running the update script and for each platform that got changed, test it locally via <a class="reference-link" href="../../../Building/Build%20deliveries%20locally.md">Build deliveries locally</a> or via the CI.

View File

@@ -1,16 +0,0 @@
# Server-side imports
Trilium Notes allowed the use of Common.js module imports inside backend scripts, such as:
```
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`:
```
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`.

View File

@@ -1,33 +0,0 @@
# Widgets
To create a basic widget, simply create a code note with type “JS frontend”. Add the `#widget` label in order for it to be loaded at startup.
```
const template = `<div id="my-widget"><button>Click Me!</button></div>`;
class MyWidget extends api.BasicWidget {
get position() { return 1; }
get parentWidget() { return "left-pane" }
doRender() {
this.$widget = $(template);
return this.$widget;
}
}
module.exports = new MyWidget();
```
`parentWidget()` can be given the following values:
* `left-pane` - This renders the widget on the left side of the screen where the note tree lives.
* `center-pane` - This renders the widget in the center of the layout in the same location that notes and splits appear.
* `note-detail-pane` - This renders the widget _with_ the note in the center pane. This means it can appear multiple times with splits.
* `right-pane` - This renders the widget to the right of any opened notes.
* * *
Reference:
* [https://trilium.rocks/X7pxYpiu0lgU](https://trilium.rocks/X7pxYpiu0lgU)
* [https://github.com/zadam/trilium/wiki/Widget-Basics](https://github.com/zadam/trilium/wiki/Widget-Basics)
* [https://github.com/zadam/trilium/wiki/Frontend-Basics](https://github.com/zadam/trilium/wiki/Frontend-Basics)

View File

@@ -1,21 +0,0 @@
# Building the editor
First, make sure <a class="reference-link" href="Environment%20setup.md">Environment setup</a> is set up.
## Trigger the build
```
cd packages/ckeditor5-build-trilium
yarn build
```
This will trigger a change in the `build` directory.
## Copy the build artifact to the main repo
Go to `packages/ckeditor5-build-balloon-trilium/build` and copy `ckeditor.js` and `ckeditor.js.map` to `libraries/ckeditor` in the `Notes` repository.
An example shell command to copy it:
```
cp build/ckeditor.* ~/Projects/TriliumNext/Notes/libraries/ckeditor/
```

View File

@@ -1,26 +0,0 @@
# Environment setup
## Clone the repository
To set up the repository:
```
git clone https://github.com/TriliumNext/trilium-ckeditor5.git
```
## Install dependencies
First, install root dependencies:
```
cd trilium-ckeditor5
yarn install
```
Secondly, install the Trilium build dependencies:
```
cd packages/ckeditor5-build-trilium
yarn install
```
To trigger the build, see <a class="reference-link" href="Building%20the%20editor.md">Building the editor</a>.

View File

@@ -1,65 +0,0 @@
# Updating to a newer version of CKEditor
## Before updating
Make sure that all the plugins are compatible with this version:  <a class="reference-link" href="Versions%20and%20external%20plugins.md">Versions and external plugins</a>. If not, they will need to be updated to the same version as the one you are updating, by altering their `package.json`.
If the plugin is external to the Trilium organisation, it needs to be forked first.
## Environment setup
The first step is to add the CKEditor source as a remote. This only needs to be done once.
```
git remote add upstream ssh://git@github.com/ckeditor/ckeditor5.git
git fetch upstream
```
## Update steps
Due to how the repository is structured, updates to the CKEditor are a bit difficult.
1. `git fetch upstream`
2. Pick a version and merge with it: `git merge -X theirs v99.2.0`
3. When there are complicated conflicts, sometimes it's easier to take everything from the target version instead, for a given path: `git checkout v99.2.0 -- "packages/ckeditor5-list/**"`.
4. Go in `packages/ckeditor5-build-trilium/package.json` and run `node sync-version.js` to update the `package.json` with the new versions. Review and commit the change.
5. Follow again the dependency setup in <a class="reference-link" href="Environment%20setup.md">Environment setup</a>, as they have changed.
6. [Run the build](Building%20the%20editor.md) and check that it works.
## Final steps
1. Start the TriliumNext server
2. If updated to a newer version of CKEditor, check type `CKEDITOR_VERSION` in the browser/Electron console to ensure that the correct version is used.
3. Do a basic sanity check as well.
4. Commit and push the change on both sides (in the `trilium-ckeditor5` repo and in the `Notes` repo).
## Troubleshooting client side errors
These errors might show up when testing the Trilium app:
```
ReferenceError: CKEditor is not defined
```
Usually this is a side effect of another error, check the logs carefully to see if there is any other related error (perhaps a `CKEditorError`).
* * *
```
Uncaught error: Message: CKEditorError: ckeditor-duplicated-modules
```
Most likely cause is one of the external plugins is incompatible with this version.
For example, to disable the Math plugin, go to `packages/ckeditor5-build-trilium/src/config.ts` and modify:
```diff
-import Math from '@triliumnext/ckeditor5-math/src/math';
-import AutoformatMath from '@triliumnext/ckeditor5-math/src/autoformatmath';
export const COMMON_PLUGINS = [
- Math,
- AutoformatMath,
]
```
In this case, make sure to align the version of all the external plugins with the one you are updating to, usually by forking the external plugin and updating its versions.

View File

@@ -1,8 +0,0 @@
# Versions and external plugins
## External plugins
| | | |
| --- | --- | --- |
| trilium-ckeditor5 | 43.2.0 | |
| `ckeditor5-math` | | See <a class="reference-link" href="../ckeditor5-math.md">ckeditor5-math</a>. |
| | | |

View File

@@ -1,29 +0,0 @@
# ckeditor5-math
<figure class="image image-style-align-right"><img src="ckeditor5-math_image.png"><figcaption><code>ckeditor5-math</code> in action.</figcaption></figure>
A fork of [isaul32/ckeditor5-math](https://github.com/isaul32/ckeditor5-math), which is the CKEditor5 plugin which adds the math functionality. The fork was created to handle <a class="reference-link" href="#root/OeKBfN6JbMIq/MF99QFRe1gVy/orRZgNnWETTw/tXFiNo5IYd31/jMHQCKORhZge">#297: Insert Math appears to be broken</a>.
## Development environment
* Tested on Node.js 20.
* The package manager is yarn 1 (v1.22.22 is known to be working fine for it at the time of writing).
Important commands:
* To check if the code has any formatting issues: `yarn lint`
* To start a live preview: `yarn start`
* To run the tests: `yarn test`
* Note that this requires Chromium, on NixOS this can be achieved by running a `nix-shell -p chromium`, and running `CHROME_BIN=$(which chromium) yarn test` inside it.
## 📦 Packages
The built artifact of the plugin is released by the CI and available on the [GitHub NPM registry](https://github.com/TriliumNext/ckeditor5-math/pkgs/npm/ckeditor5-math).
Note that due to limitations on GitHub's registry, it is not possible to install this package without setting up a personal access token (even though the package itself is public). See <a class="reference-link" href="#root/ZlxZh8NH5frM/jUH2zJGXM67N">[missing note]</a> for more information.
## ⬆️ Integrating with <a class="reference-link" href="CKEditor">CKEditor</a>
1. Release a new version: <a class="reference-link" href="ckeditor5-math/Release%20management%20%26%20continuou.md">Release management &amp; continuous integration</a>
2. In `trilium-ckeditor5`, go to `packages/ckeditor5-build-trilium/package.json` in the CKEditor repository and change the dependency of `@triliumnext/ckeditor5-math` to the newly released version.
3. Run `yarn install`.
4. Proceed with <a class="reference-link" href="CKEditor/Building%20the%20editor.md">Building the editor</a> to integrate everything into TriliumNext and then commit the change.

View File

@@ -1,16 +0,0 @@
# 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.

View File

@@ -1,21 +0,0 @@
# Updating with upstream
If there was a change in the upstream repository ([isaul32/ckeditor5-math](https://github.com/isaul32/ckeditor5-math)), it can be integrated as follows:
1. Add the upstream as remote (`git remote add upstream ssh://git@github.com/isaul32/ckeditor5-math.git`).
2. Fetch the changes: `git fetch upstream`
3. Merge with a tag: `git merge v43.1.2`
4. Solve the conflict in `package.json` by:
1. Taking the same version as the upcoming one and appending `-hotfix1`.
2. Keeping the `@triliumnext/ckeditor5-math` name.
5. Install dependencies: `yarn install`
6. Check that the build works via `yarn prepublishOnly`.
7. Commit the changes, push them.
8. Release a version with <a class="reference-link" href="Release%20management%20%26%20continuou.md">Release management &amp; continuous integration</a>.
## CI job not triggered after pushing all the upstream tags
If the CI job was not triggered, you might have accidentally pushed a lot of tags using `git push --tags`. Manually delete the tag and push it again:
```diff
git push -d origin v43.1.2-hotfix1 && git push --tags
```

View File

@@ -1,38 +0,0 @@
# Running tests
## First-time run
Before starting Playwright, it has to be installed locally via:
```
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:
```
npx playwright test --ui
```
It is also possible to run the interactive code generator instead:
```
npx playwright codegen
```

View File

@@ -1,12 +0,0 @@
# Setting up authentication
There is a setup test that stores the authentication token so that it can be reused throughout all the tests.
If tests fail due to being stuck on login, then it must be run.
To run it manually press “all” near the “Status:” text on top-left of the window
<figure class="image"><img src="1_Setting up authentication_.png"></figure>
Then check “setup” and look for `auth.setup.ts` and press its corresponding Run button:
<figure class="image"><img src="Setting up authentication_.png"></figure>

View File

@@ -17,17 +17,14 @@ Note that some integration tests rely on an in-memory database in order to funct
### REST API testing for the server
Some original work was done by Zadam in `/test-etapi`, using `.http` files.
New effort using `vitest` and `supertest` to initialize the Express server and run assertions without having to make actual requests to the server.
API tests are handled via `vitest` and `supertest` to initialize the Express server and run assertions without having to make actual requests to the server.
An important aspect is that we have access to the Express `app` which allows for interesting assertions such as checking the state of the server, registering debug middleware and so on.
One example is `src/share/routes.spec.ts`.
One example is `src/share/routes.spec.ts`, or for the ETAPI in `apps/server/spec/etapi`.
These integration tests are run alongside unit tests.
## End-to-end testing
* This tests both the client and the server, by running the server and then using Playwright to query the state of the page.
* These can be found in `/e2e`.
See <a class="reference-link" href="Testing/End-to-end%20tests.md">e2e tests</a>.

View File

@@ -0,0 +1,31 @@
# End-to-end tests
* This tests both the client and the server, by running the server and then using Playwright to query the state of the page.
* These can be found in `apps/server-e2e` and `apps/desktop/e2e`.
## First-time run
Before starting Playwright, it has to be installed locally via:
```
pnpm playwright install
```
## Starting the integration test server
Simply run `pnpm e2e` in one of the e2e projects.
The integration server doesn't have authentication enabled to avoid login issues.
## Starting the interactive test runner
After starting the integration test server, to run the Playwright UI, run in the terminal:
```
pnpm playwright test --ui
```
It is also possible to run the interactive code generator instead:
```
pnpm playwright codegen
```

View File

@@ -36,5 +36,5 @@ The solution is to remove `node_modules` and reinstall all dependencies:
```
rm -r node_modules
npm install
pnpm install
```

View File

@@ -14081,6 +14081,33 @@
"format": "markdown",
"dataFileName": "Safe mode.md",
"attachments": []
},
{
"isClone": false,
"noteId": "HAIOFBoYIIdO",
"notePath": [
"pOsGYCXsbNQG",
"tC7s2alapj8V",
"HAIOFBoYIIdO"
],
"title": "Nightly release",
"notePosition": 260,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "iconClass",
"value": "bx bx-moon",
"isInheritable": false,
"position": 30
}
],
"format": "markdown",
"dataFileName": "Nightly release.md",
"attachments": []
}
]
},
@@ -14617,152 +14644,238 @@
{
"type": "label",
"name": "iconClass",
"value": "bx bx-info-circle",
"value": "bx bx-window",
"isInheritable": false,
"position": 40
}
],
"format": "markdown",
"dataFileName": "Frontend Basics.md",
"attachments": []
},
{
"isClone": false,
"noteId": "es8OU2GuguFU",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"es8OU2GuguFU"
],
"title": "Examples",
"notePosition": 50,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "shareAlias",
"value": "examples",
"isInheritable": false,
"position": 20
},
{
"type": "label",
"name": "iconClass",
"value": "bx bx-code-alt",
"isInheritable": false,
"position": 30
}
],
"format": "markdown",
"attachments": [],
"dirFileName": "Examples",
"dirFileName": "Frontend Basics",
"children": [
{
"isClone": false,
"noteId": "TjLYAo3JMO8X",
"noteId": "MgibgPcfeuGz",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"es8OU2GuguFU",
"TjLYAo3JMO8X"
"yIhgI5H7A2Sm",
"MgibgPcfeuGz"
],
"title": "\"New Task\" launcher button",
"title": "Custom Widgets",
"notePosition": 10,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"mime": "text/markdown",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "xYjQUYhpbUEW",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "xYmIYSP6wE3F",
"type": "label",
"name": "shareAlias",
"value": "custom-widget",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "6f9hih2hXXZk",
"isInheritable": false,
"position": 30
},
{
"type": "relation",
"name": "internalLink",
"value": "zEY4DaJG4YT5",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "m1lbrzyKDaRB",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
"value": "s8alTXmpFR61",
"isInheritable": false,
"position": 60
},
{
"type": "relation",
"name": "internalLink",
"value": "yIhgI5H7A2Sm",
"isInheritable": false,
"position": 70
},
{
"type": "label",
"name": "iconClass",
"value": "bx bx-task",
"value": "bx bxs-widget",
"isInheritable": false,
"position": 80
},
{
"type": "label",
"name": "shareAlias",
"value": "new-task-button",
"isInheritable": false,
"position": 90
"position": 30
}
],
"format": "markdown",
"dataFileName": "New Task launcher button.md",
"attachments": [
"dataFileName": "Custom Widgets.md",
"attachments": [],
"dirFileName": "Custom Widgets",
"children": [
{
"attachmentId": "9C2JA6tdtRpN",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "New Task launcher button_i.png"
"isClone": false,
"noteId": "YNxAqkI5Kg1M",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"yIhgI5H7A2Sm",
"MgibgPcfeuGz",
"YNxAqkI5Kg1M"
],
"title": "Word count widget",
"notePosition": 10,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "6tZeKvSHEUiB",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "6f9hih2hXXZk",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "s8alTXmpFR61",
"isInheritable": false,
"position": 30
},
{
"type": "label",
"name": "shareAlias",
"value": "word-count",
"isInheritable": false,
"position": 40
}
],
"format": "markdown",
"dataFileName": "Word count widget.md",
"attachments": [
{
"attachmentId": "JhM9NWfebzPi",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Word count widget_image.png"
}
]
},
{
"isClone": false,
"noteId": "SynTBQiBsdYJ",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"yIhgI5H7A2Sm",
"MgibgPcfeuGz",
"SynTBQiBsdYJ"
],
"title": "Widget Basics",
"notePosition": 20,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/markdown",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "zEY4DaJG4YT5",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "BFs8mudNFgCS",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "GLks18SNjxmC",
"isInheritable": false,
"position": 30
},
{
"type": "label",
"name": "shareAlias",
"value": "widget-basics",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "s8alTXmpFR61",
"isInheritable": false,
"position": 40
}
],
"format": "markdown",
"dataFileName": "Widget Basics.md",
"attachments": []
},
{
"isClone": false,
"noteId": "M8IppdwVHSjG",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"yIhgI5H7A2Sm",
"MgibgPcfeuGz",
"M8IppdwVHSjG"
],
"title": "Right pane widget",
"notePosition": 30,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "shareAlias",
"value": "right-pane-widget",
"isInheritable": false,
"position": 20
}
],
"format": "markdown",
"dataFileName": "Right pane widget.md",
"attachments": []
},
{
"isClone": false,
"noteId": "VqGQnnPGnqAU",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"yIhgI5H7A2Sm",
"MgibgPcfeuGz",
"VqGQnnPGnqAU"
],
"title": "CSS",
"notePosition": 40,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "shareAlias",
"value": "css",
"isInheritable": false,
"position": 20
}
],
"format": "markdown",
"dataFileName": "CSS.md",
"attachments": []
}
]
},
{
"isClone": false,
"noteId": "7kZPMD0uFwkH",
"noteId": "es8OU2GuguFU",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"es8OU2GuguFU",
"7kZPMD0uFwkH"
"yIhgI5H7A2Sm",
"es8OU2GuguFU"
],
"title": "Downloading responses from Google Forms",
"title": "Examples",
"notePosition": 20,
"prefix": null,
"isExpanded": false,
@@ -14772,49 +14885,182 @@
{
"type": "label",
"name": "shareAlias",
"value": "responses-from-google-forms",
"value": "examples",
"isInheritable": false,
"position": 20
}
],
"format": "markdown",
"dataFileName": "Downloading responses from Goo.md",
"attachments": []
},
{
"isClone": false,
"noteId": "DL92EjAaXT26",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"es8OU2GuguFU",
"DL92EjAaXT26"
],
"title": "Using promoted attributes to configure scripts",
"notePosition": 30,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
},
{
"type": "label",
"name": "shareAlias",
"value": "promoted-attributes-config",
"name": "iconClass",
"value": "bx bx-code-alt",
"isInheritable": false,
"position": 20
"position": 30
}
],
"format": "markdown",
"dataFileName": "Using promoted attributes to c.md",
"attachments": [
"attachments": [],
"dirFileName": "Examples",
"children": [
{
"attachmentId": "7P3jzVEa1mk7",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Using promoted attributes .png"
"isClone": false,
"noteId": "TjLYAo3JMO8X",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"yIhgI5H7A2Sm",
"es8OU2GuguFU",
"TjLYAo3JMO8X"
],
"title": "\"New Task\" launcher button",
"notePosition": 10,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "xYjQUYhpbUEW",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "xYmIYSP6wE3F",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "6f9hih2hXXZk",
"isInheritable": false,
"position": 30
},
{
"type": "relation",
"name": "internalLink",
"value": "zEY4DaJG4YT5",
"isInheritable": false,
"position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "m1lbrzyKDaRB",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
"value": "s8alTXmpFR61",
"isInheritable": false,
"position": 60
},
{
"type": "relation",
"name": "internalLink",
"value": "yIhgI5H7A2Sm",
"isInheritable": false,
"position": 70
},
{
"type": "label",
"name": "iconClass",
"value": "bx bx-task",
"isInheritable": false,
"position": 80
},
{
"type": "label",
"name": "shareAlias",
"value": "new-task-button",
"isInheritable": false,
"position": 90
}
],
"format": "markdown",
"dataFileName": "New Task launcher button.md",
"attachments": [
{
"attachmentId": "9C2JA6tdtRpN",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "New Task launcher button_i.png"
}
]
},
{
"isClone": false,
"noteId": "7kZPMD0uFwkH",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"yIhgI5H7A2Sm",
"es8OU2GuguFU",
"7kZPMD0uFwkH"
],
"title": "Downloading responses from Google Forms",
"notePosition": 20,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "shareAlias",
"value": "responses-from-google-forms",
"isInheritable": false,
"position": 20
}
],
"format": "markdown",
"dataFileName": "Downloading responses from Goo.md",
"attachments": []
},
{
"isClone": false,
"noteId": "DL92EjAaXT26",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"yIhgI5H7A2Sm",
"es8OU2GuguFU",
"DL92EjAaXT26"
],
"title": "Using promoted attributes to configure scripts",
"notePosition": 30,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "label",
"name": "shareAlias",
"value": "promoted-attributes-config",
"isInheritable": false,
"position": 20
}
],
"format": "markdown",
"dataFileName": "Using promoted attributes to c.md",
"attachments": [
{
"attachmentId": "7P3jzVEa1mk7",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Using promoted attributes .png"
}
]
}
]
}
@@ -14822,162 +15068,76 @@
},
{
"isClone": false,
"noteId": "GPERMystNGTB",
"noteId": "SPirpZypehBG",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"GPERMystNGTB"
"SPirpZypehBG"
],
"title": "Events",
"notePosition": 70,
"title": "Backend scripts",
"notePosition": 30,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/markdown",
"mime": "text/html",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "CdNpE2pqjmI6",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "c5xB8m4g2IY6",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "zEY4DaJG4YT5",
"type": "label",
"name": "shareAlias",
"value": "backend-basics",
"isInheritable": false,
"position": 30
},
{
"type": "label",
"name": "shareAlias",
"value": "events",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "iconClass",
"value": "bx bx-rss",
"value": "bx bx-server",
"isInheritable": false,
"position": 20
"position": 40
}
],
"format": "markdown",
"dataFileName": "Events.md",
"attachments": []
},
{
"isClone": false,
"noteId": "MgibgPcfeuGz",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"MgibgPcfeuGz"
],
"title": "Custom Widgets",
"notePosition": 80,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/markdown",
"attributes": [
{
"type": "label",
"name": "shareAlias",
"value": "custom-widget",
"isInheritable": false,
"position": 20
},
{
"type": "label",
"name": "iconClass",
"value": "bx bxs-widget",
"isInheritable": false,
"position": 30
}
],
"format": "markdown",
"dataFileName": "Custom Widgets.md",
"attachments": [],
"dirFileName": "Custom Widgets",
"dirFileName": "Backend scripts",
"children": [
{
"isClone": false,
"noteId": "YNxAqkI5Kg1M",
"noteId": "fZ2IGYFXjkEy",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"MgibgPcfeuGz",
"YNxAqkI5Kg1M"
"SPirpZypehBG",
"fZ2IGYFXjkEy"
],
"title": "Word count widget",
"title": "Server-side imports",
"notePosition": 10,
"prefix": null,
"isExpanded": false,
"type": "text",
"mime": "text/html",
"attributes": [
{
"type": "relation",
"name": "internalLink",
"value": "6tZeKvSHEUiB",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "6f9hih2hXXZk",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "s8alTXmpFR61",
"isInheritable": false,
"position": 30
},
{
"type": "label",
"name": "shareAlias",
"value": "word-count",
"value": "server-imports",
"isInheritable": false,
"position": 40
"position": 20
}
],
"format": "markdown",
"dataFileName": "Word count widget.md",
"attachments": [
{
"attachmentId": "JhM9NWfebzPi",
"title": "image.png",
"role": "image",
"mime": "image/png",
"position": 10,
"dataFileName": "Word count widget_image.png"
}
]
"dataFileName": "Server-side imports.md",
"attachments": []
},
{
"isClone": false,
"noteId": "SynTBQiBsdYJ",
"noteId": "GPERMystNGTB",
"notePath": [
"pOsGYCXsbNQG",
"CdNpE2pqjmI6",
"MgibgPcfeuGz",
"SynTBQiBsdYJ"
"SPirpZypehBG",
"GPERMystNGTB"
],
"title": "Widget Basics",
"title": "Events",
"notePosition": 20,
"prefix": null,
"isExpanded": false,
@@ -14987,34 +15147,41 @@
{
"type": "relation",
"name": "internalLink",
"value": "zEY4DaJG4YT5",
"value": "CdNpE2pqjmI6",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "internalLink",
"value": "BFs8mudNFgCS",
"value": "c5xB8m4g2IY6",
"isInheritable": false,
"position": 20
},
{
"type": "relation",
"name": "internalLink",
"value": "GLks18SNjxmC",
"value": "zEY4DaJG4YT5",
"isInheritable": false,
"position": 30
},
{
"type": "label",
"name": "shareAlias",
"value": "widget-basics",
"value": "events",
"isInheritable": false,
"position": 10
},
{
"type": "label",
"name": "iconClass",
"value": "bx bx-rss",
"isInheritable": false,
"position": 20
}
],
"format": "markdown",
"dataFileName": "Widget Basics.md",
"dataFileName": "Events.md",
"attachments": []
}
]
@@ -15028,7 +15195,7 @@
"GLks18SNjxmC"
],
"title": "Script API",
"notePosition": 90,
"notePosition": 100,
"prefix": null,
"isExpanded": false,
"type": "text",
@@ -15191,7 +15358,7 @@
"vElnKeDNPSVl"
],
"title": "Logging",
"notePosition": 100,
"notePosition": 110,
"prefix": null,
"isExpanded": false,
"type": "text",

View File

@@ -15,7 +15,7 @@ New tasks are created in the TODO note which has `~child:template` [relation](..
### Attributes
Task template defines several [promoted attributes](../Attributes/Promoted%20Attributes.md) - todoDate, doneDate, tags, location. Importantly it also defines `~runOnAttributeChange` relation - [event](../../Scripting/Events.md) handler which is run on attribute change. This [script](../../Scripting.md) handles when e.g. we fill out the doneDate attribute - meaning the task is done and should be moved to "Done" note and removed from TODO, locations and tags.
Task template defines several [promoted attributes](../Attributes/Promoted%20Attributes.md) - todoDate, doneDate, tags, location. Importantly it also defines `~runOnAttributeChange` relation - [event](../../Scripting/Backend%20scripts/Events.md) handler which is run on attribute change. This [script](../../Scripting.md) handles when e.g. we fill out the doneDate attribute - meaning the task is done and should be moved to "Done" note and removed from TODO, locations and tags.
### New task button

File diff suppressed because one or more lines are too long

View File

@@ -43,7 +43,7 @@ These relations are supported and used internally by Trilium.
| Label | Description |
| --- | --- |
| `runOn*` | See <a class="reference-link" href="../../Scripting/Events.md">Events</a> |
| `runOn*` | See <a class="reference-link" href="../../Scripting/Backend%20scripts/Events.md">Events</a> |
| `template` | note's attributes will be inherited even without a parent-child relationship, note's content and subtree will be added to instance notes if empty. See documentation for details. |
| `inherit` | note's attributes will be inherited even without a parent-child relationship. See <a class="reference-link" href="../Templates.md">Templates</a> for a similar concept. See <a class="reference-link" href="Attribute%20Inheritance.md">Attribute Inheritance</a> in the documentation. |
| `renderNote` | notes of type <a class="reference-link" href="../../Note%20Types/Render%20Note.md">Render Note</a> will be rendered using a code note (HTML or script) and it is necessary to point using this relation to which note should be rendered |

View File

@@ -0,0 +1,34 @@
# Nightly release
Nightly releases are versions built every day, containing the latest improvements and bugfixes, directly from the main development branch. These versions are generally useful in preparation for a release, to ensure that there are no significant bugs that need to be addressed first, or they can be used to confirm whether a particular bug is fixed or feature is well implemented.
## Regarding the stability
Despite being on a development branch, generally the main branch is pretty stable since PRs are tested before they are merged. If you notice any issues, feel free to report them either via a ticket or via the Matrix.
## Downloading the nightly release manually
Go to [github.com/TriliumNext/Trilium/releases/tag/nightly](https://github.com/TriliumNext/Trilium/releases/tag/nightly) and look for the artifacts starting with `TriliumNotes-main`. Choose the appropriate one for your platform (e.g. `windows-x64.zip`).
Depending on your use case, you can either test the portable version or even use the installer.
> [!NOTE]
> If you choose the installable version (e.g. the .exe on Windows), it will replace your stable installation.
> [!IMPORTANT]
> By default, the nightly uses the same database as the production version. Generally you could easily downgrade if needed. However, if there are changes to the database or sync version, it will not be possible to downgrade without having to restore from a backup.
## Automatically download and install the latest nightly
This is pretty useful if you are a beta tester that wants to periodically update their version:
On Ubuntu:
```
#!/usr/bin/env bash
name=TriliumNotes-linux-x64-nightly.deb
rm -f $name*
wget https://github.com/TriliumNext/Trilium/releases/download/nightly/$name
sudo apt-get install ./$name
rm $name
```

View File

@@ -56,7 +56,7 @@ Right click either the _Available launchers_ or _Visible launchers_ sections and
2. Optionally, set a `keyboardShortcut` to trigger the launcher.
3. **Custom Widget**
Allows defining a custom widget to be rendered inside the launcher. See [Widget Basics](../../Scripting/Custom%20Widgets/Widget%20Basics.md) for more information.
Allows defining a custom widget to be rendered inside the launcher. See [Widget Basics](../../Scripting/Frontend%20Basics/Custom%20Widgets/Widget%20Basics.md) for more information.
4. **Spacers**
Launchers that create some distance between other launchers for better visual distinction.

View File

@@ -12,7 +12,7 @@ So we have frontend and backend, each with their own set of responsibilities, bu
## Use cases
* <a class="reference-link" href="Scripting/Examples/New%20Task%20launcher%20button.md">"New Task" launcher button</a>
* <a class="reference-link" href="Scripting/Frontend%20Basics/Examples/New%20Task%20launcher%20button.md">"New Task" launcher button</a>
## Action handler
@@ -34,7 +34,7 @@ You can see more scripting with explanation in <a class="reference-link" href="
## Events
See <a class="reference-link" href="Scripting/Events.md">Events</a>.
See <a class="reference-link" href="Scripting/Backend%20scripts/Events.md">Events</a>.
## Script API

View File

@@ -1,15 +1,15 @@
# Events
[Script](../Scripting.md) notes can be triggered by events. Note that these are backend events and thus relation need to point to the "JS backend" code note.
[Script](../../Scripting.md) notes can be triggered by events. Note that these are backend events and thus relation need to point to the "JS backend" code note.
## Global events
Global events are attached to the script note via label. Simply create e.g. "run" label with some of these values and script note will be executed once the event occurs.
<table><thead><tr><th>Label</th><th>Description</th></tr></thead><tbody><tr><td><code>run</code></td><td><p>Defines on which events script should run. Possible values are:</p><ul><li><code>frontendStartup</code> - when Trilium frontend starts up (or is refreshed), but not on mobile.</li><li><code>mobileStartup</code> - when Trilium frontend starts up (or is refreshed), on mobile.</li><li><code>backendStartup</code> - when Trilium backend starts up</li><li><code>hourly</code> - run once an hour. You can use additional label <code>runAtHour</code> to specify at which hour, on the back-end.</li><li><code>daily</code> - run once a day, on the back-end</li></ul></td></tr><tr><td><code>runOnInstance</code></td><td>Specifies that the script should only run on a particular&nbsp;<a class="reference-link" href="../Advanced%20Usage/Configuration%20(config.ini%20or%20environment%20variables)/Trilium%20instance.md">Trilium instance</a>.</td></tr><tr><td><code>runAtHour</code></td><td>On which hour should this run. Should be used together with <code>#run=hourly</code>. Can be defined multiple times for more runs during the day.</td></tr></tbody></table>
<table><thead><tr><th>Label</th><th>Description</th></tr></thead><tbody><tr><td><code>run</code></td><td><p>Defines on which events script should run. Possible values are:</p><ul><li><code>frontendStartup</code> - when Trilium frontend starts up (or is refreshed), but not on mobile.</li><li><code>mobileStartup</code> - when Trilium frontend starts up (or is refreshed), on mobile.</li><li><code>backendStartup</code> - when Trilium backend starts up</li><li><code>hourly</code> - run once an hour. You can use additional label <code>runAtHour</code> to specify at which hour, on the back-end.</li><li><code>daily</code> - run once a day, on the back-end</li></ul></td></tr><tr><td><code>runOnInstance</code></td><td>Specifies that the script should only run on a particular&nbsp;<a class="reference-link" href="../../Advanced%20Usage/Configuration%20(config.ini%20or%20environment%20variables)/Trilium%20instance.md">Trilium instance</a>.</td></tr><tr><td><code>runAtHour</code></td><td>On which hour should this run. Should be used together with <code>#run=hourly</code>. Can be defined multiple times for more runs during the day.</td></tr></tbody></table>
## Entity events
Other events are bound to some entity, these are defined as [relations](../Advanced%20Usage/Attributes.md) - meaning that script is triggered only if note has this script attached to it through relations (or it can inherit it).
Other events are bound to some entity, these are defined as [relations](../../Advanced%20Usage/Attributes.md) - meaning that script is triggered only if note has this script attached to it through relations (or it can inherit it).
| Relation | Description |
| --- | --- |

View File

@@ -0,0 +1,11 @@
# Server-side imports
Older versions of Trilium Notes allowed the use of Common.js module imports inside backend scripts, such as:
```
const isBetween = require('dayjs/plugin/isBetween')
api.dayjs.extend(isBetween)
```
For newer versions, Node.js imports are **not officially supported anymore**, since we've added a bundler which makes it more difficult to reuse dependencies.
Theoretically it's still possible to use imports by manually setting up a `node_modules` in the server directory via `npm` or `pnpm`.

View File

@@ -1,47 +0,0 @@
# "New Task" launcher button
In this example we are going to extend the functionality of <a class="reference-link" href="../../Advanced%20Usage/Advanced%20Showcases/Task%20Manager.md">Task Manager</a> showcase (which comes by default with Trilium) by adding a button in the <a class="reference-link" href="../../Basic%20Concepts%20and%20Features/UI%20Elements/Launch%20Bar.md">Launch Bar</a>  (![](New%20Task%20launcher%20button_i.png)) to create a new task automatically and open it.
## Creating the note
1. First, create a new <a class="reference-link" href="../../Note%20Types/Code.md">Code</a> note type with the _JS frontend_ language.
2. Define the `#run=frontendStartup` label in <a class="reference-link" href="../../Advanced%20Usage/Attributes.md">Attributes</a>.
## Content of the script
Copy-paste the following script:
```javascript
api.addButtonToToolbar({
title: "New task",
icon: "task",
shortcut: "alt+n",
action: async () => {
const taskNoteId = await api.runOnBackend(() => {
const todoRootNote = api.getNoteWithLabel("taskTodoRoot");
const resp = api.createTextNote(todoRootNote.noteId, "New task", "")
return resp.note.noteId;
});
await api.waitUntilSynced();
await api.activateNewNote(taskNoteId);
}
});
```
## Testing the functionality
Since we set the script to be run on start-up, all we need to do is to [refresh the application](../../Troubleshooting/Refreshing%20the%20application.md).
## Understanding how the script works
<table class="ck-table-resized"><colgroup><col><col></colgroup><tbody><tr><td><pre><code class="language-application-javascript-env-frontend">api.addButtonToToolbar({
title: "New task",
icon: "task",
shortcut: "alt+n",
action: async () =&gt; {
// [...]
}
});</code></pre></td><td><p>This uses the <a href="../Frontend%20Basics.md">Front-end API</a> to create a icon in the&nbsp;<a class="reference-link" href="../../Basic%20Concepts%20and%20Features/UI%20Elements/Launch%20Bar.md">Launch Bar</a>, by specifying:</p><ul><li>A title</li><li>A corresponding boxicons icon (without the <code>bx-</code> prefix).</li><li>Optionally, a keyboard shortcut to assign to it.</li><li>The action, which will be executed when the button is pressed.</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">const taskNoteId = await api.runOnBackend(() =&gt; {
// Shown below.
return resp.note.noteId;
});</code></pre></td><td><ul><li>This portion of code is actually executed on the server (backend) and not on the client (i.e. browser).<ul><li>The reason is that the creating notes is the responsibility of the server.</li></ul></li><li>Here we can also see that it is possible to return results from the server execution and read them in the client (<code>taskNoteId</code>).</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">const todoRootNote = api.getNoteWithLabel("taskTodoRoot");</code></pre></td><td><ul><li>Here we identify a note with the <a href="../../Advanced%20Usage/Attributes.md">label</a> <code>#taskTodoRoot</code>. This is how the&nbsp;<a class="reference-link" href="../../Advanced%20Usage/Advanced%20Showcases/Task%20Manager.md">Task Manager</a>&nbsp;showcase knows where to place all the different tasks.</li><li>Normally this might return a <code>null</code> value if no such note could be identified, but error handling is outside the scope of this example.&nbsp;</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">const resp = api.createTextNote(todoRootNote.noteId, "New task", "")</code></pre></td><td><ul><li>We create a new child note within the to-do root note (first argument) with the title “New task" (second argument) and no content by default (third argument).</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">await api.waitUntilSynced();</code></pre></td><td><ul><li>Back on the client, since we created a new note on the server, we now need to wait for the change to be reflected in the client.</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">await api.activateNewNote(taskNoteId);</code></pre></td><td><ul><li>Since we know the <a href="../../Advanced%20Usage/Note%20ID.md">ID</a> of the newly created note, all we have to do now is to show this note to the user.</li></ul></td></tr></tbody></table>

View File

@@ -54,4 +54,4 @@ Conversely to scripts, widgets do have some specific requirements in order to wo
### Tutorial
For more information on building widgets, take a look at [Widget Basics](Custom%20Widgets/Widget%20Basics.md).
For more information on building widgets, take a look at [Widget Basics](Frontend%20Basics/Custom%20Widgets/Widget%20Basics.md).

View File

@@ -22,10 +22,10 @@ module.exports = new MyWidget();
To implement this widget:
1. Create a new `JS Frontend` note in Trilium and paste in the code above.
2. Assign the `#widget` [attribute](../../Advanced%20Usage/Attributes.md) to the [note](../../Basic%20Concepts%20and%20Features/Notes.md).
2. Assign the `#widget` [attribute](../../../Advanced%20Usage/Attributes.md) to the [note](../../../Basic%20Concepts%20and%20Features/Notes.md).
3. Restart Trilium or reload the window.
To verify that the widget is working, open the developer tools (`Cmd` + `Shift` + `I`) and run `document.querySelector("#my-widget")`. If the element is found, the widget is functioning correctly. If `undefined` is returned, double-check that the [note](../../Basic%20Concepts%20and%20Features/Notes.md) has the `#widget` [attribute](../../Advanced%20Usage/Attributes.md).
To verify that the widget is working, open the developer tools (`Cmd` + `Shift` + `I`) and run `document.querySelector("#my-widget")`. If the element is found, the widget is functioning correctly. If `undefined` is returned, double-check that the [note](../../../Basic%20Concepts%20and%20Features/Notes.md) has the `#widget` [attribute](../../../Advanced%20Usage/Attributes.md).
### Step 2: Adding an UI Element
@@ -85,7 +85,7 @@ After reloading Trilium, the button should now appear at the bottom left of the
### Step 4: Adding User Interaction
Lets make the button interactive by showing a message when its clicked. We'll use the `api.showMessage` method from the [Script API](../Script%20API.md).
Lets make the button interactive by showing a message when its clicked. We'll use the `api.showMessage` method from the [Script API](../../Script%20API.md).
```
class MyWidget extends api.BasicWidget {
@@ -108,4 +108,11 @@ class MyWidget extends api.BasicWidget {
module.exports = new MyWidget();
```
Reload the application one last time. When you click the button, a "Hello World!" message should appear, confirming that your widget is fully functional.
`parentWidget()` can be given the following values:
* `left-pane` - This renders the widget on the left side of the screen where the note tree lives.
* `center-pane` - This renders the widget in the center of the layout in the same location that notes and splits appear.
* `note-detail-pane` - This renders the widget _with_ the note in the center pane. This means it can appear multiple times with splits.
* `right-pane` - This renders the widget to the right of any opened notes.
[Reload](../../../Troubleshooting/Refreshing%20the%20application.md) the application one last time. When you click the button, a "Hello World!" message should appear, confirming that your widget is fully functional.

View File

@@ -1,8 +1,8 @@
# Word count widget
> [!TIP]
> This widget is also present in new installations in the <a class="reference-link" href="../../Advanced%20Usage/Database/Demo%20Notes.md">Demo Notes</a>.
> This widget is also present in new installations in the <a class="reference-link" href="../../../Advanced%20Usage/Database/Demo%20Notes.md">Demo Notes</a>.
Create a <a class="reference-link" href="../../Note%20Types/Code.md">Code</a> note of type JS frontend and **give it a** `#widget` **label**.
Create a <a class="reference-link" href="../../../Note%20Types/Code.md">Code</a> note of type JS frontend and **give it a** `#widget` **label**.
```
/*
@@ -82,7 +82,7 @@ class WordCountWidget extends api.NoteContextAwareWidget {
module.exports = new WordCountWidget();
```
After you make changes it is necessary to [restart Trilium](../../Troubleshooting/Refreshing%20the%20application.md) so that the layout can be rebuilt.
After you make changes it is necessary to [restart Trilium](../../../Troubleshooting/Refreshing%20the%20application.md) so that the layout can be rebuilt.
At the bottom of the note you can see the resulting widget:

View File

@@ -0,0 +1,47 @@
# "New Task" launcher button
In this example we are going to extend the functionality of <a class="reference-link" href="../../../Advanced%20Usage/Advanced%20Showcases/Task%20Manager.md">Task Manager</a> showcase (which comes by default with Trilium) by adding a button in the <a class="reference-link" href="../../../Basic%20Concepts%20and%20Features/UI%20Elements/Launch%20Bar.md">Launch Bar</a>  (![](New%20Task%20launcher%20button_i.png)) to create a new task automatically and open it.
## Creating the note
1. First, create a new <a class="reference-link" href="../../../Note%20Types/Code.md">Code</a> note type with the _JS frontend_ language.
2. Define the `#run=frontendStartup` label in <a class="reference-link" href="../../../Advanced%20Usage/Attributes.md">Attributes</a>.
## Content of the script
Copy-paste the following script:
```javascript
api.addButtonToToolbar({
title: "New task",
icon: "task",
shortcut: "alt+n",
action: async () => {
const taskNoteId = await api.runOnBackend(() => {
const todoRootNote = api.getNoteWithLabel("taskTodoRoot");
const resp = api.createTextNote(todoRootNote.noteId, "New task", "")
return resp.note.noteId;
});
await api.waitUntilSynced();
await api.activateNewNote(taskNoteId);
}
});
```
## Testing the functionality
Since we set the script to be run on start-up, all we need to do is to [refresh the application](../../../Troubleshooting/Refreshing%20the%20application.md).
## Understanding how the script works
<table class="ck-table-resized"><colgroup><col><col></colgroup><tbody><tr><td><pre><code class="language-application-javascript-env-frontend">api.addButtonToToolbar({
title: "New task",
icon: "task",
shortcut: "alt+n",
action: async () =&gt; {
// [...]
}
});</code></pre></td><td><p>This uses the <a href="../../Frontend%20Basics.md">Front-end API</a> to create a icon in the&nbsp;<a class="reference-link" href="../../../Basic%20Concepts%20and%20Features/UI%20Elements/Launch%20Bar.md">Launch Bar</a>, by specifying:</p><ul><li>A title</li><li>A corresponding boxicons icon (without the <code>bx-</code> prefix).</li><li>Optionally, a keyboard shortcut to assign to it.</li><li>The action, which will be executed when the button is pressed.</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">const taskNoteId = await api.runOnBackend(() =&gt; {
// Shown below.
return resp.note.noteId;
});</code></pre></td><td><ul><li>This portion of code is actually executed on the server (backend) and not on the client (i.e. browser).<ul><li>The reason is that the creating notes is the responsibility of the server.</li></ul></li><li>Here we can also see that it is possible to return results from the server execution and read them in the client (<code>taskNoteId</code>).</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">const todoRootNote = api.getNoteWithLabel("taskTodoRoot");</code></pre></td><td><ul><li>Here we identify a note with the <a href="../../../Advanced%20Usage/Attributes.md">label</a> <code>#taskTodoRoot</code>. This is how the&nbsp;<a class="reference-link" href="../../../Advanced%20Usage/Advanced%20Showcases/Task%20Manager.md">Task Manager</a>&nbsp;showcase knows where to place all the different tasks.</li><li>Normally this might return a <code>null</code> value if no such note could be identified, but error handling is outside the scope of this example.&nbsp;</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">const resp = api.createTextNote(todoRootNote.noteId, "New task", "")</code></pre></td><td><ul><li>We create a new child note within the to-do root note (first argument) with the title “New task" (second argument) and no content by default (third argument).</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">await api.waitUntilSynced();</code></pre></td><td><ul><li>Back on the client, since we created a new note on the server, we now need to wait for the change to be reflected in the client.</li></ul></td></tr><tr><td><pre><code class="language-application-javascript-env-frontend">await api.activateNewNote(taskNoteId);</code></pre></td><td><ul><li>Since we know the <a href="../../../Advanced%20Usage/Note%20ID.md">ID</a> of the newly created note, all we have to do now is to show this note to the user.</li></ul></td></tr></tbody></table>

View File

@@ -21,7 +21,7 @@ TRILIUM_START_NOTE_ID=root ./trilium
## Broken Script Prevents Application Startup
If a custom script causes Triliumto crash, and it is set as a startup script or in an active [custom widget](Scripting/Custom%20Widgets.md), start Triliumin "safe mode" to prevent any custom scripts from executing:
If a custom script causes Triliumto crash, and it is set as a startup script or in an active [custom widget](Scripting/Frontend%20Basics/Custom%20Widgets.md), start Triliumin "safe mode" to prevent any custom scripts from executing:
```
TRILIUM_SAFE_MODE=true ./trilium