Merge branch 'develop' into date/time

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

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>
<h2>Week Note and Quarter Note</h2>
<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.
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
@@ -40,15 +40,26 @@
(identified by <code>#calendarRoot</code> label):</p>
<ul>
<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>weekTemplate (if <code>#enableWeekNotes</code> is set)</li>
<li>weekTemplate (if <code>#enableWeekNote</code> is set)</li>
<li>dateTemplate</li>
</ul>
<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
to the newly created role. Using this, you can e.g. create your daily template
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>
<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).
@@ -138,9 +149,4 @@
<p>Trilium has some special support for day notes in the form of <a href="https://triliumnext.github.io/Notes/backend_api/BackendScriptApi.html">backend Script API</a> -
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
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>
can then be used by other scripts to add new notes to day note etc.</p>

View File

@@ -40,7 +40,19 @@
you can also mark templates with <code>#workspaceTemplate</code> to display
them only in the workspace.</p>
<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>
<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

View File

@@ -65,8 +65,16 @@ function load() {
new BEtapiToken(row);
}
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();
try {
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();
}
} 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;
}
}
});

View File

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

View File

@@ -266,7 +266,7 @@ function getMonthNote(dateStr: string, _rootNote: BNote | null = null): BNote {
return monthNote;
}
let monthParentNote;
let monthParentNote: BNote | null;
if (rootNote.hasLabel("enableQuarterNote")) {
monthParentNote = getQuarterNote(getQuarterNumberStr(dayjs(dateStr)), rootNote);
@@ -296,7 +296,7 @@ function getMonthNote(dateStr: string, _rootNote: BNote | null = null): BNote {
function getWeekStartDate(date: Dayjs): Dayjs {
const day = date.day();
let diff;
let diff: number;
if (optionService.getOption("firstDayOfWeek") === "0") { // 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;
}
let dateParentNote;
let dateParentNote: BNote | null;
if (rootNote.hasLabel("enableWeekNote")) {
dateParentNote = getWeekNote(getWeekNumberStr(dayjs(dateStr)), rootNote);

View File

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

View File

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

View File

@@ -76,7 +76,7 @@ function deriveMime(type: string, mime?: string) {
function copyChildAttributes(parentNote: BNote, childNote: BNote) {
for (const attr of parentNote.getAttributes()) {
if (attr.name.startsWith("child:")) {
const name = attr.name.substr(6);
const name = attr.name.substring(6);
const hasAlreadyTemplate = childNote.hasRelation("template");
if (hasAlreadyTemplate && attr.type === "relation" && name === "template") {
@@ -472,7 +472,7 @@ async function downloadImage(noteId: string, imageUrl: string) {
if (imageUrl.toLowerCase().startsWith("file://")) {
imageBuffer = await new Promise((res, rej) => {
const localFilePath = imageUrl.substr("file://".length);
const localFilePath = imageUrl.substring("file://".length);
return fs.readFile(localFilePath, (err, data) => {
if (err) {
@@ -521,14 +521,14 @@ function downloadImages(noteId: string, content: string) {
const inlineImageMatch = /^data:image\/[a-z]+;base64,/.exec(url);
if (inlineImageMatch) {
const imageBase64 = url.substr(inlineImageMatch[0].length);
const imageBase64 = url.substring(inlineImageMatch[0].length);
const imageBuffer = Buffer.from(imageBase64, "base64");
const attachment = imageService.saveImageToAttachment(noteId, imageBuffer, "inline image", true, true);
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 (
!url.includes("api/images/") &&
!/api\/attachments\/.+\/image\/?.*/.test(url) &&
@@ -631,7 +631,7 @@ function saveAttachments(note: BNote, content: string) {
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,

View File

@@ -14,37 +14,35 @@ import type { Express } from "express";
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) => {
// this makes sure that stacktrace of failed promise is printed out
console.log(error);
process.on("unhandledRejection", (error: Error) => {
// this makes sure that stacktrace of failed promise is printed out
console.log(error);
// but also try to log it into file
log.info(error);
});
// but also try to log it into file
log.info(error);
});
function exit() {
console.log("Caught interrupt/termination signal. Exiting.");
process.exit(0);
}
function exit() {
console.log("Caught interrupt/termination signal. Exiting.");
process.exit(0);
}
process.on("SIGINT", exit);
process.on("SIGTERM", exit);
process.on("SIGINT", exit);
process.on("SIGTERM", exit);
if (utils.compareVersions(process.versions.node, MINIMUM_NODE_VERSION) < 0) {
console.error();
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(`\tExpected version:\t${MINIMUM_NODE_VERSION}`);
console.error();
process.exit(1);
}
if (utils.compareVersions(process.versions.node, MINIMUM_NODE_VERSION) < 0) {
console.error();
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(`\tExpected version:\t${MINIMUM_NODE_VERSION}`);
console.error();
process.exit(1);
}
tmp.setGracefulCleanup();
tmp.setGracefulCleanup();
startTrilium();
async function startTrilium() {
const app = await buildApp();
/**
@@ -98,7 +96,7 @@ function startHttpServer(app: Express) {
log.info(`Trusted reverse proxy: ${app.get("trust proxy")}`);
let httpServer;
let httpServer: http.Server | https.Server;
if (config["Network"]["https"]) {
if (!config["Network"]["keyPath"] || !config["Network"]["keyPath"].trim().length) {