From c1e01467a5fb30d0581f529a434cf3d01ff971e6 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 19:12:18 +0200 Subject: [PATCH 01/23] fix(breadcrumbs): not showing on first render --- apps/client/src/widgets/react/hooks.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 10a5983c6..9a3b690eb 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -369,7 +369,8 @@ export function useActiveNoteContext() { useEffect(() => { setNote(noteContext?.note); - }, [ notePath ]); + setNotePath(noteContext?.notePath); + }, [ notePath, noteContext?.note, noteContext?.notePath ]); useTriliumEvents([ "setNoteContext", "activeContextChanged", "noteSwitchedAndActivated", "noteSwitched" ], () => { const noteContext = appContext.tabManager.getActiveContext() ?? undefined; From 8434549a9b0bc3c6373c3319c06971e004305f25 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 19:15:48 +0200 Subject: [PATCH 02/23] feat(breadcrumbs): display separator even if no child notes --- apps/client/src/widgets/layout/Breadcrumb.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/client/src/widgets/layout/Breadcrumb.tsx b/apps/client/src/widgets/layout/Breadcrumb.tsx index bc1494d3d..043a68206 100644 --- a/apps/client/src/widgets/layout/Breadcrumb.tsx +++ b/apps/client/src/widgets/layout/Breadcrumb.tsx @@ -65,8 +65,7 @@ export default function Breadcrumb() { ? : } - {(index < notePaths.length - 1 || note?.hasChildren()) && - } + )) )} @@ -226,7 +225,7 @@ function BreadcrumbSeparatorDropdownContent({ notePath, noteContext, activeNoteP ; })} - + {childNotes.length > 0 && } note_create.createNote(notePath, { activate: true })} From 494b99d0738022163b81be1d2bd7099ab46b376e Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 19:23:32 +0200 Subject: [PATCH 03/23] chore(layout): integrate edited notes into note title actions --- .../client/src/widgets/layout/InlineTitle.css | 20 --------- .../client/src/widgets/layout/InlineTitle.tsx | 45 +------------------ .../src/widgets/layout/NoteTitleActions.css | 20 +++++++++ .../src/widgets/layout/NoteTitleActions.tsx | 45 ++++++++++++++++++- 4 files changed, 65 insertions(+), 65 deletions(-) diff --git a/apps/client/src/widgets/layout/InlineTitle.css b/apps/client/src/widgets/layout/InlineTitle.css index 613c845e3..94a601c53 100644 --- a/apps/client/src/widgets/layout/InlineTitle.css +++ b/apps/client/src/widgets/layout/InlineTitle.css @@ -134,23 +134,3 @@ body.prefers-centered-content .inline-title { } } -.edited-notes { - padding: 1.5em 0; - - .collapsible-inner-body { - display: flex; - flex-wrap: wrap; - gap: 0.3em; - - .badge { - margin: 0; - a.tn-link { - color: inherit; - text-transform: none; - text-decoration: none; - display: inline-block; - } - } - } - -} diff --git a/apps/client/src/widgets/layout/InlineTitle.tsx b/apps/client/src/widgets/layout/InlineTitle.tsx index bb043cca2..95cb2d379 100644 --- a/apps/client/src/widgets/layout/InlineTitle.tsx +++ b/apps/client/src/widgets/layout/InlineTitle.tsx @@ -16,13 +16,10 @@ import server from "../../services/server"; import { formatDateTime } from "../../utils/formatters"; import NoteIcon from "../note_icon"; import NoteTitleWidget from "../note_title"; -import SimpleBadge, { Badge, BadgeWithDropdown } from "../react/Badge"; -import Collapsible from "../react/Collapsible"; +import { Badge, BadgeWithDropdown } from "../react/Badge"; import { FormDropdownDivider, FormListItem } from "../react/FormList"; -import { useNoteBlob, useNoteContext, useNoteLabel, useNoteProperty, useStaticTooltip, useTriliumEvent, useTriliumOptionBool } from "../react/hooks"; -import NoteLink from "../react/NoteLink"; +import { useNoteBlob, useNoteContext, useNoteProperty, useStaticTooltip, useTriliumEvent } from "../react/hooks"; import { joinElements } from "../react/react_utils"; -import { useEditedNotes } from "../ribbon/EditedNotesTab"; import { useNoteMetadata } from "../ribbon/NoteInfoTab"; import { onWheelHorizontalScroll } from "../widget_utils"; @@ -77,7 +74,6 @@ export default function InlineTitle() { - ); @@ -307,40 +303,3 @@ function useBuiltinTemplates() { return templates; } //#endregion - -//#region Edited Notes -function EditedNotes() { - const { note } = useNoteContext(); - const [ dateNote ] = useNoteLabel(note, "dateNote"); - const [ editedNotesOpenInRibbon ] = useTriliumOptionBool("editedNotesOpenInRibbon"); - - return (note && dateNote && - - - - ); -} - -function EditedNotesContent({ note }: { note: FNote }) { - const editedNotes = useEditedNotes(note); - - return (editedNotes !== undefined && - (editedNotes.length > 0 ? editedNotes?.map(editedNote => ( - - )} - /> - )) : ( -
{t("edited_notes.no_edited_notes_found")}
- ))); -} -//#endregion diff --git a/apps/client/src/widgets/layout/NoteTitleActions.css b/apps/client/src/widgets/layout/NoteTitleActions.css index 7d025ef87..8485e0d5e 100644 --- a/apps/client/src/widgets/layout/NoteTitleActions.css +++ b/apps/client/src/widgets/layout/NoteTitleActions.css @@ -8,4 +8,24 @@ body.experimental-feature-new-layout { padding: 0.75em 15px; } } + + .edited-notes { + padding: 1.5em 0; + + .collapsible-inner-body { + display: flex; + flex-wrap: wrap; + gap: 0.3em; + + .badge { + margin: 0; + a.tn-link { + color: inherit; + text-transform: none; + text-decoration: none; + display: inline-block; + } + } + } + } } diff --git a/apps/client/src/widgets/layout/NoteTitleActions.tsx b/apps/client/src/widgets/layout/NoteTitleActions.tsx index 2e13c5f10..eabfada7e 100644 --- a/apps/client/src/widgets/layout/NoteTitleActions.tsx +++ b/apps/client/src/widgets/layout/NoteTitleActions.tsx @@ -1,7 +1,7 @@ import "./NoteTitleActions.css"; import clsx from "clsx"; -import { useEffect, useRef, useState } from "preact/hooks"; +import { useEffect, useState } from "preact/hooks"; import NoteContext from "../../components/note_context"; import FNote from "../../entities/fnote"; @@ -9,8 +9,11 @@ import { t } from "../../services/i18n"; import CollectionProperties from "../note_bars/CollectionProperties"; import { checkFullHeight, getExtendedWidgetType } from "../NoteDetail"; import { PromotedAttributesContent, usePromotedAttributeData } from "../PromotedAttributes"; +import SimpleBadge from "../react/Badge"; import Collapsible, { ExternallyControlledCollapsible } from "../react/Collapsible"; -import { useNoteContext, useNoteProperty, useTriliumEvent } from "../react/hooks"; +import { useNoteContext, useNoteLabel, useNoteProperty, useTriliumEvent, useTriliumOptionBool } from "../react/hooks"; +import NoteLink from "../react/NoteLink"; +import { useEditedNotes } from "../ribbon/EditedNotesTab"; import SearchDefinitionTab from "../ribbon/SearchDefinitionTab"; export default function NoteTitleActions() { @@ -27,6 +30,7 @@ export default function NoteTitleActions() { return (
0 && "visible")}> {items} +
); } @@ -71,3 +75,40 @@ function PromotedAttributes({ note, componentId, noteContext }: { )); } + +//#region Edited Notes +function EditedNotes() { + const { note } = useNoteContext(); + const [ dateNote ] = useNoteLabel(note, "dateNote"); + const [ editedNotesOpenInRibbon ] = useTriliumOptionBool("editedNotesOpenInRibbon"); + + return (note && dateNote && + + + + ); +} + +function EditedNotesContent({ note }: { note: FNote }) { + const editedNotes = useEditedNotes(note); + + return (editedNotes !== undefined && + (editedNotes.length > 0 ? editedNotes?.map(editedNote => ( + + )} + /> + )) : ( +
{t("edited_notes.no_edited_notes_found")}
+ ))); +} +//#endregion From 2ac3d3aaed07b0850cf72224195a4305b6733d07 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 19:49:21 +0200 Subject: [PATCH 04/23] style(layout): adjust paddings slightly --- .../src/widgets/layout/NoteTitleActions.css | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/apps/client/src/widgets/layout/NoteTitleActions.css b/apps/client/src/widgets/layout/NoteTitleActions.css index 8485e0d5e..1ec632e86 100644 --- a/apps/client/src/widgets/layout/NoteTitleActions.css +++ b/apps/client/src/widgets/layout/NoteTitleActions.css @@ -4,26 +4,28 @@ body.experimental-feature-new-layout { } .title-actions { - &.visible:not(:empty) { + display: flex; + flex-direction: column; + gap: 1em; + + &:not(:empty) { padding: 0.75em 15px; } - } - .edited-notes { - padding: 1.5em 0; + .edited-notes { + .collapsible-inner-body { + display: flex; + flex-wrap: wrap; + gap: 0.3em; - .collapsible-inner-body { - display: flex; - flex-wrap: wrap; - gap: 0.3em; - - .badge { - margin: 0; - a.tn-link { - color: inherit; - text-transform: none; - text-decoration: none; - display: inline-block; + .badge { + margin: 0; + a.tn-link { + color: inherit; + text-transform: none; + text-decoration: none; + display: inline-block; + } } } } From a4f34ce6c5e1bc7197d74e39d10c08ab52fcf637 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 19:55:50 +0200 Subject: [PATCH 05/23] refactor(client): remove items array --- .../client/src/widgets/layout/NoteTitleActions.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/client/src/widgets/layout/NoteTitleActions.tsx b/apps/client/src/widgets/layout/NoteTitleActions.tsx index eabfada7e..52f70a991 100644 --- a/apps/client/src/widgets/layout/NoteTitleActions.tsx +++ b/apps/client/src/widgets/layout/NoteTitleActions.tsx @@ -21,21 +21,17 @@ export default function NoteTitleActions() { const isHiddenNote = note && note.noteId !== "_search" && note.noteId.startsWith("_"); const noteType = useNoteProperty(note, "type"); - const items = [ - note && , - note && noteType === "search" && , - note && !isHiddenNote && noteType === "book" && - ].filter(Boolean); - return ( -
0 && "visible")}> - {items} +
+ + {noteType === "search" && } + {!isHiddenNote && note && noteType === "book" && }
); } -function SearchProperties({ note, ntxId }: { note: FNote, ntxId: string | null | undefined }) { +function SearchProperties({ note, ntxId }: { note: FNote | null | undefined, ntxId: string | null | undefined }) { return (note && Date: Tue, 23 Dec 2025 20:03:27 +0200 Subject: [PATCH 06/23] chore(layout): relocate note type switcher right above content --- .../client/src/widgets/layout/InlineTitle.css | 35 ---- .../client/src/widgets/layout/InlineTitle.tsx | 179 +---------------- .../src/widgets/layout/NoteTitleActions.tsx | 2 + .../src/widgets/layout/NoteTypeSwitcher.css | 33 ++++ .../src/widgets/layout/NoteTypeSwitcher.tsx | 182 ++++++++++++++++++ 5 files changed, 219 insertions(+), 212 deletions(-) create mode 100644 apps/client/src/widgets/layout/NoteTypeSwitcher.css create mode 100644 apps/client/src/widgets/layout/NoteTypeSwitcher.tsx diff --git a/apps/client/src/widgets/layout/InlineTitle.css b/apps/client/src/widgets/layout/InlineTitle.css index 94a601c53..c6f8e899e 100644 --- a/apps/client/src/widgets/layout/InlineTitle.css +++ b/apps/client/src/widgets/layout/InlineTitle.css @@ -99,38 +99,3 @@ body.prefers-centered-content .inline-title { font-weight: 500; } } - -@keyframes note-type-switcher-intro { - from { - opacity: 0; - } to { - opacity: 1; - } -} - -.note-type-switcher { - --badge-radius: 12px; - - position: relative; - top: 5px; - padding: .25em 0; - display: flex; - align-items: center; - overflow-x: auto; - min-width: 0; - gap: 5px; - min-height: 35px; - - >* { - flex-shrink: 0; - animation: note-type-switcher-intro 200ms ease-in; - } - - .ext-badge { - --color: var(--input-background-color); - color: var(--main-text-color); - font-size: 0.9rem; - flex-shrink: 0; - } -} - diff --git a/apps/client/src/widgets/layout/InlineTitle.tsx b/apps/client/src/widgets/layout/InlineTitle.tsx index 95cb2d379..42df13bc3 100644 --- a/apps/client/src/widgets/layout/InlineTitle.tsx +++ b/apps/client/src/widgets/layout/InlineTitle.tsx @@ -3,25 +3,16 @@ import "./InlineTitle.css"; import { NoteType } from "@triliumnext/commons"; import clsx from "clsx"; import { ComponentChild } from "preact"; -import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks"; +import { useLayoutEffect, useRef, useState } from "preact/hooks"; import { Trans } from "react-i18next"; -import FNote from "../../entities/fnote"; -import attributes from "../../services/attributes"; -import froca from "../../services/froca"; -import { t } from "../../services/i18n"; import { ViewScope } from "../../services/link"; -import { NOTE_TYPES, NoteTypeMapping } from "../../services/note_types"; -import server from "../../services/server"; import { formatDateTime } from "../../utils/formatters"; import NoteIcon from "../note_icon"; import NoteTitleWidget from "../note_title"; -import { Badge, BadgeWithDropdown } from "../react/Badge"; -import { FormDropdownDivider, FormListItem } from "../react/FormList"; -import { useNoteBlob, useNoteContext, useNoteProperty, useStaticTooltip, useTriliumEvent } from "../react/hooks"; +import { useNoteContext, useNoteProperty, useStaticTooltip } from "../react/hooks"; import { joinElements } from "../react/react_utils"; import { useNoteMetadata } from "../ribbon/NoteInfoTab"; -import { onWheelHorizontalScroll } from "../widget_utils"; const supportedNoteTypes = new Set([ "text", "code" @@ -73,8 +64,6 @@ export default function InlineTitle() {
- - ); } @@ -138,168 +127,4 @@ function TextWithValue({ i18nKey, value, valueTooltip }: { } //#endregion -//#region Note type switcher -const SWITCHER_PINNED_NOTE_TYPES = new Set([ "text", "code", "book", "canvas" ]); -function NoteTypeSwitcher() { - const { note } = useNoteContext(); - const blob = useNoteBlob(note); - const currentNoteType = useNoteProperty(note, "type"); - const { pinnedNoteTypes, restNoteTypes } = useMemo(() => { - const pinnedNoteTypes: NoteTypeMapping[] = []; - const restNoteTypes: NoteTypeMapping[] = []; - for (const noteType of NOTE_TYPES) { - if (noteType.reserved || noteType.static || noteType.type === "book") continue; - if (SWITCHER_PINNED_NOTE_TYPES.has(noteType.type)) { - pinnedNoteTypes.push(noteType); - } else { - restNoteTypes.push(noteType); - } - } - return { pinnedNoteTypes, restNoteTypes }; - }, []); - const currentNoteTypeData = useMemo(() => NOTE_TYPES.find(t => t.type === currentNoteType), [ currentNoteType ]); - const { builtinTemplates, collectionTemplates } = useBuiltinTemplates(); - - return (currentNoteType && supportedNoteTypes.has(currentNoteType) && -
- {note && blob?.contentLength === 0 && ( - <> -
{t("note_title.note_type_switcher_label", { type: currentNoteTypeData?.title.toLocaleLowerCase() })}
- {pinnedNoteTypes.map(noteType => noteType.type !== currentNoteType && ( - switchNoteType(note.noteId, noteType)} - /> - ))} - {collectionTemplates.length > 0 && } - {builtinTemplates.length > 0 && } - {restNoteTypes.length > 0 && } - - )} -
- ); -} - -function MoreNoteTypes({ noteId, restNoteTypes }: { noteId: string, restNoteTypes: NoteTypeMapping[] }) { - return ( - - {restNoteTypes.map(noteType => ( - switchNoteType(noteId, noteType)} - >{noteType.title} - ))} - - ); -} - -function CollectionNoteTypes({ noteId, collectionTemplates }: { noteId: string, collectionTemplates: FNote[] }) { - return ( - - {collectionTemplates.map(collectionTemplate => ( - setTemplate(noteId, collectionTemplate.noteId)} - >{collectionTemplate.title} - ))} - - ); -} - -function TemplateNoteTypes({ noteId, builtinTemplates }: { noteId: string, builtinTemplates: FNote[] }) { - const [ userTemplates, setUserTemplates ] = useState([]); - - async function refreshTemplates() { - const templateNoteIds = await server.get("search-templates"); - const templateNotes = await froca.getNotes(templateNoteIds); - setUserTemplates(templateNotes); - } - - // First load. - useEffect(() => { - refreshTemplates(); - }, []); - - // React to external changes. - useTriliumEvent("entitiesReloaded", ({ loadResults }) => { - if (loadResults.getAttributeRows().some(attr => attr.type === "label" && attr.name === "template")) { - refreshTemplates(); - } - }); - - return ( - - {userTemplates.map(template => )} - {userTemplates.length > 0 && } - {builtinTemplates.map(template => )} - - ); -} - -function TemplateItem({ noteId, template }: { noteId: string, template: FNote }) { - return ( - setTemplate(noteId, template.noteId)} - >{template.title} - ); -} - -function switchNoteType(noteId: string, { type, mime }: NoteTypeMapping) { - return server.put(`notes/${noteId}/type`, { type, mime }); -} - -function setTemplate(noteId: string, templateId: string) { - return attributes.setRelation(noteId, "template", templateId); -} - -function useBuiltinTemplates() { - const [ templates, setTemplates ] = useState<{ - builtinTemplates: FNote[]; - collectionTemplates: FNote[]; - }>({ - builtinTemplates: [], - collectionTemplates: [] - }); - - async function loadBuiltinTemplates() { - const templatesRoot = await froca.getNote("_templates"); - if (!templatesRoot) return; - const childNotes = await templatesRoot.getChildNotes(); - const builtinTemplates: FNote[] = []; - const collectionTemplates: FNote[] = []; - for (const childNote of childNotes) { - if (!childNote.hasLabel("template")) continue; - if (childNote.hasLabel("collection")) { - collectionTemplates.push(childNote); - } else { - builtinTemplates.push(childNote); - } - } - setTemplates({ builtinTemplates, collectionTemplates }); - } - - useEffect(() => { - loadBuiltinTemplates(); - }, []); - - return templates; -} -//#endregion diff --git a/apps/client/src/widgets/layout/NoteTitleActions.tsx b/apps/client/src/widgets/layout/NoteTitleActions.tsx index 52f70a991..26ec8e553 100644 --- a/apps/client/src/widgets/layout/NoteTitleActions.tsx +++ b/apps/client/src/widgets/layout/NoteTitleActions.tsx @@ -15,6 +15,7 @@ import { useNoteContext, useNoteLabel, useNoteProperty, useTriliumEvent, useTril import NoteLink from "../react/NoteLink"; import { useEditedNotes } from "../ribbon/EditedNotesTab"; import SearchDefinitionTab from "../ribbon/SearchDefinitionTab"; +import NoteTypeSwitcher from "./NoteTypeSwitcher"; export default function NoteTitleActions() { const { note, ntxId, componentId, noteContext } = useNoteContext(); @@ -27,6 +28,7 @@ export default function NoteTitleActions() { {noteType === "search" && } {!isHiddenNote && note && noteType === "book" && } + ); } diff --git a/apps/client/src/widgets/layout/NoteTypeSwitcher.css b/apps/client/src/widgets/layout/NoteTypeSwitcher.css new file mode 100644 index 000000000..574924d03 --- /dev/null +++ b/apps/client/src/widgets/layout/NoteTypeSwitcher.css @@ -0,0 +1,33 @@ +@keyframes note-type-switcher-intro { + from { + opacity: 0; + } to { + opacity: 1; + } +} + +.note-type-switcher { + --badge-radius: 12px; + + position: relative; + top: 5px; + padding: .25em 0; + display: flex; + align-items: center; + overflow-x: auto; + min-width: 0; + gap: 5px; + min-height: 35px; + + >* { + flex-shrink: 0; + animation: note-type-switcher-intro 200ms ease-in; + } + + .ext-badge { + --color: var(--input-background-color); + color: var(--main-text-color); + font-size: 0.9rem; + flex-shrink: 0; + } +} diff --git a/apps/client/src/widgets/layout/NoteTypeSwitcher.tsx b/apps/client/src/widgets/layout/NoteTypeSwitcher.tsx new file mode 100644 index 000000000..60e597bb0 --- /dev/null +++ b/apps/client/src/widgets/layout/NoteTypeSwitcher.tsx @@ -0,0 +1,182 @@ +import "./NoteTypeSwitcher.css"; + +import { NoteType } from "@triliumnext/commons"; +import { useEffect, useMemo, useState } from "preact/hooks"; + +import FNote from "../../entities/fnote"; +import attributes from "../../services/attributes"; +import froca from "../../services/froca"; +import { t } from "../../services/i18n"; +import { NOTE_TYPES, NoteTypeMapping } from "../../services/note_types"; +import server from "../../services/server"; +import { Badge, BadgeWithDropdown } from "../react/Badge"; +import { FormDropdownDivider, FormListItem } from "../react/FormList"; +import { useNoteBlob, useNoteContext, useNoteProperty, useTriliumEvent } from "../react/hooks"; +import { onWheelHorizontalScroll } from "../widget_utils"; + +const SWITCHER_PINNED_NOTE_TYPES = new Set([ "text", "code", "book", "canvas" ]); +const supportedNoteTypes = new Set([ + "text", "code" +]); + +export default function NoteTypeSwitcher() { + const { note } = useNoteContext(); + const blob = useNoteBlob(note); + const currentNoteType = useNoteProperty(note, "type"); + const { pinnedNoteTypes, restNoteTypes } = useMemo(() => { + const pinnedNoteTypes: NoteTypeMapping[] = []; + const restNoteTypes: NoteTypeMapping[] = []; + for (const noteType of NOTE_TYPES) { + if (noteType.reserved || noteType.static || noteType.type === "book") continue; + if (SWITCHER_PINNED_NOTE_TYPES.has(noteType.type)) { + pinnedNoteTypes.push(noteType); + } else { + restNoteTypes.push(noteType); + } + } + return { pinnedNoteTypes, restNoteTypes }; + }, []); + const currentNoteTypeData = useMemo(() => NOTE_TYPES.find(t => t.type === currentNoteType), [ currentNoteType ]); + const { builtinTemplates, collectionTemplates } = useBuiltinTemplates(); + + return (currentNoteType && supportedNoteTypes.has(currentNoteType) && +
+ {note && blob?.contentLength === 0 && ( + <> +
{t("note_title.note_type_switcher_label", { type: currentNoteTypeData?.title.toLocaleLowerCase() })}
+ {pinnedNoteTypes.map(noteType => noteType.type !== currentNoteType && ( + switchNoteType(note.noteId, noteType)} + /> + ))} + {collectionTemplates.length > 0 && } + {builtinTemplates.length > 0 && } + {restNoteTypes.length > 0 && } + + )} +
+ ); +} + +function MoreNoteTypes({ noteId, restNoteTypes }: { noteId: string, restNoteTypes: NoteTypeMapping[] }) { + return ( + + {restNoteTypes.map(noteType => ( + switchNoteType(noteId, noteType)} + >{noteType.title} + ))} + + ); +} + +function CollectionNoteTypes({ noteId, collectionTemplates }: { noteId: string, collectionTemplates: FNote[] }) { + return ( + + {collectionTemplates.map(collectionTemplate => ( + setTemplate(noteId, collectionTemplate.noteId)} + >{collectionTemplate.title} + ))} + + ); +} + +function TemplateNoteTypes({ noteId, builtinTemplates }: { noteId: string, builtinTemplates: FNote[] }) { + const [ userTemplates, setUserTemplates ] = useState([]); + + async function refreshTemplates() { + const templateNoteIds = await server.get("search-templates"); + const templateNotes = await froca.getNotes(templateNoteIds); + setUserTemplates(templateNotes); + } + + // First load. + useEffect(() => { + refreshTemplates(); + }, []); + + // React to external changes. + useTriliumEvent("entitiesReloaded", ({ loadResults }) => { + if (loadResults.getAttributeRows().some(attr => attr.type === "label" && attr.name === "template")) { + refreshTemplates(); + } + }); + + return ( + + {userTemplates.map(template => )} + {userTemplates.length > 0 && } + {builtinTemplates.map(template => )} + + ); +} + +function TemplateItem({ noteId, template }: { noteId: string, template: FNote }) { + return ( + setTemplate(noteId, template.noteId)} + >{template.title} + ); +} + +function switchNoteType(noteId: string, { type, mime }: NoteTypeMapping) { + return server.put(`notes/${noteId}/type`, { type, mime }); +} + +function setTemplate(noteId: string, templateId: string) { + return attributes.setRelation(noteId, "template", templateId); +} + +function useBuiltinTemplates() { + const [ templates, setTemplates ] = useState<{ + builtinTemplates: FNote[]; + collectionTemplates: FNote[]; + }>({ + builtinTemplates: [], + collectionTemplates: [] + }); + + async function loadBuiltinTemplates() { + const templatesRoot = await froca.getNote("_templates"); + if (!templatesRoot) return; + const childNotes = await templatesRoot.getChildNotes(); + const builtinTemplates: FNote[] = []; + const collectionTemplates: FNote[] = []; + for (const childNote of childNotes) { + if (!childNote.hasLabel("template")) continue; + if (childNote.hasLabel("collection")) { + collectionTemplates.push(childNote); + } else { + builtinTemplates.push(childNote); + } + } + setTemplates({ builtinTemplates, collectionTemplates }); + } + + useEffect(() => { + loadBuiltinTemplates(); + }, []); + + return templates; +} From cf039916d30a505749f41de4bb662305009c4964 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 20:08:21 +0200 Subject: [PATCH 07/23] chore(note_title_actions): rephrase edited notes --- apps/client/src/translations/en/translation.json | 3 ++- apps/client/src/widgets/layout/NoteTitleActions.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 75e116753..7f34841f8 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1779,7 +1779,8 @@ "note_type_switcher_others": "Other note type", "note_type_switcher_templates": "Template", "note_type_switcher_collection": "Collection", - "edited_notes": "Edited notes" + "edited_notes": "Notes edited on this day", + "promoted_attributes": "Promoted attributes" }, "search_result": { "no_notes_found": "No notes have been found for given search parameters.", diff --git a/apps/client/src/widgets/layout/NoteTitleActions.tsx b/apps/client/src/widgets/layout/NoteTitleActions.tsx index 26ec8e553..db2e1b44e 100644 --- a/apps/client/src/widgets/layout/NoteTitleActions.tsx +++ b/apps/client/src/widgets/layout/NoteTitleActions.tsx @@ -66,7 +66,7 @@ function PromotedAttributes({ note, componentId, noteContext }: { return (note && ( From 1ac7db41d3225c17f4d0891de5905e77aa79c0f2 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 20:33:46 +0200 Subject: [PATCH 08/23] fix(note_title_actions): edited notes link looking strange --- .../src/widgets/layout/NoteTitleActions.css | 14 +++++---- .../src/widgets/layout/NoteTitleActions.tsx | 14 ++++----- apps/client/src/widgets/react/NoteLink.tsx | 29 ++++++++----------- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/apps/client/src/widgets/layout/NoteTitleActions.css b/apps/client/src/widgets/layout/NoteTitleActions.css index 1ec632e86..08426c19a 100644 --- a/apps/client/src/widgets/layout/NoteTitleActions.css +++ b/apps/client/src/widgets/layout/NoteTitleActions.css @@ -20,11 +20,15 @@ body.experimental-feature-new-layout { .badge { margin: 0; - a.tn-link { - color: inherit; - text-transform: none; - text-decoration: none; - display: inline-block; + color: inherit; + text-transform: none; + text-decoration: none; + display: inline-block; + transition: background-color 250ms ease-in, color 250ms ease-in; + + &:hover { + background-color: var(--link-hover-background); + color: var(--link-hover-color); } } } diff --git a/apps/client/src/widgets/layout/NoteTitleActions.tsx b/apps/client/src/widgets/layout/NoteTitleActions.tsx index db2e1b44e..dd1ed2342 100644 --- a/apps/client/src/widgets/layout/NoteTitleActions.tsx +++ b/apps/client/src/widgets/layout/NoteTitleActions.tsx @@ -12,7 +12,7 @@ import { PromotedAttributesContent, usePromotedAttributeData } from "../Promoted import SimpleBadge from "../react/Badge"; import Collapsible, { ExternallyControlledCollapsible } from "../react/Collapsible"; import { useNoteContext, useNoteLabel, useNoteProperty, useTriliumEvent, useTriliumOptionBool } from "../react/hooks"; -import NoteLink from "../react/NoteLink"; +import NoteLink, { NewNoteLink } from "../react/NoteLink"; import { useEditedNotes } from "../ribbon/EditedNotesTab"; import SearchDefinitionTab from "../ribbon/SearchDefinitionTab"; import NoteTypeSwitcher from "./NoteTypeSwitcher"; @@ -96,14 +96,10 @@ function EditedNotesContent({ note }: { note: FNote }) { return (editedNotes !== undefined && (editedNotes.length > 0 ? editedNotes?.map(editedNote => ( - - )} + )) : (
{t("edited_notes.no_edited_notes_found")}
diff --git a/apps/client/src/widgets/react/NoteLink.tsx b/apps/client/src/widgets/react/NoteLink.tsx index a415c8b86..7a86be7ef 100644 --- a/apps/client/src/widgets/react/NoteLink.tsx +++ b/apps/client/src/widgets/react/NoteLink.tsx @@ -105,22 +105,17 @@ export function NewNoteLink({ notePath, viewScope, noContextMenu, showNoteIcon, const [ archived ] = useNoteLabelBoolean(note, "archived"); return ( - - - {icon && } - - - {title} - - - + + {icon && <> } + {title} + ); } From 00592025c08b68075a97d32c5dcd7ec2aa6c7bfc Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 20:40:56 +0200 Subject: [PATCH 09/23] fix(breadcrumb): overflow hides more items than threshold --- apps/client/src/widgets/layout/Breadcrumb.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/layout/Breadcrumb.tsx b/apps/client/src/widgets/layout/Breadcrumb.tsx index 043a68206..b4030faae 100644 --- a/apps/client/src/widgets/layout/Breadcrumb.tsx +++ b/apps/client/src/widgets/layout/Breadcrumb.tsx @@ -32,7 +32,7 @@ import { ParentComponent } from "../react/react_utils"; const COLLAPSE_THRESHOLD = 5; const INITIAL_ITEMS = 2; -const FINAL_ITEMS = 2; +const FINAL_ITEMS = 3; export default function Breadcrumb() { const { note, notePath, notePaths, noteContext } = useNotePaths(); From 1eebc8ff77530f89395c881186e7dbde8d68eab0 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 20:44:12 +0200 Subject: [PATCH 10/23] fix(note_badges): avoid "shared locally" on server build --- apps/client/src/widgets/shared_info.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/shared_info.tsx b/apps/client/src/widgets/shared_info.tsx index 54c6da785..bbcdbe982 100644 --- a/apps/client/src/widgets/shared_info.tsx +++ b/apps/client/src/widgets/shared_info.tsx @@ -5,6 +5,7 @@ import { useEffect, useState } from "preact/hooks"; import FNote from "../entities/fnote"; import attributes from "../services/attributes"; import { t } from "../services/i18n"; +import { isElectron } from "../services/utils"; import HelpButton from "./react/HelpButton"; import { useNoteContext, useTriliumEvent, useTriliumOption } from "./react/hooks"; import InfoBar from "./react/InfoBar"; @@ -68,7 +69,11 @@ export function useShareInfo(note: FNote | null | undefined) { } }); - return { link, linkHref, isSharedExternally: !!syncServerHost }; + return { + link, + linkHref, + isSharedExternally: !isElectron() || !!syncServerHost // on server we can't reliably detect if the note is shared locally or available publicly. + }; } function getShareId(note: FNote) { From a6c7610fcce2cf6952dc1fcd62caf81208172c23 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 21:06:47 +0200 Subject: [PATCH 11/23] fix(dropdown): clicking in the outer area of a menu dismisses it --- apps/client/src/widgets/react/Dropdown.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/client/src/widgets/react/Dropdown.tsx b/apps/client/src/widgets/react/Dropdown.tsx index dec0660c0..d6aab922b 100644 --- a/apps/client/src/widgets/react/Dropdown.tsx +++ b/apps/client/src/widgets/react/Dropdown.tsx @@ -2,10 +2,11 @@ import { Dropdown as BootstrapDropdown, Tooltip } from "bootstrap"; import { ComponentChildren, HTMLAttributes } from "preact"; import { CSSProperties, HTMLProps } from "preact/compat"; import { MutableRef, useCallback, useEffect, useRef, useState } from "preact/hooks"; + import { useTooltip, useUniqueName } from "./hooks"; type DataAttributes = { - [key: `data-${string}`]: string | number | boolean | undefined; + [key: `data-${string}`]: string | number | boolean | undefined; }; export interface DropdownProps extends Pick, "id" | "className"> { @@ -66,14 +67,14 @@ export default function Dropdown({ id, className, buttonClassName, isStatic, chi return () => { resizeObserver.disconnect(); dropdown.dispose(); - } + }; }, []); const onShown = useCallback(() => { setShown(true); externalOnShown?.(); hideTooltip(); - }, [ hideTooltip ]) + }, [ hideTooltip ]); const onHidden = useCallback(() => { setShown(false); @@ -122,7 +123,7 @@ export default function Dropdown({ id, className, buttonClassName, isStatic, chi {...buttonProps} > {text} - +
    { + // Prevent clicks directly inside the dropdown from closing. + if (e.target === dropdownContainerRef.current) { + e.stopPropagation(); + } + }} > {shown && children}
- ) + ); } From ae83126903ebb32335839421259f72b8102a1ed2 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 21:09:03 +0200 Subject: [PATCH 12/23] chore(tab_navigation): enable on server as well --- apps/client/src/widgets/TabHistoryNavigationButtons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/client/src/widgets/TabHistoryNavigationButtons.tsx b/apps/client/src/widgets/TabHistoryNavigationButtons.tsx index 07ecf6b66..42b461df0 100644 --- a/apps/client/src/widgets/TabHistoryNavigationButtons.tsx +++ b/apps/client/src/widgets/TabHistoryNavigationButtons.tsx @@ -15,7 +15,7 @@ export default function TabHistoryNavigationButtons() { const legacyBackVisible = useLauncherVisibility("_lbBackInHistory"); const legacyForwardVisible = useLauncherVisibility("_lbForwardInHistory"); - return (isElectron() && + return (
{!legacyBackVisible && Date: Tue, 23 Dec 2025 21:16:24 +0200 Subject: [PATCH 13/23] docs(user): mention history navigation buttons in the tab bar --- .../Navigation/Note Navigation.html | 32 +- .../Navigation/Note Navigation_image.png | Bin 36465 -> 0 bytes .../UI Elements/Tabs.html | 37 +- .../User Guide/Note Types/Render Note.html | 43 +-- .../Common concepts/Script bundles.html | 26 +- .../Frontend Basics/Custom Widgets.html | 250 ++++++------- .../Frontend Basics/Custom Widgets/CSS.html | 22 +- .../Note context aware widget.html | 13 +- .../Custom Widgets/Right pane widget.html | 24 +- .../Custom Widgets/Widget Basics.html | 26 +- .../Launch Bar Widgets/Note Title Widget.html | 2 +- .../Scripting/Frontend Basics/Preact.html | 44 +-- .../Preact/Built-in components.html | 24 +- .../Scripting/Frontend Basics/Preact/CSS.html | 13 +- .../Preact/Component libraries.html | 29 +- .../Frontend Basics/Preact/Hooks.html | 32 +- .../Developer Guide/Documentation.md | 2 +- docs/User Guide/!!!meta.json | 351 +++++++++--------- .../Navigation/Note Navigation.md | 11 +- .../Navigation/Note Navigation_image.png | Bin 36465 -> 0 bytes .../UI Elements/Tabs.md | 2 + .../User Guide/Note Types/Render Note.md | 2 +- .../Frontend Basics/Custom Widgets.md | 26 +- .../Launch Bar Widgets/Note Title Widget.md | 2 +- .../Scripting/Frontend Basics/Preact.md | 8 +- .../Preact/Built-in components.md | 4 +- .../Scripting/Frontend Basics/Preact/CSS.md | 2 +- 27 files changed, 513 insertions(+), 514 deletions(-) delete mode 100644 apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation_image.png delete mode 100644 docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation_image.png diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation.html index 7b999c53b..73b4c3ef3 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation.html @@ -1,16 +1,26 @@

One of the Trilium's goals is to provide fast and comfortable navigation between notes.

Backwards and forward

-

You can use alt-left and alt-right to move back and forward in history - of viewed pages.

-

This works identically to browser backwards / forwards, it's actually - using built-in browser support for this.

-

- -

+

To move back and forward in history of viewed pages:

+
    +
  • The Launch Bar can + be configured to display navigation buttons (see Go to Next Note and Go to Previous Note in Available Launchers).
  • +
  • On the left side of the tab bar, + there are two dedicated buttons. +
      +
    • These buttons will appear only if the launch bar is not already displaying + the history navigation buttons to avoid duplication.
    • +
    +
  • +
  • Alternatively, use the Alt + Left and Alt + Right keyboard + shortcuts.
  • +

Jump to note

-

This is useful to quickly find and view arbitrary notes - click on Jump to button - on the top or press Ctrl + J . Then type part of the - note name and autocomplete will help you pick the desired note.

-

See Jump to Note for +

This is useful to quickly find and view arbitrary notes - click on + Jump tobutton on the top or press Ctrl + J . + Then type part of the note name and autocomplete will help you pick the + desired note.

+

See Jump to... for more information.

\ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation_image.png b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation_image.png deleted file mode 100644 index 2ea2690121cc87cb0522acd265c8337c59ece913..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36465 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 diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs.html index 8d9b0ad58..e4c690f92 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs.html @@ -6,37 +6,48 @@

Layout

Depending on the Vertical and horizontal layout:

    -
  • For the vertical layout, the tabs will be placed at the top but to the +
  • For the vertical layout, the tabs will be placed at the top but to the right of the Note Tree.
  • -
  • For the horizontal layout, the tabs will be placed at the top in full-width, +
  • For the horizontal layout, the tabs will be placed at the top in full-width, above the note tree, allowing for more tabs to be comfortably displayed.

Interaction

    -
  • To create a new tab, press the +
  • To create a new tab, press the button that is to the right of the last tab.
  • -
  • To close a tab, press the corresponding +
  • To close a tab, press the corresponding button.
  • -
  • For multitasking, tabs can be used alongside For multitasking, tabs can be used alongside Split View. Each tab can have one or more notes, displayed horizontally.
  • -
  • Tabs can be reordered by drag-and-dropping it into a new position.
  • -
  • An existing tab can be displayed in a new window by dragging the tab upwards +
  • Tabs can be reordered by drag-and-dropping it into a new position.
  • +
  • An existing tab can be displayed in a new window by dragging the tab upwards or downwards. It is not possible to combine tabs back into another window.
  • +
  • On the left side of the tab bar there are two buttons to navigate through + the note history backwards or forwards (see Note Navigation). +
      +
    • This feature is only enabled if the Launch Bar doesn't + already contain the history navigation buttons.
    • +
    +

Keyboard interaction

Since tabs are a commonly used feature, there are multiple keyboard shortcuts that can be used:

    -
  • Ctrl+T to open a new tab.
  • -
  • Ctrl+W to close the current tab.
  • -
  • Ctrl+Shift+T to reopen the last closed +
  • Ctrl+T to open a new tab.
  • +
  • Ctrl+W to close the current tab.
  • +
  • Ctrl+Shift+T to reopen the last closed tab.
  • -
  • Ctrl+Tab and Ctrl+Shift+Tab to +
  • Ctrl+Tab and Ctrl+Shift+Tab to go to the next or previous tab.
  • -
  • Ctrl+1, Ctrl+2, up to Ctrl+9 to +
  • Ctrl+1, Ctrl+2, up to Ctrl+9 to activate the first, second and up til ninth tab.
  • -
  • There is also a shortcut to go to the last tab, but it is not assigned +
  • There is also a shortcut to go to the last tab, but it is not assigned a key by default.
\ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html index c65b789d0..f0f720b87 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html @@ -7,13 +7,10 @@ via an attribute.

Creating a render note

    -
  1. Create a Code note - with the HTML language, with what needs to be displayed (for example - <p>Hello world.</p>).
  2. -
  3. Create a Render Note.
  4. -
  5. Assign the renderNote relation to +
  6. Create a Code note + with the HTML language, with what needs to be displayed (for example <p>Hello world.</p>).
  7. +
  8. Create a Render Note.
  9. +
  10. Assign the renderNote relation to point at the previously created code note.

Legacy scripting using jQuery

@@ -29,7 +26,7 @@ The current date & time is <span class="date"></span>
const $dateEl = api.$container.find(".date");
 $dateEl.text(new Date());
-

Now create a render note at any place and set its ~renderNote relation +

Now create a render note at any place and set its ~renderNote relation to point to the HTML note. When the render note is accessed it will display:

Current date & time @@ -42,33 +39,37 @@ $dateEl.text(new Date()); need to provide a HTML anymore.

Here are the steps to creating a simple render note:

    -
  1. Create a note of type Render Note.
  2. -
  3. -

    Create a child Code note +

  4. +

    Create a note of type Render Note.

    +
  5. +
  6. +

    Create a child Code note with JSX as the language. -
    As an example, use the following content:

    export default function() {
    +      
    As an example, use the following content:

    export default function() {
         return (
             <>
                 <p>Hello world.</p>
             </>
         );
     }
    -
  7. -
  8. In the parent render note, define a ~renderNote relation - pointing to the newly created child.
  9. -
  10. Refresh the render note and it should display a “Hello world” message.
  11. + +
  12. +

    In the parent render note, define a ~renderNote relation pointing + to the newly created child.

    +
  13. +
  14. +

    Refresh the render note and it should display a “Hello world” message.

    +

Refreshing the note

It's possible to refresh the note via:

Examples

\ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Common concepts/Script bundles.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Common concepts/Script bundles.html index 2abeaa288..f9d7fab75 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Common concepts/Script bundles.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Common concepts/Script bundles.html @@ -1,22 +1,22 @@ -

For both Render Note and +

For both Render Note and more complicated scripts, it's generally useful to split the code into - multiple Code notes.

+ multiple Code notes.

When a script is run, the sub-children of the script being run (or the  Render Note) are checked for children. If the children are Code notes + class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note) are checked for children. If the children are Code notes of the corresponding type (front-end or backend) as the code being run, they will be evaluated as well.

The collection of a script and its child notes is called a bundle. A child note inside a bundle is called a module.

As a basic example of dependencies, consider the following note structure:

    -
  • +
  • Script with dependency -

    api.log(MyMath.sum(2, 2));
    +

    api.log(MyMath.sum(2, 2));
      -
    • +
    • MyMath -

      module.exports = {
      +        

      module.exports = {
           sum(a, b) {
               return a + b;
           }
      @@ -26,21 +26,21 @@
         

    When Script with dependency is run, it will detect MyMath as - a submodule and provide the result of its module.exports object + a submodule and provide the result of its module.exports object into a global object with the same name as the note.

    Alternative syntax

    -

    Instead of providing an object to module.exports, - it's also possible to add fields individually:

    module.exports.sum = (a, b) => a + b;
    +

    Instead of providing an object to module.exports, it's also + possible to add fields individually:

    module.exports.sum = (a, b) => a + b;
     module.exports.subtract = (a, b) => a - b;

    Ignoring a code script from a bundle

    To ignore a script from being included in a bundle (e.g. if it's unrelated - to the parent script note), apply the #disableInclusion label.

    + to the parent script note), apply the #disableInclusion label.

    Sharing a module across multiple bundles

    Modules can be reused across multiple scripts by simply cloning the shared - module between two modules (see Cloning Notes).

    + module between two modules (see Cloning Notes).

    Optionally, a separate note can be used to contain all the different reusable modules for an easy way to discover them.

    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html index 6c1830f8f..df40a3d49 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html @@ -4,164 +4,150 @@

    Preact with JSX vs. vanilla jQuery

    In older versions of Trilium, custom widgets were exclusively written in a combination of jQuery with Trilium's internal widget architecture - (e.g., BasicWidget, NoteContextAwareWidget).

    + (e.g., BasicWidget, NoteContextAwareWidget).

    Starting with v0.101.0, custom widgets can also be written in JSX using - the Preact framework. + the Preact framework. Both legacy and Preact widgets have the same capabilities, with a single difference:

      -
    • Preact widgets are content-sized by default whereas legacy widgets need - this.contentSized()applied in the constructor. For more information, - see the corresponding section in Troubleshooting.
    • +
    • Preact widgets are content-sized by default whereas legacy widgets need this.contentSized() applied + in the constructor. For more information, see the corresponding section + in Troubleshooting.

    Wherever possible, widget examples will be both in the legacy and Preact format.

    Creating a custom widget

      -
    1. Create a Code note.
    2. -
    3. Set the language to: +
    4. Create a Code note.
    5. +
    6. Set the language to:
        -
      1. JavaScript (frontend) for legacy widgets using jQuery.
      2. -
      3. JSX for Preact widgets. You might need to go to Options → Code to enable +
      4. JavaScript (frontend) for legacy widgets using jQuery.
      5. +
      6. JSX for Preact widgets. You might need to go to Options → Code to enable the language first.
      -
    7. -
    8. Apply the #widget label.
    9. + +
    10. Apply the #widget label.

    Getting started with a simple example

    Let's start by creating a widget that shows a message near the content area. Follow the previous section to create a code note, and use the following content.

    -
    - - - - - - - - - - + + +
    LegacyPreact (v0.101.0+)
    class HelloNoteDetail extends api.BasicWidget {
    +
    +  
    +    
    +      
    +      
    +    
    +  
    +  
    +    
    +      
    -        
    +      
    -      
    -    
    -  
    LegacyPreact (v0.101.0+)
    class HelloNoteDetail extends api.BasicWidget {
     
    -    constructor() {
    -        super();
    -        this.contentSized();
    -    }
    -
    -    get parentWidget() { return "center-pane" }
    -
    -    doRender() {
    -        this.$widget = $("<span>Center pane</span>");
    -    }
    -    
    +
    constructor() {
    +    super();
    +    this.contentSized();
     }
     
    -module.exports = new HelloNoteDetail();
    -
    import { defineWidget } from "trilium:preact";
    +get parentWidget() { return "center-pane" }
     
    -export default defineWidget({
    +doRender() {
    +    this.<!--FORMULA_INLINE_1766517018225_0-->("&lt;span&gt;Center pane&lt;/span&gt;");
    +}
    +

    }

    +

    module.exports = new HelloNoteDetail();

    + + +
    import { defineWidget } from "trilium:preact";

    export default defineWidget({ parent: "center-pane", render: () => <span>Center pane from Preact.</span> -});

    -
    - -

    Refresh the application and - the widget should appear underneath the content area.

    +});

    +
    +

    +

    Refresh the application and the widget + should appear underneath the content area.

    Widget location (parent widget)

    A widget can be placed in one of the following sections of the applications:

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    Value for parentWidget - DescriptionSample widgetSpecial requirements
    left-pane - Appears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
    center-pane - In the content area. If a split is open, the widget will span all of the - splits.See example above.None.
    note-detail-pane - -

    In the content area, inside the note detail area. If a split is open, - the widget will be contained inside the split.

    -

    This is ideal if the widget is note-specific.

    -
    Note context aware widget - -
      -
    • The widget must export a class and not an - instance of the class (e.g. no new) because - it needs to be multiplied for each note, so that splits work correctly.
    • -
    • Since the class is exported instead of an - instance, the parentWidget getter must be - static, otherwise the widget is ignored.
    • -
    -
    right-pane - In the Right Sidebar, - as a dedicated section.Right pane widget - -
      -
    • Although not mandatory, it's best to use a RightPanelWidget instead - of a BasicWidget or a NoteContextAwareWidget.
    • -
    -
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Value for parentWidget + DescriptionSample widgetSpecial requirements
    left-pane + Appears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
    center-pane + In the content area. If a split is open, the widget will span all of the + splits.See example above.None.
    note-detail-pane + +

    In the content area, inside the note detail area. If a split is open, + the widget will be contained inside the split.

    +

    This is ideal if the widget is note-specific.

    +
    Note context aware widget + +
      +
    • The widget must export a class and not an instance of the class + (e.g. no new) because it needs to be multiplied for each note, + so that splits work correctly.
    • +
    • Since the class is exported instead of an instance, the parentWidget getter + must be static, otherwise the widget is ignored.
    • +
    +
    right-pane + In the Right Sidebar, + as a dedicated section.Right pane widget + +
      +
    • Although not mandatory, it's best to use a RightPanelWidget instead + of a BasicWidget or a NoteContextAwareWidget.
    • +
    +
    -
    -

    To position the widget somewhere else, just change the value passed to - get parentWidget()for legacy widgets or the parent field - for Preact. Do note that some positions such as note-detail-pane and - right-panehave special requirements that need to be accounted for - (see the table above).

    +

    To position the widget somewhere else, just change the value passed to get parentWidget() for + legacy widgets or the parent field for Preact. Do note that + some positions such as note-detail-pane and right-pane have + special requirements that need to be accounted for (see the table above).

    Launch bar widgets

    Launch bar widgets are similar to Custom widgets but are specific - to the Launch Bar. - See Launch Bar Widgets for + to the Launch Bar. + See Launch Bar Widgets for more information.

    -

    Custom position

    -

     

    \ No newline at end of file +

    Custom position

    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/CSS.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/CSS.html index 5de2258eb..481a6d62b 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/CSS.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/CSS.html @@ -1,7 +1,5 @@

    Classic widgets

    -

    In doRender():[1] +

    In doRender():[1]

    this.cssBlock(`#my-widget {
     	position: absolute;
         bottom: 40px;
    @@ -9,14 +7,12 @@
         z-index: 1;
     }`);

    Preact widgets

    -

    See the dedicated page: CSS.

    -
      -
    1. ^ - +

      See the dedicated page: CSS.

      +
        +
      1. +

        ^ +

        +

        Reference: https://trilium.rocks/X7pxYpiu0lgU +

      2. -
      \ No newline at end of file +
    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget.html index 7f3ed50a7..d0da3c335 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Note context aware widget.html @@ -2,14 +2,11 @@ react to changes in the current note.

    Important aspects:

      -
    • The widget must export a class and not an - instance of the class (e.g. no new) because - it needs to be multiplied for each note, so that splits work correctly.
    • -
    • Since the class is exported instead of an - instance, the parentWidget getter must be - static, otherwise the widget is ignored.
    • +
    • The widget must export a class and not an instance of the class + (e.g. no new) because it needs to be multiplied for each note, + so that splits work correctly.
    • +
    • Since the class is exported instead of an instance, the parentWidget getter + must be static, otherwise the widget is ignored.

    Example displaying the current note title

    This is a note context-aware widget that simply displays the name the diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Right pane widget.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Right pane widget.html index 94127a539..31bf6d40d 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Right pane widget.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Right pane widget.html @@ -1,16 +1,14 @@

    Key highlights

      -
    • doRender must not be overridden, instead - doRenderBody()has to be overridden. -
        -
      • doRenderBody can optionally be async.
      • -
      +
    • doRender must not be overridden, instead doRenderBody() has + to be overridden. +
        +
      • doRenderBody can optionally be async.
      • +
    • -
    • parentWidget() must be set to “rightPane”.
    • -
    • widgetTitle() getter can optionally be - overriden, otherwise the widget will be displayed as “Untitled widget”.
    • +
    • parentWidget() must be set to “rightPane”.
    • +
    • widgetTitle() getter can optionally be overriden, otherwise + the widget will be displayed as “Untitled widget”.

    Example for new layout

    Conditionally changing visibility

    -

    In refreshWithNote:

    const visible = true;	// replace with your own visibility logic
    +

    In refreshWithNote:

    const visible = true;	// replace with your own visibility logic
     this.toggleInt(visible);
     this.triggerCommand("reEvaluateRightPaneVisibility");

    Altering the position within the sidebar

    By default, the sidebar items are displayed in the order they are found - by the application when searching for #widget notes.

    + by the application when searching for #widget notes.

    It is possible to make a widget appear higher or lower up, by adjusting - its position property:

    class MyWidget extends api.RightPanelWidget {
    +  its position property:

    class MyWidget extends api.RightPanelWidget {
     
     +    get position() { return 20 };
             
    diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Widget Basics.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Widget Basics.html
    index 5d548bb39..57bf28d96 100644
    --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Widget Basics.html	
    +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets/Widget Basics.html	
    @@ -16,17 +16,17 @@
     module.exports = new MyWidget();

    To implement this widget:

      -
    1. Create a new JS Frontend note in Trilium - and paste in the code above.
    2. -
    3. Assign the #widget attribute to +
    4. Create a new JS Frontend note in Trilium and paste in the code + above.
    5. +
    6. Assign the #widget attribute to the note.
    7. -
    8. Restart Trilium or reload the window.
    9. +
    10. Restart Trilium or reload the window.

    To verify that the widget is working, open the developer tools (Ctrl + Shift + I) - and run document.querySelector("#my-widget"). - If the element is found, the widget is functioning correctly. If undefined is + and run document.querySelector("#my-widget"). If the element + is found, the widget is functioning correctly. If undefined is returned, double-check that the note has - the #widget attribute.

    + the #widget attribute.

    Step 2: Adding an UI Element

    Next, let's improve the widget by adding a button to it.

    const template = `<div id="my-widget"><button>Click Me!</button></div>`;
     
    @@ -46,9 +46,7 @@ module.exports = new MyWidget();

    Step 3: Styling the Widget

    To make the button more visually appealing and position it correctly, we'll apply some custom styling. Trilium includes Box Icons, - which we'll use to replace the button text with an icon. For example the - bx bxs-magic-wandicon.

    + which we'll use to replace the button text with an icon. For example the bx bxs-magic-wand icon.

    Here's the updated template:

    const template = `<div id="my-widget"><button class="tree-floating-button bx bxs-magic-wand tree-settings-button"></button></div>`;

    Next, we'll adjust the button's position using CSS:

    class MyWidget extends api.BasicWidget {
         get position() { return 1; }
    @@ -71,8 +69,7 @@ module.exports = new MyWidget();
    of the left pane, alongside other action buttons.

    Step 4: Adding User Interaction

    Let’s make the button interactive by showing a message when it’s clicked. - We'll use the api.showMessage method from - the Script API.

    class MyWidget extends api.BasicWidget {
    +  We'll use the api.showMessage method from the Script API.

    class MyWidget extends api.BasicWidget {
         get position() { return 1; }
         get parentWidget() { return "left-pane"; }
         
    @@ -90,8 +87,9 @@ module.exports = new MyWidget();
    } module.exports = new MyWidget();
    -

    For the list of possible values for parentWidget(), - see Custom Widgets

    +

    For the list of possible values for parentWidget(), see  + Custom Widgets

    Reload the application one last time. When you click the button, a "Hello World!" message should appear, confirming that your widget is fully functional.

    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.html index 5386d0052..70ab3f1ac 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.html @@ -30,7 +30,7 @@ class NoteTitleWidget extends api.NoteContextAwareWidget { } module.exports = new NoteTitleWidget();
    -

    Preact widget (v0.101.0+)

    import { defineLauncherWidget, useActiveNoteContext } from "trilium:preact";
    +

    Preact widget (v0.101.0+)

    import { defineLauncherWidget, useActiveNoteContext } from "trilium:preact";
     
     export default defineLauncherWidget({
         render: () => {
    diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact.html
    index 1f99be5c5..82c823548 100644
    --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact.html	
    +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact.html	
    @@ -2,16 +2,16 @@
       support for JSX.

    Preact can be used for:

    To get started, the first step is to enable JSX in the list of Code languages. Go to Options → Code Notes and check the “JSX” language. Afterwards, proceed - with the documentation in either Render Note or  + with the documentation in either Render Note or  Custom Widgets, which will both have a section on how to use the new + class="reference-link" href="#root/_help_MgibgPcfeuGz">Custom Widgets, which will both have a section on how to use the new Preact integration.

    Import/exports

    When using Preact with JSX, there is a special syntax which provides ES-like - imports. This import syntax makes way for - a more intuitive that doesn't make use of global objects and paves the - way for better auto-completion support that might be introduced in the - future. 

    + imports. This import syntax makes way for a more intuitive that + doesn't make use of global objects and paves the way for better auto-completion + support that might be introduced in the future. 

    API imports

    -

    Instead of:

    api.showMessage("Hello");
    -

    the JSX version looks like this:

    import { showMessage } from "trilium:api";
    +

    Instead of:

    api.showMessage("Hello");
    +

    the JSX version looks like this:

    import { showMessage } from "trilium:api";
     showMessage("hello");

    Preact API imports (hooks, components)

    -

    There's a new Script API dedicated +

    There's a new Script API dedicated to Preact, which provides shared components that are also used by Trilium - internally as well as hooks, for example.

    import { useState } from "trilium:preact";
    +  internally as well as hooks, for example.

    import { useState } from "trilium:preact";
     const [ myState, setMyState ] = useState("Hi");

    Exporting

    JSX notes can export a component for use in Render Note or - for Component libraries: 

    export default function() {
    +  href="#root/_help_HcABDtFCkbFN">Render Note or for Component libraries: 

    export default function() {
         return (
             <>
                 <p>Hello world.</p>
    @@ -44,15 +43,13 @@ const [ myState, setMyState ] = useState("Hi");
    ); }

    Import/export are not required

    -

    These imports are syntactic sugar meant to replace the usage for the - apiglobal object (see Script API). 

    +

    These imports are syntactic sugar meant to replace the usage for the api global + object (see Script API). 

    Under the hood

    Unlike JavaScript, JSX requires pre-processing to turn it into JavaScript @@ -60,5 +57,4 @@ class="admonition note"> a JavaScript library which processes the JSX to pure JavaScript. The processing is done each time a script is run (for widgets this happens at every program startup). If you notice any performance degradation due to long compilation, - consider reporting the issue to - us.

    \ No newline at end of file + consider reporting the issue to us.

    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.html index a4c6faa6f..27aa57582 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.html @@ -4,11 +4,11 @@
    A partial screenshot from the Widget showcase example (see below).

    Trilium comes with its own set of Preact components, some of which are - also available to Custom Widgets and  + also available to Custom Widgets and  Render Note.

    -

    To use these components, simply import them from trilium:preact:

    import { ActionButton, Button, LinkButton } from "trilium:preact";
    -

    and then use them:

    export default function MyRenderNote() {
    +  class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note.

    +

    To use these components, simply import them from trilium:preact:

    import { ActionButton, Button, LinkButton } from "trilium:preact";
    +

    and then use them:

    export default function MyRenderNote() {
         const onClick = () => showMessage("A button was pressed");
         
         return (
    @@ -26,19 +26,19 @@
     
    -

    This is a Render Note example +

    This is a Render Note example with JSX that shows most of the built-in components that are accessible to custom widgets and JSX render notes.

    To use it, simply:

      -
    1. Create a render note.
    2. -
    3. Create a child code note of JSX type with the content of this file:  +
    4. Create a render note.
    5. +
    6. Create a child code note of JSX type with the content of this file:  Widget showcase + class="reference-link" href="#root/_help_i9B4IW7b6V6z">Widget showcase
    7. -
    8. Set the ~renderNote relation of the parent - note to the child note.
    9. -
    10. Refresh the render note to see the results.
    11. +
    12. Set the ~renderNote relation of the parent note to the child + note.
    13. +
    14. Refresh the render note to see the results.
    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.html index d11c1e1d8..2cdec3295 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.html @@ -1,19 +1,12 @@ -

    Inline styles

    <div style={{
    -
    +

    Inline styles

    <div style={{
         display: "flex",
    -
         height: "53px",
    -
         width: "fit-content",
    -
         fontSize: "0.75em",
    -
         alignItems: "center",
    -
         flexShrink: 0            
    -
     }}>/* [...] */</div>

    Custom CSS file

    -

    Simply create a Custom app-wide CSS. +

    Simply create a Custom app-wide CSS. Make sure the class names are unique enough to not intersect with other - UI elements, consider adding a prefix (e.g. x-mywidget-).

    \ No newline at end of file + UI elements, consider adding a prefix (e.g. x-mywidget-).

    \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries.html index 8b0ffb3bb..08c797737 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Component libraries.html @@ -1,26 +1,26 @@ -

    Using the concept of Script bundles, +

    Using the concept of Script bundles, it's possible to create components that are shared for multiple widgets - or Render Note.

    + or Render Note.

    Exporting a single component

    This is generally useful for big components.

    Here's an example child hierarchy using Render Note: 

    + href="#root/_help_HcABDtFCkbFN">Render Note: 

      -
    • My render note +
    • My render note
      Note type: Render Note -
      Link ~renderNote to the child note (Render note with subcomponents) +
      Link ~renderNote to the child note (Render note with subcomponents)
        -
      • +
      • Render note with subcomponents -
        Type: JSX

        export default function() {
        +          
        Type: JSX

        export default function() {
             return (
                 <MyComponent />        
             );
         }
          -
        • +
        • MyComponent -
          Type: Code / JSX

          export default function MyComponent() {
          +              
          Type: Code / JSX

          export default function MyComponent() {
               return <p>Hi</p>;
           }
        • @@ -30,17 +30,16 @@

        Multiple components per note

        -

        To export multiple components, use the export keyword - next to each of the function components.

        -

        Here's how a sub-note called MyComponents would - look like:

        export function MyFirstComponent() {
        +

        To export multiple components, use the export keyword next + to each of the function components.

        +

        Here's how a sub-note called MyComponents would look like:

        export function MyFirstComponent() {
             return <p>First</p>;
         }
         
         export function MySecondComponent() {
             return <p>Bar</p>;
         }
        -

        Then in its parent note:

        const { MyFirstComponent, MySecondComponent } = MyComponents;
        +

        Then in its parent note:

        const { MyFirstComponent, MySecondComponent } = MyComponents;
         
         export default function() {
             return (
        @@ -51,5 +50,5 @@ export default function() {
             );
         }

        Alternatively, it's also possible to use the components directly without - assigning them to a const first:

        <MyComponents.MyFirstComponent />
        +  assigning them to a const first:

        <MyComponents.MyFirstComponent />
         <MyComponents.MySecondComponent />
        \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks.html index 5dadda792..45255b1d7 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Hooks.html @@ -1,14 +1,14 @@

        Standard Preact hooks

        -

        All standard Preact hooks are available as an import in trilium:api.

        +

        All standard Preact hooks are available as an import in trilium:api.

        For example:

        import { useState } from "trilium:preact";
         const [ myState, setMyState ] = useState("Hi");

        Custom hooks

        Trilium comes with a large set of custom hooks for Preact, all of which are also available to custom widgets and Render Note.

        -

        useNoteContext

        -

        As a replacement to Note context aware widget, - Preact exposes the current note context in the useNoteContext hook:

        import { defineWidget, useNoteContext, useNoteProperty } from "trilium:preact";
        +  href="#root/_help_HcABDtFCkbFN">Render Note.

        +

        useNoteContext

        +

        As a replacement to Note context aware widget, + Preact exposes the current note context in the useNoteContext hook:

        import { defineWidget, useNoteContext, useNoteProperty } from "trilium:preact";
         
         export default defineWidget({    
             parent: "note-detail-pane",
        @@ -21,15 +21,13 @@ export default defineWidget({
         });

        Note that the custom widget must be inside the content area (so note detail widget) for this to work properly, especially when dealing with splits.

        -

        useActiveNoteContext

        -

        useActiveNoteContext is an alternative - to useNoteContext which works even if the - widget is not within the note detail section and it automatically switches - the note context as the user is navigating around between tabs and splits.

        -

        useNoteProperty

        -

        This hook allows “listening” for changes to a particular property of a - FNote, such as the title or - typeof a note. The benefit from using the hook is that it actually - reacts to changes, for example if the note title or type is changed.

        \ No newline at end of file +

        useActiveNoteContext

        +

        useActiveNoteContext is an alternative to useNoteContext which + works even if the widget is not within the note detail section and it automatically + switches the note context as the user is navigating around between tabs + and splits.

        +

        useNoteProperty

        +

        This hook allows “listening” for changes to a particular property of a FNote, + such as the title or type of a note. The benefit + from using the hook is that it actually reacts to changes, for example + if the note title or type is changed.

        \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md index 52e727229..19837d588 100644 --- a/docs/Developer Guide/Developer Guide/Documentation.md +++ b/docs/Developer Guide/Developer Guide/Documentation.md @@ -1,5 +1,5 @@ # Documentation -There are multiple types of documentation for Trilium: +There are multiple types of documentation for Trilium: * The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing F1. * The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers. diff --git a/docs/User Guide/!!!meta.json b/docs/User Guide/!!!meta.json index cdb0b0ffd..a6cd7e10d 100644 --- a/docs/User Guide/!!!meta.json +++ b/docs/User Guide/!!!meta.json @@ -2677,6 +2677,20 @@ "value": "tabs", "isInheritable": false, "position": 40 + }, + { + "type": "relation", + "name": "internalLink", + "value": "MMiBEQljMQh2", + "isInheritable": false, + "position": 50 + }, + { + "type": "relation", + "name": "internalLink", + "value": "xYmIYSP6wE3F", + "isInheritable": false, + "position": 60 } ], "format": "markdown", @@ -4786,20 +4800,25 @@ "value": "bx bxs-navigation", "isInheritable": false, "position": 30 + }, + { + "type": "relation", + "name": "internalLink", + "value": "3seOhtN8uLIY", + "isInheritable": false, + "position": 40 + }, + { + "type": "relation", + "name": "internalLink", + "value": "xYmIYSP6wE3F", + "isInheritable": false, + "position": 50 } ], "format": "markdown", "dataFileName": "Note Navigation.md", - "attachments": [ - { - "attachmentId": "jDuwVaU8bNtG", - "title": "image.png", - "role": "image", - "mime": "image/jpg", - "position": 10, - "dataFileName": "Note Navigation_image.png" - } - ] + "attachments": [] }, { "isClone": false, @@ -15618,6 +15637,83 @@ "type": "text", "mime": "text/markdown", "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "KLsqhjaqh1QW", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "gMkgcLJ6jBkg", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "6f9hih2hXXZk", + "isInheritable": false, + "position": 30 + }, + { + "type": "relation", + "name": "internalLink", + "value": "HI6GBBIduIgv", + "isInheritable": false, + "position": 40 + }, + { + "type": "relation", + "name": "internalLink", + "value": "s8alTXmpFR61", + "isInheritable": false, + "position": 50 + }, + { + "type": "relation", + "name": "internalLink", + "value": "oPVyFC7WL2Lp", + "isInheritable": false, + "position": 60 + }, + { + "type": "relation", + "name": "internalLink", + "value": "GhurYZjh8e1V", + "isInheritable": false, + "position": 70 + }, + { + "type": "relation", + "name": "internalLink", + "value": "RnaPdbciOfeq", + "isInheritable": false, + "position": 80 + }, + { + "type": "relation", + "name": "internalLink", + "value": "M8IppdwVHSjG", + "isInheritable": false, + "position": 90 + }, + { + "type": "relation", + "name": "internalLink", + "value": "xYmIYSP6wE3F", + "isInheritable": false, + "position": 100 + }, + { + "type": "relation", + "name": "internalLink", + "value": "4Gn3psZKsfSm", + "isInheritable": false, + "position": 110 + }, { "type": "label", "name": "shareAlias", @@ -15631,83 +15727,6 @@ "value": "bx bxs-widget", "isInheritable": false, "position": 30 - }, - { - "type": "relation", - "name": "internalLink", - "value": "oPVyFC7WL2Lp", - "isInheritable": false, - "position": 40 - }, - { - "type": "relation", - "name": "internalLink", - "value": "RnaPdbciOfeq", - "isInheritable": false, - "position": 50 - }, - { - "type": "relation", - "name": "internalLink", - "value": "xYmIYSP6wE3F", - "isInheritable": false, - "position": 60 - }, - { - "type": "relation", - "name": "internalLink", - "value": "4Gn3psZKsfSm", - "isInheritable": false, - "position": 70 - }, - { - "type": "relation", - "name": "internalLink", - "value": "KLsqhjaqh1QW", - "isInheritable": false, - "position": 80 - }, - { - "type": "relation", - "name": "internalLink", - "value": "6f9hih2hXXZk", - "isInheritable": false, - "position": 90 - }, - { - "type": "relation", - "name": "internalLink", - "value": "HI6GBBIduIgv", - "isInheritable": false, - "position": 100 - }, - { - "type": "relation", - "name": "internalLink", - "value": "s8alTXmpFR61", - "isInheritable": false, - "position": 110 - }, - { - "type": "relation", - "name": "internalLink", - "value": "GhurYZjh8e1V", - "isInheritable": false, - "position": 120 - }, - { - "type": "relation", - "name": "internalLink", - "value": "M8IppdwVHSjG", - "isInheritable": false, - "position": 130 - }, - { - "type": "relation", - "name": "internalLink", - "value": "gMkgcLJ6jBkg", - "isInheritable": false, - "position": 140 } ], "format": "markdown", @@ -15756,23 +15775,23 @@ { "type": "relation", "name": "internalLink", - "value": "s8alTXmpFR61", + "value": "MgibgPcfeuGz", "isInheritable": false, "position": 40 }, + { + "type": "relation", + "name": "internalLink", + "value": "s8alTXmpFR61", + "isInheritable": false, + "position": 50 + }, { "type": "label", "name": "shareAlias", "value": "widget-basics", "isInheritable": false, "position": 20 - }, - { - "type": "relation", - "name": "internalLink", - "value": "MgibgPcfeuGz", - "isInheritable": false, - "position": 50 } ], "format": "markdown", @@ -15912,19 +15931,19 @@ "type": "text", "mime": "text/html", "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "Sg9GrCtyftZf", + "isInheritable": false, + "position": 10 + }, { "type": "label", "name": "shareAlias", "value": "css", "isInheritable": false, "position": 20 - }, - { - "type": "relation", - "name": "internalLink", - "value": "Sg9GrCtyftZf", - "isInheritable": false, - "position": 30 } ], "format": "markdown", @@ -16347,46 +16366,46 @@ "mime": "text/html", "attributes": [ { - "type": "label", - "name": "iconClass", - "value": "bx bxl-react", + "type": "relation", + "name": "internalLink", + "value": "HcABDtFCkbFN", "isInheritable": false, - "position": 30 + "position": 10 }, { "type": "relation", "name": "internalLink", "value": "MgibgPcfeuGz", "isInheritable": false, - "position": 40 - }, - { - "type": "relation", - "name": "internalLink", - "value": "HcABDtFCkbFN", - "isInheritable": false, - "position": 50 - }, - { - "type": "relation", - "name": "internalLink", - "value": "wy8So3yZZlH9", - "isInheritable": false, - "position": 60 + "position": 20 }, { "type": "relation", "name": "internalLink", "value": "GLks18SNjxmC", "isInheritable": false, - "position": 70 + "position": 30 }, { "type": "relation", "name": "internalLink", "value": "Bqde6BvPo05g", "isInheritable": false, - "position": 80 + "position": 40 + }, + { + "type": "relation", + "name": "internalLink", + "value": "wy8So3yZZlH9", + "isInheritable": false, + "position": 50 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bxl-react", + "isInheritable": false, + "position": 30 } ], "format": "markdown", @@ -16416,14 +16435,14 @@ "name": "internalLink", "value": "hA834UaHhSNn", "isInheritable": false, - "position": 30 + "position": 10 }, { "type": "relation", "name": "internalLink", "value": "HcABDtFCkbFN", "isInheritable": false, - "position": 40 + "position": 20 }, { "type": "label", @@ -16459,14 +16478,14 @@ "name": "internalLink", "value": "HcABDtFCkbFN", "isInheritable": false, - "position": 40 + "position": 10 }, { "type": "relation", "name": "internalLink", "value": "GhurYZjh8e1V", "isInheritable": false, - "position": 50 + "position": 20 }, { "type": "label", @@ -16502,7 +16521,7 @@ "name": "internalLink", "value": "AlhDUqhENtH7", "isInheritable": false, - "position": 30 + "position": 10 }, { "type": "label", @@ -16533,40 +16552,40 @@ "type": "text", "mime": "text/html", "attributes": [ - { - "type": "label", - "name": "iconClass", - "value": "bx bxs-component", - "isInheritable": false, - "position": 30 - }, { "type": "relation", "name": "internalLink", "value": "MgibgPcfeuGz", "isInheritable": false, - "position": 40 + "position": 10 }, { "type": "relation", "name": "internalLink", "value": "HcABDtFCkbFN", "isInheritable": false, - "position": 50 - }, - { - "type": "relation", - "name": "internalLink", - "value": "i9B4IW7b6V6z", - "isInheritable": false, - "position": 60 + "position": 20 }, { "type": "relation", "name": "internalLink", "value": "6tZeKvSHEUiB", "isInheritable": false, - "position": 70 + "position": 30 + }, + { + "type": "relation", + "name": "internalLink", + "value": "i9B4IW7b6V6z", + "isInheritable": false, + "position": 40 + }, + { + "type": "label", + "name": "iconClass", + "value": "bx bxs-component", + "isInheritable": false, + "position": 30 } ], "format": "markdown", @@ -16788,6 +16807,27 @@ "type": "text", "mime": "text/html", "attributes": [ + { + "type": "relation", + "name": "internalLink", + "value": "HcABDtFCkbFN", + "isInheritable": false, + "position": 10 + }, + { + "type": "relation", + "name": "internalLink", + "value": "6f9hih2hXXZk", + "isInheritable": false, + "position": 20 + }, + { + "type": "relation", + "name": "internalLink", + "value": "IakOLONlIfGI", + "isInheritable": false, + "position": 30 + }, { "type": "label", "name": "iconClass", @@ -16795,33 +16835,12 @@ "isInheritable": false, "position": 30 }, - { - "type": "relation", - "name": "internalLink", - "value": "HcABDtFCkbFN", - "isInheritable": false, - "position": 40 - }, - { - "type": "relation", - "name": "internalLink", - "value": "6f9hih2hXXZk", - "isInheritable": false, - "position": 50 - }, { "type": "label", "name": "shareAlias", "value": "bundles", "isInheritable": false, "position": 60 - }, - { - "type": "relation", - "name": "internalLink", - "value": "IakOLONlIfGI", - "isInheritable": false, - "position": 70 } ], "format": "markdown", diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation.md b/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation.md index 94530ed19..290d43680 100644 --- a/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation.md +++ b/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation.md @@ -3,14 +3,15 @@ One of the Trilium's goals is to provide fast and comfortable navigation between ## Backwards and forward -You can use alt-left and alt-right to move back and forward in history of viewed pages. +To move back and forward in history of viewed pages: -This works identically to browser backwards / forwards, it's actually using built-in browser support for this. - -![](Note%20Navigation_image.png) +* The Launch Bar can be configured to display navigation buttons (see _Go to Next Note_ and _Go to Previous Note_ in _Available Launchers_). +* On the left side of the [tab bar](../UI%20Elements/Tabs.md), there are two dedicated buttons. + * These buttons will appear only if the launch bar is not already displaying the history navigation buttons to avoid duplication. +* Alternatively, use the Alt + Left and Alt + Right keyboard shortcuts. ## Jump to note This is useful to quickly find and view arbitrary notes - click on `Jump to` button on the top or press Ctrl + J . Then type part of the note name and autocomplete will help you pick the desired note. -See Jump to Note for more information. \ No newline at end of file +See Jump to... for more information. \ No newline at end of file diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation_image.png b/docs/User Guide/User Guide/Basic Concepts and Features/Navigation/Note Navigation_image.png deleted file mode 100644 index 2ea2690121cc87cb0522acd265c8337c59ece913..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36465 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 diff --git a/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs.md b/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs.md index 912070b6f..5ece15397 100644 --- a/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs.md +++ b/docs/User Guide/User Guide/Basic Concepts and Features/UI Elements/Tabs.md @@ -17,6 +17,8 @@ Depending on the Split View. Each tab can have one or more notes, displayed horizontally. * Tabs can be reordered by drag-and-dropping it into a new position. * An existing tab can be displayed in a new window by dragging the tab upwards or downwards. It is not possible to combine tabs back into another window. +* On the left side of the tab bar there are two buttons to navigate through the note history backwards or forwards (see Note Navigation). + * This feature is only enabled if the Launch Bar doesn't already contain the history navigation buttons. ## Keyboard interaction diff --git a/docs/User Guide/User Guide/Note Types/Render Note.md b/docs/User Guide/User Guide/Note Types/Render Note.md index b05591c63..828d7dfef 100644 --- a/docs/User Guide/User Guide/Note Types/Render Note.md +++ b/docs/User Guide/User Guide/Note Types/Render Note.md @@ -44,7 +44,7 @@ Here are the steps to creating a simple render note: 2. Create a child Code note with JSX as the language. As an example, use the following content: - ```jsx + ``` export default function() { return ( <> diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md index 8eb2cb145..7c189fc3a 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md @@ -23,27 +23,21 @@ Wherever possible, widget examples will be both in the legacy and Preact format. Let's start by creating a widget that shows a message near the content area. Follow the previous section to create a code note, and use the following content. -
        LegacyPreact (v0.101.0+)
        class HelloNoteDetail extends api.BasicWidget {
        +
        LegacyPreact (v0.101.0+)
        class HelloNoteDetail extends api.BasicWidget {
         
        -    constructor() {
        -        super();
        -        this.contentSized();
        -    }
        -
        -    get parentWidget() { return "center-pane" }
        -
        -    doRender() {
        -        this.$widget = $("<span>Center pane</span>");
        -    }
        -    
        +
        constructor() {
        +    super();
        +    this.contentSized();
         }
         
        -module.exports = new HelloNoteDetail();
        import { defineWidget } from "trilium:preact";
        +get parentWidget() { return "center-pane" }
         
        -export default defineWidget({
        +doRender() {
        +    this.<!--FORMULA_INLINE_1766517018225_0-->("&lt;span&gt;Center pane&lt;/span&gt;");
        +}

        }

        module.exports = new HelloNoteDetail();

        import { defineWidget } from "trilium:preact";

        export default defineWidget({ parent: "center-pane", render: () => <span>Center pane from Preact.</span> -});

        +});

        [Refresh the application](../../Troubleshooting/Refreshing%20the%20application.md) and the widget should appear underneath the content area. @@ -51,7 +45,7 @@ export default defineWidget({ A widget can be placed in one of the following sections of the applications: -
        Value for parentWidgetDescriptionSample widgetSpecial requirements
        left-paneAppears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-paneIn the content area. If a split is open, the widget will span all of the splits.See example above.None.
        note-detail-pane

        In the content area, inside the note detail area. If a split is open, the widget will be contained inside the split.

        This is ideal if the widget is note-specific.

        Note context aware widget
        • The widget must export a class and not an instance of the class (e.g. no new) because it needs to be multiplied for each note, so that splits work correctly.
        • Since the class is exported instead of an instance, the parentWidget getter must be static, otherwise the widget is ignored.
        right-paneIn the Right Sidebar, as a dedicated section.Right pane widget
        • Although not mandatory, it's best to use a RightPanelWidget instead of a BasicWidget or a NoteContextAwareWidget.
        +
        Value for parentWidgetDescriptionSample widgetSpecial requirements
        left-paneAppears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-paneIn the content area. If a split is open, the widget will span all of the splits.See example above.None.
        note-detail-pane

        In the content area, inside the note detail area. If a split is open, the widget will be contained inside the split.

        This is ideal if the widget is note-specific.

        Note context aware widget
        • The widget must export a class and not an instance of the class (e.g. no new) because it needs to be multiplied for each note, so that splits work correctly.
        • Since the class is exported instead of an instance, the parentWidget getter must be static, otherwise the widget is ignored.
        right-paneIn the Right Sidebar, as a dedicated section.Right pane widget
        • Although not mandatory, it's best to use a RightPanelWidget instead of a BasicWidget or a NoteContextAwareWidget.
        To position the widget somewhere else, just change the value passed to `get parentWidget()` for legacy widgets or the `parent` field for Preact. Do note that some positions such as `note-detail-pane` and `right-pane` have special requirements that need to be accounted for (see the table above). diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.md index c892e3365..78a69eb8a 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.md @@ -35,7 +35,7 @@ module.exports = new NoteTitleWidget(); ## Preact widget (v0.101.0+) -```jsx +``` import { defineLauncherWidget, useActiveNoteContext } from "trilium:preact"; export default defineLauncherWidget({ diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md index 6f05765fc..db3811479 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md @@ -19,13 +19,13 @@ When using Preact with JSX, there is a special syntax which provides ES-like imp Instead of: -```jsx +``` api.showMessage("Hello"); ``` the JSX version looks like this: -```jsx +``` import { showMessage } from "trilium:api"; showMessage("hello"); ``` @@ -34,7 +34,7 @@ showMessage("hello"); There's a new Script API dedicated to Preact, which provides shared components that are also used by Trilium internally as well as hooks, for example. -```jsx +``` import { useState } from "trilium:preact"; const [ myState, setMyState ] = useState("Hi"); ``` @@ -43,7 +43,7 @@ const [ myState, setMyState ] = useState("Hi"); JSX notes can export a component for use in Render Note or for Component libraries:  -```jsx +``` export default function() { return ( <> diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md index db05c9255..8d3c86e4f 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md @@ -5,13 +5,13 @@ Trilium comes with its own set of Preact components, some of which are also avai To use these components, simply import them from `trilium:preact`: -```jsx +``` import { ActionButton, Button, LinkButton } from "trilium:preact"; ``` and then use them: -```jsx +``` export default function MyRenderNote() { const onClick = () => showMessage("A button was pressed"); diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md index 3b959261f..bda9f195a 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md @@ -1,7 +1,7 @@ # CSS ## Inline styles -```jsx +```
        Date: Tue, 23 Dec 2025 22:01:49 +0200 Subject: [PATCH 14/23] chore(badges): allow overflow with clipping --- apps/client/src/widgets/layout/NoteBadges.css | 7 +++++++ apps/client/src/widgets/react/Badge.css | 16 +++++++++++++++- apps/client/src/widgets/react/Badge.tsx | 5 ++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/client/src/widgets/layout/NoteBadges.css b/apps/client/src/widgets/layout/NoteBadges.css index 39fafd90e..8de12b810 100644 --- a/apps/client/src/widgets/layout/NoteBadges.css +++ b/apps/client/src/widgets/layout/NoteBadges.css @@ -16,6 +16,13 @@ &.share-badge {--color: var(--badge-share-background-color);} &.clipped-note-badge {--color: var(--badge-clipped-note-background-color);} &.execute-badge {--color: var(--badge-execute-background-color);} + min-width: 0; + + .text { + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; + } } .dropdown-badge { diff --git a/apps/client/src/widgets/react/Badge.css b/apps/client/src/widgets/react/Badge.css index cf661b4f9..8eac80b7d 100644 --- a/apps/client/src/widgets/react/Badge.css +++ b/apps/client/src/widgets/react/Badge.css @@ -50,16 +50,30 @@ white-space: nowrap; border-radius: var(--badge-radius); + button, + .btn { + min-width: 0; + overflow: hidden; + } + .ext-badge { border-radius: 0; .text { display: inline-flex; align-items: center; + min-width: 0; + + .text-inner { + min-width: 0; + text-overflow: ellipsis; + overflow: hidden; + } .arrow { font-size: 1.3em; - margin-left: 0.25em; + margin-inline-start: 0.25em; + margin-inline-end: 0; } } } diff --git a/apps/client/src/widgets/react/Badge.tsx b/apps/client/src/widgets/react/Badge.tsx index 6e17ed0ce..48fa7fe09 100644 --- a/apps/client/src/widgets/react/Badge.tsx +++ b/apps/client/src/widgets/react/Badge.tsx @@ -60,7 +60,10 @@ export function BadgeWithDropdown({ text, children, tooltip, className, dropdown {text} } + text={<> + {text} + + } className={className} {...props} />} From 6e3a020d0f4884cef721dcb107f2ac10b0c5993a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 22:02:06 +0200 Subject: [PATCH 15/23] chore(badges): increase threshold for hiding text --- apps/client/src/widgets/note_title.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/client/src/widgets/note_title.css b/apps/client/src/widgets/note_title.css index 637661095..cdca69cef 100644 --- a/apps/client/src/widgets/note_title.css +++ b/apps/client/src/widgets/note_title.css @@ -37,7 +37,7 @@ body.experimental-feature-new-layout { .title-row { container-type: size; transition: border 400ms ease-out; - + &.note-split-title { border-bottom: 1px solid var(--main-border-color); @@ -56,13 +56,13 @@ body.experimental-feature-new-layout { --note-icon-container-padding-size: 6px; margin-inline: 0; } - + .note-title-widget { --note-title-size: 18px; --note-title-padding-inline: 0; } - @container (max-width: 700px) { + @container (max-width: 600px) { .note-title-widget { --note-title-size: 1.25rem; --note-title-padding-inline: 4px; @@ -80,7 +80,7 @@ body.experimental-feature-new-layout { .ext-badge .text { display: none; } - + .ext-badge { --size: 2em; width: var(--size); From 8fda283977b63e28da260048ccafa0776cfcf8c6 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 22:14:07 +0200 Subject: [PATCH 16/23] fix(title_actions): dark background in code affecting readability --- .../src/widgets/containers/scrolling_container.css | 13 ++++++++++--- apps/client/src/widgets/layout/InlineTitle.css | 4 ---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/client/src/widgets/containers/scrolling_container.css b/apps/client/src/widgets/containers/scrolling_container.css index 25ceef124..b9af1ccde 100644 --- a/apps/client/src/widgets/containers/scrolling_container.css +++ b/apps/client/src/widgets/containers/scrolling_container.css @@ -4,7 +4,14 @@ position: relative; } -.note-split.type-code:not(.mime-text-x-sqlite) > .scrolling-container { - background-color: var(--code-background-color); - --scrollbar-background-color: var(--main-background-color); +.note-split.type-code:not(.mime-text-x-sqlite) { + &> .scrolling-container { + background-color: var(--code-background-color); + --scrollbar-background-color: var(--main-background-color); + } + + .inline-title, + .title-actions { + background-color: var(--main-background-color); + } } diff --git a/apps/client/src/widgets/layout/InlineTitle.css b/apps/client/src/widgets/layout/InlineTitle.css index c6f8e899e..295bbd102 100644 --- a/apps/client/src/widgets/layout/InlineTitle.css +++ b/apps/client/src/widgets/layout/InlineTitle.css @@ -75,10 +75,6 @@ } } -.note-split.type-code:not(.mime-text-x-sqlite) .inline-title { - background-color: var(--main-background-color); -} - body.prefers-centered-content .inline-title { margin-inline: auto; } From 19cd7a0cad97421e211d2e05718c715a9dcb35be Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 23:02:15 +0200 Subject: [PATCH 17/23] feat(script): improve script error message --- apps/client/src/services/bundle.ts | 24 ++----------------- apps/client/src/services/toast.ts | 2 +- .../src/translations/en/translation.json | 5 ++-- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/apps/client/src/services/bundle.ts b/apps/client/src/services/bundle.ts index a56139d36..d33ba76a0 100644 --- a/apps/client/src/services/bundle.ts +++ b/apps/client/src/services/bundle.ts @@ -46,17 +46,7 @@ export async function executeBundle(bundle: Bundle, originEntity?: Entity | null return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`); }.call(apiContext); } catch (e: any) { - const note = await froca.getNote(bundle.noteId); - toastService.showPersistent({ - id: `custom-script-failure-${note?.noteId}`, - title: t("toast.bundle-error.title"), - icon: "bx bx-error-circle", - message: t("toast.bundle-error.message", { - id: note?.noteId, - title: note?.title, - message: e.message - }) - }); + showErrorForScriptNote(bundle.noteId, t("toast.bundle-error.message", { message: e.message })); logError("Widget initialization failed: ", e); } } @@ -151,17 +141,7 @@ async function getWidgetBundlesByParent() { } } catch (e: any) { const noteId = bundle.noteId; - const note = await froca.getNote(noteId); - toastService.showPersistent({ - id: `custom-script-failure-${noteId}`, - title: t("toast.bundle-error.title"), - icon: "bx bx-error-circle", - message: t("toast.bundle-error.message", { - id: noteId, - title: note?.title, - message: e.message - }) - }); + showErrorForScriptNote(noteId, t("toast.bundle-error.message", { message: e.message })); logError("Widget initialization failed: ", e); continue; diff --git a/apps/client/src/services/toast.ts b/apps/client/src/services/toast.ts index 641b3cdce..f31e242cd 100644 --- a/apps/client/src/services/toast.ts +++ b/apps/client/src/services/toast.ts @@ -69,7 +69,7 @@ export async function showErrorForScriptNote(noteId: string, message: string) { showPersistent({ id: `custom-widget-failure-${noteId}`, - title: note?.title ?? "", + title: t("toast.scripting-error", { title: note?.title ?? "" }), icon: note?.getIcon() ?? "bx bx-error-circle", message, timeout: 15_000, diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 7f34841f8..9cfc8b2e1 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -21,7 +21,7 @@ }, "bundle-error": { "title": "Failed to load a custom script", - "message": "Script from note with ID \"{{id}}\", titled \"{{title}}\" could not be executed due to:\n\n{{message}}" + "message": "Script could not be executed due to:\n\n{{message}}" }, "widget-list-error": { "title": "Failed to obtain the list of widgets from the server" @@ -30,7 +30,8 @@ "title": "Failed to render a custom React widget" }, "widget-missing-parent": "Custom widget does not have mandatory '{{property}}' property defined.", - "open-script-note": "Open script note" + "open-script-note": "Open script note", + "scripting-error": "Custom script error: {{title}}" }, "add_link": { "add_link": "Add link", From 1185d4b10b10a305afbaa318bc96613da2b0165c Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 23:02:26 +0200 Subject: [PATCH 18/23] chore(layout): reduce padding for promoted attributes --- apps/client/src/widgets/layout/NoteTitleActions.css | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/layout/NoteTitleActions.css b/apps/client/src/widgets/layout/NoteTitleActions.css index 08426c19a..db2810c1e 100644 --- a/apps/client/src/widgets/layout/NoteTitleActions.css +++ b/apps/client/src/widgets/layout/NoteTitleActions.css @@ -6,7 +6,7 @@ body.experimental-feature-new-layout { .title-actions { display: flex; flex-direction: column; - gap: 1em; + gap: 0.5em; &:not(:empty) { padding: 0.75em 15px; @@ -33,5 +33,12 @@ body.experimental-feature-new-layout { } } } + + .promoted-attributes-widget { + .promoted-attributes-container { + margin: 0; + padding: 0; + } + } } } From 4e5c97d5483abe998b89fa75316c2a2da62de899 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 23:06:29 +0200 Subject: [PATCH 19/23] fix(toast): unreadable buttons on light theme --- apps/client/src/widgets/Toast.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/client/src/widgets/Toast.css b/apps/client/src/widgets/Toast.css index 519e3b62d..ecc20c205 100644 --- a/apps/client/src/widgets/Toast.css +++ b/apps/client/src/widgets/Toast.css @@ -54,6 +54,16 @@ display: flex; gap: 1em; justify-content: space-between; + + .btn { + color: var(--bs-toast-color); + background: var(--modal-control-button-background); + + &:hover { + background: var(--modal-control-button-hover-background); + color: var(--bs-toast-color); + } + } } .toast-progress { From 3e50262665ec820dbee109dcb1fde46dc3c15ed2 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 23 Dec 2025 23:40:16 +0200 Subject: [PATCH 20/23] fix(status_bar): attribute pane not shown when adding new attribute def --- apps/client/src/widgets/react/hooks.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/react/hooks.tsx b/apps/client/src/widgets/react/hooks.tsx index 9a3b690eb..57b830934 100644 --- a/apps/client/src/widgets/react/hooks.tsx +++ b/apps/client/src/widgets/react/hooks.tsx @@ -635,7 +635,8 @@ export function useLegacyWidget(widgetFactory: () => T, { const renderedWidget = widget.render(); return [ widget, renderedWidget ]; - }, [ noteContext, parentComponent, widgetFactory]); + }, [ noteContext, parentComponent ]); // eslint-disable-line react-hooks/exhaustive-deps + // widgetFactory() is intentionally left out // Attach the widget to the parent. useEffect(() => { From fc8605a14f837b748bbe54a1ab7e33a4f674ae37 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 24 Dec 2025 00:26:29 +0200 Subject: [PATCH 21/23] docs(user): broken code blocks due to table --- .../Frontend Basics/Custom Widgets.html | 229 +++++++++--------- .../Frontend Basics/Custom Widgets.md | 41 +++- 2 files changed, 143 insertions(+), 127 deletions(-) diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html index ce9190a4e..56a1c3491 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html @@ -4,149 +4,146 @@

        Preact with JSX vs. vanilla jQuery

        In older versions of Trilium, custom widgets were exclusively written in a combination of jQuery with Trilium's internal widget architecture - (e.g., BasicWidget, NoteContextAwareWidget).

        + (e.g., BasicWidget, NoteContextAwareWidget).

        Starting with v0.101.0, custom widgets can also be written in JSX using the Preact framework. Both legacy and Preact widgets have the same capabilities, with a single difference:

          -
        • Preact widgets are content-sized by default whereas legacy widgets need this.contentSized() applied - in the constructor. For more information, see the corresponding section - in Troubleshooting.
        • +
        • Preact widgets are content-sized by default whereas legacy widgets need + this.contentSized()applied in the constructor. For more information, + see the corresponding section in Troubleshooting.

        Wherever possible, widget examples will be both in the legacy and Preact format.

        Creating a custom widget

          -
        1. Create a Code note.
        2. -
        3. Set the language to: +
        4. Create a Code note.
        5. +
        6. Set the language to:
            -
          1. JavaScript (frontend) for legacy widgets using jQuery.
          2. -
          3. JSX for Preact widgets. You might need to go to Options → Code to enable +
          4. JavaScript (frontend) for legacy widgets using jQuery.
          5. +
          6. JSX for Preact widgets. You might need to go to Options → Code to enable the language first.
          -
        7. -
        8. Apply the #widget label.
        9. + +
        10. Apply the #widget label.

        Getting started with a simple example

        Let's start by creating a widget that shows a message near the content area. Follow the previous section to create a code note, and use the following content.

        - - - - - - - - - - - - - -
        LegacyPreact (v0.101.0+)
        class HelloNoteDetail extends api.BasicWidget {
        +

        Legacy version (jQuery)

        class HelloCenterPane extends api.BasicWidget {
         
        -
        constructor() {
        -    super();
        -    this.contentSized();
        +    constructor() {
        +        super();
        +        this.contentSized();
        +    }
        +
        +    get parentWidget() { return "center-pane" }
        +
        +    doRender() {
        +        this.$widget = $("<span>Center pane</span>");
        +    }
        +    
         }
         
        -get parentWidget() { return "center-pane" }
        +module.exports = new HelloCenterPane();
        +

        Refresh the application and the widget + should appear underneath the content area.

        +

        Preact version

        import { defineWidget } from "trilium:preact";
         
        -doRender() {
        -    this.<!--FORMULA_INLINE_1766517018225_0-->("&lt;span&gt;Center pane&lt;/span&gt;");
        -}
        -

        }

        -

        module.exports = new HelloNoteDetail();

        -
        -
        -
        import { defineWidget } from "trilium:preact";

        export default defineWidget({ -

        parent: "center-pane",
        -render: () =&gt; &lt;span&gt;Center pane from Preact.&lt;/span&gt;
        -

        });

        -
        -
        -
        -

        +export default defineWidget({ + parent: "center-pane", + render: () => <span>Center pane from Preact.</span> +});

        Refresh the application and the widget should appear underneath the content area.

        Widget location (parent widget)

        A widget can be placed in one of the following sections of the applications:

        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
        +
        Value for parentWidget - DescriptionSample widgetSpecial requirements
        left-pane - Appears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-pane - In the content area. If a split is open, the widget will span all of the - splits.See example above.None.
        note-detail-pane - -

        In the content area, inside the note detail area. If a split is open, - the widget will be contained inside the split.

        -

        This is ideal if the widget is note-specific.

        -
        Note context aware widget - -
          -
        • The widget must export a class and not an instance of the class - (e.g. no new) because it needs to be multiplied for each note, - so that splits work correctly.
        • -
        • Since the class is exported instead of an instance, the parentWidget getter - must be static, otherwise the widget is ignored.
        • -
        -
        right-pane - In the Right Sidebar, - as a dedicated section.Right pane widget - -
          -
        • Although not mandatory, it's best to use a RightPanelWidget instead - of a BasicWidget or a NoteContextAwareWidget.
        • -
        -
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Value for parentWidget + DescriptionSample widgetSpecial requirements
        left-pane + Appears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-pane + In the content area. If a split is open, the widget will span all of the + splits.See example above.None.
        note-detail-pane + +

        In the content area, inside the note detail area. If a split is open, + the widget will be contained inside the split.

        +

        This is ideal if the widget is note-specific.

        +
        Note context aware widget + +
          +
        • The widget must export a class and not an + instance of the class (e.g. no new) because + it needs to be multiplied for each note, so that splits work correctly.
        • +
        • Since the class is exported instead of an + instance, the parentWidget getter must be + static, otherwise the widget is ignored.
        • +
        +
        right-pane + In the Right Sidebar, + as a dedicated section.Right pane widget + +
          +
        • Although not mandatory, it's best to use a RightPanelWidget instead + of a BasicWidget or a NoteContextAwareWidget.
        • +
        +
        -

        To position the widget somewhere else, just change the value passed to get parentWidget() for - legacy widgets or the parent field for Preact. Do note that - some positions such as note-detail-pane and right-pane have - special requirements that need to be accounted for (see the table above).

        + +

        To position the widget somewhere else, just change the value passed to + get parentWidget()for legacy widgets or the parent field + for Preact. Do note that some positions such as note-detail-pane and + right-panehave special requirements that need to be accounted for + (see the table above).

        Launch bar widgets

        Launch bar widgets are similar to Custom widgets but are specific to the Launch Bar. diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md index 431b814db..392f5a734 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md @@ -23,20 +23,39 @@ Wherever possible, widget examples will be both in the legacy and Preact format. Let's start by creating a widget that shows a message near the content area. Follow the previous section to create a code note, and use the following content. -
        LegacyPreact (v0.101.0+)
        class HelloNoteDetail extends api.BasicWidget {
        +### Legacy version (jQuery)
         
        -
        constructor() {
        -    super();
        -    this.contentSized();
        +```
        +class HelloCenterPane extends api.BasicWidget {
        +
        +    constructor() {
        +        super();
        +        this.contentSized();
        +    }
        +
        +    get parentWidget() { return "center-pane" }
        +
        +    doRender() {
        +        this.$widget = $("Center pane");
        +    }
        +    
         }
         
        -get parentWidget() { return "center-pane" }
        +module.exports = new HelloCenterPane();
        +```
         
        -doRender() {
        -    this.<!--FORMULA_INLINE_1766517018225_0-->("&lt;span&gt;Center pane&lt;/span&gt;");
        -}

        }

        module.exports = new HelloNoteDetail();

        import { defineWidget } from "trilium:preact";

        export default defineWidget({ -

        parent: "center-pane",
        -render: () =&gt; &lt;span&gt;Center pane from Preact.&lt;/span&gt;

        });

        +[Refresh the application](../../Troubleshooting/Refreshing%20the%20application.md) and the widget should appear underneath the content area. + +### Preact version + +``` +import { defineWidget } from "trilium:preact"; + +export default defineWidget({ + parent: "center-pane", + render: () => Center pane from Preact. +}); +``` [Refresh the application](../../Troubleshooting/Refreshing%20the%20application.md) and the widget should appear underneath the content area. @@ -44,7 +63,7 @@ render: () =&gt; &lt;span&gt;Center pane from Preact.&lt;/span&a A widget can be placed in one of the following sections of the applications: -
        Value for parentWidgetDescriptionSample widgetSpecial requirements
        left-paneAppears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-paneIn the content area. If a split is open, the widget will span all of the splits.See example above.None.
        note-detail-pane

        In the content area, inside the note detail area. If a split is open, the widget will be contained inside the split.

        This is ideal if the widget is note-specific.

        Note context aware widget
        • The widget must export a class and not an instance of the class (e.g. no new) because it needs to be multiplied for each note, so that splits work correctly.
        • Since the class is exported instead of an instance, the parentWidget getter must be static, otherwise the widget is ignored.
        right-paneIn the Right Sidebar, as a dedicated section.Right pane widget
        • Although not mandatory, it's best to use a RightPanelWidget instead of a BasicWidget or a NoteContextAwareWidget.
        +
        Value for parentWidgetDescriptionSample widgetSpecial requirements
        left-paneAppears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-paneIn the content area. If a split is open, the widget will span all of the splits.See example above.None.
        note-detail-pane

        In the content area, inside the note detail area. If a split is open, the widget will be contained inside the split.

        This is ideal if the widget is note-specific.

        Note context aware widget
        • The widget must export a class and not an instance of the class (e.g. no new) because it needs to be multiplied for each note, so that splits work correctly.
        • Since the class is exported instead of an instance, the parentWidget getter must be static, otherwise the widget is ignored.
        right-paneIn the Right Sidebar, as a dedicated section.Right pane widget
        • Although not mandatory, it's best to use a RightPanelWidget instead of a BasicWidget or a NoteContextAwareWidget.
        To position the widget somewhere else, just change the value passed to `get parentWidget()` for legacy widgets or the `parent` field for Preact. Do note that some positions such as `note-detail-pane` and `right-pane` have special requirements that need to be accounted for (see the table above). From 963fcd615a349c34e1f09885d4e3c0183413d6ef Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 24 Dec 2025 00:30:16 +0200 Subject: [PATCH 22/23] docs(user): missing language tags for code blocks --- .../User Guide/Note Types/Render Note.html | 45 +++++++++---------- .../User Guide/Note Types/Render Note.md | 6 +-- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html index f0f720b87..9300b08cb 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html @@ -7,10 +7,13 @@ via an attribute.

        Creating a render note

          -
        1. Create a Code note - with the HTML language, with what needs to be displayed (for example <p>Hello world.</p>).
        2. -
        3. Create a Render Note.
        4. -
        5. Assign the renderNote relation to +
        6. Create a Code note + with the HTML language, with what needs to be displayed (for example + <p>Hello world.</p>).
        7. +
        8. Create a Render Note.
        9. +
        10. Assign the renderNote relation to point at the previously created code note.

        Legacy scripting using jQuery

        @@ -19,14 +22,14 @@ change parts of the note using JavaScript.

        For a simple example, we are going to create a render note that displays the current date in a field.

        -

        To do so, first create an HTML code note with the following content:

        <h1>Current date & time</h1>
        +

        To do so, first create an HTML code note with the following content:

        <h1>Current date & time</h1>
         The current date & time is <span class="date"></span>

        Now we need to add the script. Create another Code, but this time of JavaScript (frontend) language. Make sure the newly created note is a direct child of the HTML - note created previously; with the following content:

        const $dateEl = api.$container.find(".date");
        +  note created previously; with the following content:

        const $dateEl = api.$container.find(".date");
         $dateEl.text(new Date());
        -

        Now create a render note at any place and set its ~renderNote relation +

        Now create a render note at any place and set its ~renderNote relation to point to the HTML note. When the render note is accessed it will display:

        Current date & time @@ -39,37 +42,33 @@ $dateEl.text(new Date());

        need to provide a HTML anymore.

        Here are the steps to creating a simple render note:

          -
        1. -

          Create a note of type Render Note.

          -
        2. -
        3. +
        4. Create a note of type Render Note.
        5. +
        6. Create a child Code note with JSX as the language. -
          As an example, use the following content:

          export default function() {
          +      
          As an example, use the following content:

          export default function() {
               return (
                   <>
                       <p>Hello world.</p>
                   </>
               );
           }
          -
        7. -
        8. -

          In the parent render note, define a ~renderNote relation pointing - to the newly created child.

          -
        9. -
        10. -

          Refresh the render note and it should display a “Hello world” message.

          -
        11. + +
        12. In the parent render note, define a ~renderNote relation + pointing to the newly created child.
        13. +
        14. Refresh the render note and it should display a “Hello world” message.

        Refreshing the note

        It's possible to refresh the note via:

        Examples

        \ No newline at end of file diff --git a/docs/User Guide/User Guide/Note Types/Render Note.md b/docs/User Guide/User Guide/Note Types/Render Note.md index 828d7dfef..c0c4e4eea 100644 --- a/docs/User Guide/User Guide/Note Types/Render Note.md +++ b/docs/User Guide/User Guide/Note Types/Render Note.md @@ -17,14 +17,14 @@ For a simple example, we are going to create a render note that displays the cur To do so, first create an HTML code note with the following content: -``` +```html

        Current date & time

        The current date & time is ``` Now we need to add the script. Create another Code, but this time of JavaScript (frontend) language. Make sure the newly created note is a direct child of the HTML note created previously; with the following content: -``` +```javascript const $dateEl = api.$container.find(".date"); $dateEl.text(new Date()); ``` @@ -44,7 +44,7 @@ Here are the steps to creating a simple render note: 2. Create a child Code note with JSX as the language. As an example, use the following content: - ``` + ```jsx export default function() { return ( <> From e3595a43c2ab4b319ded52a17a7b8cde80272791 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Wed, 24 Dec 2025 00:41:08 +0200 Subject: [PATCH 23/23] docs(user): missing language tags for JSX code blocks --- .../User Guide/Note Types/Render Note.html | 45 ++--- .../Frontend Basics/Custom Widgets.html | 176 +++++++++--------- .../Launch Bar Widgets/Note Title Widget.html | 2 +- .../Scripting/Frontend Basics/Preact.html | 31 +-- .../Preact/Built-in components.html | 14 +- .../Scripting/Frontend Basics/Preact/CSS.html | 4 +- .../Developer Guide/Documentation.md | 2 +- docs/User Guide/User Guide.md | 2 +- .../User Guide/Note Types/Render Note.md | 4 +- .../Frontend Basics/Custom Widgets.md | 2 +- .../Launch Bar Widgets/Note Title Widget.md | 2 +- .../Scripting/Frontend Basics/Preact.md | 8 +- .../Preact/Built-in components.md | 4 +- .../Scripting/Frontend Basics/Preact/CSS.md | 2 +- 14 files changed, 146 insertions(+), 152 deletions(-) diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html index 9300b08cb..832156f7a 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Note Types/Render Note.html @@ -7,13 +7,10 @@ via an attribute.

        Creating a render note

          -
        1. Create a Code note - with the HTML language, with what needs to be displayed (for example - <p>Hello world.</p>).
        2. -
        3. Create a Render Note.
        4. -
        5. Assign the renderNote relation to +
        6. Create a Code note + with the HTML language, with what needs to be displayed (for example <p>Hello world.</p>).
        7. +
        8. Create a Render Note.
        9. +
        10. Assign the renderNote relation to point at the previously created code note.

        Legacy scripting using jQuery

        @@ -22,14 +19,14 @@ change parts of the note using JavaScript.

        For a simple example, we are going to create a render note that displays the current date in a field.

        -

        To do so, first create an HTML code note with the following content:

        <h1>Current date & time</h1>
        +

        To do so, first create an HTML code note with the following content:

        <h1>Current date & time</h1>
         The current date & time is <span class="date"></span>

        Now we need to add the script. Create another Code, but this time of JavaScript (frontend) language. Make sure the newly created note is a direct child of the HTML - note created previously; with the following content:

        const $dateEl = api.$container.find(".date");
        +  note created previously; with the following content:

        const $dateEl = api.$container.find(".date");
         $dateEl.text(new Date());
        -

        Now create a render note at any place and set its ~renderNote relation +

        Now create a render note at any place and set its ~renderNote relation to point to the HTML note. When the render note is accessed it will display:

        Current date & time @@ -42,33 +39,37 @@ $dateEl.text(new Date());

        need to provide a HTML anymore.

        Here are the steps to creating a simple render note:

          -
        1. Create a note of type Render Note.
        2. -
        3. +
        4. +

          Create a note of type Render Note.

          +
        5. +
        6. Create a child Code note with JSX as the language. -
          As an example, use the following content:

          export default function() {
          +      
          As an example, use the following content:

          export default function() {
               return (
                   <>
                       <p>Hello world.</p>
                   </>
               );
           }
          -
        7. -
        8. In the parent render note, define a ~renderNote relation - pointing to the newly created child.
        9. -
        10. Refresh the render note and it should display a “Hello world” message.
        11. + +
        12. +

          In the parent render note, define a ~renderNote relation pointing + to the newly created child.

          +
        13. +
        14. +

          Refresh the render note and it should display a “Hello world” message.

          +

        Refreshing the note

        It's possible to refresh the note via:

        Examples

        \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html index 56a1c3491..7617ab9a4 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.html @@ -4,31 +4,29 @@

        Preact with JSX vs. vanilla jQuery

        In older versions of Trilium, custom widgets were exclusively written in a combination of jQuery with Trilium's internal widget architecture - (e.g., BasicWidget, NoteContextAwareWidget).

        + (e.g., BasicWidget, NoteContextAwareWidget).

        Starting with v0.101.0, custom widgets can also be written in JSX using the Preact framework. Both legacy and Preact widgets have the same capabilities, with a single difference:

          -
        • Preact widgets are content-sized by default whereas legacy widgets need - this.contentSized()applied in the constructor. For more information, - see the corresponding section in Troubleshooting.
        • +
        • Preact widgets are content-sized by default whereas legacy widgets need this.contentSized() applied + in the constructor. For more information, see the corresponding section + in Troubleshooting.

        Wherever possible, widget examples will be both in the legacy and Preact format.

        Creating a custom widget

          -
        1. Create a Code note.
        2. -
        3. Set the language to: +
        4. Create a Code note.
        5. +
        6. Set the language to:
            -
          1. JavaScript (frontend) for legacy widgets using jQuery.
          2. -
          3. JSX for Preact widgets. You might need to go to Options → Code to enable +
          4. JavaScript (frontend) for legacy widgets using jQuery.
          5. +
          6. JSX for Preact widgets. You might need to go to Options → Code to enable the language first.
          -
        7. -
        8. Apply the #widget label.
        9. + +
        10. Apply the #widget label.

        Getting started with a simple example

        Let's start by creating a widget that shows a message near the content @@ -62,88 +60,80 @@ export default defineWidget({ should appear underneath the content area.

        Widget location (parent widget)

        A widget can be placed in one of the following sections of the applications:

        -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
        Value for parentWidget - DescriptionSample widgetSpecial requirements
        left-pane - Appears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-pane - In the content area. If a split is open, the widget will span all of the - splits.See example above.None.
        note-detail-pane - -

        In the content area, inside the note detail area. If a split is open, - the widget will be contained inside the split.

        -

        This is ideal if the widget is note-specific.

        -
        Note context aware widget - -
          -
        • The widget must export a class and not an - instance of the class (e.g. no new) because - it needs to be multiplied for each note, so that splits work correctly.
        • -
        • Since the class is exported instead of an - instance, the parentWidget getter must be - static, otherwise the widget is ignored.
        • -
        -
        right-pane - In the Right Sidebar, - as a dedicated section.Right pane widget - -
          -
        • Although not mandatory, it's best to use a RightPanelWidget instead - of a BasicWidget or a NoteContextAwareWidget.
        • -
        -
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Value for parentWidget + DescriptionSample widgetSpecial requirements
        left-pane + Appears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-pane + In the content area. If a split is open, the widget will span all of the + splits.See example above.None.
        note-detail-pane + +

        In the content area, inside the note detail area. If a split is open, + the widget will be contained inside the split.

        +

        This is ideal if the widget is note-specific.

        +
        Note context aware widget + +
          +
        • The widget must export a class and not an instance of the class + (e.g. no new) because it needs to be multiplied for each note, + so that splits work correctly.
        • +
        • Since the class is exported instead of an instance, the parentWidget getter + must be static, otherwise the widget is ignored.
        • +
        +
        right-pane + In the Right Sidebar, + as a dedicated section.Right pane widget + +
          +
        • Although not mandatory, it's best to use a RightPanelWidget instead + of a BasicWidget or a NoteContextAwareWidget.
        • +
        +
        -
        -

        To position the widget somewhere else, just change the value passed to - get parentWidget()for legacy widgets or the parent field - for Preact. Do note that some positions such as note-detail-pane and - right-panehave special requirements that need to be accounted for - (see the table above).

        +

        To position the widget somewhere else, just change the value passed to get parentWidget() for + legacy widgets or the parent field for Preact. Do note that + some positions such as note-detail-pane and right-pane have + special requirements that need to be accounted for (see the table above).

        Launch bar widgets

        Launch bar widgets are similar to Custom widgets but are specific to the Launch Bar. diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.html index 70ab3f1ac..5386d0052 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.html @@ -30,7 +30,7 @@ class NoteTitleWidget extends api.NoteContextAwareWidget { } module.exports = new NoteTitleWidget();

        -

        Preact widget (v0.101.0+)

        import { defineLauncherWidget, useActiveNoteContext } from "trilium:preact";
        +

        Preact widget (v0.101.0+)

        import { defineLauncherWidget, useActiveNoteContext } from "trilium:preact";
         
         export default defineLauncherWidget({
             render: () => {
        diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact.html
        index 82c823548..1a2ce8779 100644
        --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact.html	
        +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact.html	
        @@ -2,9 +2,9 @@
           support for JSX.

        Preact can be used for:

        To get started, the first step is to enable JSX in the list of Code languages. @@ -20,22 +20,23 @@

        Import/exports

        When using Preact with JSX, there is a special syntax which provides ES-like - imports. This import syntax makes way for a more intuitive that - doesn't make use of global objects and paves the way for better auto-completion - support that might be introduced in the future. 

        + imports. This import syntax makes way for + a more intuitive that doesn't make use of global objects and paves the + way for better auto-completion support that might be introduced in the + future. 

        API imports

        -

        Instead of:

        api.showMessage("Hello");
        -

        the JSX version looks like this:

        import { showMessage } from "trilium:api";
        +

        Instead of:

        api.showMessage("Hello");
        +

        the JSX version looks like this:

        import { showMessage } from "trilium:api";
         showMessage("hello");

        Preact API imports (hooks, components)

        There's a new Script API dedicated to Preact, which provides shared components that are also used by Trilium - internally as well as hooks, for example.

        import { useState } from "trilium:preact";
        +  internally as well as hooks, for example.

        import { useState } from "trilium:preact";
         const [ myState, setMyState ] = useState("Hi");

        Exporting

        JSX notes can export a component for use in Render Note or for Component libraries: 

        export default function() {
        +  href="#root/_help_Bqde6BvPo05g">Component libraries: 

        export default function() {
             return (
                 <>
                     <p>Hello world.</p>
        @@ -43,13 +44,15 @@ const [ myState, setMyState ] = useState("Hi");
        ); }

        Import/export are not required

        -

        These imports are syntactic sugar meant to replace the usage for the api global - object (see Script API). 

        +

        These imports are syntactic sugar meant to replace the usage for the + apiglobal object (see Script API). 

        Under the hood

        Unlike JavaScript, JSX requires pre-processing to turn it into JavaScript diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.html index 27aa57582..209519800 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.html @@ -7,8 +7,8 @@ also available to Custom Widgets and  Render Note.

        -

        To use these components, simply import them from trilium:preact:

        import { ActionButton, Button, LinkButton } from "trilium:preact";
        -

        and then use them:

        export default function MyRenderNote() {
        +

        To use these components, simply import them from trilium:preact:

        import { ActionButton, Button, LinkButton } from "trilium:preact";
        +

        and then use them:

        export default function MyRenderNote() {
             const onClick = () => showMessage("A button was pressed");
             
             return (
        @@ -33,12 +33,12 @@
           to custom widgets and JSX render notes.

        To use it, simply:

          -
        1. Create a render note.
        2. -
        3. Create a child code note of JSX type with the content of this file:  +
        4. Create a render note.
        5. +
        6. Create a child code note of JSX type with the content of this file:  Widget showcase
        7. -
        8. Set the ~renderNote relation of the parent note to the child - note.
        9. -
        10. Refresh the render note to see the results.
        11. +
        12. Set the ~renderNote relation of the parent + note to the child note.
        13. +
        14. Refresh the render note to see the results.
        \ No newline at end of file diff --git a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.html b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.html index 2cdec3295..a148c29f2 100644 --- a/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.html +++ b/apps/server/src/assets/doc_notes/en/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.html @@ -1,4 +1,4 @@ -

        Inline styles

        <div style={{
        +

        Inline styles

        <div style={{
             display: "flex",
             height: "53px",
             width: "fit-content",
        @@ -9,4 +9,4 @@
         

        Custom CSS file

        Simply create a Custom app-wide CSS. Make sure the class names are unique enough to not intersect with other - UI elements, consider adding a prefix (e.g. x-mywidget-).

        \ No newline at end of file + UI elements, consider adding a prefix (e.g. x-mywidget-).

        \ No newline at end of file diff --git a/docs/Developer Guide/Developer Guide/Documentation.md b/docs/Developer Guide/Developer Guide/Documentation.md index 6e8ff9fe3..a7dbaff23 100644 --- a/docs/Developer Guide/Developer Guide/Documentation.md +++ b/docs/Developer Guide/Developer Guide/Documentation.md @@ -1,5 +1,5 @@ # Documentation -There are multiple types of documentation for Trilium: +There are multiple types of documentation for Trilium: * The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing F1. * The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers. diff --git a/docs/User Guide/User Guide.md b/docs/User Guide/User Guide.md index d264728b9..3e5537135 100644 --- a/docs/User Guide/User Guide.md +++ b/docs/User Guide/User Guide.md @@ -1,5 +1,5 @@ # User Guide -cTrilium is an open-source solution for note-taking and organizing a personal knowledge base. Use it locally on your desktop, or sync it with your self-hosted server to keep your notes everywhere you go. +Trilium is an open-source solution for note-taking and organizing a personal knowledge base. Use it locally on your desktop, or sync it with your self-hosted server to keep your notes everywhere you go. > [!TIP] > The same documentation can be accessed locally from within the Trilium Notes application by pressing F1. diff --git a/docs/User Guide/User Guide/Note Types/Render Note.md b/docs/User Guide/User Guide/Note Types/Render Note.md index c0c4e4eea..01e2e1c24 100644 --- a/docs/User Guide/User Guide/Note Types/Render Note.md +++ b/docs/User Guide/User Guide/Note Types/Render Note.md @@ -17,7 +17,7 @@ For a simple example, we are going to create a render note that displays the cur To do so, first create an HTML code note with the following content: -```html +```

        Current date & time

        The current date & time is ``` @@ -44,7 +44,7 @@ Here are the steps to creating a simple render note: 2. Create a child Code note with JSX as the language. As an example, use the following content: - ```jsx + ``` export default function() { return ( <> diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md index 392f5a734..dec4cfd3f 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Custom Widgets.md @@ -63,7 +63,7 @@ export default defineWidget({ A widget can be placed in one of the following sections of the applications: -
        Value for parentWidgetDescriptionSample widgetSpecial requirements
        left-paneAppears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-paneIn the content area. If a split is open, the widget will span all of the splits.See example above.None.
        note-detail-pane

        In the content area, inside the note detail area. If a split is open, the widget will be contained inside the split.

        This is ideal if the widget is note-specific.

        Note context aware widget
        • The widget must export a class and not an instance of the class (e.g. no new) because it needs to be multiplied for each note, so that splits work correctly.
        • Since the class is exported instead of an instance, the parentWidget getter must be static, otherwise the widget is ignored.
        right-paneIn the Right Sidebar, as a dedicated section.Right pane widget
        • Although not mandatory, it's best to use a RightPanelWidget instead of a BasicWidget or a NoteContextAwareWidget.
        +
        Value for parentWidgetDescriptionSample widgetSpecial requirements
        left-paneAppears within the same pane that holds the Note Tree.Same as above, with only a different parentWidget.None.
        center-paneIn the content area. If a split is open, the widget will span all of the splits.See example above.None.
        note-detail-pane

        In the content area, inside the note detail area. If a split is open, the widget will be contained inside the split.

        This is ideal if the widget is note-specific.

        Note context aware widget
        • The widget must export a class and not an instance of the class (e.g. no new) because it needs to be multiplied for each note, so that splits work correctly.
        • Since the class is exported instead of an instance, the parentWidget getter must be static, otherwise the widget is ignored.
        right-paneIn the Right Sidebar, as a dedicated section.Right pane widget
        • Although not mandatory, it's best to use a RightPanelWidget instead of a BasicWidget or a NoteContextAwareWidget.
        To position the widget somewhere else, just change the value passed to `get parentWidget()` for legacy widgets or the `parent` field for Preact. Do note that some positions such as `note-detail-pane` and `right-pane` have special requirements that need to be accounted for (see the table above). diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.md index 78a69eb8a..c892e3365 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Launch Bar Widgets/Note Title Widget.md @@ -35,7 +35,7 @@ module.exports = new NoteTitleWidget(); ## Preact widget (v0.101.0+) -``` +```jsx import { defineLauncherWidget, useActiveNoteContext } from "trilium:preact"; export default defineLauncherWidget({ diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md index db3811479..6f05765fc 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact.md @@ -19,13 +19,13 @@ When using Preact with JSX, there is a special syntax which provides ES-like imp Instead of: -``` +```jsx api.showMessage("Hello"); ``` the JSX version looks like this: -``` +```jsx import { showMessage } from "trilium:api"; showMessage("hello"); ``` @@ -34,7 +34,7 @@ showMessage("hello"); There's a new Script API dedicated to Preact, which provides shared components that are also used by Trilium internally as well as hooks, for example. -``` +```jsx import { useState } from "trilium:preact"; const [ myState, setMyState ] = useState("Hi"); ``` @@ -43,7 +43,7 @@ const [ myState, setMyState ] = useState("Hi"); JSX notes can export a component for use in Render Note or for Component libraries:  -``` +```jsx export default function() { return ( <> diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md index 8d3c86e4f..db05c9255 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/Built-in components.md @@ -5,13 +5,13 @@ Trilium comes with its own set of Preact components, some of which are also avai To use these components, simply import them from `trilium:preact`: -``` +```jsx import { ActionButton, Button, LinkButton } from "trilium:preact"; ``` and then use them: -``` +```jsx export default function MyRenderNote() { const onClick = () => showMessage("A button was pressed"); diff --git a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md index bda9f195a..3b959261f 100644 --- a/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md +++ b/docs/User Guide/User Guide/Scripting/Frontend Basics/Preact/CSS.md @@ -1,7 +1,7 @@ # CSS ## Inline styles -``` +```jsx