mirror of
https://github.com/zadam/trilium.git
synced 2026-05-28 19:00:12 +02:00
feat(llm): basic skill to write scripts
This commit is contained in:
156
apps/server/src/services/llm/skills/backend_scripting.md
Normal file
156
apps/server/src/services/llm/skills/backend_scripting.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Trilium Backend Scripting
|
||||
|
||||
Backend scripts run in Node.js on the server. They have direct access to notes in memory and can interact with the system (files, processes).
|
||||
|
||||
## Creating a backend script
|
||||
|
||||
1. Create a Code note with language "JS backend".
|
||||
2. The script can be run manually (Execute button) or triggered automatically.
|
||||
|
||||
## Script API (`api` global)
|
||||
|
||||
### Note retrieval
|
||||
- `api.getNote(noteId)` - get note by ID
|
||||
- `api.searchForNotes(query, searchParams)` - search notes (returns array)
|
||||
- `api.searchForNote(query)` - search notes (returns first match)
|
||||
- `api.getNotesWithLabel(name, value?)` - find notes by label
|
||||
- `api.getNoteWithLabel(name, value?)` - find first note by label
|
||||
- `api.getBranch(branchId)` - get branch by ID
|
||||
- `api.getAttribute(attributeId)` - get attribute by ID
|
||||
|
||||
### Note creation
|
||||
- `api.createTextNote(parentNoteId, title, content)` - create text note
|
||||
- `api.createDataNote(parentNoteId, title, content)` - create JSON note
|
||||
- `api.createNewNote({ parentNoteId, title, content, type })` - create note with full options
|
||||
|
||||
### Branch management
|
||||
- `api.ensureNoteIsPresentInParent(noteId, parentNoteId, prefix?)` - create or reuse branch
|
||||
- `api.ensureNoteIsAbsentFromParent(noteId, parentNoteId)` - remove branch if exists
|
||||
- `api.toggleNoteInParent(present, noteId, parentNoteId, prefix?)` - toggle branch
|
||||
|
||||
### Calendar/date notes
|
||||
- `api.getTodayNote()` - get/create today's day note
|
||||
- `api.getDayNote(date)` - get/create day note (YYYY-MM-DD)
|
||||
- `api.getWeekNote(date)` - get/create week note
|
||||
- `api.getMonthNote(date)` - get/create month note (YYYY-MM)
|
||||
- `api.getYearNote(year)` - get/create year note (YYYY)
|
||||
|
||||
### Utilities
|
||||
- `api.log(message)` - log to Trilium logs and UI
|
||||
- `api.randomString(length)` - generate random string
|
||||
- `api.escapeHtml(string)` / `api.unescapeHtml(string)`
|
||||
- `api.getInstanceName()` - get instance name
|
||||
- `api.getAppInfo()` - get application info
|
||||
|
||||
### Libraries
|
||||
- `api.axios` - HTTP client
|
||||
- `api.dayjs` - date manipulation
|
||||
- `api.xml2js` - XML parser
|
||||
- `api.cheerio` - HTML/XML parser
|
||||
|
||||
### Advanced
|
||||
- `api.transactional(func)` - wrap code in a database transaction
|
||||
- `api.sql` - direct SQL access
|
||||
- `api.sortNotes(parentNoteId, sortConfig)` - sort child notes
|
||||
- `api.runOnFrontend(script, params)` - execute code on all connected frontends
|
||||
- `api.backupNow(backupName)` - create a backup
|
||||
- `api.exportSubtreeToZipFile(noteId, format, zipFilePath)` - export subtree (format: "markdown" or "html")
|
||||
- `api.duplicateSubtree(origNoteId, newParentNoteId)` - clone note and children
|
||||
|
||||
## BNote object
|
||||
|
||||
Available on notes returned from API methods (`api.getNote()`, `api.originEntity`, etc.).
|
||||
|
||||
### Content
|
||||
- `note.getContent()` / `note.setContent(content)`
|
||||
- `note.getJsonContent()` / `note.setJsonContent(obj)`
|
||||
- `note.getJsonContentSafely()` - returns null on parse error
|
||||
|
||||
### Properties
|
||||
- `note.noteId`, `note.title`, `note.type`, `note.mime`
|
||||
- `note.dateCreated`, `note.dateModified`
|
||||
- `note.isProtected`, `note.isArchived`
|
||||
|
||||
### Hierarchy
|
||||
- `note.getParentNotes()` / `note.getChildNotes()`
|
||||
- `note.getParentBranches()` / `note.getChildBranches()`
|
||||
- `note.hasChildren()`, `note.getAncestors()`
|
||||
- `note.getSubtreeNoteIds()` - all descendant IDs
|
||||
- `note.hasAncestor(ancestorNoteId)`
|
||||
|
||||
### Attributes (including inherited)
|
||||
- `note.getLabels(name?)` / `note.getLabelValue(name)`
|
||||
- `note.getRelations(name?)` / `note.getRelation(name)`
|
||||
- `note.hasLabel(name, value?)` / `note.hasRelation(name, value?)`
|
||||
|
||||
### Attribute modification
|
||||
- `note.setLabel(name, value?)` / `note.removeLabel(name, value?)`
|
||||
- `note.setRelation(name, targetNoteId)` / `note.removeRelation(name, value?)`
|
||||
- `note.addLabel(name, value?, isInheritable?)` / `note.addRelation(name, targetNoteId, isInheritable?)`
|
||||
- `note.toggleLabel(enabled, name, value?)`
|
||||
|
||||
### Operations
|
||||
- `note.save()` - persist changes
|
||||
- `note.deleteNote()` - soft delete
|
||||
- `note.cloneTo(parentNoteId)` - clone to another parent
|
||||
|
||||
### Type checks
|
||||
- `note.isJson()`, `note.isJavaScript()`, `note.isHtml()`, `note.isImage()`
|
||||
- `note.hasStringContent()` - true if not binary
|
||||
|
||||
## Events and triggers
|
||||
|
||||
### Global events (via `#run` label on the script note)
|
||||
- `#run=backendStartup` - run when server starts
|
||||
- `#run=hourly` - run once per hour (use `#runAtHour=N` to specify which hours)
|
||||
- `#run=daily` - run once per day
|
||||
|
||||
### Entity events (via relation from the entity to the script note)
|
||||
These are defined as relations. `api.originEntity` contains the entity that triggered the event.
|
||||
|
||||
| Relation | Trigger | originEntity |
|
||||
|---|---|---|
|
||||
| `~runOnNoteCreation` | note created | BNote |
|
||||
| `~runOnChildNoteCreation` | child note created under this note | BNote (child) |
|
||||
| `~runOnNoteTitleChange` | note title changed | BNote |
|
||||
| `~runOnNoteContentChange` | note content changed | BNote |
|
||||
| `~runOnNoteChange` | note metadata changed (not content) | BNote |
|
||||
| `~runOnNoteDeletion` | note deleted | BNote |
|
||||
| `~runOnBranchCreation` | branch created (clone/move) | BBranch |
|
||||
| `~runOnBranchChange` | branch updated | BBranch |
|
||||
| `~runOnBranchDeletion` | branch deleted | BBranch |
|
||||
| `~runOnAttributeCreation` | attribute created on this note | BAttribute |
|
||||
| `~runOnAttributeChange` | attribute changed/deleted on this note | BAttribute |
|
||||
|
||||
Relations can be inheritable — when set, they apply to all descendant notes.
|
||||
|
||||
## Example: auto-color notes by category
|
||||
|
||||
```javascript
|
||||
// Attach via ~runOnAttributeChange relation
|
||||
const attr = api.originEntity;
|
||||
if (attr.name !== "mycategory") return;
|
||||
const note = api.getNote(attr.noteId);
|
||||
if (attr.value === "Health") {
|
||||
note.setLabel("color", "green");
|
||||
} else {
|
||||
note.removeLabel("color");
|
||||
}
|
||||
```
|
||||
|
||||
## Example: create a daily summary
|
||||
|
||||
```javascript
|
||||
// Attach #run=daily label
|
||||
const today = api.getTodayNote();
|
||||
const tasks = api.searchForNotes('#task #!completed');
|
||||
let summary = "## Open Tasks\n";
|
||||
for (const task of tasks) {
|
||||
summary += `- ${task.title}\n`;
|
||||
}
|
||||
api.createTextNote(today.noteId, "Daily Summary", summary);
|
||||
```
|
||||
|
||||
## Module system
|
||||
|
||||
Child notes of a script act as modules. Export with `module.exports = ...` and import via function parameters matching the child note title, or use `require('noteName')`.
|
||||
197
apps/server/src/services/llm/skills/frontend_scripting.md
Normal file
197
apps/server/src/services/llm/skills/frontend_scripting.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# Trilium Frontend Scripting
|
||||
|
||||
Frontend scripts run in the browser. They can manipulate the UI, navigate notes, show dialogs, and create custom widgets.
|
||||
|
||||
## Creating a frontend script
|
||||
|
||||
1. Create a Code note with language "JS frontend" (or "JSX" for Preact widgets).
|
||||
2. Run manually (Execute button) or set `#run=frontendStartup` to auto-run on startup.
|
||||
3. For mobile, use `#run=mobileStartup` instead.
|
||||
|
||||
## Script types
|
||||
|
||||
| Type | Description | Required attribute |
|
||||
|---|---|---|
|
||||
| Regular script | Runs with current app/note context | `#run=frontendStartup` (optional) |
|
||||
| Custom widget | UI element in various positions | `#widget` |
|
||||
| Launch bar widget | Button in the launch bar | `#widget` |
|
||||
| Render note | Custom content inside a note | None (used via render relation) |
|
||||
|
||||
## Script API (`api` global)
|
||||
|
||||
### Navigation & tabs
|
||||
- `api.activateNote(notePath)` - navigate to a note
|
||||
- `api.activateNewNote(notePath)` - navigate and wait for sync
|
||||
- `api.openTabWithNote(notePath, activate?)` - open in new tab
|
||||
- `api.openSplitWithNote(notePath, activate?)` - open in new split
|
||||
- `api.getActiveContextNote()` - get currently active note
|
||||
- `api.getActiveContextNotePath()` - get path of active note
|
||||
- `api.setHoistedNoteId(noteId)` - hoist/unhoist note in current tab
|
||||
|
||||
### Note access & search
|
||||
- `api.getNote(noteId)` - get note by ID
|
||||
- `api.getNotes(noteIds)` - bulk fetch notes
|
||||
- `api.searchForNotes(searchString)` - search with full query syntax
|
||||
- `api.searchForNote(searchString)` - search returning first result
|
||||
- `api.reloadNotes(noteIds)` - refresh cache from backend
|
||||
|
||||
### Calendar/date notes
|
||||
- `api.getTodayNote()` - get/create today's note
|
||||
- `api.getDayNote(date)` - get/create day note for date
|
||||
- `api.getWeekNote(date)` / `api.getMonthNote(month)` / `api.getYearNote(year)`
|
||||
|
||||
### Editor access
|
||||
- `api.getActiveContextTextEditor()` - get CKEditor instance
|
||||
- `api.getActiveContextCodeEditor()` - get CodeMirror instance
|
||||
- `api.addTextToActiveContextEditor(text)` - insert text into active editor
|
||||
|
||||
### Dialogs & notifications
|
||||
- `api.showMessage(msg)` - show info toast
|
||||
- `api.showError(msg)` - show error toast
|
||||
- `api.showInfoDialog(msg)` - show info dialog
|
||||
- `api.showConfirmDialog(msg)` - show confirm dialog (returns boolean)
|
||||
- `api.showPromptDialog(msg)` - show prompt dialog (returns user input)
|
||||
|
||||
### Links
|
||||
- `api.createLink(notePath, { title?, showTooltip?, showNoteIcon? })` - create jQuery link element
|
||||
|
||||
### Backend integration
|
||||
- `api.runOnBackend(func, params)` - execute a function on the backend (sync)
|
||||
|
||||
### Protection
|
||||
- `api.protectNote(noteId, protect)` - protect/unprotect note
|
||||
- `api.protectSubTree(noteId, protect)` - protect/unprotect subtree
|
||||
|
||||
### UI interaction
|
||||
- `api.triggerCommand(name, data)` - trigger a command
|
||||
- `api.triggerEvent(name, data)` - trigger an event
|
||||
- `api.bindGlobalShortcut(shortcut, handler, namespace?)` - add keyboard shortcut
|
||||
|
||||
### Utilities
|
||||
- `api.formatDateISO(date)` - format as YYYY-MM-DD
|
||||
- `api.randomString(length)` - generate random string
|
||||
- `api.dayjs` - day.js library
|
||||
- `api.log(message)` - log to script log pane
|
||||
|
||||
### Widget base classes
|
||||
- `api.BasicWidget` - base widget class
|
||||
- `api.NoteContextAwareWidget` - widget aware of note context changes
|
||||
- `api.RightPanelWidget` - right sidebar widget
|
||||
|
||||
## FNote object
|
||||
|
||||
Available via `api.getNote()`, `api.getActiveContextNote()`, etc.
|
||||
|
||||
### Properties
|
||||
- `note.noteId`, `note.title`, `note.type`, `note.mime`
|
||||
- `note.isProtected`, `note.isArchived`
|
||||
|
||||
### Content
|
||||
- `note.getContent()` - get note content
|
||||
- `note.getJsonContent()` - parse content as JSON
|
||||
|
||||
### Hierarchy
|
||||
- `note.getParentNotes()` / `note.getChildNotes()`
|
||||
- `note.hasChildren()`, `note.getSubtreeNoteIds()`
|
||||
|
||||
### Attributes
|
||||
- `note.getAttributes(type?, name?)` - get all attributes (including inherited)
|
||||
- `note.getOwnedAttributes(type?, name?)` - get only owned attributes
|
||||
- `note.hasAttribute(type, name)` - check for attribute
|
||||
|
||||
## Custom widgets (legacy jQuery)
|
||||
|
||||
```javascript
|
||||
class MyWidget extends api.BasicWidget {
|
||||
get position() { return 1; }
|
||||
get parentWidget() { return "center-pane"; }
|
||||
|
||||
doRender() {
|
||||
this.$widget = $("<div>");
|
||||
this.$widget.append($("<button>Click me</button>")
|
||||
.on("click", () => api.showMessage("Hello!")));
|
||||
return this.$widget;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new MyWidget();
|
||||
```
|
||||
|
||||
### Widget locations (`parentWidget` values)
|
||||
- `left-pane` - alongside the note tree
|
||||
- `center-pane` - in the content area, spanning all splits
|
||||
- `note-detail-pane` - inside a note (split-aware, export class not instance, use static parentWidget)
|
||||
- `right-pane` - in the right sidebar (use `RightPanelWidget`)
|
||||
|
||||
### Note context aware widget
|
||||
|
||||
```javascript
|
||||
class MyWidget extends api.NoteContextAwareWidget {
|
||||
static get parentWidget() { return "note-detail-pane"; }
|
||||
get position() { return 100; }
|
||||
|
||||
doRender() {
|
||||
this.$widget = $("<div>");
|
||||
return this.$widget;
|
||||
}
|
||||
|
||||
async refreshWithNote(note) {
|
||||
// Called when the active note changes
|
||||
this.$widget.text(`Current note: ${note.title}`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MyWidget; // Export class, not instance!
|
||||
```
|
||||
|
||||
## Custom widgets (Preact JSX)
|
||||
|
||||
Requires JSX language enabled in Options -> Code Notes.
|
||||
|
||||
```jsx
|
||||
import { defineWidget } from "trilium:preact";
|
||||
import { useState } from "trilium:preact";
|
||||
|
||||
export default defineWidget({
|
||||
parent: "center-pane",
|
||||
position: 10,
|
||||
render: () => {
|
||||
const [count, setCount] = useState(0);
|
||||
return (
|
||||
<div>
|
||||
<button onClick={() => setCount(c => c + 1)}>
|
||||
Clicked {count} times
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Preact imports
|
||||
- `import { showMessage, getNote, ... } from "trilium:api"` - API methods
|
||||
- `import { useState, useEffect, ... } from "trilium:preact"` - hooks
|
||||
- `import { defineWidget, defineLauncherWidget } from "trilium:preact"` - widget helpers
|
||||
- Built-in components: Button, ActionButton, Modal, NoteAutocomplete, FormTextBox, FormToggle, etc.
|
||||
|
||||
## Example: launcher button
|
||||
|
||||
```javascript
|
||||
// Set #run=frontendStartup
|
||||
api.createOrUpdateLauncher({
|
||||
id: "my-task-button",
|
||||
type: "customWidget",
|
||||
title: "New Task",
|
||||
icon: "bx bx-task",
|
||||
action: async () => {
|
||||
const todayNote = await api.getTodayNote();
|
||||
await api.runOnBackend(async (parentNoteId) => {
|
||||
api.createTextNote(parentNoteId, "New Task", "");
|
||||
}, [todayNote.noteId]);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Module system
|
||||
|
||||
Child notes of a script act as modules. For JS frontend, use `module.exports` and function parameters. For JSX, use `import`/`export` syntax.
|
||||
@@ -23,6 +23,16 @@ const SKILLS: SkillDefinition[] = [
|
||||
name: "search_syntax",
|
||||
description: "Trilium search query syntax reference — labels, relations, note properties, boolean logic, ordering, and more.",
|
||||
file: "search_syntax.md"
|
||||
},
|
||||
{
|
||||
name: "backend_scripting",
|
||||
description: "Backend (Node.js) scripting API — creating notes, handling events, accessing entities, database operations, and automation.",
|
||||
file: "backend_scripting.md"
|
||||
},
|
||||
{
|
||||
name: "frontend_scripting",
|
||||
description: "Frontend (browser) scripting API — UI widgets, navigation, dialogs, editor access, Preact/JSX components, and keyboard shortcuts.",
|
||||
file: "frontend_scripting.md"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user