Merge remote-tracking branch 'origin/main' into react/collections

; Conflicts:
;	pnpm-lock.yaml
This commit is contained in:
Elian Doran 2025-09-12 13:58:00 +03:00
commit 85949a0464
No known key found for this signature in database
46 changed files with 2336 additions and 1908 deletions

View File

@ -40,7 +40,7 @@
"debounce": "2.2.0", "debounce": "2.2.0",
"draggabilly": "3.0.0", "draggabilly": "3.0.0",
"force-graph": "1.51.0", "force-graph": "1.51.0",
"globals": "16.3.0", "globals": "16.4.0",
"i18next": "25.5.2", "i18next": "25.5.2",
"i18next-http-backend": "3.0.2", "i18next-http-backend": "3.0.2",
"jquery": "3.7.1", "jquery": "3.7.1",
@ -71,7 +71,7 @@
"@types/leaflet": "1.9.20", "@types/leaflet": "1.9.20",
"@types/leaflet-gpx": "1.3.8", "@types/leaflet-gpx": "1.3.8",
"@types/mark.js": "8.11.12", "@types/mark.js": "8.11.12",
"@types/tabulator-tables": "6.2.10", "@types/tabulator-tables": "6.2.11",
"copy-webpack-plugin": "13.0.1", "copy-webpack-plugin": "13.0.1",
"happy-dom": "18.0.1", "happy-dom": "18.0.1",
"script-loader": "0.7.2", "script-loader": "0.7.2",

View File

@ -161,7 +161,8 @@ textarea,
color: var(--muted-text-color); color: var(--muted-text-color);
} }
.form-group.disabled { .form-group.disabled,
.form-checkbox.disabled {
opacity: 0.5; opacity: 0.5;
pointer-events: none; pointer-events: none;
} }

View File

@ -276,7 +276,12 @@
"mime": "MIME 类型: ", "mime": "MIME 类型: ",
"file_size": "文件大小:", "file_size": "文件大小:",
"preview": "预览:", "preview": "预览:",
"preview_not_available": "无法预览此类型的笔记。" "preview_not_available": "无法预览此类型的笔记。",
"diff_on": "显示差异",
"diff_off": "显示内容",
"diff_on_hint": "点击以显示笔记源代码差异",
"diff_off_hint": "点击以显示笔记内容",
"diff_not_available": "差异不可用。"
}, },
"sort_child_notes": { "sort_child_notes": {
"sort_children_by": "按...排序子笔记", "sort_children_by": "按...排序子笔记",
@ -2021,6 +2026,8 @@
"title": "性能", "title": "性能",
"enable-motion": "启用过渡和动画", "enable-motion": "启用过渡和动画",
"enable-shadows": "启用阴影", "enable-shadows": "启用阴影",
"enable-backdrop-effects": "启用菜单、弹窗和面板的背景效果" "enable-backdrop-effects": "启用菜单、弹窗和面板的背景效果",
"enable-smooth-scroll": "启用平滑滚动",
"app-restart-required": "(需重启程序以应用更改)"
} }
} }

View File

@ -0,0 +1 @@
{}

View File

@ -276,7 +276,12 @@
"preview": "Vorschau:", "preview": "Vorschau:",
"preview_not_available": "Für diesen Notiztyp ist keine Vorschau verfügbar.", "preview_not_available": "Für diesen Notiztyp ist keine Vorschau verfügbar.",
"restore_button": "Wiederherstellen", "restore_button": "Wiederherstellen",
"delete_button": "Löschen" "delete_button": "Löschen",
"diff_on": "Zeige Differenz",
"diff_off": "Zeige Inhalt",
"diff_on_hint": "Klicke, um die Differenz des Notiz-Quellcodes zu zeigen",
"diff_off_hint": "Klicke, um den Notizinhalt zu zeigen",
"diff_not_available": "Differenz-Abgleich ist nicht verfügbar."
}, },
"sort_child_notes": { "sort_child_notes": {
"sort_children_by": "Unternotizen sortieren nach...", "sort_children_by": "Unternotizen sortieren nach...",
@ -1839,7 +1844,9 @@
"title": "Leistung", "title": "Leistung",
"enable-motion": "Aktiviere Übergänge und Animationen", "enable-motion": "Aktiviere Übergänge und Animationen",
"enable-shadows": "Aktiviere Schatten", "enable-shadows": "Aktiviere Schatten",
"enable-backdrop-effects": "Aktiviere Hintergrundeffekte für Menüs, Pop-up Fenster und Panele" "enable-backdrop-effects": "Aktiviere Hintergrundeffekte für Menüs, Pop-up Fenster und Panele",
"enable-smooth-scroll": "Aktiviere sanftes Scrollen",
"app-restart-required": "(Ein Neustart der Anwendung ist erforderlich, damit die Änderungen wirksam werden)"
}, },
"code-editor-options": { "code-editor-options": {
"title": "Editor" "title": "Editor"

View File

@ -63,7 +63,7 @@
"search_for_note_by_its_name": "cerca una nota per nome", "search_for_note_by_its_name": "cerca una nota per nome",
"cloned_note_prefix_title": "Le note clonate saranno mostrate nell'albero delle note con il dato prefisso", "cloned_note_prefix_title": "Le note clonate saranno mostrate nell'albero delle note con il dato prefisso",
"prefix_optional": "Prefisso (opzionale)", "prefix_optional": "Prefisso (opzionale)",
"clone_to_selected_note": "Clona sotto la nota selezionata <kbd>invio</kbd>", "clone_to_selected_note": "Clona verso la nota selezionata <kbd>invio</kbd>",
"no_path_to_clone_to": "Nessun percorso per clonare dentro.", "no_path_to_clone_to": "Nessun percorso per clonare dentro.",
"note_cloned": "La nota \"{{clonedTitle}}\" è stata clonata in \"{{targetTitle}}\"" "note_cloned": "La nota \"{{clonedTitle}}\" è stata clonata in \"{{targetTitle}}\""
}, },
@ -79,7 +79,7 @@
"ok": "OK", "ok": "OK",
"close": "Chiudi", "close": "Chiudi",
"delete_notes_preview": "Anteprima di eliminazione delle note", "delete_notes_preview": "Anteprima di eliminazione delle note",
"delete_all_clones_description": "Elimina anche tutti i cloni (può essere disfatto tramite i cambiamenti recenti)", "delete_all_clones_description": "Elimina anche tutti i cloni (può essere ripristinato nella sezione cambiamenti recenti)",
"erase_notes_description": "L'eliminazione normale (soft) marca le note come eliminate e potranno essere recuperate entro un certo lasso di tempo (dalla finestra dei cambiamenti recenti). Selezionando questa opzione le note si elimineranno immediatamente e non sarà possibile recuperarle.", "erase_notes_description": "L'eliminazione normale (soft) marca le note come eliminate e potranno essere recuperate entro un certo lasso di tempo (dalla finestra dei cambiamenti recenti). Selezionando questa opzione le note si elimineranno immediatamente e non sarà possibile recuperarle.",
"erase_notes_warning": "Elimina le note in modo permanente (non potrà essere disfatto), compresi tutti i cloni. Ciò forzerà un nuovo caricamento dell'applicazione.", "erase_notes_warning": "Elimina le note in modo permanente (non potrà essere disfatto), compresi tutti i cloni. Ciò forzerà un nuovo caricamento dell'applicazione.",
"cancel": "Annulla", "cancel": "Annulla",
@ -105,7 +105,10 @@
"format_html": "HTML - raccomandato in quanto mantiene tutti i formati", "format_html": "HTML - raccomandato in quanto mantiene tutti i formati",
"format_html_zip": "HTML in archivio ZIP - questo è raccomandato in quanto conserva tutta la formattazione.", "format_html_zip": "HTML in archivio ZIP - questo è raccomandato in quanto conserva tutta la formattazione.",
"format_markdown": "MArkdown - questo conserva la maggior parte della formattazione.", "format_markdown": "MArkdown - questo conserva la maggior parte della formattazione.",
"export_type_single": "Solo questa nota, senza le sottostanti" "export_type_single": "Solo questa nota, senza le sottostanti",
"format_opml": "OPML - formato per scambio informazioni outline. Formattazione, immagini e files non sono inclusi.",
"opml_version_1": "OPML v.1.0 - solo testo semplice",
"opml_version_2": "OPML v2.0 - supporta anche HTML"
}, },
"password_not_set": { "password_not_set": {
"body1": "Le note protette sono crittografate utilizzando una password utente, ma la password non è stata ancora impostata.", "body1": "Le note protette sono crittografate utilizzando una password utente, ma la password non è stata ancora impostata.",

View File

@ -4,6 +4,64 @@
"homepage": "Homepagina:", "homepage": "Homepagina:",
"app_version": "App versie:", "app_version": "App versie:",
"db_version": "DB Versie:", "db_version": "DB Versie:",
"sync_version": "Sync Versie:" "sync_version": "Sync Versie:",
"build_date": "Build datum:",
"build_revision": "Build revisie:",
"data_directory": "Gegevensmap:"
},
"toast": {
"critical-error": {
"title": "Kritische Error"
}
},
"add_link": {
"add_link": "Voeg link toe",
"help_on_links": "Hulp bij links",
"note": "notitie",
"search_note": "zoek voor notitie op naam",
"link_title_mirrors": "De link titel is hetzelfde als de notitie's huidige titel",
"link_title": "Link titel",
"button_add_link": "Link toevoegen"
},
"branch_prefix": {
"edit_branch_prefix": "Bewerk branch prefix",
"save": "Opslaan",
"branch_prefix_saved": "Branch prefix is opgeslagen."
},
"bulk_actions": {
"bulk_actions": "Bulk acties",
"affected_notes": "Getroffen notities",
"available_actions": "Beschikbare acties",
"chosen_actions": "Kies acties",
"execute_bulk_actions": "Bulk acties uitvoeren",
"bulk_actions_executed": "Bulk acties zijn succesvol uitgevoerd.",
"none_yet": "Nog niks... voeg een actie toe door een van de beschikbare bovenstaande opties te klikken.",
"labels": "Labels",
"relations": "Relaties",
"notes": "Notities",
"other": "Andere"
},
"calendar": {
"april": "April",
"may": "Mei",
"june": "Juni",
"july": "Juli",
"august": "Augustus",
"september": "September",
"october": "Oktober",
"november": "November",
"december": "December"
},
"close_pane_button": {
"close_this_pane": "Sluit dit paneel"
},
"create_pane_button": {
"create_new_split": "Maak nieuwe split"
},
"edit_button": {
"edit_this_note": "Notitie bewerken"
},
"show_toc_widget_button": {
"show_toc": "Laat Inhoudsopgave zien"
} }
} }

View File

@ -267,7 +267,8 @@
"basic_properties": "Proprietăți de bază", "basic_properties": "Proprietăți de bază",
"editable": "Editabil", "editable": "Editabil",
"note_type": "Tipul notiței", "note_type": "Tipul notiței",
"language": "Limbă" "language": "Limbă",
"configure_code_notes": "Configurează notițele de tip cod..."
}, },
"book": { "book": {
"no_children_help": "Această notiță de tip Carte nu are nicio subnotiță așadar nu este nimic de afișat. Vedeți <a href=\"https://triliumnext.github.io/Docs/Wiki/book-note.html\">wiki</a> pentru detalii." "no_children_help": "Această notiță de tip Carte nu are nicio subnotiță așadar nu este nimic de afișat. Vedeți <a href=\"https://triliumnext.github.io/Docs/Wiki/book-note.html\">wiki</a> pentru detalii."
@ -1070,7 +1071,12 @@
"revisions_deleted": "Notița reviziei a fost ștearsă.", "revisions_deleted": "Notița reviziei a fost ștearsă.",
"maximum_revisions": "Numărul maxim de revizii pentru notița curentă: {{number}}.", "maximum_revisions": "Numărul maxim de revizii pentru notița curentă: {{number}}.",
"settings": "Setări revizii ale notițelor", "settings": "Setări revizii ale notițelor",
"snapshot_interval": "Intervalul de creare a reviziilor pentru notițe: {{seconds}}s." "snapshot_interval": "Intervalul de creare a reviziilor pentru notițe: {{seconds}}s.",
"diff_on": "Evidențiază diferențele",
"diff_off": "Afișează conținutul",
"diff_on_hint": "Clic pentru a afișa diferențele față de revizia anterioară, la nivel de cod sursă",
"diff_off_hint": "Clic pentru a afișa întregul conținut al reviziei",
"diff_not_available": "Diferențele nu pot fi evidențiate."
}, },
"revisions_button": { "revisions_button": {
"note_revisions": "Revizii ale notiței" "note_revisions": "Revizii ale notiței"
@ -1373,8 +1379,8 @@
}, },
"shared_info": { "shared_info": {
"help_link": "Pentru informații vizitați <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki-ul</a>.", "help_link": "Pentru informații vizitați <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki-ul</a>.",
"shared_locally": "Această notiță este partajată local la {{- link}}", "shared_locally": "Această notiță este partajată local la {{- link}}.",
"shared_publicly": "Această notiță este partajată public la {{- link}}" "shared_publicly": "Această notiță este partajată public la {{- link}}."
}, },
"note_types": { "note_types": {
"book": "Colecție", "book": "Colecție",
@ -1472,7 +1478,8 @@
"create-child-note": "Crează subnotiță", "create-child-note": "Crează subnotiță",
"hoist-this-note-workspace": "Focalizează spațiul de lucru", "hoist-this-note-workspace": "Focalizează spațiul de lucru",
"refresh-saved-search-results": "Reîmprospătează căutarea salvată", "refresh-saved-search-results": "Reîmprospătează căutarea salvată",
"unhoist": "Defocalizează notița" "unhoist": "Defocalizează notița",
"toggle-sidebar": "Comută bara laterală"
}, },
"title_bar_buttons": { "title_bar_buttons": {
"window-on-top": "Menține fereastra mereu vizibilă" "window-on-top": "Menține fereastra mereu vizibilă"

View File

@ -379,7 +379,12 @@
"settings": "Настройка версионирования заметок", "settings": "Настройка версионирования заметок",
"no_revisions": "У этой заметки еще нет версий...", "no_revisions": "У этой заметки еще нет версий...",
"snapshot_interval": "Интервал создания версии заметки: {{seconds}} с.", "snapshot_interval": "Интервал создания версии заметки: {{seconds}} с.",
"maximum_revisions": "Максимальное количество версий заметки: {{number}}." "maximum_revisions": "Максимальное количество версий заметки: {{number}}.",
"diff_on": "Сравнить",
"diff_off": "Показать содержимое",
"diff_on_hint": "Отобразить сравнение исходного кода заметки",
"diff_off_hint": "Отобразить контент заметки",
"diff_not_available": "Сравнение недоступно."
}, },
"sort_child_notes": { "sort_child_notes": {
"sort_children_by": "Сортировать дочерние заметки по...", "sort_children_by": "Сортировать дочерние заметки по...",
@ -2025,6 +2030,8 @@
"title": "Производительность", "title": "Производительность",
"enable-motion": "Включить визуальные эффекты и анимации", "enable-motion": "Включить визуальные эффекты и анимации",
"enable-shadows": "Включить тени", "enable-shadows": "Включить тени",
"enable-backdrop-effects": "Включить эффекты размытия фона меню, всплывающих окон и панелей" "enable-backdrop-effects": "Включить эффекты размытия фона меню, всплывающих окон и панелей",
"enable-smooth-scroll": "Включить плавную прокрутку",
"app-restart-required": "(для вступления изменений в силу требуется перезапуск приложения)"
} }
} }

View File

@ -276,7 +276,12 @@
"preview": "預覽:", "preview": "預覽:",
"preview_not_available": "無法預覽此類型的筆記。", "preview_not_available": "無法預覽此類型的筆記。",
"restore_button": "還原", "restore_button": "還原",
"delete_button": "刪除" "delete_button": "刪除",
"diff_on": "顯示差異",
"diff_off": "顯示內容",
"diff_on_hint": "點擊以顯示筆記原始碼差異",
"diff_off_hint": "點擊以顯示筆記內容",
"diff_not_available": "差異不可用。"
}, },
"sort_child_notes": { "sort_child_notes": {
"sort_children_by": "依…排序子筆記", "sort_children_by": "依…排序子筆記",
@ -2021,6 +2026,8 @@
"title": "效能", "title": "效能",
"enable-motion": "啟用轉場與動畫", "enable-motion": "啟用轉場與動畫",
"enable-shadows": "啟用陰影", "enable-shadows": "啟用陰影",
"enable-backdrop-effects": "啟用選單、彈出視窗和面板的背景特效" "enable-backdrop-effects": "啟用選單、彈出視窗和面板的背景特效",
"enable-smooth-scroll": "啟用平滑滾動",
"app-restart-required": "(需要重啟程式以套用更改)"
} }
} }

View File

@ -326,7 +326,12 @@
"mime": "МІМЕ: ", "mime": "МІМЕ: ",
"file_size": "Розмір файлу:", "file_size": "Розмір файлу:",
"preview": "Попередній перегляд:", "preview": "Попередній перегляд:",
"preview_not_available": "Попередній перегляд недоступний для цього типу нотатки." "preview_not_available": "Попередній перегляд недоступний для цього типу нотатки.",
"diff_on": "Показати різницю",
"diff_off": "Показати вміст",
"diff_on_hint": "Натисніть, щоб показати різницю в джерелі нотатки",
"diff_off_hint": "Натисніть, щоб показати вміст нотатки",
"diff_not_available": "Різниця недоступна."
}, },
"include_note": { "include_note": {
"dialog_title": "Включити нотатку", "dialog_title": "Включити нотатку",
@ -2025,6 +2030,8 @@
"title": "Продуктивність", "title": "Продуктивність",
"enable-motion": "Увімкнути переходи та анімацію", "enable-motion": "Увімкнути переходи та анімацію",
"enable-shadows": "Увімкнути тіні", "enable-shadows": "Увімкнути тіні",
"enable-backdrop-effects": "Увімкнути фонові ефекти для меню, спливаючих вікон та панелей" "enable-backdrop-effects": "Увімкнути фонові ефекти для меню, спливаючих вікон та панелей",
"enable-smooth-scroll": "Увімкнути плавне прокручування",
"app-restart-required": "(щоб зміни набули чинності, потрібен перезапуск програми)"
} }
} }

View File

@ -8,15 +8,16 @@ import FormGroup, { FormMultiGroup } from "../react/FormGroup";
import Modal from "../react/Modal"; import Modal from "../react/Modal";
import RawHtml from "../react/RawHtml"; import RawHtml from "../react/RawHtml";
import importService, { UploadFilesOptions } from "../../services/import"; import importService, { UploadFilesOptions } from "../../services/import";
import { useTriliumEvent } from "../react/hooks"; import { useTriliumEvent, useTriliumOptionBool } from "../react/hooks";
export default function ImportDialog() { export default function ImportDialog() {
const [ compressImages ] = useTriliumOptionBool("compressImages");
const [ parentNoteId, setParentNoteId ] = useState<string>(); const [ parentNoteId, setParentNoteId ] = useState<string>();
const [ noteTitle, setNoteTitle ] = useState<string>(); const [ noteTitle, setNoteTitle ] = useState<string>();
const [ files, setFiles ] = useState<FileList | null>(null); const [ files, setFiles ] = useState<FileList | null>(null);
const [ safeImport, setSafeImport ] = useState(true); const [ safeImport, setSafeImport ] = useState(true);
const [ explodeArchives, setExplodeArchives ] = useState(true); const [ explodeArchives, setExplodeArchives ] = useState(true);
const [ shrinkImages, setShrinkImages ] = useState(true); const [ shrinkImages, setShrinkImages ] = useState(compressImages);
const [ textImportedAsText, setTextImportedAsText ] = useState(true); const [ textImportedAsText, setTextImportedAsText ] = useState(true);
const [ codeImportedAsCode, setCodeImportedAsCode ] = useState(true); const [ codeImportedAsCode, setCodeImportedAsCode ] = useState(true);
const [ replaceUnderscoresWithSpaces, setReplaceUnderscoresWithSpaces ] = useState(true); const [ replaceUnderscoresWithSpaces, setReplaceUnderscoresWithSpaces ] = useState(true);
@ -69,7 +70,8 @@ export default function ImportDialog() {
/> />
<FormCheckbox <FormCheckbox
name="shrink-images" hint={t("import.shrinkImagesTooltip")} label={t("import.shrinkImages")} name="shrink-images" hint={t("import.shrinkImagesTooltip")} label={t("import.shrinkImages")}
currentValue={shrinkImages} onChange={setShrinkImages} currentValue={compressImages && shrinkImages} onChange={setShrinkImages}
disabled={!compressImages}
/> />
<FormCheckbox <FormCheckbox
name="text-imported-as-text" label={t("import.textImportedAsText")} name="text-imported-as-text" label={t("import.textImportedAsText")}

View File

@ -8,7 +8,7 @@ import Button from "../react/Button";
import { Suggestion, triggerRecentNotes } from "../../services/note_autocomplete"; import { Suggestion, triggerRecentNotes } from "../../services/note_autocomplete";
import tree from "../../services/tree"; import tree from "../../services/tree";
import froca from "../../services/froca"; import froca from "../../services/froca";
import EditableTextTypeWidget from "../type_widgets/editable_text"; import EditableTextTypeWidget, { type BoxSize } from "../type_widgets/editable_text";
import { useTriliumEvent } from "../react/hooks"; import { useTriliumEvent } from "../react/hooks";
export default function IncludeNoteDialog() { export default function IncludeNoteDialog() {
@ -37,7 +37,7 @@ export default function IncludeNoteDialog() {
} }
setShown(false); setShown(false);
includeNote(suggestion.notePath, textTypeWidget); includeNote(suggestion.notePath, textTypeWidget, boxSize as BoxSize);
}} }}
footer={<Button text={t("include_note.button_include")} keyboardShortcut="Enter" />} footer={<Button text={t("include_note.button_include")} keyboardShortcut="Enter" />}
show={shown} show={shown}
@ -69,13 +69,12 @@ export default function IncludeNoteDialog() {
) )
} }
async function includeNote(notePath: string, textTypeWidget: EditableTextTypeWidget) { async function includeNote(notePath: string, textTypeWidget: EditableTextTypeWidget, boxSize: BoxSize) {
const noteId = tree.getNoteIdFromUrl(notePath); const noteId = tree.getNoteIdFromUrl(notePath);
if (!noteId) { if (!noteId) {
return; return;
} }
const note = await froca.getNote(noteId); const note = await froca.getNote(noteId);
const boxSize = $("input[name='include-note-box-size']:checked").val() as string;
if (["image", "canvas", "mermaid"].includes(note?.type ?? "")) { if (["image", "canvas", "mermaid"].includes(note?.type ?? "")) {
// there's no benefit to use insert note functionlity for images, // there's no benefit to use insert note functionlity for images,

View File

@ -219,21 +219,22 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
this.$tree = this.$widget.find(".tree"); this.$tree = this.$widget.find(".tree");
this.$treeActions = this.$widget.find(".tree-actions"); this.$treeActions = this.$widget.find(".tree-actions");
this.$tree.on("mousedown", ".unhoist-button", () => hoistedNoteService.unhoist()); this.$tree.on("mousedown", (e: JQuery.MouseDownEvent) => {
this.$tree.on("mousedown", ".refresh-search-button", (e) => this.refreshSearch(e)); const target = e.target as HTMLElement;
this.$tree.on("mousedown", ".add-note-button", (e) => { if (e.button !== 0) return;
const node = $.ui.fancytree.getNode(e as unknown as Event);
const parentNotePath = treeService.getNotePath(node);
noteCreateService.createNote(parentNotePath, { if (target.classList.contains("unhoist-button")) {
isProtected: node.data.isProtected hoistedNoteService.unhoist();
}); } else if (target.classList.contains("refresh-search-button")) {
}); this.refreshSearch(e);
} else if (target.classList.contains("add-note-button")) {
this.$tree.on("mousedown", ".enter-workspace-button", (e) => { const node = $.ui.fancytree.getNode(e as unknown as Event);
const node = $.ui.fancytree.getNode(e as unknown as Event); const parentNotePath = treeService.getNotePath(node);
noteCreateService.createNote(parentNotePath, { isProtected: node.data.isProtected });
this.triggerCommand("hoistNote", { noteId: node.data.noteId }); } else if (target.classList.contains("enter-workspace-button")) {
const node = $.ui.fancytree.getNode(e as unknown as Event);
this.triggerCommand("hoistNote", { noteId: node.data.noteId });
}
}); });
// fancytree doesn't support middle click, so this is a way to support it // fancytree doesn't support middle click, so this is a way to support it

View File

@ -22,7 +22,6 @@ const FormCheckbox = memo(({ name, disabled, label, currentValue, onChange, hint
const labelRef = useRef<HTMLLabelElement>(null); const labelRef = useRef<HTMLLabelElement>(null);
const id = useUniqueName(name); const id = useUniqueName(name);
// Fix: Move useEffect outside conditional
useEffect(() => { useEffect(() => {
if (!hint || !labelRef.current) return; if (!hint || !labelRef.current) return;
@ -34,22 +33,19 @@ const FormCheckbox = memo(({ name, disabled, label, currentValue, onChange, hint
return () => tooltipInstance?.dispose(); return () => tooltipInstance?.dispose();
}, [hint]); // Proper dependency }, [hint]); // Proper dependency
// Memoize style object
const labelStyle = useMemo(() => const labelStyle = useMemo(() =>
hint ? { textDecoration: "underline dotted var(--main-text-color)" } : undefined, hint ? { textDecoration: "underline dotted var(--main-text-color)" } : undefined,
[hint] [hint]
); );
// Memoize onChange handler
const handleChange = useCallback((e: Event) => { const handleChange = useCallback((e: Event) => {
onChange((e.target as HTMLInputElement).checked); onChange((e.target as HTMLInputElement).checked);
}, [onChange]); }, [onChange]);
// Memoize title attribute
const titleText = useMemo(() => hint ? escapeQuotes(hint) : undefined, [hint]); const titleText = useMemo(() => hint ? escapeQuotes(hint) : undefined, [hint]);
return ( return (
<div className="form-checkbox" style={containerStyle}> <div className={`form-checkbox ${disabled ? "disabled" : ""}`} style={containerStyle}>
<label <label
className="form-check-label tn-checkbox" className="form-check-label tn-checkbox"
style={labelStyle} style={labelStyle}

View File

@ -14,6 +14,8 @@ import type FNote from "../../entities/fnote.js";
import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig, EditorConfig } from "@triliumnext/ckeditor5"; import { PopupEditor, ClassicEditor, EditorWatchdog, type CKTextEditor, type MentionFeed, type WatchdogConfig, EditorConfig } from "@triliumnext/ckeditor5";
import { updateTemplateCache } from "./ckeditor/snippets.js"; import { updateTemplateCache } from "./ckeditor/snippets.js";
export type BoxSize = "small" | "medium" | "full";
const TPL = /*html*/` const TPL = /*html*/`
<div class="note-detail-editable-text note-detail-printable"> <div class="note-detail-editable-text note-detail-printable">
<style> <style>
@ -434,7 +436,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
this.triggerCommand("showIncludeNoteDialog", { textTypeWidget: this }); this.triggerCommand("showIncludeNoteDialog", { textTypeWidget: this });
} }
addIncludeNote(noteId: string, boxSize?: string) { addIncludeNote(noteId: string, boxSize?: BoxSize) {
this.watchdog.editor?.model.change((writer) => { this.watchdog.editor?.model.change((writer) => {
// Insert <includeNote>*</includeNote> at the current selection position // Insert <includeNote>*</includeNote> at the current selection position
// in a way that will result in creating a valid model structure // in a way that will result in creating a valid model structure

View File

@ -34,15 +34,15 @@
"@triliumnext/commons": "workspace:*", "@triliumnext/commons": "workspace:*",
"@triliumnext/server": "workspace:*", "@triliumnext/server": "workspace:*",
"copy-webpack-plugin": "13.0.1", "copy-webpack-plugin": "13.0.1",
"electron": "37.4.0", "electron": "37.5.0",
"@electron-forge/cli": "7.8.3", "@electron-forge/cli": "7.9.0",
"@electron-forge/maker-deb": "7.8.3", "@electron-forge/maker-deb": "7.9.0",
"@electron-forge/maker-dmg": "7.8.3", "@electron-forge/maker-dmg": "7.9.0",
"@electron-forge/maker-flatpak": "7.8.3", "@electron-forge/maker-flatpak": "7.9.0",
"@electron-forge/maker-rpm": "7.8.3", "@electron-forge/maker-rpm": "7.9.0",
"@electron-forge/maker-squirrel": "7.8.3", "@electron-forge/maker-squirrel": "7.9.0",
"@electron-forge/maker-zip": "7.8.3", "@electron-forge/maker-zip": "7.9.0",
"@electron-forge/plugin-auto-unpack-natives": "7.8.3", "@electron-forge/plugin-auto-unpack-natives": "7.9.0",
"prebuild-install": "^7.1.1" "prebuild-install": "^7.1.1"
} }
} }

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.1", "copy-webpack-plugin": "13.0.1",
"electron": "37.4.0", "electron": "37.5.0",
"fs-extra": "11.3.1" "fs-extra": "11.3.1"
}, },
"scripts": { "scripts": {

View File

@ -28,7 +28,7 @@
"better-sqlite3": "12.2.0" "better-sqlite3": "12.2.0"
}, },
"devDependencies": { "devDependencies": {
"@anthropic-ai/sdk": "0.61.0", "@anthropic-ai/sdk": "0.62.0",
"@braintree/sanitize-url": "7.1.1", "@braintree/sanitize-url": "7.1.1",
"@electron/remote": "2.1.3", "@electron/remote": "2.1.3",
"@preact/preset-vite": "2.10.2", "@preact/preset-vite": "2.10.2",
@ -81,12 +81,12 @@
"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": "37.4.0", "electron": "37.5.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",
"express": "5.1.0", "express": "5.1.0",
"express-http-proxy": "2.1.1", "express-http-proxy": "2.1.2",
"express-openid-connect": "^2.17.1", "express-openid-connect": "^2.17.1",
"express-rate-limit": "8.1.0", "express-rate-limit": "8.1.0",
"express-session": "1.18.2", "express-session": "1.18.2",

View File

@ -27,27 +27,6 @@ export default async function buildApp() {
// Initialize DB // Initialize DB
sql_init.initializeDb(); sql_init.initializeDb();
// Listen for database initialization event
eventService.subscribe(eventService.DB_INITIALIZED, async () => {
try {
log.info("Database initialized, LLM features available");
log.info("LLM features ready");
} catch (error) {
console.error("Error initializing LLM features:", error);
}
});
// Initialize LLM features only if database is already initialized
if (sql_init.isDbInitialized()) {
try {
log.info("LLM features ready");
} catch (error) {
console.error("Error initializing LLM features:", error);
}
} else {
console.log("Database not initialized yet. LLM features will be initialized after setup.");
}
const publicDir = isDev ? path.join(getResourceDir(), "../dist/public") : path.join(getResourceDir(), "public"); const publicDir = isDev ? path.join(getResourceDir(), "../dist/public") : path.join(getResourceDir(), "public");
const publicAssetsDir = path.join(publicDir, "assets"); const publicAssetsDir = path.join(publicDir, "assets");
const assetsDir = RESOURCE_DIR; const assetsDir = RESOURCE_DIR;

File diff suppressed because one or more lines are too long

View File

@ -345,7 +345,7 @@
# TRILIUM_NETWORK_CORS_ALLOW_ORIGIN: "https://myapp.com" # TRILIUM_NETWORK_CORS_ALLOW_ORIGIN: "https://myapp.com"
# TRILIUM_SYNC_SERVER_HOST: "https://sync.example.com" # TRILIUM_SYNC_SERVER_HOST: "https://sync.example.com"
# TRILIUM_OAUTH_BASE_URL: "https://auth.example.com"</code></pre> # TRILIUM_OAUTH_BASE_URL: "https://auth.example.com"</code></pre>
<h3>Shell Export Example</h3><pre><code class="language-text-x-sh"># Using either format <h3>Shell Export Example</h3><pre><code class="language-text-x-trilium-auto"># Using either format
export TRILIUM_GENERAL_NOAUTHENTICATION=false export TRILIUM_GENERAL_NOAUTHENTICATION=false
export TRILIUM_NETWORK_HTTPS=true export TRILIUM_NETWORK_HTTPS=true
export TRILIUM_NETWORK_CERTPATH=/path/to/cert.pem export TRILIUM_NETWORK_CERTPATH=/path/to/cert.pem

View File

@ -1 +0,0 @@
<p>This is a clone of a note. Go to its <a href="../UI%20Elements/Quick%20edit.html">primary location</a>.</p>

View File

@ -15,9 +15,9 @@
<p>Based on the&nbsp;<a class="reference-link" href="#root/_help_x0JgW8UqGXvq">Vertical and horizontal layout</a>, <p>Based on the&nbsp;<a class="reference-link" href="#root/_help_x0JgW8UqGXvq">Vertical and horizontal layout</a>,
the quick search is placed:</p> the quick search is placed:</p>
<ul> <ul>
<li data-list-item-id="eb498e0518c4efc433c9569270c9c7a5c">On the vertical layout, it is displayed right above the&nbsp;<a class="reference-link" <li>On the vertical layout, it is displayed right above the&nbsp;<a class="reference-link"
href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</li> href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</li>
<li data-list-item-id="e6a9159606a513e839ca71ff4735857bb">On the horizontal layout, it is displayed in the&nbsp;<a class="reference-link" <li>On the horizontal layout, it is displayed in the&nbsp;<a class="reference-link"
href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>, where it can be positioned href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>, where it can be positioned
just like any other icon.</li> just like any other icon.</li>
</ul> </ul>
@ -31,44 +31,39 @@
<h3>Infinite Scrolling</h3> <h3>Infinite Scrolling</h3>
<p>Results are loaded progressively as you scroll:</p> <p>Results are loaded progressively as you scroll:</p>
<ul> <ul>
<li data-list-item-id="e6d151aab6b52d08e9a93e6f9c29c081a">Initial display shows 15 results</li> <li>Initial display shows 15 results</li>
<li data-list-item-id="e006eeac7574a398324f214edcb9a383b">Scrolling near the bottom automatically loads 10 more results</li> <li>Scrolling near the bottom automatically loads 10 more results</li>
<li <li>Continue scrolling to load all matching notes</li>
data-list-item-id="e5f6fcb1ec0d496fcf599fa90c3911c89">Continue scrolling to load all matching notes</li>
</ul> </ul>
<h3>Visual Features</h3> <h3>Visual Features</h3>
<ul> <ul>
<li data-list-item-id="e44f3402a55ac37c63abae20490d66d70"><strong>Highlighting</strong>: Search terms appear in bold with accent <li><strong>Highlighting</strong>: Search terms appear in bold with accent
colors</li> colors</li>
<li data-list-item-id="e1c8743ac639f15750171788790df2bb0"><strong>Separation</strong>: Results are separated with dividers</li> <li><strong>Separation</strong>: Results are separated with dividers</li>
<li <li><strong>Theme Support</strong>: Highlighting colors adapt to light/dark
data-list-item-id="ec5c5dbaa44ba426d220718804b9b27db"><strong>Theme Support</strong>: Highlighting colors adapt to light/dark
themes</li> themes</li>
</ul> </ul>
<h3>Search Behavior</h3> <h3>Search Behavior</h3>
<p>Quick search uses progressive search:</p> <p>Quick search uses progressive search:</p>
<ol> <ol>
<li data-list-item-id="e9a34edaccc0174140e1183c5e43a2327">Shows exact matches first</li> <li>Shows exact matches first</li>
<li data-list-item-id="e5b751c044ae5189095fd08655a55372f">Includes fuzzy matches when exact results are fewer than 5</li> <li>Includes fuzzy matches when exact results are fewer than 5</li>
<li data-list-item-id="ee63c39a04b7511cd4e031cdd963f58d2">Exact matches appear before fuzzy matches</li> <li>Exact matches appear before fuzzy matches</li>
</ol> </ol>
<h3>Keyboard Navigation</h3> <h3>Keyboard Navigation</h3>
<ul> <ul>
<li data-list-item-id="e1161754a60afdea3656561abcb46f9ea">Press <code>Enter</code> to open the first result</li> <li>Press <code>Enter</code> to open the first result</li>
<li data-list-item-id="ebdffa32bcd3d8e24c3938b472521034d">Use arrow keys to navigate through results</li> <li>Use arrow keys to navigate through results</li>
<li data-list-item-id="eed08e1e6867dcef7eaa6ce7a21fd5e02">Press <code>Escape</code> to close the quick search</li> <li>Press <code>Escape</code> to close the quick search</li>
</ul> </ul>
<h2>Using Quick Search</h2> <h2>Using Quick Search</h2>
<ol> <ol>
<li data-list-item-id="e88738101cdad95c7ffe2fc45d19250b7"><strong>Typo tolerance</strong>: Search finds results despite minor typos</li> <li><strong>Typo tolerance</strong>: Search finds results despite minor typos</li>
<li <li><strong>Content previews</strong>: 200-character snippets show match context</li>
data-list-item-id="ead4c50c8ae5e86987073741285271140"><strong>Content previews</strong>: 200-character snippets show match context</li> <li><strong>Infinite scrolling</strong>: Additional results load on scroll</li>
<li <li><strong>Specific terms</strong>: Specific search terms return more focused
data-list-item-id="ee135ac66eafef5962b5221fa149dc31c"><strong>Infinite scrolling</strong>: Additional results load on scroll</li> results</li>
<li <li><strong>Match locations</strong>: Bold text indicates where matches occur</li>
data-list-item-id="ebecf1647bb3e6631383fa1cad5e0d222"><strong>Specific terms</strong>: Specific search terms return more focused
results</li>
<li data-list-item-id="e7d6ee3a67dbf55e7c72788cde795f2b2"><strong>Match locations</strong>: Bold text indicates where matches occur</li>
</ol> </ol>
<h2>Quick Search - Exact Match Operator</h2> <h2>Quick Search - Exact Match Operator</h2>
<p>Quick Search now supports the exact match operator (<code>=</code>) at <p>Quick Search now supports the exact match operator (<code>=</code>) at
@ -78,73 +73,69 @@
<h3>Usage</h3> <h3>Usage</h3>
<p>To use exact match in Quick Search:</p> <p>To use exact match in Quick Search:</p>
<ol> <ol>
<li data-list-item-id="e98c91a13502a0ddd321432cd2cdab193">Start your search query with the <code>=</code> operator</li> <li>Start your search query with the <code>=</code> operator</li>
<li data-list-item-id="e0db9fc3f530c8e0eb96f4df5ef74d955">Follow it immediately with your search term (no space after <code>=</code>)</li> <li>Follow it immediately with your search term (no space after <code>=</code>)</li>
</ol> </ol>
<h4>Examples</h4> <h4>Examples</h4>
<ul> <ul>
<li data-list-item-id="e188d5c0a39291e2f665072b02b4b6cc0"><code>=example</code> - Finds notes with title exactly "example" or content <li><code>=example</code> - Finds notes with title exactly "example" or content
exactly "example"</li> exactly "example"</li>
<li data-list-item-id="e275568bc5123c979fddff51fac370983"><code>=Project Plan</code> - Finds notes with title exactly "Project Plan" <li><code>=Project Plan</code> - Finds notes with title exactly "Project Plan"
or content exactly "Project Plan"</li> or content exactly "Project Plan"</li>
<li data-list-item-id="e04c10070d9800148f641efcbee16ab3d"><code>='hello world'</code> - Use quotes for multi-word exact matches</li> <li><code>='hello world'</code> - Use quotes for multi-word exact matches</li>
</ul> </ul>
<h4>Comparison with Regular Search</h4> <h4>Comparison with Regular Search</h4>
<figure class="table"> <table>
<table> <thead>
<thead> <tr>
<tr> <th>Query</th>
<th>Query</th> <th>Behavior</th>
<th>Behavior</th> </tr>
</tr> </thead>
</thead> <tbody>
<tbody> <tr>
<tr> <td><code>example</code>
<td><code>example</code> </td>
</td> <td>Finds all notes containing "example" anywhere in title or content</td>
<td>Finds all notes containing "example" anywhere in title or content</td> </tr>
</tr> <tr>
<tr> <td><code>=example</code>
<td><code>=example</code> </td>
</td> <td>Finds only notes where the title equals "example" or content equals "example"
<td>Finds only notes where the title equals "example" or content equals "example" exactly</td>
exactly</td> </tr>
</tr> </tbody>
</tbody> </table>
</table>
</figure>
<h3>Technical Details</h3> <h3>Technical Details</h3>
<p>When you use the <code>=</code> operator:</p> <p>When you use the <code>=</code> operator:</p>
<ul> <ul>
<li data-list-item-id="ebd357e2f6afa77ccb3aed347103d47c3">The search performs an exact match on note titles</li> <li>The search performs an exact match on note titles</li>
<li data-list-item-id="e64c84d77017e4cd43fe95c0e4f537044">For note content, it looks for exact matches of the entire content</li> <li>For note content, it looks for exact matches of the entire content</li>
<li <li>Partial word matches are excluded</li>
data-list-item-id="ef4f790816f24b9484fea127837025935">Partial word matches are excluded</li> <li>The search is case-insensitive</li>
<li data-list-item-id="e94a53c59dc4f1a8bef101df66538d06a">The search is case-insensitive</li>
</ul> </ul>
<h3>Limitations</h3> <h3>Limitations</h3>
<ul> <ul>
<li data-list-item-id="e4ed2c12de6681eb26d2ec2daa1985956">The <code>=</code> operator must be at the very beginning of the search <li>The <code>=</code> operator must be at the very beginning of the search
query</li> query</li>
<li data-list-item-id="e30845adb77a12106475b88b68b614009">Spaces after <code>=</code> will treat it as a regular search</li> <li>Spaces after <code>=</code> will treat it as a regular search</li>
<li data-list-item-id="e89322d60b3f5cb6b2b318cc7247721cf">Multiple <code>=</code> operators (like <code>==example</code>) are treated <li>Multiple <code>=</code> operators (like <code>==example</code>) are treated
as regular text search</li> as regular text search</li>
</ul> </ul>
<h3>Use Cases</h3> <h3>Use Cases</h3>
<p>This feature is particularly useful when:</p> <p>This feature is particularly useful when:</p>
<ul> <ul>
<li data-list-item-id="eb23079c90785534a68963977e993d253">You know the exact title of a note</li> <li>You know the exact title of a note</li>
<li data-list-item-id="e92f02cb8b28fc02f264ebeb09376af91">You want to find notes with specific, complete content</li> <li>You want to find notes with specific, complete content</li>
<li data-list-item-id="e37aa1707a8440213fe404d1ed7a2e941">You need to distinguish between notes with similar but not identical titles</li> <li>You need to distinguish between notes with similar but not identical titles</li>
<li <li>You want to avoid false positives from partial matches</li>
data-list-item-id="e8b04a0a97aa970e6984370ff17160208">You want to avoid false positives from partial matches</li>
</ul> </ul>
<h3>Related Features</h3> <h3>Related Features</h3>
<ul> <ul>
<li data-list-item-id="e3d0656590d49c6e09ae5f39a0a773dff">For more complex exact matching queries, use the full <a href="Search.md">Search</a> functionality</li> <li>For more complex exact matching queries, use the full <a href="#root/_help_eIg8jdvaoNNd">Search</a> functionality</li>
<li <li>For fuzzy matching (finding results despite typos), use the <code>~=</code> operator
data-list-item-id="e7d77021ebedb1b1d25e8bfe2726af21e">For fuzzy matching (finding results despite typos), use the <code>~=</code> operator
in the full search</li> in the full search</li>
<li data-list-item-id="eabcf1ff7a9dfa822192ee9afe3268469">For partial matches with wildcards, use operators like <code>*=*</code>, <code>=*</code>, <li>For partial matches with wildcards, use operators like <code>*=*</code>, <code>=*</code>,
or <code>*=</code> in the full search</li> or <code>*=</code> in the full search</li>
</ul> </ul>

View File

@ -27,7 +27,7 @@ certPath=/[username]/.acme.sh/[hostname]/fullchain.cer
keyPath=/[username]/.acme.sh/[hostname]/example.com.key</code></pre> keyPath=/[username]/.acme.sh/[hostname]/example.com.key</code></pre>
<p>You can also review the <a href="#root/_help_Gzjqa934BdH4">configuration</a> file <p>You can also review the <a href="#root/_help_Gzjqa934BdH4">configuration</a> file
to provide all <code>config.ini</code> values as environment variables instead. to provide all <code>config.ini</code> values as environment variables instead.
For example, you can configure TLS using environment variables:</p><pre><code class="language-text-x-sh">export TRILIUM_NETWORK_HTTPS=true For example, you can configure TLS using environment variables:</p><pre><code class="language-text-x-trilium-auto">export TRILIUM_NETWORK_HTTPS=true
export TRILIUM_NETWORK_CERTPATH=/path/to/cert.pem export TRILIUM_NETWORK_CERTPATH=/path/to/cert.pem
export TRILIUM_NETWORK_KEYPATH=/path/to/key.pem</code></pre> export TRILIUM_NETWORK_KEYPATH=/path/to/key.pem</code></pre>
<p>The above example shows how this is set up in an environment where the <p>The above example shows how this is set up in an environment where the

View File

@ -0,0 +1,5 @@
{
"keyboard_actions": {
"back-in-note-history": "Navigovat na předchozí poznámku v historii"
}
}

View File

@ -424,5 +424,9 @@
"board_status_todo": "To Do", "board_status_todo": "To Do",
"board_status_progress": "In Progress", "board_status_progress": "In Progress",
"board_status_done": "Done" "board_status_done": "Done"
},
"sql_init": {
"db_not_initialized_desktop": "DB not initialized, please follow on-screen instructions.",
"db_not_initialized_server": "DB not initialized, please visit setup page - http://[your-server-host]:{{port}} to see instructions on how to initialize Trilium."
} }
} }

View File

@ -457,8 +457,8 @@ function checkHiddenSubtreeRecursively(parentNoteId: string, item: HiddenSubtree
}).save(); }).save();
} else if (attr.name === "docName" || (existingAttribute.noteId.startsWith("_help") && attr.name === "iconClass")) { } else if (attr.name === "docName" || (existingAttribute.noteId.startsWith("_help") && attr.name === "iconClass")) {
if (existingAttribute.value !== attr.value) { if (existingAttribute.value !== attr.value) {
console.log(`Updating attribute ${attrId} from "${existingAttribute.value}" to "${attr.value}"`);
existingAttribute.value = attr.value ?? ""; existingAttribute.value = attr.value ?? "";
console.log("Updating attribute ", attrId);
existingAttribute.save(); existingAttribute.save();
} }
} }

View File

@ -17,6 +17,7 @@ import zipImportService from "./import/zip.js";
import password from "./encryption/password.js"; import password from "./encryption/password.js";
import backup from "./backup.js"; import backup from "./backup.js";
import eventService from "./events.js"; import eventService from "./events.js";
import { t } from "i18next";
export const dbReady = deferred<void>(); export const dbReady = deferred<void>();
@ -37,7 +38,11 @@ function isDbInitialized() {
async function initDbConnection() { async function initDbConnection() {
if (!isDbInitialized()) { if (!isDbInitialized()) {
log.info(`DB not initialized, please visit setup page` + (isElectron ? "" : ` - http://[your-server-host]:${port} to see instructions on how to initialize Trilium.`)); if (isElectron) {
log.info(t("sql_init.db_not_initialized_desktop"));
} else {
log.info(t("sql_init.db_not_initialized_server", { port }));
}
return; return;
} }
@ -197,15 +202,13 @@ function optimize() {
log.info(`Optimization finished in ${Date.now() - start}ms.`); log.info(`Optimization finished in ${Date.now() - start}ms.`);
} }
function getDbSize() { export function getDbSize() {
return sql.getValue<number>("SELECT page_count * page_size / 1000 as size FROM pragma_page_count(), pragma_page_size()"); return sql.getValue<number>("SELECT page_count * page_size / 1000 as size FROM pragma_page_count(), pragma_page_size()");
} }
function initializeDb() { function initializeDb() {
cls.init(initDbConnection); cls.init(initDbConnection);
log.info(`DB size: ${getDbSize()} KB`);
dbReady.then(() => { dbReady.then(() => {
if (config.General && config.General.noBackup === true) { if (config.General && config.General.noBackup === true) {
log.info("Disabling scheduled backups."); log.info("Disabling scheduled backups.");

View File

@ -478,6 +478,24 @@ export function normalizeCustomHandlerPattern(pattern: string | null | undefined
} }
} }
export function formatUtcTime(time: string) {
return time.replace("T", " ").substring(0, 19)
}
// TODO: Deduplicate with client utils
export function formatSize(size: number | null | undefined) {
if (size === null || size === undefined) {
return "";
}
size = Math.max(Math.round(size / 1024), 1);
if (size < 1024) {
return `${size} KiB`;
} else {
return `${Math.round(size / 102.4) / 10} MiB`;
}
}
export default { export default {
compareVersions, compareVersions,

View File

@ -6,15 +6,26 @@ import config from "./services/config.js";
import log from "./services/log.js"; import log from "./services/log.js";
import appInfo from "./services/app_info.js"; import appInfo from "./services/app_info.js";
import ws from "./services/ws.js"; import ws from "./services/ws.js";
import utils from "./services/utils.js"; import utils, { formatSize, formatUtcTime } from "./services/utils.js";
import port from "./services/port.js"; import port from "./services/port.js";
import host from "./services/host.js"; import host from "./services/host.js";
import buildApp from "./app.js"; import buildApp from "./app.js";
import type { Express } from "express"; import type { Express } from "express";
import { getDbSize } from "./services/sql_init.js";
const MINIMUM_NODE_VERSION = "20.0.0"; const MINIMUM_NODE_VERSION = "20.0.0";
const LOGO = `\
_____ _ _ _
|_ _| __(_) (_)_ _ _ __ ___ | \\ | | ___ | |_ ___ ___
| || '__| | | | | | | '_ \` _ \\ | \\| |/ _ \\| __/ _ \\/ __|
| || | | | | | |_| | | | | | | | |\\ | (_) | || __/\\__ \\
|_||_| |_|_|_|\\__,_|_| |_| |_| |_| \\_|\\___/ \\__\\___||___/ [version]
`;
export default async function startTriliumServer() { export default async function startTriliumServer() {
await displayStartupMessage();
// setup basic error handling even before requiring dependencies, since those can produce errors as well // 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
@ -62,16 +73,6 @@ export default async function startTriliumServer() {
(await import("electron")).app.requestSingleInstanceLock(); (await import("electron")).app.requestSingleInstanceLock();
} }
log.info(JSON.stringify(appInfo, null, 2));
// for perf. issues it's good to know the rough configuration
const cpuInfos = (await import("os")).cpus();
if (cpuInfos && cpuInfos[0] !== undefined) {
// https://github.com/zadam/trilium/pull/3957
const cpuModel = (cpuInfos[0].model || "").trimEnd();
log.info(`CPU model: ${cpuModel}, logical cores: ${cpuInfos.length}, freq: ${cpuInfos[0].speed} Mhz`);
}
const httpServer = startHttpServer(app); const httpServer = startHttpServer(app);
const sessionParser = (await import("./routes/session_parser.js")).default; const sessionParser = (await import("./routes/session_parser.js")).default;
@ -83,6 +84,24 @@ export default async function startTriliumServer() {
} }
} }
async function displayStartupMessage() {
log.info("\n" + LOGO.replace("[version]", appInfo.appVersion));
log.info(`📦 Versions: app=${appInfo.appVersion} db=${appInfo.dbVersion} sync=${appInfo.syncVersion} clipper=${appInfo.clipperProtocolVersion}`)
log.info(`🔧 Build: ${formatUtcTime(appInfo.buildDate)} (${appInfo.buildRevision.substring(0, 10)})`);
log.info(`📂 Data dir: ${appInfo.dataDirectory}`);
log.info(`⏰ UTC time: ${formatUtcTime(appInfo.utcDateTime)}`);
// for perf. issues it's good to know the rough configuration
const cpuInfos = (await import("os")).cpus();
if (cpuInfos && cpuInfos[0] !== undefined) {
// https://github.com/zadam/trilium/pull/3957
const cpuModel = (cpuInfos[0].model || "").trimEnd();
log.info(`💻 CPU: ${cpuModel} (${cpuInfos.length}-core @ ${cpuInfos[0].speed} Mhz)`);
}
log.info(`💾 DB size: ${formatSize(getDbSize() * 1024)}`);
log.info("");
}
function startHttpServer(app: Express) { function startHttpServer(app: Express) {
app.set("port", port); app.set("port", port);
app.set("host", host); app.set("host", host);

View File

@ -1,6 +1,6 @@
{ {
"formatVersion": 2, "formatVersion": 2,
"appVersion": "0.98.0", "appVersion": "0.98.1",
"files": [ "files": [
{ {
"isClone": false, "isClone": false,

View File

@ -1,6 +1,6 @@
{ {
"formatVersion": 2, "formatVersion": 2,
"appVersion": "0.98.0", "appVersion": "0.98.1",
"files": [ "files": [
{ {
"isClone": false, "isClone": false,

View File

@ -1,6 +1,6 @@
{ {
"formatVersion": 2, "formatVersion": 2,
"appVersion": "0.98.0", "appVersion": "0.98.1",
"files": [ "files": [
{ {
"isClone": false, "isClone": false,

View File

@ -146,7 +146,7 @@ services:
### Shell Export Example ### Shell Export Example
```sh ```
# Using either format # Using either format
export TRILIUM_GENERAL_NOAUTHENTICATION=false export TRILIUM_GENERAL_NOAUTHENTICATION=false
export TRILIUM_NETWORK_HTTPS=true export TRILIUM_NETWORK_HTTPS=true

View File

@ -27,7 +27,7 @@ keyPath=/[username]/.acme.sh/[hostname]/example.com.key
You can also review the [configuration](../../Advanced%20Usage/Configuration%20\(config.ini%20or%20e.md) file to provide all `config.ini` values as environment variables instead. For example, you can configure TLS using environment variables: You can also review the [configuration](../../Advanced%20Usage/Configuration%20\(config.ini%20or%20e.md) file to provide all `config.ini` values as environment variables instead. For example, you can configure TLS using environment variables:
```sh ```
export TRILIUM_NETWORK_HTTPS=true export TRILIUM_NETWORK_HTTPS=true
export TRILIUM_NETWORK_CERTPATH=/path/to/cert.pem export TRILIUM_NETWORK_CERTPATH=/path/to/cert.pem
export TRILIUM_NETWORK_KEYPATH=/path/to/key.pem export TRILIUM_NETWORK_KEYPATH=/path/to/key.pem

View File

@ -42,7 +42,7 @@
"@types/node": "22.18.1", "@types/node": "22.18.1",
"@vitest/coverage-v8": "^3.0.5", "@vitest/coverage-v8": "^3.0.5",
"@vitest/ui": "^3.0.0", "@vitest/ui": "^3.0.0",
"chalk": "5.6.0", "chalk": "5.6.2",
"cross-env": "10.0.0", "cross-env": "10.0.0",
"dpdm": "3.14.0", "dpdm": "3.14.0",
"esbuild": "^0.25.0", "esbuild": "^0.25.0",

View File

@ -24,11 +24,11 @@
"@ckeditor/ckeditor5-dev-build-tools": "43.1.0", "@ckeditor/ckeditor5-dev-build-tools": "43.1.0",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^4.0.0", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.42.0", "@typescript-eslint/eslint-plugin": "~8.43.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": "46.0.3", "ckeditor5": "46.1.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",
@ -42,7 +42,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "46.0.3" "ckeditor5": "46.1.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

@ -25,11 +25,11 @@
"@ckeditor/ckeditor5-dev-build-tools": "43.1.0", "@ckeditor/ckeditor5-dev-build-tools": "43.1.0",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^4.0.0", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.42.0", "@typescript-eslint/eslint-plugin": "~8.43.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": "46.0.3", "ckeditor5": "46.1.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",
@ -43,7 +43,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "46.0.3" "ckeditor5": "46.1.0"
}, },
"scripts": { "scripts": {
"build": "node ./scripts/build-dist.mjs", "build": "node ./scripts/build-dist.mjs",

View File

@ -27,11 +27,11 @@
"@ckeditor/ckeditor5-dev-build-tools": "43.1.0", "@ckeditor/ckeditor5-dev-build-tools": "43.1.0",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^4.0.0", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.42.0", "@typescript-eslint/eslint-plugin": "~8.43.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": "46.0.3", "ckeditor5": "46.1.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",
@ -45,7 +45,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "46.0.3" "ckeditor5": "46.1.0"
}, },
"scripts": { "scripts": {
"build": "node ./scripts/build-dist.mjs", "build": "node ./scripts/build-dist.mjs",

View File

@ -28,11 +28,11 @@
"@ckeditor/ckeditor5-dev-utils": "43.1.0", "@ckeditor/ckeditor5-dev-utils": "43.1.0",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^4.0.0", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.42.0", "@typescript-eslint/eslint-plugin": "~8.43.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": "46.0.3", "ckeditor5": "46.1.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",
@ -46,7 +46,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "46.0.3" "ckeditor5": "46.1.0"
}, },
"scripts": { "scripts": {
"build": "node ./scripts/build-dist.mjs", "build": "node ./scripts/build-dist.mjs",
@ -71,6 +71,6 @@
] ]
}, },
"dependencies": { "dependencies": {
"@ckeditor/ckeditor5-icons": "46.0.3" "@ckeditor/ckeditor5-icons": "46.1.0"
} }
} }

View File

@ -27,11 +27,11 @@
"@ckeditor/ckeditor5-dev-build-tools": "43.1.0", "@ckeditor/ckeditor5-dev-build-tools": "43.1.0",
"@ckeditor/ckeditor5-inspector": ">=4.1.0", "@ckeditor/ckeditor5-inspector": ">=4.1.0",
"@ckeditor/ckeditor5-package-tools": "^4.0.0", "@ckeditor/ckeditor5-package-tools": "^4.0.0",
"@typescript-eslint/eslint-plugin": "~8.42.0", "@typescript-eslint/eslint-plugin": "~8.43.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": "46.0.3", "ckeditor5": "46.1.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",
@ -45,7 +45,7 @@
"webdriverio": "^9.0.7" "webdriverio": "^9.0.7"
}, },
"peerDependencies": { "peerDependencies": {
"ckeditor5": "46.0.3" "ckeditor5": "46.1.0"
}, },
"scripts": { "scripts": {
"build": "node ./scripts/build-dist.mjs", "build": "node ./scripts/build-dist.mjs",

View File

@ -11,11 +11,11 @@
"@triliumnext/ckeditor5-keyboard-marker": "workspace:*", "@triliumnext/ckeditor5-keyboard-marker": "workspace:*",
"@triliumnext/ckeditor5-math": "workspace:*", "@triliumnext/ckeditor5-math": "workspace:*",
"@triliumnext/ckeditor5-mermaid": "workspace:*", "@triliumnext/ckeditor5-mermaid": "workspace:*",
"ckeditor5": "46.0.3", "ckeditor5": "46.1.0",
"ckeditor5-premium-features": "46.0.3" "ckeditor5-premium-features": "46.1.0"
}, },
"devDependencies": { "devDependencies": {
"@smithy/middleware-retry": "4.2.0", "@smithy/middleware-retry": "4.2.1",
"@types/jquery": "3.5.33" "@types/jquery": "3.5.33"
} }
} }

3723
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff