Compare commits

...

43 Commits

Author SHA1 Message Date
Elian Doran
2ef4eb7eae chore(client): fix type errors 2026-02-14 12:43:40 +02:00
Elian Doran
7b230706cb feat(badges/content): improve color support 2026-02-14 12:41:41 +02:00
Elian Doran
5a2b04adba feat(badges/content): add support for web view 2026-02-14 12:35:06 +02:00
Elian Doran
50dcd3ba44 feat(badges/content): add support for render note 2026-02-14 12:30:43 +02:00
Elian Doran
740b02952f feat(badges/content): disable toggle when not necessary 2026-02-14 12:22:27 +02:00
Elian Doran
866d3110da feat(badges/content): add badge for custom CSS 2026-02-14 12:05:24 +02:00
Elian Doran
7a3e7fccec feat(badges/content): handle widgets as separate content type 2026-02-14 11:56:40 +02:00
Elian Doran
3107bc8840 feat(badges/content): add toggle for widget 2026-02-14 11:47:42 +02:00
Elian Doran
2d34cdef5e feat(badges/content): integrate options for frontend script 2026-02-14 11:37:20 +02:00
Elian Doran
bd1f0909a2 feat(badges/content): configurable backend run options 2026-02-14 11:30:05 +02:00
Elian Doran
ef75de63fe feat(badges/content): option to execute now 2026-02-14 11:15:08 +02:00
Elian Doran
a739d28563 feat(badges/content): option to open API docs 2026-02-14 11:12:55 +02:00
Elian Doran
66ff009b72 feat(badges/content): option to open documentation 2026-02-14 11:08:30 +02:00
Elian Doran
a68e82c1c8 feat(badges/content): basic support for backend scripts 2026-02-14 10:57:32 +02:00
Elian Doran
46556c1c14 chore(badges/content): make toggle more compact 2026-02-14 10:40:19 +02:00
Elian Doran
7be637798f feat(badges/content): functional enable/disable toggle 2026-02-14 10:30:34 +02:00
Elian Doran
9b3396349e refactor(commons): add builtin_attributes to commons 2026-02-14 10:30:24 +02:00
Elian Doran
ccff210b4c feat(badges/content): indicate enabled/disabled state 2026-02-14 09:57:40 +02:00
Elian Doran
a2264847b6 refactor(badges/content): use shared mechanism for extracting info 2026-02-14 09:53:37 +02:00
Elian Doran
7d103f8c50 refactor(badges/content): extract to separate file 2026-02-14 09:37:48 +02:00
Elian Doran
f3dccc0aec feat(badges/content): detect icon pack 2026-02-14 09:19:46 +02:00
Elian Doran
5da9963f31 fix(website): web clipper URL incorrect 2026-02-13 19:43:07 +02:00
Elian Doran
34e885812f fix(global_menu): advanced menu not accessible in other languages (closes #8694) 2026-02-13 19:40:51 +02:00
Elian Doran
a9ac11452d fix(relation_map): crash when valid JSON, but missing data (closes #8702) 2026-02-13 19:32:29 +02:00
Elian Doran
04b91308b1 chore(search): remove redundant border on mobile 2026-02-13 19:26:08 +02:00
Elian Doran
b09ef222f5 fix(search): errors not displayed (closes #8704) 2026-02-13 19:22:21 +02:00
Elian Doran
2d0ed06d50 Update Node.js to v24.13.1 (#8629) 2026-02-13 13:23:22 +02:00
renovate[bot]
8dd7cf6085 Update Node.js to v24.13.1 2026-02-13 05:50:03 +00:00
Elian Doran
4999bd4f1e Translations update from Hosted Weblate (#8701) 2026-02-13 07:48:43 +02:00
green
22f408addb Translated using Weblate (Japanese)
Currently translated at 100.0% (1777 of 1777 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/ja/
2026-02-13 06:48:06 +01:00
AggelosPnS
9ca1dbe638 Translated using Weblate (Greek)
Currently translated at 8.9% (35 of 389 strings)

Translation: Trilium Notes/Server
Translate-URL: https://hosted.weblate.org/projects/trilium/server/el/
2026-02-13 06:48:06 +01:00
AggelosPnS
26662952e3 Translated using Weblate (Greek)
Currently translated at 2.0% (36 of 1777 strings)

Translation: Trilium Notes/Client
Translate-URL: https://hosted.weblate.org/projects/trilium/client/el/
2026-02-13 06:48:06 +01:00
Hosted Weblate
f0c9fa4ca3 Update translation files
Updated by "Cleanup translation files" add-on in Weblate.

Translation: Trilium Notes/README
Translate-URL: https://hosted.weblate.org/projects/trilium/readme/
2026-02-13 06:48:06 +01:00
Elian Doran
b51aa1dd71 Update pnpm to v10.29.3 (#8683) 2026-02-13 07:48:00 +02:00
Elian Doran
846253c9e3 Update dependency @codemirror/view to v6.39.14 (#8695) 2026-02-13 07:47:46 +02:00
Elian Doran
6ab6ea97ac Update dependency i18next to v25.8.6 (#8696) 2026-02-13 07:47:31 +02:00
Elian Doran
bf41f70b98 Update dependency wxt to v0.20.17 (#8697) 2026-02-13 07:46:48 +02:00
Elian Doran
67ddbedd08 Update dependency dotenv to v17.3.1 (#8698) 2026-02-13 07:45:17 +02:00
renovate[bot]
2573e219dc Update dependency dotenv to v17.3.1 2026-02-13 01:02:52 +00:00
renovate[bot]
7e368678ab Update dependency wxt to v0.20.17 2026-02-13 01:02:01 +00:00
renovate[bot]
4a9fcf7ab6 Update dependency i18next to v25.8.6 2026-02-13 01:01:10 +00:00
renovate[bot]
65856c61c5 Update dependency @codemirror/view to v6.39.14 2026-02-13 01:00:19 +00:00
renovate[bot]
b5a97bffab Update pnpm to v10.29.3 2026-02-12 17:58:49 +00:00
37 changed files with 889 additions and 443 deletions

2
.nvmrc
View File

@@ -1 +1 @@
24.13.0
24.13.1

View File

@@ -9,7 +9,7 @@
"keywords": [],
"author": "Elian Doran <contact@eliandoran.me>",
"license": "AGPL-3.0-only",
"packageManager": "pnpm@10.29.2",
"packageManager": "pnpm@10.29.3",
"devDependencies": {
"@redocly/cli": "2.18.0",
"archiver": "7.0.1",

View File

@@ -44,7 +44,7 @@
"draggabilly": "3.0.0",
"force-graph": "1.51.1",
"globals": "17.3.0",
"i18next": "25.8.5",
"i18next": "25.8.6",
"i18next-http-backend": "3.0.2",
"jquery": "4.0.0",
"jquery.fancytree": "2.38.5",

View File

@@ -700,6 +700,10 @@ export default class FNote {
return this.hasAttribute(LABEL, name);
}
hasLabelOrDisabled(name: string) {
return this.hasLabel(name) || this.hasLabel(`disabled:${name}`);
}
/**
* @param name - label name
* @returns true if label exists (including inherited) and does not have "false" value.

View File

@@ -1336,15 +1336,12 @@ body.desktop .dropdown-submenu > .dropdown-menu {
max-width: 300px;
}
.dropdown-submenu.dropstart > .dropdown-menu {
.dropdown-submenu.dropstart > .dropdown-menu,
body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
inset-inline-start: auto;
inset-inline-end: calc(100% - 2px);
}
body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu {
inset-inline-start: calc(-100% + 10px);
}
.right-dropdown-widget {
flex-shrink: 0;
}

View File

@@ -210,6 +210,7 @@
--badge-share-background-color: #4d4d4d;
--badge-clipped-note-background-color: #295773;
--badge-execute-background-color: #604180;
--badge-active-content-background-color: rgb(12, 68, 70);
--note-icon-background-color: #444444;
--note-icon-color: #d4d4d4;
@@ -238,9 +239,9 @@
--bottom-panel-background-color: #11111180;
--bottom-panel-title-bar-background-color: #3F3F3F80;
--status-bar-border-color: var(--main-border-color);
--scrollbar-thumb-color: #fdfdfd5c;
--scrollbar-thumb-hover-color: #ffffff7d;
--scrollbar-background-color: transparent;
@@ -351,4 +352,4 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
.note-split.with-hue *::selection,
.quick-edit-dialog-wrapper.with-hue *::selection {
--selection-background-color: hsl(var(--custom-color-hue), 49.2%, 35%);
}
}

View File

@@ -202,6 +202,7 @@
--badge-share-background-color: #6b6b6b;
--badge-clipped-note-background-color: #2284c0;
--badge-execute-background-color: #7b47af;
--badge-active-content-background-color: rgb(27, 164, 168);
--note-icon-background-color: #4f4f4f;
--note-icon-color: white;
@@ -322,4 +323,4 @@
.note-split.with-hue *::selection,
.quick-edit-dialog-wrapper.with-hue *::selection {
--selection-background-color: hsl(var(--custom-color-hue), 60%, 90%);
}
}

View File

@@ -13,6 +13,46 @@
"critical-error": {
"title": "Κρίσιμο σφάλμα",
"message": "Συνέβη κάποιο κρίσιμο σφάλμα, το οποίο δεν επιτρέπει στην εφαρμογή χρήστη να ξεκινήσει:\n\n{{message}}\n\nΤο πιθανότερο είναι να προκλήθηκε από κάποιο script που απέτυχε απρόοπτα. Δοκιμάστε να ξεκινήσετε την εφαρμογή σε ασφαλή λειτουργία για να λύσετε το πρόβλημα."
}
},
"widget-error": {
"title": "Δεν ήταν δυνατή η αρχικοποίηση του widget",
"message-custom": "Προσαρμοσμένο widget της σημείωσης με ID \"{{id}}\", με τίτλο \"{{title}}\", δεν ήταν δυνατό να αρχικοποιηθεί λόγω:\n\n{{message}}",
"message-unknown": "Άγνωστο widget δεν ήταν δυνατό να αρχικοποιηθεί λόγω:\n\n{{message}}"
},
"bundle-error": {
"title": "Δεν ήταν δυνατή η φόρτωση προσαρμοσμένου script",
"message": "Το script δεν ήταν δυνατό να εκτελεστεί λόγω:\n\n{{message}}"
},
"widget-list-error": {
"title": "Δεν ήταν δυνατή η λήψη της λίστας των widgets από τον server"
},
"widget-render-error": {
"title": "Δεν ήταν δυνατή η απόδοση προσαρμοσμένου React widget"
},
"widget-missing-parent": "Το προσαρμοσμένο widget δεν έχει ορισμένη την υποχρεωτική ιδιότητα '{{property}}'.\n\nΕάν το script προορίζεται για εκτέλεση χωρίς UI element, χρησιμοποιήστε '#run=frontendStartup' αντί για αυτό.",
"open-script-note": "Άνοιγμα σημείωσης script",
"scripting-error": "Σφάλμα προσαρμοσμένου script: {{title}}"
},
"bookmark_buttons": {
"bookmarks": "Σελιδοδείκτες"
},
"add_link": {
"add_link": "Προσθήκη συνδέσμου",
"help_on_links": "Βοήθεια για συνδέσμους",
"note": "Σημείωση",
"search_note": "Αναζήτηση σημείωσης με βάση το όνομά της",
"link_title_mirrors": "Ο τίτλος του συνδέσμου αντικατοπτρίζει τον τρέχοντα τίτλο της σημείωσης",
"link_title_arbitrary": "Ο τίτλος του συνδέσμου μπορεί να τροποποιηθεί ελεύθερα",
"link_title": "Τίτλος συνδέσμου",
"button_add_link": "Προσθήκη συνδέσμου"
},
"branch_prefix": {
"edit_branch_prefix": "Επεξεργασία προθέματος κλάδου",
"edit_branch_prefix_multiple": "Επεξεργασία προθέματος κλάδου για {{count}} κλάδους",
"help_on_tree_prefix": "Βοήθεια για πρόθεμα δέντρου",
"prefix": "Πρόθεμα: ",
"save": "Αποθήκευση",
"branch_prefix_saved": "Το πρόθεμα κλάδου αποθηκεύτηκε.",
"branch_prefix_saved_multiple": "Το πρόθεμα κλάδου αποθηκεύτηκε για {{count}} κλάδους."
}
}

View File

@@ -2288,5 +2288,27 @@
},
"bookmark_buttons": {
"bookmarks": "Bookmarks"
},
"active_content_badges": {
"type_icon_pack": "Icon pack",
"type_backend_script": "Backend script",
"type_frontend_script": "Frontend script",
"type_widget": "Widget",
"type_app_css": "Custom CSS",
"type_render_note": "Render note",
"type_web_view": "Web view",
"toggle_tooltip_enable_tooltip": "Click to enable this {{type}}.",
"toggle_tooltip_disable_tooltip": "Click to disable this {{type}}.",
"menu_docs": "Open documentation",
"menu_execute_now": "Execute script now",
"menu_run": "Run automatically",
"menu_run_disabled": "Manually",
"menu_run_backend_startup": "When the backend starts up",
"menu_run_hourly": "Hourly",
"menu_run_daily": "Daily",
"menu_run_frontend_startup": "When the desktop frontend starts up",
"menu_run_mobile_startup": "When the mobile frontend starts up",
"menu_change_to_widget": "Change to widget",
"menu_change_to_frontend_script": "Change to frontend script"
}
}

View File

@@ -249,7 +249,8 @@
"reload_app": "リロードして変更を適用する",
"set_all_to_default": "すべてのショートカットをデフォルトに戻す",
"confirm_reset": "キーボードショートカットをすべてデフォルトにリセットしますか?",
"keyboard_shortcuts": "キーボードショートカット"
"keyboard_shortcuts": "キーボードショートカット",
"no_results": "'{{filter}}' に一致するショートカットが見つかりません"
},
"confirm": {
"confirmation": "確認",

View File

@@ -0,0 +1,308 @@
import { BUILTIN_ATTRIBUTES } from "@triliumnext/commons";
import clsx from "clsx";
import { useEffect, useState } from "preact/hooks";
import FNote from "../../entities/fnote";
import attributes from "../../services/attributes";
import { t } from "../../services/i18n";
import { openInAppHelpFromUrl } from "../../services/utils";
import { BadgeWithDropdown } from "../react/Badge";
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../react/FormList";
import FormToggle from "../react/FormToggle";
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent } from "../react/hooks";
const DANGEROUS_ATTRIBUTES = BUILTIN_ATTRIBUTES.filter(a => a.isDangerous || a.name === "appCss");
const activeContentLabels = [ "iconPack", "widget", "appCss" ] as const;
interface ActiveContentInfo {
type: "iconPack" | "backendScript" | "frontendScript" | "widget" | "appCss" | "renderNote" | "webView";
isEnabled: boolean;
canToggleEnabled: boolean;
}
const typeMappings: Record<ActiveContentInfo["type"], {
icon: string;
helpPage: string;
apiDocsPage?: string;
isExecutable?: boolean
}> = {
iconPack: {
icon: "bx bx-package",
helpPage: "g1mlRoU8CsqC",
},
backendScript: {
icon: "bx bx-server",
helpPage: "SPirpZypehBG",
apiDocsPage: "MEtfsqa5VwNi",
isExecutable: true,
},
frontendScript: {
icon: "bx bx-window",
helpPage: "yIhgI5H7A2Sm",
apiDocsPage: "Q2z6av6JZVWm",
isExecutable: true
},
widget: {
icon: "bx bxs-widget",
helpPage: "MgibgPcfeuGz"
},
appCss: {
icon: "bx bxs-file-css",
helpPage: "AlhDUqhENtH7"
},
renderNote: {
icon: "bx bx-extension",
helpPage: "HcABDtFCkbFN"
},
webView: {
icon: "bx bx-globe",
helpPage: "1vHRoWCEjj0L"
}
};
export function ActiveContentBadges() {
const { note } = useNoteContext();
const info = useActiveContentInfo(note);
return (note && info &&
<>
{info.canToggleEnabled && <ActiveContentToggle info={info} note={note} />}
<ActiveContentBadge info={info} note={note} />
</>
);
}
function ActiveContentBadge({ info, note }: { note: FNote, info: ActiveContentInfo }) {
const { icon, helpPage, apiDocsPage, isExecutable } = typeMappings[info.type];
return (
<BadgeWithDropdown
className={clsx("active-content-badge", !!info.isEnabled && "disabled")}
icon={icon}
text={getTranslationForType(info.type)}
>
{isExecutable && (
<>
<FormListItem
icon="bx bx-play"
triggerCommand="runActiveNote"
>{t("active_content_badges.menu_execute_now")}</FormListItem>
<ScriptRunOptions note={note} info={info} />
<FormDropdownDivider />
</>
)}
{(info.type === "frontendScript" || info.type === "widget") && (
<>
<WidgetSwitcher note={note} />
<FormDropdownDivider />
</>
)}
<FormListItem
icon="bx bx-help-circle"
onClick={() => openInAppHelpFromUrl(helpPage)}
>{t("active_content_badges.menu_docs")}</FormListItem>
{apiDocsPage && <FormListItem
icon="bx bx-book-content"
onClick={() => openInAppHelpFromUrl(apiDocsPage)}
>{t("code_buttons.trilium_api_docs_button_title")}</FormListItem>}
</BadgeWithDropdown>
);
}
function ScriptRunOptions({ info, note }: { note: FNote, info: ActiveContentInfo }) {
const [ run, setRun ] = useNoteLabel(note, "run");
const options: {
title: string;
value: string | null;
type: "both" | "backendScript" | "frontendScript";
}[] = ([
{
title: t("active_content_badges.menu_run_disabled"),
value: null,
type: "both"
},
{
title: t("active_content_badges.menu_run_backend_startup"),
value: "backendStartup",
type: "backendScript"
},
{
title: t("active_content_badges.menu_run_daily"),
value: "daily",
type: "backendScript"
},
{
title: t("active_content_badges.menu_run_hourly"),
value: "hourly",
type: "backendScript"
},
{
title: t("active_content_badges.menu_run_frontend_startup"),
value: "frontendStartup",
type: "frontendScript"
},
{
title: t("active_content_badges.menu_run_mobile_startup"),
value: "mobileStartup",
type: "frontendScript"
}
] as const).filter(option => option.type === "both" || option.type === info.type);
return (
<FormDropdownSubmenu title={t("active_content_badges.menu_run")} icon="bx bx-rss" dropStart>
{options.map(({ title, value }) => (
<FormListItem
key={value}
onClick={() => setRun(value)}
checked={run ? run === value : value === null }
>{title}</FormListItem>
))}
</FormDropdownSubmenu>
);
}
function WidgetSwitcher({ note }: { note: FNote }) {
const [ widget, setWidget ] = useNoteLabelBoolean(note, "widget");
const [ disabledWidget, setDisabledWidget ] = useNoteLabelBoolean(note, "disabled:widget");
return (widget || disabledWidget)
? <FormListItem
icon="bx bx-window"
onClick={() => {
setWidget(false);
setDisabledWidget(false);
}}
>{t("active_content_badges.menu_change_to_frontend_script")}</FormListItem>
: <FormListItem
icon={widget ? "bx bx-window" : "bx bxs-widget"}
onClick={() => {
setWidget(true);
}}
>{t("active_content_badges.menu_change_to_widget")}</FormListItem>;
}
function getTranslationForType(type: ActiveContentInfo["type"]) {
switch (type) {
case "iconPack":
return t("active_content_badges.type_icon_pack");
case "backendScript":
return t("active_content_badges.type_backend_script");
case "frontendScript":
return t("active_content_badges.type_frontend_script");
case "widget":
return t("active_content_badges.type_widget");
case "appCss":
return t("active_content_badges.type_app_css");
case "renderNote":
return t("active_content_badges.type_render_note");
case "webView":
return t("note_types.web-view");
}
}
function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentInfo }) {
const typeTranslation = getTranslationForType(info.type);
return info && <FormToggle
switchOnName="" switchOffName=""
currentValue={info.isEnabled}
switchOnTooltip={t("active_content_badges.toggle_tooltip_disable_tooltip", { type: typeTranslation })}
switchOffTooltip={t("active_content_badges.toggle_tooltip_enable_tooltip", { type: typeTranslation })}
onChange={async (willEnable) => {
const attrs = note.getOwnedAttributes()
.filter(attr => {
if (attr.isInheritable) return false;
const baseName = getNameWithoutPrefix(attr.name);
return DANGEROUS_ATTRIBUTES.some(item => item.name === baseName && item.type === attr.type);
});
for (const attr of attrs) {
const baseName = getNameWithoutPrefix(attr.name);
const newName = willEnable ? baseName : `disabled:${baseName}`;
if (newName === attr.name) continue;
// We are adding and removing afterwards to avoid a flicker (because for a moment there would be no active content attribute anymore) because the operations are done in sequence and not atomically.
if (attr.type === "label") {
await attributes.addLabel(note.noteId, newName, attr.value);
} else {
await attributes.setRelation(note.noteId, newName, attr.value);
}
await attributes.removeAttributeById(note.noteId, attr.attributeId);
}
}}
/>;
}
function getNameWithoutPrefix(name: string) {
return name.startsWith("disabled:") ? name.substring(9) : name;
}
function useActiveContentInfo(note: FNote | null | undefined) {
const [ info, setInfo ] = useState<ActiveContentInfo | null>(null);
function refresh() {
let type: ActiveContentInfo["type"] | null = null;
let isEnabled = true;
let canToggleEnabled = false;
if (!note) {
setInfo(null);
return;
}
if (note.type === "render") {
type = "renderNote";
isEnabled = note.hasRelation("renderNote");
canToggleEnabled = note.hasRelation("renderNote") || note.hasRelation("disabled:renderNote");
} else if (note.type === "webView") {
type = "webView";
isEnabled = note.hasLabel("webViewSrc");
canToggleEnabled = note.hasLabelOrDisabled("webViewSrc");
} else if (note.type === "code" && note.mime === "application/javascript;env=backend") {
type = "backendScript";
for (const backendLabel of [ "run", "customRequestHandler", "customResourceProvider" ]) {
isEnabled ||= note.hasLabel(backendLabel);
if (!canToggleEnabled && note.hasLabelOrDisabled(backendLabel)) {
canToggleEnabled = true;
}
}
} else if (note.type === "code" && note.mime === "application/javascript;env=frontend") {
type = "frontendScript";
isEnabled = note.hasLabel("widget") || note.hasLabel("run");
canToggleEnabled = note.hasLabelOrDisabled("widget") || note.hasLabelOrDisabled("run");
}
for (const labelToCheck of activeContentLabels ) {
if (note.hasLabel(labelToCheck)) {
type = labelToCheck;
break;
} else if (note.hasLabel(`disabled:${labelToCheck}`)) {
type = labelToCheck;
isEnabled = false;
break;
}
}
if (type) {
setInfo({ type, isEnabled, canToggleEnabled });
} else {
setInfo(null);
}
}
// Refresh on note change.
useEffect(refresh, [ note ]);
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (loadResults.getAttributeRows().some(attr => attributes.isAffecting(attr, note))) {
refresh();
}
});
return info;
}

View File

@@ -37,6 +37,10 @@
pointer-events: none;
}
}
&.active-content-badge { --color: var(--badge-active-content-background-color); }
&.active-content-badge.disabled {
opacity: 0.5;
}
min-width: 0;
@@ -45,6 +49,11 @@
text-overflow: ellipsis;
min-width: 0;
}
.switch-button {
--switch-track-height: 8px;
--switch-track-width: 30px;
}
}
.dropdown-badge {

View File

@@ -10,6 +10,7 @@ import { FormDropdownDivider, FormListItem } from "../react/FormList";
import { useGetContextData, useIsNoteReadOnly, useNoteContext, useNoteLabel, useNoteLabelBoolean } from "../react/hooks";
import { useShareState } from "../ribbon/BasicPropertiesTab";
import { useShareInfo } from "../shared_info";
import { ActiveContentBadges } from "./ActiveContentBadges";
export default function NoteBadges() {
return (
@@ -19,6 +20,7 @@ export default function NoteBadges() {
<ShareBadge />
<ClippedNoteBadge />
<ExecuteBadge />
<ActiveContentBadges />
</div>
);
}

View File

@@ -70,7 +70,6 @@ export default function NoteActionsCustom(props: NoteActionsCustomProps) {
>
<AddChildButton {...innerProps} />
<RunActiveNoteButton {...innerProps } />
<OpenTriliumApiDocsButton {...innerProps} />
<SwitchSplitOrientationButton {...innerProps} />
<ToggleReadOnlyButton {...innerProps} />
<SaveToNoteButton {...innerProps} />
@@ -230,15 +229,6 @@ function SaveToNoteButton({ note, noteMime }: NoteActionsCustomInnerProps) {
/>;
}
function OpenTriliumApiDocsButton({ noteMime }: NoteActionsCustomInnerProps) {
const isEnabled = noteMime.startsWith("application/javascript;env=");
return isEnabled && <NoteAction
icon="bx bx-help-circle"
text={t("code_buttons.trilium_api_docs_button_title")}
onClick={() => openInAppHelpFromUrl(noteMime.endsWith("frontend") ? "Q2z6av6JZVWm" : "MEtfsqa5VwNi")}
/>;
}
function InAppHelpButton({ note }: NoteActionsCustomInnerProps) {
const helpUrl = getHelpUrlForNote(note);
const isEnabled = !!helpUrl;

View File

@@ -1,4 +1,5 @@
import { AttributeType } from "@triliumnext/commons";
import clsx from "clsx";
import { ComponentChildren, VNode } from "preact";
import { useEffect, useMemo, useRef } from "preact/hooks";
@@ -7,6 +8,7 @@ import FNote from "../../entities/fnote";
import { removeOwnedAttributesByNameOrType } from "../../services/attributes";
import { t } from "../../services/i18n";
import server from "../../services/server";
import Admonition from "../react/Admonition";
import FormSelect from "../react/FormSelect";
import FormTextArea from "../react/FormTextArea";
import FormTextBox from "../react/FormTextBox";
@@ -105,8 +107,9 @@ export const SEARCH_OPTIONS: SearchOption[] = [
}
];
function SearchOption({ note, title, titleIcon, children, help, attributeName, attributeType, additionalAttributesToDelete }: {
function SearchOption({ note, className, title, titleIcon, children, help, attributeName, attributeType, additionalAttributesToDelete }: {
note: FNote;
className?: string;
title: string,
titleIcon?: string,
children?: ComponentChildren,
@@ -116,7 +119,7 @@ function SearchOption({ note, title, titleIcon, children, help, attributeName, a
additionalAttributesToDelete?: { type: "label" | "relation", name: string }[]
}) {
return (
<tr className={attributeName}>
<tr className={clsx(attributeName, className)}>
<td className="title-column">
{titleIcon && <><Icon icon={titleIcon} />{" "}</>}
{title}
@@ -154,64 +157,57 @@ function SearchStringOption({ note, refreshResults, error, ...restProps }: Searc
}
}, 1000);
// React to errors
const { showTooltip, hideTooltip } = useTooltip(inputRef, {
trigger: "manual",
title: `${t("search_string.error", { error: error?.message })}`,
html: true,
placement: "bottom"
});
// Auto-focus.
useEffect(() => inputRef.current?.focus(), []);
useEffect(() => {
if (error) {
showTooltip();
setTimeout(() => hideTooltip(), 4000);
} else {
hideTooltip();
}
}, [ error ]);
return <>
<SearchOption
title={t("search_string.title_column")}
className={clsx({ "has-error": !!error })}
help={<>
<strong>{t("search_string.search_syntax")}</strong> - {t("search_string.also_see")} <a href="#" data-help-page="search.html">{t("search_string.complete_help")}</a>
<ul style="marigin-bottom: 0;">
<li>{t("search_string.full_text_search")}</li>
<li><code>#abc</code> - {t("search_string.label_abc")}</li>
<li><code>#year = 2019</code> - {t("search_string.label_year")}</li>
<li><code>#rock #pop</code> - {t("search_string.label_rock_pop")}</li>
<li><code>#rock or #pop</code> - {t("search_string.label_rock_or_pop")}</li>
<li><code>#year &lt;= 2000</code> - {t("search_string.label_year_comparison")}</li>
<li><code>note.dateCreated &gt;= MONTH-1</code> - {t("search_string.label_date_created")}</li>
</ul>
</>}
note={note} {...restProps}
>
<FormTextArea
inputRef={inputRef}
className="search-string"
placeholder={t("search_string.placeholder")}
currentValue={searchString ?? ""}
onChange={text => {
currentValue.current = text;
spacedUpdate.scheduleUpdate();
}}
onKeyDown={async (e) => {
if (e.key === "Enter") {
e.preventDefault();
return <SearchOption
title={t("search_string.title_column")}
help={<>
<strong>{t("search_string.search_syntax")}</strong> - {t("search_string.also_see")} <a href="#" data-help-page="search.html">{t("search_string.complete_help")}</a>
<ul style="marigin-bottom: 0;">
<li>{t("search_string.full_text_search")}</li>
<li><code>#abc</code> - {t("search_string.label_abc")}</li>
<li><code>#year = 2019</code> - {t("search_string.label_year")}</li>
<li><code>#rock #pop</code> - {t("search_string.label_rock_pop")}</li>
<li><code>#rock or #pop</code> - {t("search_string.label_rock_or_pop")}</li>
<li><code>#year &lt;= 2000</code> - {t("search_string.label_year_comparison")}</li>
<li><code>note.dateCreated &gt;= MONTH-1</code> - {t("search_string.label_date_created")}</li>
</ul>
</>}
note={note} {...restProps}
>
<FormTextArea
inputRef={inputRef}
className="search-string"
placeholder={t("search_string.placeholder")}
currentValue={searchString ?? ""}
onChange={text => {
currentValue.current = text;
spacedUpdate.scheduleUpdate();
}}
onKeyDown={async (e) => {
if (e.key === "Enter") {
e.preventDefault();
// this also in effect disallows new lines in query string.
// on one hand, this makes sense since search string is a label
// on the other hand, it could be nice for structuring long search string. It's probably a niche case though.
await spacedUpdate.updateNowIfNecessary();
refreshResults();
}
}}
/>
</SearchOption>;
// this also in effect disallows new lines in query string.
// on one hand, this makes sense since search string is a label
// on the other hand, it could be nice for structuring long search string. It's probably a niche case though.
await spacedUpdate.updateNowIfNecessary();
refreshResults();
}
}}
/>
</SearchOption>
{error?.message && (
<tr>
<td colspan={3}>
<Admonition type="caution">{error.message}</Admonition>
</td>
</tr>
)}
</>;
}
function SearchScriptOption({ note, ...restProps }: SearchOptionProps) {

View File

@@ -4,6 +4,12 @@
width: 100%;
border-collapse: separate;
border-spacing: 10px;
.admonition {
margin-top: 0.25em;
margin-bottom: 1em;
text-wrap: wrap;
}
}
.search-setting-table div {
@@ -141,20 +147,26 @@ body.mobile .search-definition-widget {
gap: 0.5em;
}
.search-setting-table tr.searchString td:nth-of-type(2) {
flex-grow: 1;
}
.search-setting-table tr.searchString {
td:nth-of-type(2) {
flex-grow: 1;
}
.search-setting-table tr.searchString .button-column {
flex-grow: 0;
flex-shrink: 0;
width: 64px;
.button-column {
flex-grow: 0;
flex-shrink: 0;
width: 64px;
}
&.has-error {
border-bottom: 0;
}
}
.search-setting-table tr.ancestor > td > div {
flex-direction: column;
align-items: flex-start !important;
}
}
.search-actions tr {
border-bottom: 0;
@@ -171,4 +183,4 @@ body.mobile .search-definition-widget {
overflow: unset;
height: unset !important;
}
}
}

View File

@@ -65,7 +65,7 @@ export default function RelationMap({ note, noteContext, ntxId, parentComponent
};
},
onContentChange(content) {
let newData: MapData | null = null;
let newData: Partial<MapData> | null = null;
if (content) {
try {
@@ -75,7 +75,7 @@ export default function RelationMap({ note, noteContext, ntxId, parentComponent
}
}
if (!newData) {
if (!newData || !newData.notes || !newData.transform) {
newData = {
notes: [],
// it is important to have this exact value here so that initial transform is the same as this
@@ -90,8 +90,8 @@ export default function RelationMap({ note, noteContext, ntxId, parentComponent
};
}
setData(newData);
mapApiRef.current = new RelationMapApi(note, newData, (newData, refreshUi) => {
setData(newData as MapData);
mapApiRef.current = new RelationMapApi(note, newData as MapData, (newData, refreshUi) => {
if (refreshUi) {
setData(newData);
}

View File

@@ -6,6 +6,6 @@
"e2e": "playwright test"
},
"devDependencies": {
"dotenv": "17.2.4"
"dotenv": "17.3.1"
}
}

View File

@@ -1,4 +1,4 @@
FROM node:24.13.0-bullseye-slim AS builder
FROM node:24.13.1-bullseye-slim AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:24.13.0-bullseye-slim
FROM node:24.13.1-bullseye-slim
# Install only runtime dependencies
RUN apt-get update && \
apt-get install -y --no-install-recommends \

View File

@@ -1,4 +1,4 @@
FROM node:24.13.0-alpine AS builder
FROM node:24.13.1-alpine AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:24.13.0-alpine
FROM node:24.13.1-alpine
# Install runtime dependencies
RUN apk add --no-cache su-exec shadow

View File

@@ -1,4 +1,4 @@
FROM node:24.13.0-alpine AS builder
FROM node:24.13.1-alpine AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:24.13.0-alpine
FROM node:24.13.1-alpine
# Create a non-root user with configurable UID/GID
ARG USER=trilium
ARG UID=1001

View File

@@ -1,4 +1,4 @@
FROM node:24.13.0-bullseye-slim AS builder
FROM node:24.13.1-bullseye-slim AS builder
RUN corepack enable
# Install native dependencies since we might be building cross-platform.
@@ -7,7 +7,7 @@ COPY ./docker/package.json ./docker/pnpm-workspace.yaml /usr/src/app/
# We have to use --no-frozen-lockfile due to CKEditor patches
RUN pnpm install --no-frozen-lockfile --prod && pnpm rebuild
FROM node:24.13.0-bullseye-slim
FROM node:24.13.1-bullseye-slim
# Create a non-root user with configurable UID/GID
ARG USER=trilium
ARG UID=1001

View File

@@ -99,7 +99,7 @@
"html2plaintext": "2.1.4",
"http-proxy-agent": "7.0.2",
"https-proxy-agent": "7.0.6",
"i18next": "25.8.5",
"i18next": "25.8.6",
"i18next-fs-backend": "2.6.1",
"image-type": "6.0.0",
"ini": "6.0.0",

View File

@@ -3,6 +3,37 @@
"back-in-note-history": "Μετάβαση στην προηγούμενη σημείωση στο ιστορικό",
"forward-in-note-history": "Μεταβείτε στην επόμενη σημείωση στο ιστορικό",
"open-jump-to-note-dialog": "Ανοίξτε το παράθυρο διαλόγου \"Μετάβαση στη σημείωση\"",
"open-command-palette": "Άνοιγμα παλέτας εντολών"
"open-command-palette": "Άνοιγμα παλέτας εντολών",
"scroll-to-active-note": "Μετακίνηση του δέντρου σημειώσεων στην ενεργή σημείωση",
"quick-search": "Ενεργοποίηση γραμμής γρήγορης αναζήτησης",
"search-in-subtree": "Αναζήτηση σημειώσεων στο υποδέντρο της ενεργής σημείωσης",
"expand-subtree": "Ανάπτυξη υποδέντρου της τρέχουσας σημείωσης",
"collapse-tree": "Σύμπτυξη ολόκληρου του δέντρου σημειώσεων",
"collapse-subtree": "Σύμπτυξη υποδέντρου της τρέχουσας σημείωσης",
"sort-child-notes": "Ταξινόμηση υποσημειώσεων",
"creating-and-moving-notes": "Δημιουργία και μετακίνηση σημειώσεων",
"create-note-after": "Δημιουργία σημείωσης μετά την ενεργή σημείωση",
"create-note-into": "Δημιουργία σημείωσης ως υποσημείωσης της ενεργής σημείωσης",
"create-note-into-inbox": "Δημιουργία σημείωσης στα Εισερχόμενα (εάν έχουν οριστεί) ή στη σημείωση ημέρας",
"delete-note": "Διαγραφή σημείωσης",
"move-note-up": "Μετακίνηση σημείωσης προς τα επάνω",
"move-note-down": "Μετακίνηση σημείωσης προς τα κάτω",
"move-note-up-in-hierarchy": "Μετακίνηση σημείωσης προς τα επάνω στην ιεραρχία",
"move-note-down-in-hierarchy": "Μετακίνηση σημείωσης προς τα κάτω στην ιεραρχία",
"edit-note-title": "Μετάβαση από το δέντρο στις λεπτομέρειες της σημείωσης και επεξεργασία τίτλου",
"edit-branch-prefix": "Εμφάνιση παραθύρου «Επεξεργασία προθέματος κλάδου»",
"clone-notes-to": "Κλωνοποίηση επιλεγμένων σημειώσεων",
"move-notes-to": "Μετακίνηση επιλεγμένων σημειώσεων",
"note-clipboard": "Πρόχειρο σημειώσεων",
"copy-notes-to-clipboard": "Αντιγραφή επιλεγμένων σημειώσεων στο πρόχειρο",
"paste-notes-from-clipboard": "Επικόλληση σημειώσεων από το πρόχειρο στην ενεργή σημείωση",
"cut-notes-to-clipboard": "Αποκοπή επιλεγμένων σημειώσεων στο πρόχειρο",
"select-all-notes-in-parent": "Επιλογή όλων των σημειώσεων από το τρέχον επίπεδο σημείωσης",
"add-note-above-to-the-selection": "Προσθήκη της παραπάνω σημείωσης στην επιλογή",
"add-note-below-to-selection": "Προσθήκη της παρακάτω σημείωσης στην επιλογή",
"duplicate-subtree": "Αντιγραφή υποδέντρου",
"tabs-and-windows": "Καρτέλες & Παράθυρα",
"open-new-tab": "Άνοιγμα νέας καρτέλας",
"close-active-tab": "Κλείσιμο ενεργής καρτέλας"
}
}

View File

@@ -1,11 +1,11 @@
import BUILTIN_ATTRIBUTES from "./builtin_attributes.js";
import { AnonymizedDbResponse, BUILTIN_ATTRIBUTES, DatabaseAnonymizeResponse } from "@triliumnext/commons";
import Database from "better-sqlite3";
import fs from "fs";
import path from "path";
import dataDir from "./data_dir.js";
import dateUtils from "./date_utils.js";
import Database from "better-sqlite3";
import sql from "./sql.js";
import path from "path";
import { AnonymizedDbResponse, DatabaseAnonymizeResponse } from "@triliumnext/commons";
function getFullAnonymizationScript() {
// we want to delete all non-builtin attributes because they can contain sensitive names and values
@@ -86,7 +86,7 @@ function getExistingAnonymizedDatabases() {
.readdirSync(dataDir.ANONYMIZED_DB_DIR)
.filter((fileName) => fileName.includes("anonymized"))
.map((fileName) => ({
fileName: fileName,
fileName,
filePath: path.resolve(dataDir.ANONYMIZED_DB_DIR, fileName)
})) satisfies AnonymizedDbResponse[];
}

View File

@@ -1,13 +1,11 @@
"use strict";
import { type AttributeRow, BUILTIN_ATTRIBUTES } from "@triliumnext/commons";
import searchService from "./search/services/search.js";
import sql from "./sql.js";
import becca from "../becca/becca.js";
import BAttribute from "../becca/entities/battribute.js";
import attributeFormatter from "./attribute_formatter.js";
import BUILTIN_ATTRIBUTES from "./builtin_attributes.js";
import type BNote from "../becca/entities/bnote.js";
import type { AttributeRow } from "@triliumnext/commons";
import attributeFormatter from "./attribute_formatter.js";
import searchService from "./search/services/search.js";
import sql from "./sql.js";
const ATTRIBUTE_TYPES = new Set(["label", "relation"]);
@@ -41,18 +39,18 @@ function getNoteWithLabel(name: string, value?: string): BNote | null {
function createLabel(noteId: string, name: string, value: string = "") {
return createAttribute({
noteId: noteId,
noteId,
type: "label",
name: name,
value: value
name,
value
});
}
function createRelation(noteId: string, name: string, targetNoteId: string) {
return createAttribute({
noteId: noteId,
noteId,
type: "relation",
name: name,
name,
value: targetNoteId
});
}

View File

@@ -13,10 +13,10 @@
"postinstall": "wxt prepare"
},
"keywords": [],
"packageManager": "pnpm@10.29.2",
"packageManager": "pnpm@10.29.3",
"devDependencies": {
"@wxt-dev/auto-icons": "1.1.0",
"wxt": "0.20.15"
"wxt": "0.20.17"
},
"dependencies": {
"cash-dom": "8.1.5"

View File

@@ -9,7 +9,7 @@
"preview": "pnpm build && vite preview"
},
"dependencies": {
"i18next": "25.8.5",
"i18next": "25.8.6",
"i18next-http-backend": "3.0.2",
"preact": "10.28.3",
"preact-iso": "2.11.1",

View File

@@ -1,43 +1,45 @@
import { ComponentChildren } from 'preact';
import Card from '../../components/Card.js';
import Section from '../../components/Section.js';
import DownloadButton from '../../components/DownloadButton.js';
import "./index.css";
import { useColorScheme, usePageTitle } from '../../hooks.js';
import Button, { Link } from '../../components/Button.js';
import gitHubIcon from "../../assets/boxicons/bx-github.svg?raw";
import dockerIcon from "../../assets/boxicons/bx-docker.svg?raw";
import noteStructureIcon from "../../assets/boxicons/bx-folder.svg?raw";
import attributesIcon from "../../assets/boxicons/bx-tag.svg?raw";
import hoistingIcon from "../../assets/boxicons/bx-chevrons-up.svg?raw";
import revisionsIcon from "../../assets/boxicons/bx-history.svg?raw";
import syncIcon from "../../assets/boxicons/bx-refresh-cw.svg?raw";
import protectedNotesIcon from "../../assets/boxicons/bx-shield.svg?raw";
import jumpToIcon from "../../assets/boxicons/bx-send-alt.svg?raw";
import searchIcon from "../../assets/boxicons/bx-search.svg?raw";
import webClipperIcon from "../../assets/boxicons/bx-paperclip.svg?raw";
import importExportIcon from "../../assets/boxicons/bx-swap-horizontal.svg?raw";
import shareIcon from "../../assets/boxicons/bx-globe.svg?raw";
import codeIcon from "../../assets/boxicons/bx-code.svg?raw";
import restApiIcon from "../../assets/boxicons/bx-extension.svg?raw";
import textNoteIcon from "../../assets/boxicons/bx-note.svg?raw";
import fileIcon from "../../assets/boxicons/bx-file.svg?raw";
import canvasIcon from "../../assets/boxicons/bx-pen.svg?raw";
import mermaidIcon from "../../assets/boxicons/bx-vector-square.svg?raw";
import mindmapIcon from "../../assets/boxicons/bx-network-chart.svg?raw";
import calendarIcon from "../../assets/boxicons/bx-calendar.svg?raw";
import tableIcon from "../../assets/boxicons/bx-table.svg?raw";
import boardIcon from "../../assets/boxicons/bx-columns-3.svg?raw";
import geomapIcon from "../../assets/boxicons/bx-map.svg?raw";
import presentationIcon from "../../assets/boxicons/bx-slideshow.svg?raw";
import { getPlatform } from '../../download-helper.js';
import { ComponentChildren } from 'preact';
import { useEffect, useState } from 'preact/hooks';
import { Trans, useTranslation } from 'react-i18next';
import calendarIcon from "../../assets/boxicons/bx-calendar.svg?raw";
import hoistingIcon from "../../assets/boxicons/bx-chevrons-up.svg?raw";
import codeIcon from "../../assets/boxicons/bx-code.svg?raw";
import boardIcon from "../../assets/boxicons/bx-columns-3.svg?raw";
import dockerIcon from "../../assets/boxicons/bx-docker.svg?raw";
import restApiIcon from "../../assets/boxicons/bx-extension.svg?raw";
import fileIcon from "../../assets/boxicons/bx-file.svg?raw";
import noteStructureIcon from "../../assets/boxicons/bx-folder.svg?raw";
import gitHubIcon from "../../assets/boxicons/bx-github.svg?raw";
import shareIcon from "../../assets/boxicons/bx-globe.svg?raw";
import revisionsIcon from "../../assets/boxicons/bx-history.svg?raw";
import geomapIcon from "../../assets/boxicons/bx-map.svg?raw";
import mindmapIcon from "../../assets/boxicons/bx-network-chart.svg?raw";
import textNoteIcon from "../../assets/boxicons/bx-note.svg?raw";
import webClipperIcon from "../../assets/boxicons/bx-paperclip.svg?raw";
import canvasIcon from "../../assets/boxicons/bx-pen.svg?raw";
import syncIcon from "../../assets/boxicons/bx-refresh-cw.svg?raw";
import searchIcon from "../../assets/boxicons/bx-search.svg?raw";
import jumpToIcon from "../../assets/boxicons/bx-send-alt.svg?raw";
import protectedNotesIcon from "../../assets/boxicons/bx-shield.svg?raw";
import presentationIcon from "../../assets/boxicons/bx-slideshow.svg?raw";
import importExportIcon from "../../assets/boxicons/bx-swap-horizontal.svg?raw";
import tableIcon from "../../assets/boxicons/bx-table.svg?raw";
import attributesIcon from "../../assets/boxicons/bx-tag.svg?raw";
import mermaidIcon from "../../assets/boxicons/bx-vector-square.svg?raw";
import Button, { Link } from '../../components/Button.js';
import Card from '../../components/Card.js';
import DownloadButton from '../../components/DownloadButton.js';
import Section from '../../components/Section.js';
import { getPlatform } from '../../download-helper.js';
import { useColorScheme, usePageTitle } from '../../hooks.js';
export function Home() {
usePageTitle("");
return (
return (
<>
<HeroSection />
<OrganizationBenefitsSection />
@@ -48,7 +50,7 @@ export function Home() {
<FaqSection />
<FinalCta />
</>
);
);
}
function HeroSection() {
@@ -93,7 +95,7 @@ function HeroSection() {
{screenshotUrl && <img class="screenshot" src={screenshotUrl} alt={t("hero_section.screenshot_alt")} />}
</div>
</Section>
)
);
}
function OrganizationBenefitsSection() {
@@ -122,7 +124,7 @@ function ProductivityBenefitsSection() {
<Card iconSvg={protectedNotesIcon} title={t("productivity_benefits.protected_notes_title")} moreInfoUrl="https://docs.triliumnotes.org/user-guide/concepts/notes/protected-notes">{t("productivity_benefits.protected_notes_content")}</Card>
<Card iconSvg={jumpToIcon} title={t("productivity_benefits.jump_to_title")} moreInfoUrl="https://docs.triliumnotes.org/user-guide/concepts/navigation/jump-to">{t("productivity_benefits.jump_to_content")}</Card>
<Card iconSvg={searchIcon} title={t("productivity_benefits.search_title")} moreInfoUrl="https://docs.triliumnotes.org/user-guide/concepts/navigation/search">{t("productivity_benefits.search_content")}</Card>
<Card iconSvg={webClipperIcon} title={t("productivity_benefits.web_clipper_title")} moreInfoUrl="docs.triliumnotes.org/user-guide/setup/web-clipper">{t("productivity_benefits.web_clipper_content")}</Card>
<Card iconSvg={webClipperIcon} title={t("productivity_benefits.web_clipper_title")} moreInfoUrl="https://docs.triliumnotes.org/user-guide/setup/web-clipper">{t("productivity_benefits.web_clipper_content")}</Card>
</div>
</Section>
</>
@@ -275,7 +277,7 @@ function ListWithScreenshot({ items, cardExtra }: {
))}
</ul>
</div>
)
);
}
function FaqSection() {
@@ -299,7 +301,7 @@ function FaqItem({ question, children }: { question: string; children: Component
<Card title={question}>
{children}
</Card>
)
);
}
function FinalCta() {
@@ -312,5 +314,5 @@ function FinalCta() {
<Button href="./get-started/" text={t("final_cta.get_started")} />
</div>
</Section>
)
);
}

13
docs/README-el.md vendored
View File

@@ -97,8 +97,8 @@ script)](./README-ZH_TW.md) | [English](../README.md) | [French](./README-fr.md)
ασφαλή σύνδεση
* [Συγχρονισμός](https://docs.triliumnotes.org/user-guide/setup/synchronization)
με self-hosted διακομιστή συγχρονισμού
* there are [3rd party services for hosting synchronisation
server](https://docs.triliumnotes.org/user-guide/setup/server/cloud-hosting)
* Υπάρχουν [υπηρεσίες τρίτων για φιλοξενία διακομιστή
συγχρονισμού](https://docs.triliumnotes.org/user-guide/setup/server/cloud-hosting)
* [Κοινή χρήση](https://docs.triliumnotes.org/user-guide/advanced-usage/sharing)
(δημοσίευση) σημειώσεων στο δημόσιο διαδίκτυο
* Ισχυρή [κρυπτογράφηση
@@ -106,10 +106,11 @@ script)](./README-ZH_TW.md) | [English](../README.md) | [French](./README-fr.md)
με υποδιαίρεση ανά σημείωση
* Σχεδίαση διαγραμμάτων, με βάση το [Excalidraw](https://excalidraw.com/) (τύπος
σημείωσης "καμβάς")
* [Relation
maps](https://docs.triliumnotes.org/user-guide/note-types/relation-map) and
[note/link maps](https://docs.triliumnotes.org/user-guide/note-types/note-map)
for visualizing notes and their relations
* [Χάρτες
συσχετίσεων](https://docs.triliumnotes.org/user-guide/note-types/relation-map)
και [χάρτες
σημειώσεων/συνδέσμων](https://docs.triliumnotes.org/user-guide/note-types/note-map)
για την οπτικοποίηση σημειώσεων και των συσχετίσεών τους
* Νοητικοί χάρτες, βασισμένοι στο [Mind Elixir](https://docs.mind-elixir.com/)
* [Γεωγραφικοί
χάρτες](https://docs.triliumnotes.org/user-guide/collections/geomap) με

View File

@@ -50,7 +50,7 @@
"@triliumnext/server": "workspace:*",
"@types/express": "5.0.6",
"@types/js-yaml": "4.0.9",
"@types/node": "24.10.10",
"@types/node": "24.10.13",
"@vitest/browser-webdriverio": "4.0.18",
"@vitest/coverage-v8": "4.0.18",
"@vitest/ui": "4.0.18",
@@ -93,7 +93,7 @@
"url": "https://github.com/TriliumNext/Trilium/issues"
},
"homepage": "https://triliumnotes.org",
"packageManager": "pnpm@10.29.2",
"packageManager": "pnpm@10.29.3",
"pnpm": {
"patchedDependencies": {
"@ckeditor/ckeditor5-mention": "patches/@ckeditor__ckeditor5-mention.patch",

View File

@@ -16,7 +16,7 @@
"@codemirror/lang-xml": "6.1.0",
"@codemirror/legacy-modes": "6.5.2",
"@codemirror/search": "6.6.0",
"@codemirror/view": "6.39.13",
"@codemirror/view": "6.39.14",
"@fsegurai/codemirror-theme-abcdef": "6.2.3",
"@fsegurai/codemirror-theme-abyss": "6.2.3",
"@fsegurai/codemirror-theme-android-studio": "6.2.3",

View File

@@ -13,3 +13,4 @@ export * from "./lib/attribute_names.js";
export * from "./lib/utils.js";
export * from "./lib/dayjs.js";
export * from "./lib/notes.js";
export { default as BUILTIN_ATTRIBUTES } from "./lib/builtin_attributes.js";

View File

@@ -22,6 +22,11 @@ type Labels = {
pageUrl: string;
dateNote: string;
// Scripting
run: string;
widget: boolean;
"disabled:widget": boolean;
// Tree specific
subtreeHidden: boolean;

View File

@@ -33,7 +33,7 @@
"@triliumnext/ckeditor5": "workspace:*",
"@typescript-eslint/eslint-plugin": "8.55.0",
"@typescript-eslint/parser": "8.55.0",
"dotenv": "17.2.4",
"dotenv": "17.3.1",
"esbuild": "0.27.3",
"eslint": "10.0.0",
"highlight.js": "11.11.1",

567
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff