chore(monorepo): move everything into subdirectory for easy diffing

This commit is contained in:
Elian Doran
2025-04-18 01:28:32 +03:00
parent 3b69eadbf6
commit 318808f9ec
2646 changed files with 654 additions and 654 deletions

View File

@@ -1,9 +0,0 @@
# Custom Widgets
It's possible to create custom widget in three possible locations where you can display your custom content.
Positions are:
* `left-pane`
* `center-pane`
* `note-detail-pane` - located within `center-pane`, but specific to note (split)
* `right-pane`

View File

@@ -1,111 +0,0 @@
# Widget Basics
This guide will walk you through creating a basic widget inside Trilium. By following these steps, you'll learn how to build a simple UI element that interacts with the user.
### Step 1: The Basic Widget Structure
To start, we'll create the most basic widget possible. Here's a simple example:
```
class MyWidget extends api.BasicWidget {
get position() { return 1; }
get parentWidget() { return "left-pane"; }
doRender() {
this.$widget = $("");
return this.$widget;
}
}
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).
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).
### Step 2: Adding an UI Element
Next, let's improve the widget by adding a button to it.
```
const template = ``;
class MyWidget extends api.BasicWidget {
get position() {return 1;}
get parentWidget() {return "left-pane"}
doRender() {
this.$widget = $(template);
return this.$widget;
}
}
module.exports = new MyWidget();
```
After making this change, reload Trilium. You should now see a button in the top-left corner of the left pane.
### Step 3: Styling the Widget
To make the button more visually appealing and position it correctly, we'll apply some custom styling. Trilium includes [Box Icons](https://boxicons.com), which we'll use to replace the button text with an icon. For example the `bx bxs-magic-wand` icon.
Here's the updated template:
```
const template = ``;
```
Next, we'll adjust the button's position using CSS:
```
class MyWidget extends api.BasicWidget {
get position() { return 1; }
get parentWidget() { return "left-pane"; }
doRender() {
this.$widget = $(template);
this.cssBlock(`#my-widget {
position: absolute;
bottom: 40px;
left: 60px;
z-index: 1;
}`);
return this.$widget;
}
}
module.exports = new MyWidget();
```
After reloading Trilium, the button should now appear at the bottom left of the left pane, alongside other action buttons.
### 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).
```
class MyWidget extends api.BasicWidget {
get position() { return 1; }
get parentWidget() { return "left-pane"; }
doRender() {
this.$widget = $(template);
this.cssBlock(`#my-widget {
position: absolute;
bottom: 40px;
left: 60px;
z-index: 1;
}`);
this.$widget.find("button").on("click", () => api.showMessage("Hello World!"));
return this.$widget;
}
}
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.

View File

@@ -1,89 +0,0 @@
# 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>.
Create a <a class="reference-link" href="../../Note%20Types/Code.md">Code</a> note of type JS frontend and **give it a** `#widget` **label**.
```
/*
* This defines a custom widget which displays number of words and characters in a current text note.
* To be activated for a given note, add label 'wordCount' to the note, you can also make it inheritable and thus activate it for the whole subtree.
*
* See it in action in "Books" and its subtree.
*/
const TPL = `<div style="padding: 10px; border-top: 1px solid var(--main-border-color); contain: none;">
<strong>Word count: </strong>
<span class="word-count"></span>
&nbsp;
<strong>Character count: </strong>
<span class="character-count"></span>
</div`;
class WordCountWidget extends api.NoteContextAwareWidget {
get position() { return 100; } // higher value means position towards the bottom/right
get parentWidget() { return 'center-pane'; }
doRender() {
this.$widget = $(TPL);
this.$wordCount = this.$widget.find('.word-count');
this.$characterCount = this.$widget.find('.character-count');
return this.$widget;
}
async refreshWithNote(note) {
if (note.type !== 'text' || !note.hasLabel('wordCount')) {
// show widget only on text notes and when marked with 'wordCount' label
this.toggleInt(false); // hide
return;
}
this.toggleInt(true); // display
const {content} = await note.getNoteComplement();
const text = $(content).text(); // get plain text only
const counts = this.getCounts(text);
this.$wordCount.text(counts.words);
this.$characterCount.text(counts.characters);
}
getCounts(text) {
const chunks = text
.split(/[\s-+:,/\\]+/)
.filter(chunk => chunk !== '');
let words;
if (chunks.length === 1 && chunks[0] === '') {
words = 0;
}
else {
words = chunks.length;
}
const characters = chunks.join('').length;
return {words, characters};
}
async entitiesReloadedEvent({loadResults}) {
if (loadResults.isNoteContentReloaded(this.noteId)) {
this.refresh();
}
}
}
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.
At the bottom of the note you can see the resulting widget:
<figure class="image"><img style="aspect-ratio:792/603;" src="Word count widget_image.png" width="792" height="603"></figure>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -1,14 +0,0 @@
# 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.
## 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.
<figure class="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></figure>
## 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).
<figure class="table"><table><thead><tr><th>Relation</th><th>Description</th></tr></thead><tbody><tr><td><code>runOnNoteCreation</code></td><td>executes when note is created on backend. Use this relation if you want to run the script for all notes created under a specific subtree. In that case, create it on the subtree root note and make it inheritable. A new note created within the subtree (any depth) will trigger the script.</td></tr><tr><td><code>runOnChildNoteCreation</code></td><td>executes when new note is created under the note where this relation is defined</td></tr><tr><td><code>runOnNoteTitleChange</code></td><td>executes when note title is changed (includes note creation as well)</td></tr><tr><td><code>runOnNoteContentChange</code></td><td>executes when note content is changed (includes note creation as well).</td></tr><tr><td><code>runOnNoteChange</code></td><td>executes when note is changed (includes note creation as well). Does not include content changes</td></tr><tr><td><code>runOnNoteDeletion</code></td><td>executes when note is being deleted</td></tr><tr><td><code>runOnBranchCreation</code></td><td>executes when a branch is created. Branch is a link between parent note and child note and is created e.g. when cloning or moving note.</td></tr><tr><td><code>runOnBranchChange</code></td><td>executes when a branch is updated. (since v0.62)</td></tr><tr><td><code>runOnBranchDeletion</code></td><td>executes when a branch is deleted. Branch is a link between parent note and child note and is deleted e.g. when moving note (old branch/link is deleted).</td></tr><tr><td><code>runOnAttributeCreation</code></td><td>executes when new attribute is created for the note which defines this relation</td></tr><tr><td><code>runOnAttributeChange</code></td><td>executes when the attribute is changed of a note which defines this relation. This is triggered also when the attribute is deleted</td></tr></tbody></table></figure>

View File

@@ -1,45 +0,0 @@
# Downloading responses from Google Forms
This tutorial showcases a basic integration with Google Forms, where we are able to download the responses of a form using the “Link to Sheets" functionality.
Note that the link will be publicly accessible to everyone (however the link is in a hard-to-guess format such as `https://docs.google.com/spreadsheets/d/e/2PACX-1vTA8NU2_eZFhc8TFadCZPreBfvP7un8IHd6J0SchrLLw3ueGmntNZjwRmsH2ZRcp1pJYDAzMz1FmFaj/pub?output=csv`). Make sure you are not accidentally publishing sensitive information.
## Obtaining the CSV link
1. Open the Google Forms in a browser.
2. Select the “Responses” tab and click on “Link to Sheets”.
3. Select “Create a new spreadsheet” and press “Create”.
4. In Google Sheets, select File → Share → Publish to web.
5. In the “Publish to the web” screen, make sure the “Link” tab is selected and instead of “Web page”, select “Comma-separated values (.csv)”.
6. Copy the given link which will be used for the upcoming script.
## Creating the script
Create a “JS Frontend” script:
```
const CSV_URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vTiwooLV2whjCSVa49dJ99p_G3_qhqHHRqttMjYCJVfLXVdTgUSNJu5K0rpqmaHYF2k7Vofi3o7gW82/pub?output=csv";
async function fetchData() {
try {
const response = await fetch(CSV_URL);
return await response.text();
} catch (e) {
api.showError(e.message);
}
}
const data = await fetchData();
console.log(data);
// Do something with the data.
```
Note that the data will be received as a string and there is no library to do the CSV parsing for us. To do a very simple parsing of CSV:
```
const content = data
.split("\n")
.slice(1)
.map((row) => row.split(","));
```
This will return the data as an array of arrays.

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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,61 +0,0 @@
# Using promoted attributes to configure scripts
A good use case of promoted attributes is to easily define the various parameters a script might need, for example an input and output note if it's processing data, or a checkbox to define a particular change in behavior for the script.
![](Using%20promoted%20attributes%20.png)
## Using check boxes to toggle flags
Instead of asking the user to modify a boolean value in the script, it's much more intuitive to use a checkbox for it as a promoted attribute.
To do so, first define the promoted attribute:
```
#label:groupByExtension="promoted,alias=Group by extension,single,boolean"
```
Then use it:
```javascript
const byExtension = api.currentNote.getLabelValue("groupByExtension") === "true";
if (byExtension) {
// Do something.
}
```
This will work equally well in both front-end and back-end scripts.
## Using relations to select notes
One common use case for a script is to read data from another note and perhaps output its result in another note. To do so we need to define the following promoted attributes:
```
#relation:input="promoted,alias=Input,single" #relation:output="promoted,alias=Output,single"
```
Once we have this, we can add some basic error handling to ensure that the fields are completed by the user:
```javascript
const inputNoteId = api.currentNote.getRelationValue("input");
if (!inputNoteId) {
api.showError("Missing input.");
return;
}
const outputNoteId = api.currentNote.getRelationValue("output");
if (!outputNoteId) {
api.showError("Missing output.");
return;
}
```
Note that here we are using `api.showError` which is only available for frontend notes. If you are writing a backend note, simply remove `api.showError` but the user will no feedback on why the script did not execute properly.
Afterwards we can simply read the note and do something with it:
```javascript
const note = api.getNote(inputNoteId);
if (!note) {
return;
}
const content = note.getContent().toString("utf-8");
```

View File

@@ -1,57 +0,0 @@
# Frontend Basics
## Frontend API
The frontend api supports two styles, regular scripts that are run with the current app and note context, and widgets that export an object to Trilium to be used in the UI. In both cases, the frontend api of Trilium is available to scripts running in the frontend context as global variable `api`. The members and methods of the api can be seen on the [Script API](Script%20API.md) page.
## Scripts
Scripts don't have any special requirements. They can be run at will using the execute button in the UI or they can be configured to run at certain times using [Attributes](../Advanced%20Usage/Attributes.md) on the note containing the script.
### Global Events
This attribute is called `#run` and it can have any of the following values:
* `frontendStartup` - executes on frontend upon startup.
* `mobileStartup` - executes on mobile frontend upon startup.
* `backendStartup` - executes on backend upon startup.
* `hourly` - executes once an hour on backend.
* `daily` - executes once a day on backend.
### Entity Events
These events are triggered by certain [relations](../Advanced%20Usage/Attributes.md) to other notes. Meaning that the script is triggered only if the note has this script attached to it through relations (or it can inherit it).
* `runOnNoteCreation` - executes when note is created on backend.
* `runOnNoteTitleChange` - executes when note title is changed (includes note creation as well).
* `runOnNoteContentChange` - executes when note content is changed (includes note creation as well).
* `runOnNoteChange` - executes when note is changed (includes note creation as well).
* `runOnNoteDeletion` - executes when note is being deleted.
* `runOnBranchCreation` - executes when a branch is created. Branch is a link between parent note and child note and is created e.g. when cloning or moving note.
* `runOnBranchDeletion` - executes when a branch is delete. Branch is a link between parent note and child note and is deleted e.g. when moving note (old branch/link is deleted).
* `runOnChildNoteCreation` - executes when new note is created under this note.
* `runOnAttributeCreation` - executes when new attribute is created under this note.
* `runOnAttributeChange` - executes when attribute is changed under this note.
## Widgets
Conversely to scripts, widgets do have some specific requirements in order to work. A widget must:
* Extend [BasicWidget](https://triliumnext.github.io/Notes/frontend_api/BasicWidget.html) or one of it's subclasses.
* Create a new instance and assign it to `module.exports`.
* Define a `parentWidget` member to determine where it should be displayed.
* Define a `position` (integer) that determines the location via sort order.
* Have a `#widget` attribute on the containing note.
* Create, render, and return your element in the render function.
* For [BasicWidget](https://triliumnext.github.io/Notes/frontend_api/BasicWidget.html) and [NoteContextAwareWidget](https://triliumnext.github.io/Notes/frontend_api/NoteContextAwareWidget.html)you should create `this.$widget` and render it in `doRender()`.
* For [RightPanelWidget](https://triliumnext.github.io/Notes/frontend_api/RightPanelWidget.html) the `this.$widget` and `doRender()` are already handled and you should instead return the value in `doRenderBody()`.
### parentWidget
* `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.
### Tutorial
For more information on building widgets, take a look at [Widget Basics](Custom%20Widgets/Widget%20Basics.md).

View File

@@ -1,19 +0,0 @@
# Script API
For [script code notes](../Scripting.md), Trilium offers an API that gives them access to various features of the application.
There are two APIs:
* One for the front-end scripts: <a class="reference-link" href="Script%20API/Frontend%20API">Frontend API</a>
* One for the back-end scripts: <a class="reference-link" href="Script%20API/Backend%20API.dat">Backend API</a>
In both cases, the API resides in a global variable, `api`, that can be used anywhere in the script.
For example, to display a message to the user the following front-end script can be used:
```
api.showMessage("Hello world.");
```
> [!NOTE]
> **Note**
> The Script API is currently experimental and may undergo changes in future updates.