Merge branch 'develop' into date/time

This commit is contained in:
SiriusXT 2025-06-05 18:57:19 +08:00
commit 5d017f4524
33 changed files with 982 additions and 893 deletions

View File

@ -37,7 +37,7 @@ jobs:
shell: bash shell: bash
forge_platform: darwin forge_platform: darwin
- name: linux - name: linux
image: ubuntu-latest image: ubuntu-22.04
shell: bash shell: bash
forge_platform: linux forge_platform: linux
- name: windows - name: windows
@ -102,7 +102,7 @@ jobs:
arch: [x64, arm64] arch: [x64, arm64]
include: include:
- arch: x64 - arch: x64
runs-on: ubuntu-latest runs-on: ubuntu-22.04
- arch: arm64 - arch: arm64
runs-on: ubuntu-24.04-arm runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}

View File

@ -73,7 +73,7 @@ jobs:
arch: [x64, arm64] arch: [x64, arm64]
include: include:
- arch: x64 - arch: x64
runs-on: ubuntu-latest runs-on: ubuntu-22.04
- arch: arm64 - arch: arm64
runs-on: ubuntu-24.04-arm runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }}

View File

@ -36,7 +36,7 @@
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "1.52.0", "@playwright/test": "1.52.0",
"@stylistic/eslint-plugin": "4.4.0", "@stylistic/eslint-plugin": "4.4.1",
"@types/express": "5.0.1", "@types/express": "5.0.1",
"@types/node": "22.15.29", "@types/node": "22.15.29",
"@types/yargs": "17.0.33", "@types/yargs": "17.0.33",

View File

@ -64,7 +64,7 @@
"@types/leaflet-gpx": "1.3.7", "@types/leaflet-gpx": "1.3.7",
"@types/mark.js": "8.11.12", "@types/mark.js": "8.11.12",
"@types/react": "19.1.6", "@types/react": "19.1.6",
"@types/react-dom": "19.1.5", "@types/react-dom": "19.1.6",
"copy-webpack-plugin": "13.0.0", "copy-webpack-plugin": "13.0.0",
"happy-dom": "17.6.3", "happy-dom": "17.6.3",
"script-loader": "0.7.2", "script-loader": "0.7.2",

View File

@ -8,7 +8,7 @@ interface Entity {
export interface EntityChange { export interface EntityChange {
id?: number | null; id?: number | null;
noteId?: string; noteId?: string;
entityName: EntityRowNames; entityName: EntityType;
entityId: string; entityId: string;
entity?: Entity; entity?: Entity;
positions?: Record<string, number>; positions?: Record<string, number>;
@ -22,3 +22,5 @@ export interface EntityChange {
changeId?: string | null; changeId?: string | null;
instanceId?: string | null; instanceId?: string | null;
} }
export type EntityType = "notes" | "branches" | "attributes" | "note_reordering" | "revisions" | "options" | "attachments" | "blobs" | "etapi_tokens" | "note_embeddings";

View File

@ -17,7 +17,7 @@
"@types/electron-squirrel-startup": "1.0.2", "@types/electron-squirrel-startup": "1.0.2",
"@triliumnext/server": "workspace:*", "@triliumnext/server": "workspace:*",
"copy-webpack-plugin": "13.0.0", "copy-webpack-plugin": "13.0.0",
"electron": "36.3.2", "electron": "36.4.0",
"@electron-forge/cli": "7.8.1", "@electron-forge/cli": "7.8.1",
"@electron-forge/maker-deb": "7.8.1", "@electron-forge/maker-deb": "7.8.1",
"@electron-forge/maker-dmg": "7.8.1", "@electron-forge/maker-dmg": "7.8.1",

View File

@ -7,8 +7,11 @@ import tray from "@triliumnext/server/src/services/tray.js";
import options from "@triliumnext/server/src/services/options.js"; import options from "@triliumnext/server/src/services/options.js";
import electronDebug from "electron-debug"; import electronDebug from "electron-debug";
import electronDl from "electron-dl"; import electronDl from "electron-dl";
import { deferred } from "@triliumnext/server/src/services/utils.js";
async function main() { async function main() {
const serverInitializedPromise = deferred<void>();
// Prevent Trilium starting twice on first install and on uninstall for the Windows installer. // Prevent Trilium starting twice on first install and on uninstall for the Windows installer.
if ((require("electron-squirrel-startup")).default) { if ((require("electron-squirrel-startup")).default) {
process.exit(0); process.exit(0);
@ -37,7 +40,11 @@ async function main() {
} }
}); });
electron.app.on("ready", onReady); electron.app.on("ready", async () => {
await serverInitializedPromise;
console.log("Starting Electron...");
await onReady();
});
electron.app.on("will-quit", () => { electron.app.on("will-quit", () => {
electron.globalShortcut.unregisterAll(); electron.globalShortcut.unregisterAll();
@ -47,7 +54,10 @@ async function main() {
process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true"; process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true";
await initializeTranslations(); await initializeTranslations();
await import("@triliumnext/server/src/main.js"); const startTriliumServer = (await import("@triliumnext/server/src/www.js")).default;
await startTriliumServer();
console.log("Server loaded");
serverInitializedPromise.resolve();
} }
async function onReady() { async function onReady() {

View File

@ -12,7 +12,7 @@
"@triliumnext/desktop": "workspace:*", "@triliumnext/desktop": "workspace:*",
"@types/fs-extra": "11.0.4", "@types/fs-extra": "11.0.4",
"copy-webpack-plugin": "13.0.0", "copy-webpack-plugin": "13.0.0",
"electron": "36.3.2", "electron": "36.4.0",
"fs-extra": "11.3.0" "fs-extra": "11.3.0"
}, },
"nx": { "nx": {

View File

@ -38,7 +38,8 @@ export function startElectron(callback: () => void): DeferredPromise<void> {
console.log("Electron is ready!"); console.log("Electron is ready!");
// Start the server. // Start the server.
await import("@triliumnext/server/src/main.js"); const startTriliumServer = (await import("@triliumnext/server/src/www.js")).default;
await startTriliumServer();
// Create the main window. // Create the main window.
await windowService.createMainWindow(electron.app); await windowService.createMainWindow(electron.app);

View File

@ -0,0 +1,6 @@
TRILIUM_ENV=dev
TRILIUM_DATA_DIR=./apps/server/spec/db
TRILIUM_RESOURCE_DIR=./apps/server/dist
TRILIUM_PUBLIC_SERVER=http://localhost:4200
TRILIUM_PORT=8086
TRILIUM_INTEGRATION_TEST=edit

View File

@ -39,7 +39,7 @@
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@types/xml2js": "0.4.14", "@types/xml2js": "0.4.14",
"express-http-proxy": "2.1.1", "express-http-proxy": "2.1.1",
"@anthropic-ai/sdk": "0.52.0", "@anthropic-ai/sdk": "0.53.0",
"@braintree/sanitize-url": "7.1.1", "@braintree/sanitize-url": "7.1.1",
"@triliumnext/commons": "workspace:*", "@triliumnext/commons": "workspace:*",
"@triliumnext/express-partial-content": "workspace:*", "@triliumnext/express-partial-content": "workspace:*",
@ -59,7 +59,7 @@
"debounce": "2.2.0", "debounce": "2.2.0",
"debug": "4.4.1", "debug": "4.4.1",
"ejs": "3.1.10", "ejs": "3.1.10",
"electron": "36.3.2", "electron": "36.4.0",
"electron-debug": "4.1.0", "electron-debug": "4.1.0",
"electron-window-state": "5.0.3", "electron-window-state": "5.0.3",
"escape-html": "1.0.3", "escape-html": "1.0.3",
@ -129,6 +129,23 @@
"runBuildTargetDependencies": false "runBuildTargetDependencies": false
} }
}, },
"edit-integration-db": {
"executor": "@nx/js:node",
"dependsOn": [
{
"projects": [
"client"
],
"target": "serve"
},
"build-without-client"
],
"continuous": true,
"options": {
"buildTarget": "server:build-without-client:development",
"runBuildTargetDependencies": false
}
},
"package": { "package": {
"dependsOn": [ "dependsOn": [
"build" "build"

Binary file not shown.

View File

@ -28,7 +28,7 @@
where you can track your daily weight. This data is then used in <a href="#root/_help_R7abl2fc6Mxi">Weight tracker</a>.</p> where you can track your daily weight. This data is then used in <a href="#root/_help_R7abl2fc6Mxi">Weight tracker</a>.</p>
<h2>Week Note and Quarter Note</h2> <h2>Week Note and Quarter Note</h2>
<p>Week and quarter notes are disabled by default, since it might be too <p>Week and quarter notes are disabled by default, since it might be too
much for some people. To enable them, you need to set <code>#enableWeekNotes</code> and <code>#enableQuarterNotes</code> attributes much for some people. To enable them, you need to set <code>#enableWeekNote</code> and <code>#enableQuarterNote</code> attributes
on the root calendar note, which is identified by <code>#calendarRoot</code> label. on the root calendar note, which is identified by <code>#calendarRoot</code> label.
Week note is affected by the first week of year option. Be careful when Week note is affected by the first week of year option. Be careful when
you already have some week notes created, it will not automatically change you already have some week notes created, it will not automatically change
@ -40,15 +40,26 @@
(identified by <code>#calendarRoot</code> label):</p> (identified by <code>#calendarRoot</code> label):</p>
<ul> <ul>
<li>yearTemplate</li> <li>yearTemplate</li>
<li>quarterTemplate (if <code>#enableQuarterNotes</code> is set)</li> <li>quarterTemplate (if <code>#enableQuarterNote</code> is set)</li>
<li>monthTemplate</li> <li>monthTemplate</li>
<li>weekTemplate (if <code>#enableWeekNotes</code> is set)</li> <li>weekTemplate (if <code>#enableWeekNote</code> is set)</li>
<li>dateTemplate</li> <li>dateTemplate</li>
</ul> </ul>
<p>All of these are relations. When Trilium creates a new note for year or <p>All of these are relations. When Trilium creates a new note for year or
month or date, it will take a look at the root and attach a corresponding <code>~template</code> relation month or date, it will take a look at the root and attach a corresponding <code>~template</code> relation
to the newly created role. Using this, you can e.g. create your daily template to the newly created role. Using this, you can e.g. create your daily template
with e.g. checkboxes for daily routine etc.</p> with e.g. checkboxes for daily routine etc.</p>
<h3>Migrate from old template usage</h3>
<p>If you have been using Journal prior to version v0.93.0, the previous
template pattern likely used was <code>~child:template=</code>.
<br>To transition to the new system:</p>
<ol>
<li>Set up the new template pattern in the Calendar root note.</li>
<li>Use <a href="#root/_help_ivYnonVFBxbQ">Bulk Actions</a> to remove <code>child:template</code> and <code>child:child:template</code> from
all notes under the Journal (calendar root).</li>
<li>Ensure that all old template patterns are fully removed to prevent conflicts
with the new setup.</li>
</ol>
<h2>Naming pattern</h2> <h2>Naming pattern</h2>
<p>You can customize the title of generated journal notes by defining a <code>#datePattern</code>, <code>#weekPattern</code>, <code>#monthPattern</code>, <code>#quarterPattern</code> and <code>#yearPattern</code> attribute <p>You can customize the title of generated journal notes by defining a <code>#datePattern</code>, <code>#weekPattern</code>, <code>#monthPattern</code>, <code>#quarterPattern</code> and <code>#yearPattern</code> attribute
on a root calendar note (identified by <code>#calendarRoot</code> label). on a root calendar note (identified by <code>#calendarRoot</code> label).
@ -139,8 +150,3 @@
see e.g. getDayNote() function.</p> see e.g. getDayNote() function.</p>
<p>Day (and year, month) notes are created with a label - e.g. <code>#dateNote="2025-03-09"</code> this <p>Day (and year, month) notes are created with a label - e.g. <code>#dateNote="2025-03-09"</code> this
can then be used by other scripts to add new notes to day note etc.</p> can then be used by other scripts to add new notes to day note etc.</p>
<p>Journal also has relation <code>child:child:child:template=Day template</code> (see
[[attribute inheritance]]) which effectively adds [[template]] to day notes
(grand-grand-grand children of Journal). Please note that, when you enable
week notes or quarter notes, it will not automatically change the relation
for the child level.</p>

View File

@ -40,7 +40,19 @@
you can also mark templates with <code>#workspaceTemplate</code> to display you can also mark templates with <code>#workspaceTemplate</code> to display
them only in the workspace.</p> them only in the workspace.</p>
<p>Templates can also be added or changed after note creation by creating <p>Templates can also be added or changed after note creation by creating
a <code>~template</code> relation pointing to the desired template note.</p> a <code>~template</code> relation pointing to the desired template note.&nbsp;</p>
<p>To specify a template for child notes, you can use a <code>~child:template</code> relation
pointing to the appropriate template note. There is no limit to the depth
of the hierarchy — you can use <code>~child:child:template</code>, <code>~child:child:child:template</code>,
and so on.</p>
<aside class="admonition important">
<p>Changing the template hierarchy after the parent note is created will
not retroactively apply to newly created child notes.
<br>For example, if you initially use <code>~child:template</code> and later
switch to <code>~child:child:template</code>, it will not automatically
apply the new template to the grandchild notes. Only the structure present
at the time of note creation is considered.</p>
</aside>
<h2>Additional Notes</h2> <h2>Additional Notes</h2>
<p>From a visual perspective, templates can define <code>#iconClass</code> and <code>#cssClass</code> attributes, <p>From a visual perspective, templates can define <code>#iconClass</code> and <code>#cssClass</code> attributes,
allowing all instance notes (e.g., books) to display a specific icon and allowing all instance notes (e.g., books) to display a specific icon and

View File

@ -65,9 +65,17 @@ function load() {
new BEtapiToken(row); new BEtapiToken(row);
} }
try {
for (const row of sql.getRows<NoteEmbeddingRow>(/*sql*/`SELECT embedId, noteId, providerId, modelId, dimension, embedding, version, dateCreated, dateModified, utcDateCreated, utcDateModified FROM note_embeddings`)) { for (const row of sql.getRows<NoteEmbeddingRow>(/*sql*/`SELECT embedId, noteId, providerId, modelId, dimension, embedding, version, dateCreated, dateModified, utcDateCreated, utcDateModified FROM note_embeddings`)) {
new BNoteEmbedding(row).init(); new BNoteEmbedding(row).init();
} }
} catch (e: unknown) {
if (e && typeof e === "object" && "message" in e && typeof e.message === "string" && e.message.includes("no such table")) {
// Can be ignored.
} else {
throw e;
}
}
}); });
for (const noteId in becca.notes) { for (const noteId in becca.notes) {

View File

@ -7,7 +7,8 @@ import { initializeTranslations } from "./services/i18n.js";
async function startApplication() { async function startApplication() {
await initializeTranslations(); await initializeTranslations();
await import("./www.js"); const startTriliumServer = (await import("./www.js")).default;
await startTriliumServer();
} }
startApplication(); startApplication();

View File

@ -266,7 +266,7 @@ function getMonthNote(dateStr: string, _rootNote: BNote | null = null): BNote {
return monthNote; return monthNote;
} }
let monthParentNote; let monthParentNote: BNote | null;
if (rootNote.hasLabel("enableQuarterNote")) { if (rootNote.hasLabel("enableQuarterNote")) {
monthParentNote = getQuarterNote(getQuarterNumberStr(dayjs(dateStr)), rootNote); monthParentNote = getQuarterNote(getQuarterNumberStr(dayjs(dateStr)), rootNote);
@ -296,7 +296,7 @@ function getMonthNote(dateStr: string, _rootNote: BNote | null = null): BNote {
function getWeekStartDate(date: Dayjs): Dayjs { function getWeekStartDate(date: Dayjs): Dayjs {
const day = date.day(); const day = date.day();
let diff; let diff: number;
if (optionService.getOption("firstDayOfWeek") === "0") { // Sunday if (optionService.getOption("firstDayOfWeek") === "0") { // Sunday
diff = date.date() - day + (day === 0 ? -6 : 1); // adjust when day is sunday diff = date.date() - day + (day === 0 ? -6 : 1); // adjust when day is sunday
@ -456,7 +456,7 @@ function getDayNote(dateStr: string, _rootNote: BNote | null = null): BNote {
return dateNote; return dateNote;
} }
let dateParentNote; let dateParentNote: BNote | null;
if (rootNote.hasLabel("enableWeekNote")) { if (rootNote.hasLabel("enableWeekNote")) {
dateParentNote = getWeekNote(getWeekNumberStr(dayjs(dateStr)), rootNote); dateParentNote = getWeekNote(getWeekNumberStr(dayjs(dateStr)), rootNote);

View File

@ -16,5 +16,5 @@ describe("Migration", () => {
resolve(); resolve();
}); });
}); });
}); }, 60_000);
}); });

View File

@ -25,10 +25,12 @@ async function migrate() {
} }
// backup before attempting migration // backup before attempting migration
if (!process.env.TRILIUM_INTEGRATION_TEST) {
await backupService.backupNow( await backupService.backupNow(
// creating a special backup for version 0.60.4, the changes in 0.61 are major. // creating a special backup for version 0.60.4, the changes in 0.61 are major.
currentDbVersion === 214 ? `before-migration-v060` : "before-migration" currentDbVersion === 214 ? `before-migration-v060` : "before-migration"
); );
}
const migrations = await prepareMigrations(currentDbVersion); const migrations = await prepareMigrations(currentDbVersion);

View File

@ -76,7 +76,7 @@ function deriveMime(type: string, mime?: string) {
function copyChildAttributes(parentNote: BNote, childNote: BNote) { function copyChildAttributes(parentNote: BNote, childNote: BNote) {
for (const attr of parentNote.getAttributes()) { for (const attr of parentNote.getAttributes()) {
if (attr.name.startsWith("child:")) { if (attr.name.startsWith("child:")) {
const name = attr.name.substr(6); const name = attr.name.substring(6);
const hasAlreadyTemplate = childNote.hasRelation("template"); const hasAlreadyTemplate = childNote.hasRelation("template");
if (hasAlreadyTemplate && attr.type === "relation" && name === "template") { if (hasAlreadyTemplate && attr.type === "relation" && name === "template") {
@ -472,7 +472,7 @@ async function downloadImage(noteId: string, imageUrl: string) {
if (imageUrl.toLowerCase().startsWith("file://")) { if (imageUrl.toLowerCase().startsWith("file://")) {
imageBuffer = await new Promise((res, rej) => { imageBuffer = await new Promise((res, rej) => {
const localFilePath = imageUrl.substr("file://".length); const localFilePath = imageUrl.substring("file://".length);
return fs.readFile(localFilePath, (err, data) => { return fs.readFile(localFilePath, (err, data) => {
if (err) { if (err) {
@ -521,14 +521,14 @@ function downloadImages(noteId: string, content: string) {
const inlineImageMatch = /^data:image\/[a-z]+;base64,/.exec(url); const inlineImageMatch = /^data:image\/[a-z]+;base64,/.exec(url);
if (inlineImageMatch) { if (inlineImageMatch) {
const imageBase64 = url.substr(inlineImageMatch[0].length); const imageBase64 = url.substring(inlineImageMatch[0].length);
const imageBuffer = Buffer.from(imageBase64, "base64"); const imageBuffer = Buffer.from(imageBase64, "base64");
const attachment = imageService.saveImageToAttachment(noteId, imageBuffer, "inline image", true, true); const attachment = imageService.saveImageToAttachment(noteId, imageBuffer, "inline image", true, true);
const encodedTitle = encodeURIComponent(attachment.title); const encodedTitle = encodeURIComponent(attachment.title);
content = `${content.substr(0, imageMatch.index)}<img src="api/attachments/${attachment.attachmentId}/image/${encodedTitle}"${content.substr(imageMatch.index + imageMatch[0].length)}`; content = `${content.substring(0, imageMatch.index)}<img src="api/attachments/${attachment.attachmentId}/image/${encodedTitle}"${content.substring(imageMatch.index + imageMatch[0].length)}`;
} else if ( } else if (
!url.includes("api/images/") && !url.includes("api/images/") &&
!/api\/attachments\/.+\/image\/?.*/.test(url) && !/api\/attachments\/.+\/image\/?.*/.test(url) &&
@ -631,7 +631,7 @@ function saveAttachments(note: BNote, content: string) {
content: buffer content: buffer
}); });
content = `${content.substr(0, attachmentMatch.index)}<a class="reference-link" href="#root/${note.noteId}?viewMode=attachments&attachmentId=${attachment.attachmentId}">${title}</a>${content.substr(attachmentMatch.index + attachmentMatch[0].length)}`; content = `${content.substring(0, attachmentMatch.index)}<a class="reference-link" href="#root/${note.noteId}?viewMode=attachments&attachmentId=${attachment.attachmentId}">${title}</a>${content.substring(attachmentMatch.index + attachmentMatch[0].length)}`;
} }
// removing absolute references to server to keep it working between instances, // removing absolute references to server to keep it working between instances,

View File

@ -14,37 +14,35 @@ import type { Express } from "express";
const MINIMUM_NODE_VERSION = "20.0.0"; const MINIMUM_NODE_VERSION = "20.0.0";
// setup basic error handling even before requiring dependencies, since those can produce errors as well export default async function startTriliumServer() {
// setup basic error handling even before requiring dependencies, since those can produce errors as well
process.on("unhandledRejection", (error: Error) => { process.on("unhandledRejection", (error: Error) => {
// this makes sure that stacktrace of failed promise is printed out // this makes sure that stacktrace of failed promise is printed out
console.log(error); console.log(error);
// but also try to log it into file // but also try to log it into file
log.info(error); log.info(error);
}); });
function exit() { function exit() {
console.log("Caught interrupt/termination signal. Exiting."); console.log("Caught interrupt/termination signal. Exiting.");
process.exit(0); process.exit(0);
} }
process.on("SIGINT", exit); process.on("SIGINT", exit);
process.on("SIGTERM", exit); process.on("SIGTERM", exit);
if (utils.compareVersions(process.versions.node, MINIMUM_NODE_VERSION) < 0) { if (utils.compareVersions(process.versions.node, MINIMUM_NODE_VERSION) < 0) {
console.error(); console.error();
console.error(`The Trilium server requires Node.js ${MINIMUM_NODE_VERSION} and later in order to start.\n`); console.error(`The Trilium server requires Node.js ${MINIMUM_NODE_VERSION} and later in order to start.\n`);
console.error(`\tCurrent version:\t${process.versions.node}`); console.error(`\tCurrent version:\t${process.versions.node}`);
console.error(`\tExpected version:\t${MINIMUM_NODE_VERSION}`); console.error(`\tExpected version:\t${MINIMUM_NODE_VERSION}`);
console.error(); console.error();
process.exit(1); process.exit(1);
} }
tmp.setGracefulCleanup(); tmp.setGracefulCleanup();
startTrilium();
async function startTrilium() {
const app = await buildApp(); const app = await buildApp();
/** /**
@ -98,7 +96,7 @@ function startHttpServer(app: Express) {
log.info(`Trusted reverse proxy: ${app.get("trust proxy")}`); log.info(`Trusted reverse proxy: ${app.get("trust proxy")}`);
let httpServer; let httpServer: http.Server | https.Server;
if (config["Network"]["https"]) { if (config["Network"]["https"]) {
if (!config["Network"]["keyPath"] || !config["Network"]["keyPath"].trim().length) { if (!config["Network"]["keyPath"] || !config["Network"]["keyPath"].trim().length) {

View File

@ -9341,6 +9341,13 @@
"isInheritable": false, "isInheritable": false,
"position": 50 "position": 50
}, },
{
"type": "relation",
"name": "internalLink",
"value": "ivYnonVFBxbQ",
"isInheritable": false,
"position": 60
},
{ {
"type": "label", "type": "label",
"name": "shareAlias", "name": "shareAlias",

View File

@ -19,7 +19,7 @@ You can also notice how this day note has [promoted attribute](../Attributes/Pro
## Week Note and Quarter Note ## Week Note and Quarter Note
Week and quarter notes are disabled by default, since it might be too much for some people. To enable them, you need to set `#enableWeekNotes` and `#enableQuarterNotes` attributes on the root calendar note, which is identified by `#calendarRoot` label. Week note is affected by the first week of year option. Be careful when you already have some week notes created, it will not automatically change the existing week notes and might lead to some duplicates. Week and quarter notes are disabled by default, since it might be too much for some people. To enable them, you need to set `#enableWeekNote` and `#enableQuarterNote` attributes on the root calendar note, which is identified by `#calendarRoot` label. Week note is affected by the first week of year option. Be careful when you already have some week notes created, it will not automatically change the existing week notes and might lead to some duplicates.
## Templates ## Templates
@ -28,13 +28,22 @@ Trilium provides [template](../Templates.md) functionality, and it could be used
You can define one of the following relations on the root of the journal (identified by `#calendarRoot` label): You can define one of the following relations on the root of the journal (identified by `#calendarRoot` label):
* yearTemplate * yearTemplate
* quarterTemplate (if `#enableQuarterNotes` is set) * quarterTemplate (if `#enableQuarterNote` is set)
* monthTemplate * monthTemplate
* weekTemplate (if `#enableWeekNotes` is set) * weekTemplate (if `#enableWeekNote` is set)
* dateTemplate * dateTemplate
All of these are relations. When Trilium creates a new note for year or month or date, it will take a look at the root and attach a corresponding `~template` relation to the newly created role. Using this, you can e.g. create your daily template with e.g. checkboxes for daily routine etc. All of these are relations. When Trilium creates a new note for year or month or date, it will take a look at the root and attach a corresponding `~template` relation to the newly created role. Using this, you can e.g. create your daily template with e.g. checkboxes for daily routine etc.
### Migrate from old template usage
If you have been using Journal prior to version v0.93.0, the previous template pattern likely used was `~child:template=`.
To transition to the new system:
1. Set up the new template pattern in the Calendar root note.
2. Use [Bulk Actions](../Bulk%20Actions.md) to remove `child:template` and `child:child:template` from all notes under the Journal (calendar root).
3. Ensure that all old template patterns are fully removed to prevent conflicts with the new setup.
## Naming pattern ## Naming pattern
You can customize the title of generated journal notes by defining a `#datePattern`, `#weekPattern`, `#monthPattern`, `#quarterPattern` and `#yearPattern` attribute on a root calendar note (identified by `#calendarRoot` label). The naming pattern replacements follow a level-up compatibility - each level can use replacements from itself and all levels above it. For example, `#monthPattern` can use month, quarter and year replacements, while `#weekPattern` can use week, month, quarter and year replacements. But it is not possible to use week replacements in `#monthPattern`. You can customize the title of generated journal notes by defining a `#datePattern`, `#weekPattern`, `#monthPattern`, `#quarterPattern` and `#yearPattern` attribute on a root calendar note (identified by `#calendarRoot` label). The naming pattern replacements follow a level-up compatibility - each level can use replacements from itself and all levels above it. For example, `#monthPattern` can use month, quarter and year replacements, while `#weekPattern` can use week, month, quarter and year replacements. But it is not possible to use week replacements in `#monthPattern`.
@ -99,5 +108,3 @@ The default is `{year}`
Trilium has some special support for day notes in the form of [backend Script API](https://triliumnext.github.io/Notes/backend_api/BackendScriptApi.html) - see e.g. getDayNote() function. Trilium has some special support for day notes in the form of [backend Script API](https://triliumnext.github.io/Notes/backend_api/BackendScriptApi.html) - see e.g. getDayNote() function.
Day (and year, month) notes are created with a label - e.g. `#dateNote="2025-03-09"` this can then be used by other scripts to add new notes to day note etc. Day (and year, month) notes are created with a label - e.g. `#dateNote="2025-03-09"` this can then be used by other scripts to add new notes to day note etc.
Journal also has relation `child:child:child:template=Day template` (see \[\[attribute inheritance\]\]) which effectively adds \[\[template\]\] to day notes (grand-grand-grand children of Journal). Please note that, when you enable week notes or quarter notes, it will not automatically change the relation for the child level.

View File

@ -25,7 +25,13 @@ To create an instance note through the UI:
For the template to appear in the menu, the template note must have the `#template` label. Do not confuse this with the `~template` relation, which links the instance note to the template note. If you use [workspaces](../Basic%20Concepts%20and%20Features/Navigation/Workspaces.md), you can also mark templates with `#workspaceTemplate` to display them only in the workspace. For the template to appear in the menu, the template note must have the `#template` label. Do not confuse this with the `~template` relation, which links the instance note to the template note. If you use [workspaces](../Basic%20Concepts%20and%20Features/Navigation/Workspaces.md), you can also mark templates with `#workspaceTemplate` to display them only in the workspace.
Templates can also be added or changed after note creation by creating a `~template` relation pointing to the desired template note. Templates can also be added or changed after note creation by creating a `~template` relation pointing to the desired template note. 
To specify a template for child notes, you can use a `~child:template` relation pointing to the appropriate template note. There is no limit to the depth of the hierarchy — you can use `~child:child:template`, `~child:child:child:template`, and so on.
> [!IMPORTANT]
> Changing the template hierarchy after the parent note is created will not retroactively apply to newly created child notes.
> For example, if you initially use `~child:template` and later switch to `~child:child:template`, it will not automatically apply the new template to the grandchild notes. Only the structure present at the time of note creation is considered.
## Additional Notes ## Additional Notes

View File

@ -34,12 +34,12 @@
"devDependencies": { "devDependencies": {
"@ckeditor/ckeditor5-dev-build-tools": "43.0.1", "@ckeditor/ckeditor5-dev-build-tools": "43.0.1",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^3.0.1", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.33.0", "@typescript-eslint/eslint-plugin": "~8.33.0",
"@typescript-eslint/parser": "^8.0.0", "@typescript-eslint/parser": "^8.0.0",
"@vitest/browser": "^3.0.5", "@vitest/browser": "^3.0.5",
"@vitest/coverage-istanbul": "^3.0.5", "@vitest/coverage-istanbul": "^3.0.5",
"ckeditor5": "45.1.0", "ckeditor5": "45.2.0",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-config-ckeditor5": ">=9.1.0", "eslint-config-ckeditor5": ">=9.1.0",
"http-server": "^14.1.0", "http-server": "^14.1.0",
@ -53,7 +53,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "45.1.0" "ckeditor5": "45.2.0"
}, },
"author": "Elian Doran <contact@eliandoran.me>", "author": "Elian Doran <contact@eliandoran.me>",
"license": "GPL-2.0-or-later", "license": "GPL-2.0-or-later",

View File

@ -35,12 +35,12 @@
"devDependencies": { "devDependencies": {
"@ckeditor/ckeditor5-dev-build-tools": "43.0.1", "@ckeditor/ckeditor5-dev-build-tools": "43.0.1",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^3.0.1", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.33.0", "@typescript-eslint/eslint-plugin": "~8.33.0",
"@typescript-eslint/parser": "^8.0.0", "@typescript-eslint/parser": "^8.0.0",
"@vitest/browser": "^3.0.5", "@vitest/browser": "^3.0.5",
"@vitest/coverage-istanbul": "^3.0.5", "@vitest/coverage-istanbul": "^3.0.5",
"ckeditor5": "45.1.0", "ckeditor5": "45.2.0",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-config-ckeditor5": ">=9.1.0", "eslint-config-ckeditor5": ">=9.1.0",
"http-server": "^14.1.0", "http-server": "^14.1.0",
@ -54,7 +54,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "45.1.0" "ckeditor5": "45.2.0"
}, },
"scripts": { "scripts": {
"build": "node ./scripts/build-dist.mjs", "build": "node ./scripts/build-dist.mjs",

View File

@ -37,12 +37,12 @@
"devDependencies": { "devDependencies": {
"@ckeditor/ckeditor5-dev-build-tools": "43.0.1", "@ckeditor/ckeditor5-dev-build-tools": "43.0.1",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^3.0.1", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.33.0", "@typescript-eslint/eslint-plugin": "~8.33.0",
"@typescript-eslint/parser": "^8.0.0", "@typescript-eslint/parser": "^8.0.0",
"@vitest/browser": "^3.0.5", "@vitest/browser": "^3.0.5",
"@vitest/coverage-istanbul": "^3.0.5", "@vitest/coverage-istanbul": "^3.0.5",
"ckeditor5": "45.1.0", "ckeditor5": "45.2.0",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-config-ckeditor5": ">=9.1.0", "eslint-config-ckeditor5": ">=9.1.0",
"http-server": "^14.1.0", "http-server": "^14.1.0",
@ -56,7 +56,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "45.1.0" "ckeditor5": "45.2.0"
}, },
"scripts": { "scripts": {
"build": "node ./scripts/build-dist.mjs", "build": "node ./scripts/build-dist.mjs",

View File

@ -38,12 +38,12 @@
"@ckeditor/ckeditor5-dev-build-tools": "43.0.1", "@ckeditor/ckeditor5-dev-build-tools": "43.0.1",
"@ckeditor/ckeditor5-dev-utils": "43.0.1", "@ckeditor/ckeditor5-dev-utils": "43.0.1",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^3.0.1", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.33.0", "@typescript-eslint/eslint-plugin": "~8.33.0",
"@typescript-eslint/parser": "^8.0.0", "@typescript-eslint/parser": "^8.0.0",
"@vitest/browser": "^3.0.5", "@vitest/browser": "^3.0.5",
"@vitest/coverage-istanbul": "^3.0.5", "@vitest/coverage-istanbul": "^3.0.5",
"ckeditor5": "45.1.0", "ckeditor5": "45.2.0",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-config-ckeditor5": ">=9.1.0", "eslint-config-ckeditor5": ">=9.1.0",
"http-server": "^14.1.0", "http-server": "^14.1.0",
@ -57,7 +57,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "45.1.0" "ckeditor5": "45.2.0"
}, },
"scripts": { "scripts": {
"build": "node ./scripts/build-dist.mjs", "build": "node ./scripts/build-dist.mjs",
@ -90,6 +90,6 @@
} }
}, },
"dependencies": { "dependencies": {
"@ckeditor/ckeditor5-icons": "45.1.0" "@ckeditor/ckeditor5-icons": "45.2.0"
} }
} }

View File

@ -37,12 +37,12 @@
"devDependencies": { "devDependencies": {
"@ckeditor/ckeditor5-dev-build-tools": "43.0.1", "@ckeditor/ckeditor5-dev-build-tools": "43.0.1",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^3.0.1", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.33.0", "@typescript-eslint/eslint-plugin": "~8.33.0",
"@typescript-eslint/parser": "^8.0.0", "@typescript-eslint/parser": "^8.0.0",
"@vitest/browser": "^3.0.5", "@vitest/browser": "^3.0.5",
"@vitest/coverage-istanbul": "^3.0.5", "@vitest/coverage-istanbul": "^3.0.5",
"ckeditor5": "45.1.0", "ckeditor5": "45.2.0",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-config-ckeditor5": ">=9.1.0", "eslint-config-ckeditor5": ">=9.1.0",
"http-server": "^14.1.0", "http-server": "^14.1.0",
@ -56,7 +56,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "45.1.0" "ckeditor5": "45.2.0"
}, },
"scripts": { "scripts": {
"build": "node ./scripts/build-dist.mjs", "build": "node ./scripts/build-dist.mjs",

View File

@ -28,7 +28,7 @@
} }
}, },
"dependencies": { "dependencies": {
"ckeditor5": "45.1.0", "ckeditor5": "45.2.0",
"@triliumnext/ckeditor5-keyboard-marker": "workspace:*", "@triliumnext/ckeditor5-keyboard-marker": "workspace:*",
"@triliumnext/ckeditor5-mermaid": "workspace:*", "@triliumnext/ckeditor5-mermaid": "workspace:*",
"@triliumnext/ckeditor5-admonition": "workspace:*", "@triliumnext/ckeditor5-admonition": "workspace:*",

View File

@ -31,29 +31,29 @@
"@codemirror/legacy-modes": "6.5.1", "@codemirror/legacy-modes": "6.5.1",
"@codemirror/search": "6.5.11", "@codemirror/search": "6.5.11",
"@codemirror/view": "6.37.1", "@codemirror/view": "6.37.1",
"@fsegurai/codemirror-theme-abcdef": "6.1.4", "@fsegurai/codemirror-theme-abcdef": "6.2.0",
"@fsegurai/codemirror-theme-abyss": "6.1.4", "@fsegurai/codemirror-theme-abyss": "6.2.0",
"@fsegurai/codemirror-theme-android-studio": "6.1.4", "@fsegurai/codemirror-theme-android-studio": "6.2.0",
"@fsegurai/codemirror-theme-andromeda": "6.1.4", "@fsegurai/codemirror-theme-andromeda": "6.2.0",
"@fsegurai/codemirror-theme-basic-dark": "6.1.4", "@fsegurai/codemirror-theme-basic-dark": "6.2.0",
"@fsegurai/codemirror-theme-basic-light": "6.1.4", "@fsegurai/codemirror-theme-basic-light": "6.2.0",
"@fsegurai/codemirror-theme-forest": "6.1.4", "@fsegurai/codemirror-theme-forest": "6.2.0",
"@fsegurai/codemirror-theme-github-dark": "6.1.4", "@fsegurai/codemirror-theme-github-dark": "6.2.0",
"@fsegurai/codemirror-theme-github-light": "6.1.4", "@fsegurai/codemirror-theme-github-light": "6.2.0",
"@fsegurai/codemirror-theme-gruvbox-dark": "6.1.4", "@fsegurai/codemirror-theme-gruvbox-dark": "6.2.0",
"@fsegurai/codemirror-theme-gruvbox-light": "6.1.4", "@fsegurai/codemirror-theme-gruvbox-light": "6.2.0",
"@fsegurai/codemirror-theme-material-dark": "6.1.4", "@fsegurai/codemirror-theme-material-dark": "6.2.0",
"@fsegurai/codemirror-theme-material-light": "6.1.4", "@fsegurai/codemirror-theme-material-light": "6.2.0",
"@fsegurai/codemirror-theme-monokai": "6.1.4", "@fsegurai/codemirror-theme-monokai": "6.2.0",
"@fsegurai/codemirror-theme-nord": "6.1.4", "@fsegurai/codemirror-theme-nord": "6.2.0",
"@fsegurai/codemirror-theme-palenight": "6.1.4", "@fsegurai/codemirror-theme-palenight": "6.2.0",
"@fsegurai/codemirror-theme-solarized-dark": "6.1.4", "@fsegurai/codemirror-theme-solarized-dark": "6.2.0",
"@fsegurai/codemirror-theme-solarized-light": "6.1.4", "@fsegurai/codemirror-theme-solarized-light": "6.2.0",
"@fsegurai/codemirror-theme-tokyo-night-day": "6.1.4", "@fsegurai/codemirror-theme-tokyo-night-day": "6.2.0",
"@fsegurai/codemirror-theme-tokyo-night-storm": "6.1.4", "@fsegurai/codemirror-theme-tokyo-night-storm": "6.2.0",
"@fsegurai/codemirror-theme-volcano": "6.1.4", "@fsegurai/codemirror-theme-volcano": "6.2.0",
"@fsegurai/codemirror-theme-vscode-dark": "6.1.4", "@fsegurai/codemirror-theme-vscode-dark": "6.2.0",
"@fsegurai/codemirror-theme-vscode-light": "6.1.4", "@fsegurai/codemirror-theme-vscode-light": "6.2.0",
"@replit/codemirror-indentation-markers": "6.5.3", "@replit/codemirror-indentation-markers": "6.5.3",
"@replit/codemirror-lang-nix": "6.0.1", "@replit/codemirror-lang-nix": "6.0.1",
"@replit/codemirror-vim": "6.3.0", "@replit/codemirror-vim": "6.3.0",

1560
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -5,5 +5,11 @@
"labels": ["dependencies", "renovate"], "labels": ["dependencies", "renovate"],
"prHourlyLimit": 0, "prHourlyLimit": 0,
"prConcurrentLimit": 0, "prConcurrentLimit": 0,
"branchConcurrentLimit": 0 "branchConcurrentLimit": 0,
"packageRules": [
{
"matchPackageNames": "@fsegurai/codemirror-theme-**",
"groupName": "codemirror themes"
}
]
} }