Compare commits

..

1 Commits

Author SHA1 Message Date
renovate[bot]
660791d781 fix(deps): update dependency @codemirror/view to v6.39.6 2025-12-23 11:40:42 +00:00
133 changed files with 1176 additions and 3985 deletions

4
.envrc
View File

@@ -1,3 +1 @@
if has nix; then
use flake
fi
use flake

View File

@@ -1,5 +1,3 @@
import { MIME_TYPES_DICT } from "@triliumnext/commons";
import cssClassManager from "../services/css_class_manager.js";
import type { Froca } from "../services/froca-interface.js";
import noteAttributeCache from "../services/note_attribute_cache.js";
@@ -599,9 +597,8 @@ export default class FNote {
return "bx bx-folder";
}
return "bx bx-note";
} else if (this.type === "code") {
const correspondingMimeType = MIME_TYPES_DICT.find(m => m.mime === this.mime);
return correspondingMimeType?.icon ?? NOTE_TYPE_ICONS.code;
} else if (this.type === "code" && this.mime.startsWith("text/x-sql")) {
return "bx bx-data";
}
return NOTE_TYPE_ICONS[this.type];
}
@@ -992,10 +989,6 @@ export default class FNote {
);
}
isJsx() {
return (this.type === "code" && this.mime === "text/jsx");
}
/** @returns true if this note is HTML */
isHtml() {
return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html";
@@ -1003,7 +996,7 @@ export default class FNote {
/** @returns JS script environment - either "frontend" or "backend" */
getScriptEnv() {
if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith("env=frontend")) || this.isJsx()) {
if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith("env=frontend"))) {
return "frontend";
}
@@ -1025,7 +1018,7 @@ export default class FNote {
* @returns a promise that resolves when the script has been run. Additionally, for front-end notes, the promise will contain the value that is returned by the script.
*/
async executeScript() {
if (!(this.isJavaScript() || this.isJsx())) {
if (!this.isJavaScript()) {
throw new Error(`Note ${this.noteId} is of type ${this.type} and mime ${this.mime} and thus cannot be executed`);
}

View File

@@ -184,7 +184,7 @@ export default class DesktopLayout {
.child(new HighlightsListWidget())
.child(...this.customWidgets.get("right-pane"))
)
.optChild(isNewLayout, <RightPanelContainer widgetsByParent={this.customWidgets} />)
.optChild(isNewLayout, <RightPanelContainer customWidgets={this.customWidgets.get("right-pane")} />)
)
.optChild(!launcherPaneIsHorizontal && isNewLayout, <StatusBar />)
)

View File

@@ -1,15 +1,10 @@
import { h, VNode } from "preact";
import BasicWidget, { ReactWrappedWidget } from "../widgets/basic_widget.js";
import RightPanelWidget from "../widgets/right_panel_widget.js";
import froca from "./froca.js";
import type { Entity } from "./frontend_script_api.js";
import { WidgetDefinitionWithType } from "./frontend_script_api_preact.js";
import { t } from "./i18n.js";
import ScriptContext from "./script_context.js";
import server from "./server.js";
import toastService, { showErrorForScriptNote } from "./toast.js";
import utils, { getErrorMessage } from "./utils.js";
import toastService, { showError } from "./toast.js";
import froca from "./froca.js";
import utils from "./utils.js";
import { t } from "./i18n.js";
import type { Entity } from "./frontend_script_api.js";
// TODO: Deduplicate with server.
export interface Bundle {
@@ -19,13 +14,9 @@ export interface Bundle {
allNoteIds: string[];
}
type LegacyWidget = (BasicWidget | RightPanelWidget) & {
interface Widget {
parentWidget?: string;
};
type WithNoteId<T> = T & {
_noteId: string;
};
export type Widget = WithNoteId<(LegacyWidget | WidgetDefinitionWithType)>;
}
async function getAndExecuteBundle(noteId: string, originEntity = null, script = null, params = null) {
const bundle = await server.post<Bundle>(`script/bundle/${noteId}`, {
@@ -36,8 +27,6 @@ async function getAndExecuteBundle(noteId: string, originEntity = null, script =
return await executeBundle(bundle, originEntity);
}
export type ParentName = "left-pane" | "center-pane" | "note-detail-pane" | "right-pane";
export async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $container?: JQuery<HTMLElement>) {
const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity, $container);
@@ -46,14 +35,24 @@ 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) {
showErrorForScriptNote(bundle.noteId, t("toast.bundle-error.message", { message: e.message }));
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
})
});
logError("Widget initialization failed: ", e);
}
}
async function executeStartupBundles() {
const isMobile = utils.isMobile();
const scriptBundles = await server.get<Bundle[]>(`script/startup${ isMobile ? "?mobile=true" : ""}`);
const scriptBundles = await server.get<Bundle[]>("script/startup" + (isMobile ? "?mobile=true" : ""));
for (const bundle of scriptBundles) {
await executeBundle(bundle);
@@ -61,99 +60,68 @@ async function executeStartupBundles() {
}
export class WidgetsByParent {
private legacyWidgets: Record<string, WithNoteId<LegacyWidget>[]>;
private preactWidgets: Record<string, WithNoteId<WidgetDefinitionWithType>[]>;
private byParent: Record<string, Widget[]>;
constructor() {
this.legacyWidgets = {};
this.preactWidgets = {};
this.byParent = {};
}
add(widget: Widget) {
let hasParentWidget = false;
let isPreact = false;
if ("type" in widget && widget.type === "preact-widget") {
// React-based script.
const reactWidget = widget as WithNoteId<WidgetDefinitionWithType>;
this.preactWidgets[reactWidget.parent] = this.preactWidgets[reactWidget.parent] || [];
this.preactWidgets[reactWidget.parent].push(reactWidget);
isPreact = true;
hasParentWidget = !!reactWidget.parent;
} else if ("parentWidget" in widget && widget.parentWidget) {
this.legacyWidgets[widget.parentWidget] = this.legacyWidgets[widget.parentWidget] || [];
this.legacyWidgets[widget.parentWidget].push(widget);
hasParentWidget = !!widget.parentWidget;
if (!widget.parentWidget) {
console.log(`Custom widget does not have mandatory 'parentWidget' property defined`);
return;
}
if (!hasParentWidget) {
showErrorForScriptNote(widget._noteId, t("toast.widget-missing-parent", {
property: isPreact ? "parent" : "parentWidget"
}));
}
this.byParent[widget.parentWidget] = this.byParent[widget.parentWidget] || [];
this.byParent[widget.parentWidget].push(widget);
}
get(parentName: ParentName) {
const widgets: (BasicWidget | VNode)[] = this.getLegacyWidgets(parentName);
for (const preactWidget of this.getPreactWidgets(parentName)) {
const el = h(preactWidget.render, {});
const widget = new ReactWrappedWidget(el);
widget.contentSized();
if (preactWidget.position) {
widget.position = preactWidget.position;
}
widgets.push(widget);
get(parentName: string) {
if (!this.byParent[parentName]) {
return [];
}
return widgets;
}
getLegacyWidgets(parentName: ParentName): (BasicWidget | RightPanelWidget)[] {
if (!this.legacyWidgets[parentName]) return [];
return (
this.legacyWidgets[parentName]
this.byParent[parentName]
// previously, custom widgets were provided as a single instance, but that has the disadvantage
// for splits where we actually need multiple instaces and thus having a class to instantiate is better
// https://github.com/zadam/trilium/issues/4274
.map((w: any) => (w.prototype ? new w() : w))
);
}
getPreactWidgets(parentName: ParentName) {
return this.preactWidgets[parentName] ?? [];
}
}
async function getWidgetBundlesByParent() {
const scriptBundles = await server.get<Bundle[]>("script/widgets");
const widgetsByParent = new WidgetsByParent();
try {
const scriptBundles = await server.get<Bundle[]>("script/widgets");
for (const bundle of scriptBundles) {
let widget;
for (const bundle of scriptBundles) {
let widget;
try {
widget = await executeBundle(bundle);
if (widget) {
widget._noteId = bundle.noteId;
widgetsByParent.add(widget);
}
} catch (e: any) {
const noteId = bundle.noteId;
showErrorForScriptNote(noteId, t("toast.bundle-error.message", { message: e.message }));
logError("Widget initialization failed: ", e);
continue;
try {
widget = await executeBundle(bundle);
if (widget) {
widget._noteId = bundle.noteId;
widgetsByParent.add(widget);
}
} 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
})
});
logError("Widget initialization failed: ", e);
continue;
}
} catch (e) {
toastService.showPersistent({
id: `custom-widget-list-failure`,
title: t("toast.widget-list-error.title"),
message: getErrorMessage(e),
icon: "bx bx-error-circle"
});
}
return widgetsByParent;

View File

@@ -1,27 +1,26 @@
import { dayjs, formatLogMessage } from "@triliumnext/commons";
import appContext from "../components/app_context.js";
import type Component from "../components/component.js";
import type NoteContext from "../components/note_context.js";
import type FNote from "../entities/fnote.js";
import BasicWidget, { ReactWrappedWidget } from "../widgets/basic_widget.js";
import NoteContextAwareWidget from "../widgets/note_context_aware_widget.js";
import RightPanelWidget from "../widgets/right_panel_widget.js";
import dateNotesService from "./date_notes.js";
import dialogService from "./dialog.js";
import froca from "./froca.js";
import { preactAPI } from "./frontend_script_api_preact.js";
import { t } from "./i18n.js";
import server from "./server.js";
import utils from "./utils.js";
import toastService from "./toast.js";
import linkService from "./link.js";
import froca from "./froca.js";
import noteTooltipService from "./note_tooltip.js";
import protectedSessionService from "./protected_session.js";
import dateNotesService from "./date_notes.js";
import searchService from "./search.js";
import server from "./server.js";
import shortcutService from "./shortcuts.js";
import SpacedUpdate from "./spaced_update.js";
import toastService from "./toast.js";
import utils from "./utils.js";
import RightPanelWidget from "../widgets/right_panel_widget.js";
import ws from "./ws.js";
import appContext from "../components/app_context.js";
import NoteContextAwareWidget from "../widgets/note_context_aware_widget.js";
import BasicWidget, { ReactWrappedWidget } from "../widgets/basic_widget.js";
import SpacedUpdate from "./spaced_update.js";
import shortcutService from "./shortcuts.js";
import dialogService from "./dialog.js";
import type FNote from "../entities/fnote.js";
import { t } from "./i18n.js";
import { dayjs } from "@triliumnext/commons";
import type NoteContext from "../components/note_context.js";
import type Component from "../components/component.js";
import { formatLogMessage } from "@triliumnext/commons";
/**
* A whole number
@@ -465,8 +464,6 @@ export interface Api {
* Log given message to the log pane in UI
*/
log(message: string | object): void;
preact: typeof preactAPI;
}
/**
@@ -536,8 +533,9 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
return params.map((p) => {
if (typeof p === "function") {
return `!@#Function: ${p.toString()}`;
} else {
return p;
}
return p;
});
}
@@ -564,8 +562,9 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
await ws.waitForMaxKnownEntityChangeId();
return ret.executionResult;
} else {
throw new Error(`server error: ${ret.error}`);
}
throw new Error(`server error: ${ret.error}`);
};
this.runOnBackend = async (func, params = []) => {
@@ -722,8 +721,6 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig
this.logMessages[noteId].push(message);
this.logSpacedUpdates[noteId].scheduleUpdate();
};
this.preact = preactAPI;
}
export default FrontendScriptApi as any as {

View File

@@ -1,101 +0,0 @@
import { Fragment, h, VNode } from "preact";
import * as hooks from "preact/hooks";
import ActionButton from "../widgets/react/ActionButton";
import Admonition from "../widgets/react/Admonition";
import Button from "../widgets/react/Button";
import CKEditor from "../widgets/react/CKEditor";
import Collapsible from "../widgets/react/Collapsible";
import Dropdown from "../widgets/react/Dropdown";
import FormCheckbox from "../widgets/react/FormCheckbox";
import FormDropdownList from "../widgets/react/FormDropdownList";
import { FormFileUploadActionButton, FormFileUploadButton } from "../widgets/react/FormFileUpload";
import FormGroup from "../widgets/react/FormGroup";
import { FormDropdownDivider, FormDropdownSubmenu, FormListItem } from "../widgets/react/FormList";
import FormRadioGroup from "../widgets/react/FormRadioGroup";
import FormText from "../widgets/react/FormText";
import FormTextArea from "../widgets/react/FormTextArea";
import FormTextBox from "../widgets/react/FormTextBox";
import FormToggle from "../widgets/react/FormToggle";
import * as triliumHooks from "../widgets/react/hooks";
import Icon from "../widgets/react/Icon";
import LinkButton from "../widgets/react/LinkButton";
import LoadingSpinner from "../widgets/react/LoadingSpinner";
import Modal from "../widgets/react/Modal";
import NoteAutocomplete from "../widgets/react/NoteAutocomplete";
import NoteLink from "../widgets/react/NoteLink";
import RawHtml from "../widgets/react/RawHtml";
import Slider from "../widgets/react/Slider";
import RightPanelWidget from "../widgets/sidebar/RightPanelWidget";
export interface WidgetDefinition {
parent: "right-pane",
render: () => VNode,
position?: number,
}
export interface WidgetDefinitionWithType extends WidgetDefinition {
type: "preact-widget"
}
export interface LauncherWidgetDefinitionWithType {
type: "preact-launcher-widget"
render: () => VNode
}
export const preactAPI = Object.freeze({
// Core
h,
Fragment,
/**
* Method that must be run for widget scripts that run on Preact, using JSX. The method just returns the same definition, reserved for future typechecking and perhaps validation purposes.
*
* @param definition the widget definition.
*/
defineWidget(definition: WidgetDefinition) {
return {
type: "preact-widget",
...definition
};
},
defineLauncherWidget(definition: Omit<LauncherWidgetDefinitionWithType, "type">) {
return {
type: "preact-launcher-widget",
...definition
};
},
// Basic widgets
ActionButton,
Admonition,
Button,
CKEditor,
Collapsible,
Dropdown,
FormCheckbox,
FormDropdownList,
FormFileUploadButton, FormFileUploadActionButton,
FormGroup,
FormListItem, FormDropdownDivider, FormDropdownSubmenu,
FormRadioGroup,
FormText,
FormTextArea,
FormTextBox,
FormToggle,
Icon,
LinkButton,
LoadingSpinner,
Modal,
NoteAutocomplete,
NoteLink,
RawHtml,
Slider,
// Specialized widgets
RightPanelWidget,
...hooks,
...triliumHooks
});

View File

@@ -1,10 +1,6 @@
import { h, VNode } from "preact";
import type FNote from "../entities/fnote.js";
import { renderReactWidgetAtElement } from "../widgets/react/react_utils.jsx";
import bundleService, { type Bundle } from "./bundle.js";
import froca from "./froca.js";
import server from "./server.js";
import bundleService, { type Bundle } from "./bundle.js";
import type FNote from "../entities/fnote.js";
async function render(note: FNote, $el: JQuery<HTMLElement>) {
const relations = note.getRelations("renderNote");
@@ -21,34 +17,12 @@ async function render(note: FNote, $el: JQuery<HTMLElement>) {
$scriptContainer.append(bundle.html);
// async so that scripts cannot block trilium execution
bundleService.executeBundle(bundle, note, $scriptContainer).then(result => {
// Render JSX
if (bundle.html === "") {
renderIfJsx(bundle, result, $el);
}
});
bundleService.executeBundle(bundle, note, $scriptContainer);
}
return renderNoteIds.length > 0;
}
async function renderIfJsx(bundle: Bundle, result: unknown, $el: JQuery<HTMLElement>) {
// Ensure the root script note is actually a JSX.
const rootScriptNoteId = await froca.getNote(bundle.noteId);
if (rootScriptNoteId?.mime !== "text/jsx") return;
// Ensure the output is a valid el.
if (typeof result !== "function") return;
// Obtain the parent component.
const closestComponent = glob.getComponentByEl($el.closest(".component")[0]);
if (!closestComponent) return;
// Render the element.
const el = h(result as () => VNode, {});
renderReactWidgetAtElement(closestComponent, el, $el[0]);
}
export default {
render
};

View File

@@ -133,11 +133,11 @@ async function call<T>(method: string, url: string, componentId?: string, option
};
ipc.send("server-request", {
requestId,
headers,
method,
requestId: requestId,
headers: headers,
method: method,
url: `/${window.glob.baseApiUrl}${url}`,
data
data: data
});
})) as any;
} else {
@@ -161,7 +161,7 @@ function ajax(url: string, method: string, data: unknown, headers: Headers, sile
const options: JQueryAjaxSettings = {
url: window.glob.baseApiUrl + url,
type: method,
headers,
headers: headers,
timeout: 60000,
success: (body, textStatus, jqXhr) => {
const respHeaders: Headers = {};
@@ -288,8 +288,8 @@ async function reportError(method: string, url: string, statusCode: number, resp
t("server.unknown_http_error_content", { statusCode, method, url, message: messageStr }),
15_000);
}
const { logError } = await import("./ws.js");
logError(`${statusCode} ${method} ${url} - ${message}`);
const { throwError } = await import("./ws.js");
throwError(`${statusCode} ${method} ${url} - ${message}`);
}
}

View File

@@ -1,8 +1,5 @@
import { signal } from "@preact/signals";
import appContext from "../components/app_context.js";
import froca from "./froca.js";
import { t } from "./i18n.js";
import utils from "./utils.js";
export interface ToastOptions {
@@ -64,24 +61,6 @@ function showErrorTitleAndMessage(title: string, message: string, timeout = 1000
});
}
export async function showErrorForScriptNote(noteId: string, message: string) {
const note = await froca.getNote(noteId, true);
showPersistent({
id: `custom-widget-failure-${noteId}`,
title: t("toast.scripting-error", { title: note?.title ?? "" }),
icon: note?.getIcon() ?? "bx bx-error-circle",
message,
timeout: 15_000,
buttons: [
{
text: t("toast.open-script-note"),
onClick: () => appContext.tabManager.openInNewTab(noteId, null, true)
}
]
});
}
//#region Toast store
export const toasts = signal<ToastOptionsWithRequiredId[]>([]);
@@ -95,7 +74,7 @@ function addToast(opts: ToastOptions) {
function updateToast(id: string, partial: Partial<ToastOptions>) {
toasts.value = toasts.value.map(toast => {
if (toast.id === id) {
return { ...toast, ...partial };
return { ...toast, ...partial }
}
return toast;
});

View File

@@ -717,6 +717,7 @@ table.promoted-attributes-in-tooltip th {
.tooltip {
font-size: var(--main-font-size) !important;
z-index: calc(var(--ck-z-panel) - 1) !important;
white-space: pre-wrap;
}
.tooltip.tooltip-top {

View File

@@ -817,8 +817,7 @@
},
"inherited_attribute_list": {
"title": "继承的属性",
"no_inherited_attributes": "没有继承的属性。",
"none": "无"
"no_inherited_attributes": "没有继承的属性。"
},
"note_info_widget": {
"note_id": "笔记 ID",
@@ -2201,8 +2200,5 @@
"toggle": "切换右侧面板",
"custom_widget_go_to_source": "跳转到源码",
"empty_message": "这篇笔记没有展示内容"
},
"attributes_panel": {
"title": "笔记属性"
}
}

View File

@@ -21,17 +21,8 @@
},
"bundle-error": {
"title": "Failed to load a custom script",
"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"
},
"widget-render-error": {
"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",
"scripting-error": "Custom script error: {{title}}"
"message": "Script from note with ID \"{{id}}\", titled \"{{title}}\" could not be executed due to:\n\n{{message}}"
}
},
"add_link": {
"add_link": "Add link",
@@ -1780,8 +1771,7 @@
"note_type_switcher_others": "Other note type",
"note_type_switcher_templates": "Template",
"note_type_switcher_collection": "Collection",
"edited_notes": "Notes edited on this day",
"promoted_attributes": "Promoted attributes"
"edited_notes": "Edited notes"
},
"search_result": {
"no_notes_found": "No notes have been found for given search parameters.",

View File

@@ -523,8 +523,7 @@
},
"toc": {
"table_of_contents": "Sommario",
"options": "Opzioni",
"no_headings": "Nessun titolo."
"options": "Opzioni"
},
"table_of_contents": {
"title": "Sommario",
@@ -557,13 +556,7 @@
},
"highlights_list_2": {
"title": "Punti salienti",
"options": "Opzioni",
"title_with_count_one": "{{count}} evidenza",
"title_with_count_many": "{{count}} evidenze",
"title_with_count_other": "{{count}} evidenze",
"modal_title": "Configura elenco dei punti salienti",
"menu_configure": "Configura elenco dei punti salienti...",
"no_highlights": "Nessun punto saliente trovato."
"options": "Opzioni"
},
"quick-search": {
"placeholder": "Ricerca rapida",
@@ -1395,8 +1388,7 @@
},
"inherited_attribute_list": {
"title": "Attributi ereditati",
"no_inherited_attributes": "Nessun attributo ereditato.",
"none": "nessuno"
"no_inherited_attributes": "Nessun attributo ereditato."
},
"note_info_widget": {
"note_id": "ID nota",
@@ -1408,8 +1400,7 @@
"calculate": "calcolare",
"subtree_size": "(dimensione del sottoalbero: {{size}} in {{count}} note)",
"title": "Nota informativa",
"show_similar_notes": "Mostra note simili",
"mime": "Tipo MIME"
"show_similar_notes": "Mostra note simili"
},
"note_map": {
"open_full": "Espandi completamente",
@@ -2104,20 +2095,14 @@
"background_effects_title": "Gli effetti di sfondo sono ora stabili",
"background_effects_message": "Sui dispositivi Windows, gli effetti di sfondo sono ora completamente stabili. Gli effetti di sfondo aggiungono un tocco di colore all'interfaccia utente sfocando lo sfondo retrostante. Questa tecnica è utilizzata anche in altre applicazioni come Esplora risorse di Windows.",
"background_effects_button": "Abilita gli effetti di sfondo",
"dismiss": "Congedare",
"new_layout_title": "Nuovo layout",
"new_layout_message": "Abbiamo introdotto un layout modernizzato per Trilium. La barra multifunzione è stata rimossa e integrata perfettamente nell'interfaccia principale, con una nuova barra di stato e sezioni espandibili (come gli attributi promossi) che assumono le funzioni chiave.\n\nIl nuovo layout è abilitato di default e può essere temporaneamente disabilitato tramite Opzioni → Aspetto.",
"new_layout_button": "Maggiori informazioni"
"dismiss": "Congedare"
},
"settings": {
"related_settings": "Impostazioni correlate"
},
"settings_appearance": {
"related_code_blocks": "Schema di colori per i blocchi di codice nelle note di testo",
"related_code_notes": "Schema di colori per le note del codice",
"ui": "Interfaccia utente",
"ui_old_layout": "Vecchio layout",
"ui_new_layout": "Nuovo layout"
"related_code_notes": "Schema di colori per le note del codice"
},
"units": {
"percentage": "%"
@@ -2174,18 +2159,13 @@
"execute_script": "Esegui script",
"execute_script_description": "Questa nota è una nota di script. Clicca per eseguire lo script.",
"execute_sql": "Esegui SQL",
"execute_sql_description": "Questa nota è una nota SQL. Clicca per eseguire la query SQL.",
"shared_copy_to_clipboard": "Copia link negli appunti",
"shared_open_in_browser": "Apri il link nel browser",
"shared_unshare": "Rimuovi condivisione"
"execute_sql_description": "Questa nota è una nota SQL. Clicca per eseguire la query SQL."
},
"breadcrumb": {
"workspace_badge": "Area di lavoro",
"scroll_to_top_title": "Vai all'inizio della nota",
"hoisted_badge": "Sollevato",
"hoisted_badge_title": "Abbassato",
"create_new_note": "Crea nuova nota secondaria",
"empty_hide_archived_notes": "Nascondi note archiviate"
"hoisted_badge_title": "Abbassato"
},
"status_bar": {
"language_title": "Cambia lingua dei contenuti",
@@ -2211,14 +2191,5 @@
"note_paths_other": "{{count}} percorsi",
"note_paths_title": "Nota percorsi",
"code_note_switcher": "Cambia modalità lingua"
},
"attributes_panel": {
"title": "Attributi delle note"
},
"right_pane": {
"empty_message": "Nulla da segnalare per questa nota",
"empty_button": "Nascondi il pannello",
"toggle": "Attiva/disattiva pannello destro",
"custom_widget_go_to_source": "Vai al codice sorgente"
}
}

View File

@@ -1621,8 +1621,7 @@
},
"inherited_attribute_list": {
"title": "継承属性",
"no_inherited_attributes": "継承属性はありません。",
"none": "なし"
"no_inherited_attributes": "継承属性はありません。"
},
"note_map": {
"open_full": "拡大表示",
@@ -2201,8 +2200,5 @@
"empty_button": "パネルを非表示",
"toggle": "右パネルを切り替え",
"custom_widget_go_to_source": "ソースコードへ移動"
},
"attributes_panel": {
"title": "ノート属性"
}
}

View File

@@ -46,10 +46,7 @@
"save": "Salvar",
"edit_branch_prefix": "Editar Prefixo do Branch",
"help_on_tree_prefix": "Ajuda sobre o prefixo da árvore de notas",
"branch_prefix_saved": "O prefixo de ramificação foi salvo.",
"edit_branch_prefix_multiple": "Editar prefixo do ramo para {{count}} ramos",
"branch_prefix_saved_multiple": "O prefixo do ramo foi salvo para {{count}} ramos.",
"affected_branches": "Ramos afetados ({{count}}):"
"branch_prefix_saved": "O prefixo de ramificação foi salvo."
},
"bulk_actions": {
"bulk_actions": "Ações em massa",
@@ -257,8 +254,7 @@
"export_status": "Status da exportação",
"export_in_progress": "Exportação em andamento: {{progressCount}}",
"export_finished_successfully": "Exportação concluída com sucesso.",
"format_pdf": "PDF para impressão ou compartilhamento.",
"share-format": "HTML para publicação na web — usa o mesmo tema das notas compartilhadas, mas pode ser publicado como um site estático."
"format_pdf": "PDF para impressão ou compartilhamento."
},
"help": {
"noteNavigation": "Navegação de notas",
@@ -312,8 +308,7 @@
"other": "Outros",
"quickSearch": "focar no campo de pesquisa rápida",
"inPageSearch": "pesquisa na página",
"title": "Folha de Dicas",
"editShortcuts": "Editar atalhos de teclado"
"title": "Folha de Dicas"
},
"import": {
"importIntoNote": "Importar para a nota",
@@ -339,8 +334,7 @@
},
"import-status": "Status da importação",
"in-progress": "Importação em andamento: {{progress}}",
"successful": "Importação concluída com sucesso.",
"importZipRecommendation": "Ao importar um arquivo ZIP, a hierarquia de notas refletirá a estrutura de subdiretórios dentro do arquivo."
"successful": "Importação concluída com sucesso."
},
"include_note": {
"dialog_title": "Incluir nota",
@@ -355,8 +349,7 @@
"info": {
"modalTitle": "Mensagem informativa",
"closeButton": "Fechar",
"okButton": "OK",
"copy_to_clipboard": "Copiar para a área de transferência"
"okButton": "OK"
},
"jump_to_note": {
"search_placeholder": "Pesquise uma nota pelo nome ou digite > para comandos...",
@@ -778,7 +771,7 @@
"import-into-note": "Importar na nota",
"apply-bulk-actions": "Aplicar ações em massa",
"converted-to-attachments": "{{count}} notas foram convertidas em anexos.",
"convert-to-attachment-confirm": "Tem certeza de que deseja converter as notas selecionadas em anexos de suas notas pai? Esta operação se aplica apenas a notas de imagem; outras notas serão ignoradas.",
"convert-to-attachment-confirm": "Tem certeza de que deseja converter as notas selecionadas em anexos de suas notas-pai?",
"open-in-popup": "Edição rápida",
"archive": "Ficheiro",
"unarchive": "Desarquivar"
@@ -796,7 +789,7 @@
"show_attachments_description": "Exibir anexos da nota",
"search_notes_title": "Buscar Notas",
"search_notes_description": "Abrir busca avançada",
"configure_launch_bar_description": "Abrir a configuração da barra de atalho, para adicionar ou remover itens."
"configure_launch_bar_description": "Abrir a configuração da barra de lançamento, para adicionar ou remover itens."
},
"delete_note": {
"delete_note": "Excluir nota",
@@ -889,7 +882,7 @@
"zoom_out": "Reduzir",
"reset_zoom_level": "Redefinir Zoom",
"zoom_in": "Aumentar",
"configure_launchbar": "Configurar Barra de Atalhos",
"configure_launchbar": "Configurar Barra de Lançamento",
"show_shared_notes_subtree": "Exibir Subárvore de Notas Compartilhadas",
"advanced": "Avançado",
"open_dev_tools": "Abrir Ferramentas de Desenvolvedor",
@@ -904,9 +897,7 @@
"logout": "Sair",
"show-cheatsheet": "Exibir Cheatsheet",
"toggle-zen-mode": "Modo Zen",
"reload_hint": "Recarregar pode ajudar com alguns problemas visuais sem reiniciar toda a aplicação.",
"new-version-available": "Nova atualização disponível",
"download-update": "Obter a versão {{latestVersion}}"
"reload_hint": "Recarregar pode ajudar com alguns problemas visuais sem reiniciar toda a aplicação."
},
"zen_mode": {
"button_exit": "Sair do Modo Zen"
@@ -944,14 +935,7 @@
"convert_into_attachment_successful": "A nota '{{title}}' foi convertida para anexo.",
"print_pdf": "Exportar como PDF…",
"open_note_externally_title": "O arquivo será aberto em uma aplicação externa e monitorado por alterações. Você então poderá enviar a versão modificada de volta para o Trilium.",
"convert_into_attachment_prompt": "Você tem certeza que quer converter a nota '{{title}}' em um anexo da nota pai?",
"open_note_on_server": "Abrir nota no servidor",
"view_revisions": "Revisões da nota…",
"advanced": "Avançado",
"export_as_image": "Exportar como imagem",
"export_as_image_png": "PNG (raster)",
"export_as_image_svg": "SVG (vetorial)",
"note_map": "Mapa de notas"
"convert_into_attachment_prompt": "Você tem certeza que quer converter a nota '{{title}}' em um anexo da nota pai?"
},
"protected_session_status": {
"inactive": "Clique para entrar na sessão protegida",
@@ -995,8 +979,7 @@
"insert_child_note": "Inserir nota filha",
"delete_this_note": "Excluir essa nota",
"error_unrecognized_command": "Comando não reconhecido {{command}}",
"error_cannot_get_branch_id": "Não foi possível obter o branchId para o notePath '{{notePath}} '",
"note_revisions": "Revisões de notas"
"error_cannot_get_branch_id": "Não foi possível obter o branchId para o notePath '{{notePath}} '"
},
"note_icon": {
"change_note_icon": "Alterar ícone da nota",
@@ -1024,12 +1007,7 @@
"table": "Tabela",
"geo-map": "Mapa geográfico",
"board": "Quadro",
"include_archived_notes": "Exibir notas arquivadas",
"expand_tooltip": "Expande os filhos diretos desta coleção (um nível). Para mais opções, pressione a seta à direita.",
"expand_first_level": "Expandir filhos diretos",
"expand_nth_level": "Expandir {{depth}} níveis",
"expand_all_levels": "Expandir todos os níveis",
"presentation": "Apresentação"
"include_archived_notes": "Exibir notas arquivadas"
},
"edited_notes": {
"no_edited_notes_found": "Ainda não há nenhuma nota editada neste dia…",
@@ -1042,7 +1020,7 @@
"file_type": "Tipo do arquivo",
"file_size": "Tamanho do arquivo",
"download": "Baixar",
"open": "Abrir externamente",
"open": "Abrir",
"upload_new_revision": "Enviar nova revisão",
"upload_success": "Uma nova revisão de arquivo foi enviada.",
"upload_failed": "O envio de uma nova revisão de arquivo falhou.",
@@ -1062,8 +1040,7 @@
},
"inherited_attribute_list": {
"title": "Atributos Herdados",
"no_inherited_attributes": "Nenhum atributo herdado.",
"none": "nenhum"
"no_inherited_attributes": "Nenhum atributo herdado."
},
"note_info_widget": {
"note_id": "ID da Nota",
@@ -1074,9 +1051,7 @@
"calculate": "calcular",
"title": "Informações da nota",
"subtree_size": "(tamanho da subárvore: {{size}} em {{count}} notas)",
"note_size_info": "O tamanho da nota fornece uma estimativa aproximada dos requisitos de armazenamento para esta nota. Leva em conta o conteúdo e o conteúdo de suas revisões de nota.",
"mime": "Tipo MIME",
"show_similar_notes": "Mostrar notas semelhantes"
"note_size_info": "O tamanho da nota fornece uma estimativa aproximada dos requisitos de armazenamento para esta nota. Leva em conta o conteúdo e o conteúdo de suas revisões de nota."
},
"note_map": {
"open_full": "Expandir completamente",
@@ -1136,8 +1111,7 @@
"search_note_saved": "Nota de pesquisa foi salva em {{- notePathTitle}}",
"fast_search_description": "A opção de pesquisa rápida desabilita a pesquisa de texto completo do conteúdo de nota, o que pode acelerar a pesquisa em grandes bancos de dados.",
"include_archived_notes_description": "As notas arquivadas são por padrão excluídas dos resultados da pesquisa, com esta opção elas serão incluídas.",
"debug_description": "A depuração irá imprimir informações adicionais no console para ajudar na depuração de consultas complexas",
"view_options": "Ver opções:"
"debug_description": "A depuração irá imprimir informações adicionais no console para ajudar na depuração de consultas complexas"
},
"similar_notes": {
"title": "Notas Similares",
@@ -1218,13 +1192,7 @@
},
"editable_text": {
"placeholder": "Digite o conteúdo da sua nota aqui…",
"auto-detect-language": "Detectado automaticamente",
"editor_crashed_title": "O editor de texto travou",
"editor_crashed_content": "Seu conteúdo foi recuperado com sucesso, mas algumas das suas alterações mais recentes podem não ter sido salvas.",
"editor_crashed_details_button": "Veja mais detalhes...",
"editor_crashed_details_intro": "Se você encontrar este erro várias vezes, considere relatá-lo no GitHub colando as informações abaixo.",
"editor_crashed_details_title": "Informação técnica",
"keeps-crashing": "O componente de edição continua travando. Tente reiniciar o Trilium. Se o problema persistir, considere criar um relatório de bug."
"auto-detect-language": "Detectado automaticamente"
},
"empty": {
"search_placeholder": "buscar uma nota pelo nome",
@@ -1331,8 +1299,7 @@
"title": "Largura do Conteúdo",
"max_width_label": "Largura máxima do conteúdo",
"max_width_unit": "pixels",
"default_description": "Por padrão, o Trilium limita a largura máxima do conteúdo para melhorar a legibilidade em janelas maximizadas em telas wide.",
"centerContent": "Manter conteúdo centralizado"
"default_description": "Por padrão, o Trilium limita a largura máxima do conteúdo para melhorar a legibilidade em janelas maximizadas em telas wide."
},
"native_title_bar": {
"title": "Barra de Título Nativa (requer recarregar o app)",
@@ -1352,11 +1319,11 @@
"layout": "Layout",
"layout-vertical-title": "Vertical",
"layout-horizontal-title": "Horizontal",
"layout-vertical-description": "barra de atalho está a esquerda (padrão)",
"layout-horizontal-description": "barra de atalho está abaixo da barra de abas, a barra de abas agora tem a largura total."
"layout-vertical-description": "barra de lançamento está a esquerda (padrão)",
"layout-horizontal-description": "barra de lançamento está abaixo da barra de abas, a barra de abas agora tem a largura total."
},
"note_launcher": {
"this_launcher_doesnt_define_target_note": "Este atalho não define uma nota destino."
"this_launcher_doesnt_define_target_note": "Este lançador não define uma nota destino."
},
"copy_image_reference_button": {
"button_title": "Copiar referência da imagem para a área de transferência, pode ser colado em uma nota de texto."
@@ -1411,10 +1378,7 @@
"title": "Editor"
},
"code_mime_types": {
"title": "Tipos MIME disponíveis no dropdown",
"tooltip_syntax_highlighting": "Realce de sintaxe",
"tooltip_code_block_syntax": "Blocos de código em notas de texto",
"tooltip_code_note_syntax": "Notas de código"
"title": "Tipos MIME disponíveis no dropdown"
},
"vim_key_bindings": {
"use_vim_keybindings_in_code_notes": "Atribuições de teclas do Vim",
@@ -1534,13 +1498,7 @@
"min-days-in-first-week": "Mínimo de dias da primeira semana",
"first-week-info": "Primeira semana que contenha a primeira Quinta-feira do ano é baseado na <a href=\"https://en.wikipedia.org/wiki/ISO_week_date#First_week\">ISO 8601</a>.",
"first-week-warning": "Alterar as opções de primeira semana pode causar duplicidade nas Notas Semanais existentes e estas Notas não serão atualizadas de acordo.",
"formatting-locale": "Formato de data e número",
"tuesday": "Terça-feira",
"wednesday": "Quarta-feira",
"thursday": "Quinta-feira",
"friday": "Sexta-feira",
"saturday": "Sábado",
"formatting-locale-auto": "Com base no idioma do aplicativo"
"formatting-locale": "Formato de data e número"
},
"backup": {
"automatic_backup": "Backup automático",
@@ -1568,7 +1526,7 @@
"mind-map": "Mapa Mental",
"file": "Arquivo",
"image": "Imagem",
"launcher": "Atalho",
"launcher": "Lançador",
"doc": "Documento",
"widget": "Widget",
"confirm-change": "Não é recomentado alterar o tipo da nota quando o conteúdo da nota não está vazio. Quer continuar assim mesmo?",
@@ -1611,13 +1569,7 @@
},
"highlights_list_2": {
"title": "Lista de Destaques",
"options": "Opções",
"title_with_count_one": "{{count}} destaque",
"title_with_count_many": "{{count}} destaques",
"title_with_count_other": "{{count}} destaques",
"modal_title": "Configurar lista de destaques",
"menu_configure": "Configurar lista de destaques…",
"no_highlights": "Nenhum destaque encontrado."
"options": "Opções"
},
"quick-search": {
"placeholder": "Busca rápida",
@@ -1640,33 +1592,23 @@
"refresh-saved-search-results": "Atualizar resultados de pesquisa salvos",
"create-child-note": "Criar nota filha",
"unhoist": "Desafixar",
"toggle-sidebar": "Alternar barra lateral",
"dropping-not-allowed": "Não é permitido soltar notas neste local."
"toggle-sidebar": "Alternar barra lateral"
},
"title_bar_buttons": {
"window-on-top": "Manter Janela no Topo"
},
"note_detail": {
"could_not_find_typewidget": "Não foi possível encontrar typeWidget para o tipo '{{type}}'",
"printing": "Impressão em andamento…",
"printing_pdf": "Exportação para PDF em andamento…"
"could_not_find_typewidget": "Não foi possível encontrar typeWidget para o tipo '{{type}}'"
},
"note_title": {
"placeholder": "digite o título da nota aqui...",
"created_on": "Criado em <Value />",
"last_modified": "Modificado em <Value />",
"note_type_switcher_label": "Alternar de {{type}} para:",
"note_type_switcher_others": "Outro tipo de nota",
"note_type_switcher_templates": "Modelo",
"note_type_switcher_collection": "Coleção",
"edited_notes": "Notas editadas"
"placeholder": "digite o título da nota aqui..."
},
"search_result": {
"no_notes_found": "Nenhuma nota encontrada para os parâmetros de busca digitados.",
"search_not_executed": "A busca ainda não foi executada. Clique no botão \"Buscar\" acima para ver os resultados."
},
"spacer": {
"configure_launchbar": "Configurar Barra de Atalhos"
"configure_launchbar": "Configurar Barra de Lançamento"
},
"sql_result": {
"no_rows": "Nenhum linha foi retornada para esta consulta"
@@ -1688,8 +1630,7 @@
},
"toc": {
"table_of_contents": "Tabela de Conteúdos",
"options": "Opções",
"no_headings": "Nenhum título."
"options": "Opções"
},
"watched_file_update_status": {
"file_last_modified": "O arquivo <code class=\"file-path\"></code> foi modificado pela última vez em <span class=\"file-last-modified\"></span>.",
@@ -1732,24 +1673,22 @@
"ws": {
"sync-check-failed": "A verificação de sincronização falhou!",
"consistency-checks-failed": "A verificação de consistência falhou! Veja os logs para detalhes.",
"encountered-error": "Encontrado o erro \"{{message}}\", verifique o console.",
"lost-websocket-connection-title": "Conexão com o servidor perdida",
"lost-websocket-connection-message": "Verifique a configuração do seu proxy reverso (por exemplo, nginx ou Apache) para garantir que as conexões WebSocket estejam devidamente permitidas e não estejam sendo bloqueadas."
"encountered-error": "Encontrado o erro \"{{message}}\", verifique o console."
},
"hoisted_note": {
"confirm_unhoisting": "A nota solicitada '{{requestedNote}}' está fora da árvore da nota fixada '{{hoistedNote}}' e você precisa desafixar para acessar a nota. Quer prosseguir e desafixar?"
},
"launcher_context_menu": {
"reset_launcher_confirm": "Você deseja realmente reiniciar \"{{title}}\"? Todos os dados / configurações desta nota (e suas filhas) serão perdidos o atalho irá retornar para sua localização original.",
"add-note-launcher": "Adicionar um atalho de nota",
"add-script-launcher": "Adicionar um atalho de script",
"reset_launcher_confirm": "Você deseja realmente reiniciar \"{{title}}\"? Todos os dados / configurações desta nota (e suas filhas) serão perdidos o lançador irá retornar para sua localização original.",
"add-note-launcher": "Adicionar um lançador de nota",
"add-script-launcher": "Adicionar um lançador de script",
"add-custom-widget": "Adicionar um componente personalizado",
"add-spacer": "Adicionar um espaçador",
"delete": "Excluir <kbd data-command=\"deleteNotes\"></kbd>",
"reset": "Reiniciar",
"move-to-visible-launchers": "Mover para atalhos visíveis",
"move-to-available-launchers": "Mover para atalhos disponíveis",
"duplicate-launcher": "Duplicar o atalho <kbd data-command=\"duplicateSubtree\">"
"move-to-visible-launchers": "Mover para lançadores visíveis",
"move-to-available-launchers": "Mover para lançadores disponíveis",
"duplicate-launcher": "Duplicar o lançador <kbd data-command=\"duplicateSubtree\">"
},
"highlighting": {
"title": "Blocos de Código",
@@ -1783,8 +1722,7 @@
"copy-link": "Copiar link",
"paste": "Colar",
"paste-as-plain-text": "Colar como texto sem formatação",
"search_online": "Buscar por \"{{term}}\" usando {{searchEngine}}",
"search_in_trilium": "Pesquisar por \"{{term}}\" no Trilium"
"search_online": "Buscar por \"{{term}}\" usando {{searchEngine}}"
},
"image_context_menu": {
"copy_reference_to_clipboard": "Copiar referência para a área de transferência",
@@ -1794,8 +1732,7 @@
"open_note_in_new_tab": "Abrir nota em nova aba",
"open_note_in_new_split": "Abrir nota em nova divisão",
"open_note_in_new_window": "Abrir nota em nova janela",
"open_note_in_popup": "Edição rápida",
"open_note_in_other_split": "Abrir nota no outro painel dividido"
"open_note_in_popup": "Edição rápida"
},
"electron_integration": {
"desktop-application": "Aplicação Desktop",
@@ -1863,9 +1800,8 @@
"unknown_widget": "Componente desconhecido para \"{{id}}\"."
},
"note_language": {
"not_set": "Nenhum idioma definido",
"configure-languages": "Configurar idiomas...",
"help-on-languages": "Ajuda sobre idiomas de conteúdo…"
"not_set": "Não atribuído",
"configure-languages": "Configurar idiomas..."
},
"content_language": {
"title": "Idiomas do conteúdo",
@@ -1883,8 +1819,7 @@
"button_title": "Exportar diagrama como PNG"
},
"svg": {
"export_to_png": "O diagrama não pôde ser exportado como PNG.",
"export_to_svg": "O diagrama não pôde ser exportado para SVG."
"export_to_png": "O diagrama não pôde ser exportado como PNG."
},
"code_theme": {
"title": "Aparência",
@@ -1903,11 +1838,7 @@
"editorfeatures": {
"title": "Recursos",
"emoji_completion_enabled": "Habilitar auto-completar de Emoji",
"note_completion_enabled": "Habilitar auto-completar de notas",
"emoji_completion_description": "Se ativado, emojis podem ser inseridos facilmente no texto digitando`:`, seguido do nome do emoji.",
"note_completion_description": "Se ativado, links para notas podem ser criados digitando `@` seguido do título de uma nota.",
"slash_commands_enabled": "Ativar comandos de barra",
"slash_commands_description": "Se ativado, comandos de edição como inserir quebras de linha ou títulos podem ser acionados digitando`/`."
"note_completion_enabled": "Habilitar auto-completar de notas"
},
"table_view": {
"new-row": "Nova linha",
@@ -1932,7 +1863,7 @@
"book_properties_config": {
"hide-weekends": "Ocultar fins de semana",
"display-week-numbers": "Exibir números de semana",
"map-style": "Estilo do mapa",
"map-style": "Estilo do mapa:",
"max-nesting-depth": "Profundidade máxima de aninhamento:",
"vector_light": "Vetor (Claro)",
"vector_dark": "Vetor (Escuro)",
@@ -1957,8 +1888,7 @@
"new-item-placeholder": "Escreva o título da nota...",
"add-column-placeholder": "Escreva o nome da coluna...",
"edit-note-title": "Clique para editar o título da nota",
"edit-column-title": "Clique para editar o título da coluna",
"column-already-exists": "Esta coluna já existe no quadro."
"edit-column-title": "Clique para editar o título da coluna"
},
"call_to_action": {
"next_theme_title": "Testar no novo tema do Trilium",
@@ -1967,20 +1897,14 @@
"background_effects_title": "Efeitos de fundo estão estáveis agora",
"background_effects_message": "Em dispositivos Windows, efeitos de fundo estão estáveis agora. Os efeitos de fundo adicionam um toque de cor à interface do usuário borrando o plano de fundo atrás dela. Esta técnica também é usada em outras aplicações como o Windows Explorer.",
"background_effects_button": "Habilitar os efeitos de fundo",
"dismiss": "Dispensar",
"new_layout_title": "Novo layout",
"new_layout_message": "Introduzimos um layout modernizado para o Trilium. A faixa de opções foi removida e integrada de forma contínua à interface principal, com uma nova barra de status e seções expansíveis (como atributos promovidos) assumindo funções importantes.\n\nO novo layout vem ativado por padrão e pode ser desativado temporariamente em Opções → Aparência.",
"new_layout_button": "Mais informações"
"dismiss": "Dispensar"
},
"settings": {
"related_settings": "Configurações relacionadas"
},
"settings_appearance": {
"related_code_blocks": "Esquema de cores para blocos de código em notas de texto",
"related_code_notes": "Esquema de cores para notas de código",
"ui": "Interface do usuário",
"ui_old_layout": "Layout antigo",
"ui_new_layout": "Novo Layout"
"related_code_notes": "Esquema de cores para notas de código"
},
"units": {
"percentage": "%"
@@ -2123,102 +2047,5 @@
},
"collections": {
"rendering_error": "Não foi possível exibir o conteúdo devido a um erro."
},
"experimental_features": {
"title": "Opções experimentais",
"disclaimer": "Essas opções são experimentais e podem causar instabilidade. Use com cautela.",
"new_layout_name": "Novo Layout",
"new_layout_description": "Experimente o novo layout para um visual mais moderno e melhor usabilidade. Pode sofrer alterações significativas nas próximas versões."
},
"read-only-info": {
"read-only-note": "Você está visualizando uma nota somente leitura.",
"auto-read-only-note": "Esta nota é exibida em modo somente leitura para carregamento mais rápido.",
"edit-note": "Editar nota"
},
"presentation_view": {
"edit-slide": "Editar este slide",
"start-presentation": "Iniciar apresentação",
"slide-overview": "Alternar a visualização geral dos slides"
},
"calendar_view": {
"delete_note": "Excluir nota…"
},
"note-color": {
"clear-color": "Limpar cor da nota",
"set-color": "Definir cor da nota",
"set-custom-color": "Definir cor personalizada da nota"
},
"popup-editor": {
"maximize": "Alternar para editor completo"
},
"server": {
"unknown_http_error_title": "Erro de comunicação com o servidor",
"unknown_http_error_content": "Código de status: {{statusCode}}\nURL: {{method}} {{url}}\nMensagem: {{message}}",
"traefik_blocks_requests": "Se você estiver usando o proxy reverso Traefik, ele introduziu uma alteração que afeta a comunicação com o servidor."
},
"tab_history_navigation_buttons": {
"go-back": "Voltar para a nota anterior",
"go-forward": "Avançar para a próxima nota"
},
"breadcrumb": {
"hoisted_badge": "Destacado",
"hoisted_badge_title": "Remover destaque",
"workspace_badge": "Espaço de trabalho",
"scroll_to_top_title": "Ir para o início da nota",
"create_new_note": "Criar nova nota filha",
"empty_hide_archived_notes": "Ocultar notas arquivadas"
},
"breadcrumb_badges": {
"read_only_explicit": "Somente leitura",
"read_only_explicit_description": "Esta nota foi definida manualmente como somente leitura.\nClique para editá-la temporariamente.",
"read_only_auto": "Auto Somente leitura",
"read_only_auto_description": "Esta nota foi definida automaticamente como somente leitura por motivos de desempenho. Esse limite automático pode ser ajustado nas configurações.\n\nClique para editá-la temporariamente.",
"read_only_temporarily_disabled": "Editável temporariamente",
"read_only_temporarily_disabled_description": "Esta nota está atualmente editável, mas normalmente é somente leitura. A nota voltará a ser somente leitura assim que você navegar para outra nota.\n\nClique para reativar o modo somente leitura.",
"shared_publicly": "Compartilhado publicamente",
"shared_locally": "Compartilhado localmente",
"shared_copy_to_clipboard": "Copiar link para a área de transferência",
"shared_open_in_browser": "Abrir link no navegador",
"shared_unshare": "Remover compartilhamento",
"clipped_note": "Recorte da web",
"clipped_note_description": "Esta nota foi originalmente obtida de {{url}}.\n\nClique para navegar até a página de origem.",
"execute_script": "Executar script",
"execute_script_description": "Esta nota é uma nota de script. Clique para executar o script.",
"execute_sql": "Executar SQL",
"execute_sql_description": "Esta nota é uma nota SQL. Clique para executar a consulta SQL."
},
"status_bar": {
"language_title": "Alterar idioma do conteúdo",
"note_info_title": "Ver informações da nota (por exemplo, datas, tamanho da nota)",
"backlinks_one": "{{count}} referência inversa",
"backlinks_many": "{{count}} referências inversas",
"backlinks_other": "{{count}} referências inversas",
"backlinks_title_one": "Ver referência inversa",
"backlinks_title_many": "Ver referências inversas",
"backlinks_title_other": "Ver referências inversas",
"attachments_one": "{{count}} anexo",
"attachments_many": "{{count}} anexos",
"attachments_other": "{{count}} anexos",
"attachments_title_one": "Visualizar anexo em uma nova aba",
"attachments_title_many": "Visualizar anexos em uma nova aba",
"attachments_title_other": "Visualizar anexos em uma nova aba",
"attributes_one": "{{count}} atributo",
"attributes_many": "{{count}} atributos",
"attributes_other": "{{count}} atributos",
"attributes_title": "Atributos próprios e atributos herdados",
"note_paths_one": "{{count}} caminho",
"note_paths_many": "{{count}} caminhos",
"note_paths_other": "{{count}} caminhos",
"note_paths_title": "Caminhos da nota",
"code_note_switcher": "Alterar modo de idioma"
},
"attributes_panel": {
"title": "Atributos da nota"
},
"right_pane": {
"empty_message": "Nada para exibir nesta nota",
"empty_button": "Ocultar o painel",
"toggle": "Alternar painel direito",
"custom_widget_go_to_source": "Ir para o código-fonte"
}
}

View File

@@ -1686,8 +1686,7 @@
},
"inherited_attribute_list": {
"title": "Унаследованные атрибуты",
"no_inherited_attributes": "Нет унаследованных атрибутов.",
"none": "нет"
"no_inherited_attributes": "Нет унаследованных атрибутов."
},
"note_map": {
"title": "Карта заметок",
@@ -2217,8 +2216,5 @@
"toggle": "Переключить панель справа",
"empty_button": "Скрыть панель",
"empty_message": "Нечего отобразить для текущей заметки"
},
"attributes_panel": {
"title": "Атрибуты заметки"
}
}

View File

@@ -15,7 +15,7 @@ export default function TabHistoryNavigationButtons() {
const legacyBackVisible = useLauncherVisibility("_lbBackInHistory");
const legacyForwardVisible = useLauncherVisibility("_lbForwardInHistory");
return (
return (isElectron() &&
<div className="tab-history-navigation-buttons">
{!legacyBackVisible && <ActionButton
icon="bx bx-left-arrow-alt"

View File

@@ -54,16 +54,6 @@
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 {

View File

@@ -1,9 +1,8 @@
import { isValidElement, VNode } from "preact";
import Component, { TypedComponent } from "../components/component.js";
import froca from "../services/froca.js";
import { t } from "../services/i18n.js";
import toastService, { showErrorForScriptNote } from "../services/toast.js";
import toastService from "../services/toast.js";
import { renderReactWidget } from "./react/react_utils.jsx";
export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedComponent<T> {
@@ -57,8 +56,9 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
optChild(condition: boolean, ...components: (T | VNode)[]) {
if (condition) {
return this.child(...components);
} else {
return this;
}
return this;
}
id(id: string) {
@@ -172,11 +172,16 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
const noteId = this._noteId;
if (this._noteId) {
froca.getNote(noteId, true).then((note) => {
showErrorForScriptNote(noteId, t("toast.widget-error.message-custom", {
id: noteId,
title: note?.title,
message: e.message || e.toString()
}));
toastService.showPersistent({
id: `custom-widget-failure-${noteId}`,
title: t("toast.widget-error.title"),
icon: "bx bx-error-circle",
message: t("toast.widget-error.message-custom", {
id: noteId,
title: note?.title,
message: e.message || e.toString()
})
});
});
} else {
toastService.showPersistent({
@@ -208,7 +213,7 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
toggleInt(show: boolean | null | undefined) {
this.$widget.toggleClass("hidden-int", !show)
.toggleClass("visible", !!show);
.toggleClass("visible", !!show);
}
isHiddenInt() {
@@ -217,7 +222,7 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
toggleExt(show: boolean | null | "" | undefined) {
this.$widget.toggleClass("hidden-ext", !show)
.toggleClass("visible", !!show);
.toggleClass("visible", !!show);
}
isHiddenExt() {
@@ -245,8 +250,9 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
getClosestNtxId() {
if (this.$widget) {
return this.$widget.closest("[data-ntx-id]").attr("data-ntx-id");
} else {
return null;
}
return null;
}
cleanup() {}

View File

@@ -1,9 +1,9 @@
import type { EventData, EventNames } from "../../components/app_context.js";
import splitService from "../../services/resizer.js";
import type BasicWidget from "../basic_widget.js";
import FlexContainer from "./flex_container.js";
import splitService from "../../services/resizer.js";
import type RightPanelWidget from "../right_panel_widget.js";
import type { EventData, EventNames } from "../../components/app_context.js";
export default class RightPaneContainer extends FlexContainer<BasicWidget> {
export default class RightPaneContainer extends FlexContainer<RightPanelWidget> {
private rightPaneHidden: boolean;
private firstRender: boolean;

View File

@@ -4,14 +4,7 @@
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);
}
.inline-title,
.title-actions {
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);
}

View File

@@ -1,20 +1,17 @@
import { useCallback, useContext, useEffect, useMemo, useState } from "preact/hooks";
import appContext, { CommandNames } from "../../components/app_context";
import FNote from "../../entities/fnote";
import date_notes from "../../services/date_notes";
import dialog from "../../services/dialog";
import { LauncherWidgetDefinitionWithType } from "../../services/frontend_script_api_preact";
import { t } from "../../services/i18n";
import toast from "../../services/toast";
import { getErrorMessage, isMobile } from "../../services/utils";
import BasicWidget from "../basic_widget";
import NoteContextAwareWidget from "../note_context_aware_widget";
import QuickSearchWidget from "../quick_search";
import { useGlobalShortcut, useLegacyWidget, useNoteLabel, useNoteRelationTarget, useTriliumOptionBool } from "../react/hooks";
import { ParentComponent } from "../react/react_utils";
import BasicWidget from "../basic_widget";
import FNote from "../../entities/fnote";
import QuickSearchWidget from "../quick_search";
import { getErrorMessage, isMobile } from "../../services/utils";
import date_notes from "../../services/date_notes";
import { CustomNoteLauncher } from "./GenericButtons";
import { LaunchBarActionButton, LaunchBarContext, LauncherNoteProps, useLauncherIconAndTitle } from "./launch_bar_widgets";
import dialog from "../../services/dialog";
import { t } from "../../services/i18n";
import appContext, { CommandNames } from "../../components/app_context";
import toast from "../../services/toast";
export function CommandButton({ launcherNote }: LauncherNoteProps) {
const { icon, title } = useLauncherIconAndTitle(launcherNote);
@@ -26,7 +23,7 @@ export function CommandButton({ launcherNote }: LauncherNoteProps) {
text={title}
triggerCommand={command as CommandNames}
/>
);
)
}
// we're intentionally displaying the launcher title and icon instead of the target,
@@ -78,7 +75,7 @@ export function ScriptLauncher({ launcherNote }: LauncherNoteProps) {
text={title}
onClick={launch}
/>
);
)
}
export function AiChatButton({ launcherNote }: LauncherNoteProps) {
@@ -91,7 +88,7 @@ export function AiChatButton({ launcherNote }: LauncherNoteProps) {
text={title}
triggerCommand="createAiChat"
/>
);
)
}
export function TodayLauncher({ launcherNote }: LauncherNoteProps) {
@@ -117,13 +114,12 @@ export function QuickSearchLauncherWidget() {
<div>
{isEnabled && <LegacyWidgetRenderer widget={widget} />}
</div>
);
)
}
export function CustomWidget({ launcherNote }: LauncherNoteProps) {
const [ widgetNote ] = useNoteRelationTarget(launcherNote, "widget");
const [ widget, setWidget ] = useState<BasicWidget | NoteContextAwareWidget | LauncherWidgetDefinitionWithType>();
const [ widget, setWidget ] = useState<BasicWidget>();
const parentComponent = useContext(ParentComponent) as BasicWidget | null;
parentComponent?.contentSized();
@@ -150,13 +146,9 @@ export function CustomWidget({ launcherNote }: LauncherNoteProps) {
return (
<div>
{widget && (
("type" in widget && widget.type === "preact-launcher-widget")
? <ReactWidgetRenderer widget={widget as LauncherWidgetDefinitionWithType} />
: <LegacyWidgetRenderer widget={widget as BasicWidget} />
)}
{widget && <LegacyWidgetRenderer widget={widget} />}
</div>
);
)
}
export function LegacyWidgetRenderer({ widget }: { widget: BasicWidget }) {
@@ -166,8 +158,3 @@ export function LegacyWidgetRenderer({ widget }: { widget: BasicWidget }) {
return widgetEl;
}
function ReactWidgetRenderer({ widget }: { widget: LauncherWidgetDefinitionWithType }) {
const El = widget.render;
return <El />;
}

View File

@@ -32,7 +32,7 @@ import { ParentComponent } from "../react/react_utils";
const COLLAPSE_THRESHOLD = 5;
const INITIAL_ITEMS = 2;
const FINAL_ITEMS = 3;
const FINAL_ITEMS = 2;
export default function Breadcrumb() {
const { note, notePath, notePaths, noteContext } = useNotePaths();
@@ -65,7 +65,8 @@ export default function Breadcrumb() {
? <BreadcrumbRoot noteContext={noteContext} />
: <BreadcrumbItem index={index} notePath={item} notePathLength={notePaths.length} noteContext={noteContext} parentComponent={parentComponent} />
}
<BreadcrumbSeparator notePath={item} activeNotePath={notePaths[index + 1]} {...separatorProps} />
{(index < notePaths.length - 1 || note?.hasChildren()) &&
<BreadcrumbSeparator notePath={item} activeNotePath={notePaths[index + 1]} {...separatorProps} />}
</Fragment>
))
)}
@@ -225,7 +226,7 @@ function BreadcrumbSeparatorDropdownContent({ notePath, noteContext, activeNoteP
</li>;
})}
{childNotes.length > 0 && <FormDropdownDivider />}
<FormDropdownDivider />
<FormListItem
icon="bx bx-plus"
onClick={() => note_create.createNote(notePath, { activate: true })}

View File

@@ -75,6 +75,10 @@
}
}
.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;
}
@@ -95,3 +99,58 @@ 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;
}
}
.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;
}
}
}
}

View File

@@ -3,16 +3,28 @@ import "./InlineTitle.css";
import { NoteType } from "@triliumnext/commons";
import clsx from "clsx";
import { ComponentChild } from "preact";
import { useLayoutEffect, useRef, useState } from "preact/hooks";
import { useEffect, useLayoutEffect, useMemo, 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 { useNoteContext, useNoteProperty, useStaticTooltip } from "../react/hooks";
import SimpleBadge, { Badge, BadgeWithDropdown } from "../react/Badge";
import Collapsible from "../react/Collapsible";
import { FormDropdownDivider, FormListItem } from "../react/FormList";
import { useNoteBlob, useNoteContext, useNoteLabel, useNoteProperty, useStaticTooltip, useTriliumEvent, useTriliumOptionBool } from "../react/hooks";
import NoteLink from "../react/NoteLink";
import { joinElements } from "../react/react_utils";
import { useEditedNotes } from "../ribbon/EditedNotesTab";
import { useNoteMetadata } from "../ribbon/NoteInfoTab";
import { onWheelHorizontalScroll } from "../widget_utils";
const supportedNoteTypes = new Set<NoteType>([
"text", "code"
@@ -64,6 +76,9 @@ export default function InlineTitle() {
<NoteTitleDetails />
</div>
</div>
<EditedNotes />
<NoteTypeSwitcher />
</div>
);
}
@@ -127,4 +142,205 @@ function TextWithValue({ i18nKey, value, valueTooltip }: {
}
//#endregion
//#region Note type switcher
const SWITCHER_PINNED_NOTE_TYPES = new Set<NoteType>([ "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) &&
<div
className="note-type-switcher"
onWheel={onWheelHorizontalScroll}
>
{note && blob?.contentLength === 0 && (
<>
<div className="intro">{t("note_title.note_type_switcher_label", { type: currentNoteTypeData?.title.toLocaleLowerCase() })}</div>
{pinnedNoteTypes.map(noteType => noteType.type !== currentNoteType && (
<Badge
key={noteType.type}
text={noteType.title}
icon={`bx ${noteType.icon}`}
onClick={() => switchNoteType(note.noteId, noteType)}
/>
))}
{collectionTemplates.length > 0 && <CollectionNoteTypes noteId={note.noteId} collectionTemplates={collectionTemplates} />}
{builtinTemplates.length > 0 && <TemplateNoteTypes noteId={note.noteId} builtinTemplates={builtinTemplates} />}
{restNoteTypes.length > 0 && <MoreNoteTypes noteId={note.noteId} restNoteTypes={restNoteTypes} />}
</>
)}
</div>
);
}
function MoreNoteTypes({ noteId, restNoteTypes }: { noteId: string, restNoteTypes: NoteTypeMapping[] }) {
return (
<BadgeWithDropdown
text={t("note_title.note_type_switcher_others")}
icon="bx bx-dots-vertical-rounded"
>
{restNoteTypes.map(noteType => (
<FormListItem
key={noteType.type}
icon={`bx ${noteType.icon}`}
onClick={() => switchNoteType(noteId, noteType)}
>{noteType.title}</FormListItem>
))}
</BadgeWithDropdown>
);
}
function CollectionNoteTypes({ noteId, collectionTemplates }: { noteId: string, collectionTemplates: FNote[] }) {
return (
<BadgeWithDropdown
text={t("note_title.note_type_switcher_collection")}
icon="bx bx-book"
>
{collectionTemplates.map(collectionTemplate => (
<FormListItem
key={collectionTemplate.noteId}
icon={collectionTemplate.getIcon()}
onClick={() => setTemplate(noteId, collectionTemplate.noteId)}
>{collectionTemplate.title}</FormListItem>
))}
</BadgeWithDropdown>
);
}
function TemplateNoteTypes({ noteId, builtinTemplates }: { noteId: string, builtinTemplates: FNote[] }) {
const [ userTemplates, setUserTemplates ] = useState<FNote[]>([]);
async function refreshTemplates() {
const templateNoteIds = await server.get<string[]>("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 (
<BadgeWithDropdown
text={t("note_title.note_type_switcher_templates")}
icon="bx bx-copy-alt"
>
{userTemplates.map(template => <TemplateItem key={template.noteId} noteId={noteId} template={template} />)}
{userTemplates.length > 0 && <FormDropdownDivider />}
{builtinTemplates.map(template => <TemplateItem key={template.noteId} noteId={noteId} template={template} />)}
</BadgeWithDropdown>
);
}
function TemplateItem({ noteId, template }: { noteId: string, template: FNote }) {
return (
<FormListItem
icon={template.getIcon()}
onClick={() => setTemplate(noteId, template.noteId)}
>{template.title}</FormListItem>
);
}
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
//#region Edited Notes
function EditedNotes() {
const { note } = useNoteContext();
const [ dateNote ] = useNoteLabel(note, "dateNote");
const [ editedNotesOpenInRibbon ] = useTriliumOptionBool("editedNotesOpenInRibbon");
return (note && dateNote &&
<Collapsible
className="edited-notes"
title={t("note_title.edited_notes")}
initiallyExpanded={editedNotesOpenInRibbon}
>
<EditedNotesContent note={note} />
</Collapsible>
);
}
function EditedNotesContent({ note }: { note: FNote }) {
const editedNotes = useEditedNotes(note);
return (editedNotes !== undefined &&
(editedNotes.length > 0 ? editedNotes?.map(editedNote => (
<SimpleBadge
key={editedNote.noteId}
title={(
<NoteLink
notePath={editedNote.noteId}
showNoteIcon
/>
)}
/>
)) : (
<div className="no-edited-notes-found">{t("edited_notes.no_edited_notes_found")}</div>
)));
}
//#endregion

View File

@@ -16,13 +16,6 @@
&.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 {

View File

@@ -4,41 +4,8 @@ body.experimental-feature-new-layout {
}
.title-actions {
display: flex;
flex-direction: column;
gap: 0.5em;
&:not(:empty) {
&.visible:not(:empty) {
padding: 0.75em 15px;
}
.edited-notes {
.collapsible-inner-body {
display: flex;
flex-wrap: wrap;
gap: 0.3em;
.badge {
margin: 0;
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);
}
}
}
}
.promoted-attributes-widget {
.promoted-attributes-container {
margin: 0;
padding: 0;
}
}
}
}

View File

@@ -1,7 +1,7 @@
import "./NoteTitleActions.css";
import clsx from "clsx";
import { useEffect, useState } from "preact/hooks";
import { useEffect, useRef, useState } from "preact/hooks";
import NoteContext from "../../components/note_context";
import FNote from "../../entities/fnote";
@@ -9,31 +9,29 @@ 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, useNoteLabel, useNoteProperty, useTriliumEvent, useTriliumOptionBool } from "../react/hooks";
import NoteLink, { NewNoteLink } from "../react/NoteLink";
import { useEditedNotes } from "../ribbon/EditedNotesTab";
import { useNoteContext, useNoteProperty, useTriliumEvent } from "../react/hooks";
import SearchDefinitionTab from "../ribbon/SearchDefinitionTab";
import NoteTypeSwitcher from "./NoteTypeSwitcher";
export default function NoteTitleActions() {
const { note, ntxId, componentId, noteContext } = useNoteContext();
const isHiddenNote = note && note.noteId !== "_search" && note.noteId.startsWith("_");
const noteType = useNoteProperty(note, "type");
const items = [
note && <PromotedAttributes note={note} componentId={componentId} noteContext={noteContext} />,
note && noteType === "search" && <SearchProperties note={note} ntxId={ntxId} />,
note && !isHiddenNote && noteType === "book" && <CollectionProperties note={note} />
].filter(Boolean);
return (
<div className="title-actions">
<PromotedAttributes note={note} componentId={componentId} noteContext={noteContext} />
{noteType === "search" && <SearchProperties note={note} ntxId={ntxId} />}
{!isHiddenNote && note && noteType === "book" && <CollectionProperties note={note} />}
<EditedNotes />
<NoteTypeSwitcher />
<div className={clsx("title-actions", items.length > 0 && "visible")}>
{items}
</div>
);
}
function SearchProperties({ note, ntxId }: { note: FNote | null | undefined, ntxId: string | null | undefined }) {
function SearchProperties({ note, ntxId }: { note: FNote, ntxId: string | null | undefined }) {
return (note &&
<Collapsible
title={t("search_definition.search_parameters")}
@@ -66,43 +64,10 @@ function PromotedAttributes({ note, componentId, noteContext }: {
return (note && (
<ExternallyControlledCollapsible
key={note.noteId}
title={t("note_title.promoted_attributes")}
title={t("promoted_attributes.promoted_attributes")}
expanded={expanded} setExpanded={setExpanded}
>
<PromotedAttributesContent note={note} componentId={componentId} cells={cells} setCells={setCells} />
</ExternallyControlledCollapsible>
));
}
//#region Edited Notes
function EditedNotes() {
const { note } = useNoteContext();
const [ dateNote ] = useNoteLabel(note, "dateNote");
const [ editedNotesOpenInRibbon ] = useTriliumOptionBool("editedNotesOpenInRibbon");
return (note && dateNote &&
<Collapsible
className="edited-notes"
title={t("note_title.edited_notes")}
initiallyExpanded={editedNotesOpenInRibbon}
>
<EditedNotesContent note={note} />
</Collapsible>
);
}
function EditedNotesContent({ note }: { note: FNote }) {
const editedNotes = useEditedNotes(note);
return (editedNotes !== undefined &&
(editedNotes.length > 0 ? editedNotes?.map(editedNote => (
<NewNoteLink
className="badge"
notePath={editedNote.noteId}
showNoteIcon
/>
)) : (
<div className="no-edited-notes-found">{t("edited_notes.no_edited_notes_found")}</div>
)));
}
//#endregion

View File

@@ -1,33 +0,0 @@
@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;
}
}

View File

@@ -1,182 +0,0 @@
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<NoteType>([ "text", "code", "book", "canvas" ]);
const supportedNoteTypes = new Set<NoteType>([
"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) &&
<div
className="note-type-switcher"
onWheel={onWheelHorizontalScroll}
>
{note && blob?.contentLength === 0 && (
<>
<div className="intro">{t("note_title.note_type_switcher_label", { type: currentNoteTypeData?.title.toLocaleLowerCase() })}</div>
{pinnedNoteTypes.map(noteType => noteType.type !== currentNoteType && (
<Badge
key={noteType.type}
text={noteType.title}
icon={`bx ${noteType.icon}`}
onClick={() => switchNoteType(note.noteId, noteType)}
/>
))}
{collectionTemplates.length > 0 && <CollectionNoteTypes noteId={note.noteId} collectionTemplates={collectionTemplates} />}
{builtinTemplates.length > 0 && <TemplateNoteTypes noteId={note.noteId} builtinTemplates={builtinTemplates} />}
{restNoteTypes.length > 0 && <MoreNoteTypes noteId={note.noteId} restNoteTypes={restNoteTypes} />}
</>
)}
</div>
);
}
function MoreNoteTypes({ noteId, restNoteTypes }: { noteId: string, restNoteTypes: NoteTypeMapping[] }) {
return (
<BadgeWithDropdown
text={t("note_title.note_type_switcher_others")}
icon="bx bx-dots-vertical-rounded"
>
{restNoteTypes.map(noteType => (
<FormListItem
key={noteType.type}
icon={`bx ${noteType.icon}`}
onClick={() => switchNoteType(noteId, noteType)}
>{noteType.title}</FormListItem>
))}
</BadgeWithDropdown>
);
}
function CollectionNoteTypes({ noteId, collectionTemplates }: { noteId: string, collectionTemplates: FNote[] }) {
return (
<BadgeWithDropdown
text={t("note_title.note_type_switcher_collection")}
icon="bx bx-book"
>
{collectionTemplates.map(collectionTemplate => (
<FormListItem
key={collectionTemplate.noteId}
icon={collectionTemplate.getIcon()}
onClick={() => setTemplate(noteId, collectionTemplate.noteId)}
>{collectionTemplate.title}</FormListItem>
))}
</BadgeWithDropdown>
);
}
function TemplateNoteTypes({ noteId, builtinTemplates }: { noteId: string, builtinTemplates: FNote[] }) {
const [ userTemplates, setUserTemplates ] = useState<FNote[]>([]);
async function refreshTemplates() {
const templateNoteIds = await server.get<string[]>("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 (
<BadgeWithDropdown
text={t("note_title.note_type_switcher_templates")}
icon="bx bx-copy-alt"
>
{userTemplates.map(template => <TemplateItem key={template.noteId} noteId={noteId} template={template} />)}
{userTemplates.length > 0 && <FormDropdownDivider />}
{builtinTemplates.map(template => <TemplateItem key={template.noteId} noteId={noteId} template={template} />)}
</BadgeWithDropdown>
);
}
function TemplateItem({ noteId, template }: { noteId: string, template: FNote }) {
return (
<FormListItem
icon={template.getIcon()}
onClick={() => setTemplate(noteId, template.noteId)}
>{template.title}</FormListItem>
);
}
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;
}

View File

@@ -428,7 +428,7 @@ function CodeNoteSwitcher({ note }: StatusBarContext) {
return (note.type === "code" &&
<>
<StatusBarDropdown
icon={correspondingMimeType?.icon ?? "bx bx-code-curly"}
icon="bx bx-code-curly"
text={correspondingMimeType?.title}
title={t("status_bar.code_note_switcher")}
dropdownContainerClassName="dropdown-code-note-switcher tn-dropdown-menu-scrollable"

View File

@@ -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: 600px) {
@container (max-width: 700px) {
.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);

View File

@@ -50,30 +50,16 @@
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-inline-start: 0.25em;
margin-inline-end: 0;
margin-left: 0.25em;
}
}
}

View File

@@ -32,8 +32,7 @@ export function Badge({ icon, className, text, tooltip, href, ...containerProps
fallbackPlacements: [ "bottom" ],
animation: false,
html: true,
title: tooltip,
customClass: "pre-wrap-text"
title: tooltip
});
const content = <>
@@ -60,10 +59,7 @@ export function BadgeWithDropdown({ text, children, tooltip, className, dropdown
<Dropdown
className={`dropdown-badge dropdown-${className}`}
text={<Badge
text={<>
<span class="text-inner">{text}</span>
<Icon className="arrow" icon="bx bx-chevron-down" />
</>}
text={<>{text} <Icon className="arrow" icon="bx bx-chevron-down" /></>}
className={className}
{...props}
/>}

View File

@@ -2,11 +2,10 @@ 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<HTMLProps<HTMLDivElement>, "id" | "className"> {
@@ -67,14 +66,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);
@@ -123,7 +122,7 @@ export default function Dropdown({ id, className, buttonClassName, isStatic, chi
{...buttonProps}
>
{text}
<span className="caret" />
<span className="caret"></span>
</button>
<ul
@@ -131,15 +130,9 @@ export default function Dropdown({ id, className, buttonClassName, isStatic, chi
style={dropdownContainerStyle}
aria-labelledby={ariaId}
ref={dropdownContainerRef}
onClick={(e) => {
// Prevent clicks directly inside the dropdown from closing.
if (e.target === dropdownContainerRef.current) {
e.stopPropagation();
}
}}
>
{shown && children}
</ul>
</div>
);
)
}

View File

@@ -1,8 +1,8 @@
import { Tooltip } from "bootstrap";
import { ComponentChildren, CSSProperties } from "preact";
import { useCallback, useEffect, useMemo, useRef } from "preact/hooks";
import { useEffect, useRef, useMemo, useCallback } from "preact/hooks";
import { escapeQuotes } from "../../services/utils";
import { ComponentChildren } from "preact";
import { CSSProperties, memo } from "preact/compat";
import { useUniqueName } from "./hooks";
interface FormCheckboxProps {
@@ -18,30 +18,30 @@ interface FormCheckboxProps {
containerStyle?: CSSProperties;
}
export default function FormCheckbox({ name, disabled, label, currentValue, onChange, hint, containerStyle }: FormCheckboxProps) {
const FormCheckbox = memo(({ name, disabled, label, currentValue, onChange, hint, containerStyle }: FormCheckboxProps) => {
const labelRef = useRef<HTMLLabelElement>(null);
const id = useUniqueName(name);
useEffect(() => {
if (!hint || !labelRef.current) return;
const tooltipInstance = Tooltip.getOrCreateInstance(labelRef.current, {
html: true,
template: '<div class="tooltip tooltip-top" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>'
});
return () => tooltipInstance?.dispose();
}, [hint]); // Proper dependency
const labelStyle = useMemo(() =>
const labelStyle = useMemo(() =>
hint ? { textDecoration: "underline dotted var(--main-text-color)" } : undefined,
[hint]
[hint]
);
const handleChange = useCallback((e: Event) => {
onChange((e.target as HTMLInputElement).checked);
}, [onChange]);
const titleText = useMemo(() => hint ? escapeQuotes(hint) : undefined, [hint]);
return (
@@ -65,4 +65,6 @@ export default function FormCheckbox({ name, disabled, label, currentValue, onCh
</label>
</div>
);
}
});
export default FormCheckbox;

View File

@@ -105,17 +105,22 @@ export function NewNoteLink({ notePath, viewScope, noContextMenu, showNoteIcon,
const [ archived ] = useNoteLabelBoolean(note, "archived");
return (
<a
className={clsx("tn-link", colorClass, {
"no-tooltip-preview": noPreview,
archived
})}
href={calculateHash({ notePath, viewScope })}
data-no-context-menu={noContextMenu}
{...linkProps}
>
{icon && <><Icon icon={icon} />&nbsp;</>}
{title}
</a>
<span>
<span>
{icon && <Icon icon={icon} />}
<a
className={clsx("tn-link", colorClass, {
"no-tooltip-preview": noPreview,
archived
})}
href={calculateHash({ notePath, viewScope })}
data-no-context-menu={noContextMenu}
{...linkProps}
>
{title}
</a>
</span>
</span>
);
}

View File

@@ -369,8 +369,7 @@ export function useActiveNoteContext() {
useEffect(() => {
setNote(noteContext?.note);
setNotePath(noteContext?.notePath);
}, [ notePath, noteContext?.note, noteContext?.notePath ]);
}, [ notePath ]);
useTriliumEvents([ "setNoteContext", "activeContextChanged", "noteSwitchedAndActivated", "noteSwitched" ], () => {
const noteContext = appContext.tabManager.getActiveContext() ?? undefined;
@@ -635,8 +634,7 @@ export function useLegacyWidget<T extends BasicWidget>(widgetFactory: () => T, {
const renderedWidget = widget.render();
return [ widget, renderedWidget ];
}, [ noteContext, parentComponent ]); // eslint-disable-line react-hooks/exhaustive-deps
// widgetFactory() is intentionally left out
}, [ noteContext, parentComponent, widgetFactory]);
// Attach the widget to the parent.
useEffect(() => {

View File

@@ -5,7 +5,6 @@ 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";
@@ -69,11 +68,7 @@ export function useShareInfo(note: FNote | null | undefined) {
}
});
return {
link,
linkHref,
isSharedExternally: !isElectron() || !!syncServerHost // on server we can't reliably detect if the note is shared locally or available publicly.
};
return { link, linkHref, isSharedExternally: !!syncServerHost };
}
function getShareId(note: FNote) {

View File

@@ -6,10 +6,10 @@ import { VNode } from "preact";
import { useEffect, useRef } from "preact/hooks";
import appContext from "../../components/app_context";
import { WidgetsByParent } from "../../services/bundle";
import { t } from "../../services/i18n";
import options from "../../services/options";
import { DEFAULT_GUTTER_SIZE } from "../../services/resizer";
import BasicWidget from "../basic_widget";
import Button from "../react/Button";
import { useActiveNoteContext, useLegacyWidget, useNoteProperty, useTriliumOptionBool, useTriliumOptionJson } from "../react/hooks";
import Icon from "../react/Icon";
@@ -23,12 +23,12 @@ const MIN_WIDTH_PERCENT = 5;
interface RightPanelWidgetDefinition {
el: VNode;
enabled: boolean;
position?: number;
position: number;
}
export default function RightPanelContainer({ widgetsByParent }: { widgetsByParent: WidgetsByParent }) {
export default function RightPanelContainer({ customWidgets }: { customWidgets: BasicWidget[] }) {
const [ rightPaneVisible, setRightPaneVisible ] = useTriliumOptionBool("rightPaneVisible");
const items = useItems(rightPaneVisible, widgetsByParent);
const items = useItems(rightPaneVisible, customWidgets);
useSplit(rightPaneVisible);
return (
@@ -51,7 +51,7 @@ export default function RightPanelContainer({ widgetsByParent }: { widgetsByPare
);
}
function useItems(rightPaneVisible: boolean, widgetsByParent: WidgetsByParent) {
function useItems(rightPaneVisible: boolean, customWidgets: BasicWidget[]) {
const { note } = useActiveNoteContext();
const noteType = useNoteProperty(note, "type");
const [ highlightsList ] = useTriliumOptionJson<string[]>("highlightsList");
@@ -61,38 +61,23 @@ function useItems(rightPaneVisible: boolean, widgetsByParent: WidgetsByParent) {
{
el: <TableOfContents />,
enabled: (noteType === "text" || noteType === "doc"),
position: 10,
},
{
el: <HighlightsList />,
enabled: noteType === "text" && highlightsList.length > 0,
position: 20,
},
...widgetsByParent.getLegacyWidgets("right-pane").map((widget) => ({
el: <CustomLegacyWidget key={widget._noteId} originalWidget={widget as LegacyRightPanelWidget} />,
...customWidgets.map((w, i) => ({
el: <CustomWidget key={w._noteId} originalWidget={w as LegacyRightPanelWidget} />,
enabled: true,
position: widget.position
})),
...widgetsByParent.getPreactWidgets("right-pane").map((widget) => {
const El = widget.render;
return {
el: <El />,
enabled: true,
position: widget.position
};
})
position: w.position ?? 30 + i * 10
}))
];
// Assign a position to items that don't have one yet.
let pos = 10;
for (const definition of definitions) {
if (!definition.position) {
definition.position = pos;
pos += 10;
}
}
return definitions
.filter(e => e.enabled)
.toSorted((a, b) => (a.position ?? 10) - (b.position ?? 10))
.toSorted((a, b) => a.position - b.position)
.map(e => e.el);
}
@@ -114,7 +99,7 @@ function useSplit(visible: boolean) {
}, [ visible ]);
}
function CustomLegacyWidget({ originalWidget }: { originalWidget: LegacyRightPanelWidget }) {
function CustomWidget({ originalWidget }: { originalWidget: LegacyRightPanelWidget }) {
const containerRef = useRef<HTMLDivElement>(null);
return (

View File

@@ -1,18 +1,16 @@
import "./code.css";
import { default as VanillaCodeMirror, getThemeById } from "@triliumnext/codemirror";
import { useEffect, useRef, useState } from "preact/hooks";
import appContext, { CommandListenerData } from "../../../components/app_context";
import FNote from "../../../entities/fnote";
import { t } from "../../../services/i18n";
import utils from "../../../services/utils";
import { useEditorSpacedUpdate, useKeyboardShortcuts, useLegacyImperativeHandlers, useNoteBlob, useNoteProperty, useSyncedRef, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import { refToJQuerySelector } from "../../react/react_utils";
import TouchBar, { TouchBarButton } from "../../react/TouchBar";
import { CODE_THEME_DEFAULT_PREFIX as DEFAULT_PREFIX } from "../constants";
import { getThemeById, default as VanillaCodeMirror } from "@triliumnext/codemirror";
import { TypeWidgetProps } from "../type_widget";
import "./code.css";
import CodeMirror, { CodeMirrorProps } from "./CodeMirror";
import utils from "../../../services/utils";
import { useEditorSpacedUpdate, useKeyboardShortcuts, useLegacyImperativeHandlers, useNoteBlob, useSyncedRef, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import { t } from "../../../services/i18n";
import appContext, { CommandListenerData } from "../../../components/app_context";
import TouchBar, { TouchBarButton } from "../../react/TouchBar";
import { refToJQuerySelector } from "../../react/react_utils";
import { CODE_THEME_DEFAULT_PREFIX as DEFAULT_PREFIX } from "../constants";
import FNote from "../../../entities/fnote";
interface CodeEditorProps {
/** By default, the code editor will try to match the color of the scrolling container to match the one from the theme for a full-screen experience. If the editor is embedded, it makes sense not to have this behaviour. */
@@ -53,7 +51,7 @@ export function ReadOnlyCode({ note, viewScope, ntxId, parentComponent }: TypeWi
mime={note.mime}
readOnly
/>
);
)
}
function formatViewSource(note: FNote, content: string) {
@@ -76,7 +74,6 @@ export function EditableCode({ note, ntxId, noteContext, debounceUpdate, parentC
const editorRef = useRef<VanillaCodeMirror>(null);
const containerRef = useRef<HTMLPreElement>(null);
const [ vimKeymapEnabled ] = useTriliumOptionBool("vimKeymapEnabled");
const mime = useNoteProperty(note, "mime");
const spacedUpdate = useEditorSpacedUpdate({
note,
noteContext,
@@ -110,7 +107,7 @@ export function EditableCode({ note, ntxId, noteContext, debounceUpdate, parentC
<CodeEditor
ntxId={ntxId} parentComponent={parentComponent}
editorRef={editorRef} containerRef={containerRef}
mime={mime ?? "text/plain"}
mime={note.mime}
className="note-detail-code-editor"
placeholder={t("editable_code.placeholder")}
vimKeybindings={vimKeymapEnabled}
@@ -133,7 +130,7 @@ export function EditableCode({ note, ntxId, noteContext, debounceUpdate, parentC
)}
</TouchBar>
</>
);
)
}
export function CodeEditor({ parentComponent, ntxId, containerRef: externalContainerRef, editorRef: externalEditorRef, mime, onInitialized, lineWrapping, noBackgroundChange, ...editorProps }: CodeEditorProps & CodeMirrorProps & Pick<TypeWidgetProps, "parentComponent" | "ntxId">) {

View File

@@ -1,6 +1,6 @@
{
"formatVersion": 2,
"appVersion": "0.100.0",
"appVersion": "0.99.3",
"files": [
{
"isClone": false,
@@ -5507,7 +5507,51 @@
}
],
"dataFileName": "chartjs-plugin-datalabe.min.js",
"attachments": []
"attachments": [],
"dirFileName": "chartjs-plugin-datalabels.min.js",
"children": [
{
"isClone": true,
"noteId": "piyimQhwfcy5",
"notePath": [
"root",
"rvaX6hEaQlmk",
"KZVWidxicAfn",
"xRQuuwkaobBM",
"GXUcReLM6dSe",
"oLPbgCo7djD7",
"AlL9eFopYuHg",
"9GZB2MeW51xv",
"3jaioienOLTR",
"piyimQhwfcy5"
],
"title": "chart.js",
"prefix": null,
"dataFileName": "chart.js.clone.html",
"type": "text",
"format": "html"
}
]
},
{
"isClone": true,
"noteId": "piyimQhwfcy5",
"notePath": [
"root",
"rvaX6hEaQlmk",
"KZVWidxicAfn",
"xRQuuwkaobBM",
"GXUcReLM6dSe",
"oLPbgCo7djD7",
"AlL9eFopYuHg",
"9GZB2MeW51xv",
"piyimQhwfcy5"
],
"title": "chart.js",
"prefix": null,
"dataFileName": "chart.js.clone.html",
"type": "text",
"format": "html"
}
]
},
@@ -6000,86 +6044,6 @@
],
"dataFileName": "Custom request handler.js",
"attachments": []
},
{
"isClone": false,
"noteId": "DAybX9h5jOoG",
"notePath": [
"root",
"rvaX6hEaQlmk",
"KZVWidxicAfn",
"DAybX9h5jOoG"
],
"title": "Render note with JSX",
"notePosition": 100,
"prefix": null,
"isExpanded": false,
"type": "render",
"mime": "",
"attributes": [
{
"type": "label",
"name": "widget",
"value": "",
"isInheritable": false,
"position": 10
},
{
"type": "relation",
"name": "renderNote",
"value": "xzqr5J1V4YwY",
"isInheritable": false,
"position": 20
}
],
"attachments": [],
"dirFileName": "Render note with JSX",
"children": [
{
"isClone": false,
"noteId": "xzqr5J1V4YwY",
"notePath": [
"root",
"rvaX6hEaQlmk",
"KZVWidxicAfn",
"DAybX9h5jOoG",
"xzqr5J1V4YwY"
],
"title": "JSX",
"notePosition": 12,
"prefix": null,
"isExpanded": false,
"type": "code",
"mime": "text/jsx",
"attributes": [],
"dataFileName": "JSX.jsx",
"attachments": [],
"dirFileName": "JSX",
"children": [
{
"isClone": false,
"noteId": "mqDw6BebfE58",
"notePath": [
"root",
"rvaX6hEaQlmk",
"KZVWidxicAfn",
"DAybX9h5jOoG",
"xzqr5J1V4YwY",
"mqDw6BebfE58"
],
"title": "FormElements",
"notePosition": 10,
"prefix": null,
"isExpanded": false,
"type": "code",
"mime": "text/jsx",
"attributes": [],
"dataFileName": "FormElements.jsx",
"attachments": []
}
]
}
]
}
]
}

View File

@@ -540,6 +540,14 @@
<ul>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Statistics/Attribute%20count/template/js/renderPieChart/chartjs-plugin-datalabe.min.js"
target="detail">chartjs-plugin-datalabels.min.js</a>
<ul>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Weight%20Tracker/Implementation/JS%20code/chart.js"
target="detail">chart.js</a>
</li>
</ul>
</li>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Weight%20Tracker/Implementation/JS%20code/chart.js"
target="detail">chart.js</a>
</li>
</ul>
</li>
@@ -625,18 +633,6 @@
<li><a href="root/Trilium%20Demo/Scripting%20examples/Custom%20request%20handler.js"
target="detail">Custom request handler</a>
</li>
<li>Render note with JSX
<ul>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Render%20note%20with%20JSX/JSX.jsx"
target="detail">JSX</a>
<ul>
<li><a href="root/Trilium%20Demo/Scripting%20examples/Render%20note%20with%20JSX/JSX/FormElements.jsx"
target="detail">FormElements</a>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>

View File

@@ -18,7 +18,6 @@
width="150" height="150">
</figure>
<p><strong>Welcome to Trilium Notes!</strong>
</p>
<p>This is a "demo" document packaged with Trilium to showcase some of its
features and also give you some ideas on how you might structure your notes.
@@ -26,22 +25,17 @@
you wish.</p>
<p>If you need any help, visit <a href="https://triliumnotes.org">triliumnotes.org</a> or
our <a href="https://github.com/TriliumNext">GitHub repository</a>
</p>
<h2>Cleanup</h2>
<p>Once you're finished with experimenting and want to cleanup these pages,
you can simply delete them all.</p>
<h2>Formatting</h2>
<p>Trilium supports classic formatting like <em>italic</em>, <strong>bold</strong>, <em><strong>bold and italic</strong></em>.
You can add links pointing to <a href="https://triliumnotes.org/">external pages</a> or&nbsp;
<a
class="reference-link" href="Trilium%20Demo/Formatting%20examples">Formatting examples</a>.</p>
<h3>Lists</h3>
<p><strong>Ordered:</strong>
</p>
<ol>
<li data-list-item-id="e877cc655d0239b8bb0f38696ad5d8abb">First Item</li>
@@ -56,7 +50,6 @@
</li>
</ol>
<p><strong>Unordered:</strong>
</p>
<ul>
<li data-list-item-id="e68bf4b518a16671c314a72073c3d900a">Item</li>
@@ -67,7 +60,6 @@
</li>
</ul>
<h3>Block quotes</h3>
<blockquote>
<p>Whereof one cannot speak, thereof one must be silent”</p>
<p> Ludwig Wittgenstein</p>

View File

@@ -14,22 +14,17 @@
<div class="ck-content">
<h2>Main characters</h2>
<p>… here put main characters …</p>
<p>&nbsp;</p>
<h2>Plot</h2>
<p>… describe main plot lines …</p>
<p>&nbsp;</p>
<h2>Tone</h2>
<p>&nbsp;</p>
<h2>Genre</h2>
<p>scifi / drama / romance</p>
<p>&nbsp;</p>
<h2>Similar books</h2>
<ul>
<li></li>
</ul>

View File

@@ -16,10 +16,8 @@
<p>Check out <a href="https://www.amazon.com/amz-books/book-deals">Kindle Daily Deals</a>:</p>
<ul>
<li data-list-item-id="e5bec34ede90d88a3ad5f7362ab60cbfb">Cixin Liu - <a href="https://www.amazon.com/Dark-Forest-Remembrance-Earths-Past/dp/0765386690/">The Dark Forest</a>
</li>
<li data-list-item-id="ef10faa539920a4fd817f09ba564d69d3">Ann Leckie - <a href="https://www.amazon.com/Ancillary-Sword-Imperial-Radch-Leckie/dp/0316246654/">Ancillary Sword</a>
</li>
</ul>
</div>

View File

@@ -18,25 +18,21 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">buy milk&nbsp;&nbsp;</span>
</label>
</li>
<li>
<label class="todo-list__label">
<input type="checkbox" checked="checked" disabled="disabled"><span class="todo-list__label__description">do the laundry&nbsp;&nbsp;</span>
</label>
</li>
<li>
<label class="todo-list__label">
<input type="checkbox" checked="checked" disabled="disabled"><span class="todo-list__label__description">watch TV&nbsp;&nbsp;</span>
</label>
</li>
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">eat ice cream&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -28,7 +28,6 @@
}</code></pre>
<p>For larger pieces of code it is better to use a code note, which uses
a fully-fledged code editor (CodeMirror). For an example of a code note,
see&nbsp;<a class="reference-link" href="../Scripting%20examples/Custom%20request%20handler.js">Custom request handler</a>.</p>

View File

@@ -15,9 +15,7 @@
<div class="ck-content">
<p><span class="math-tex">\(% \f is defined as #1f(#2) using the macro \f\relax{x} = \int_{-\infty}^\infty &nbsp; &nbsp; \f\hat\xi\,e^{2 \pi i \xi x} &nbsp; &nbsp; \,d\xi\)</span>Some
math examples:</p><span class="math-tex">\[\displaystyle \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\cdots} } } }\]</span>
<p>Another:</p><span class="math-tex">\[\displaystyle \left( \sum_{k=1}^n a_k b_k \right)^2 \leq \left( \sum_{k=1}^n a_k^2 \right) \left( \sum_{k=1}^n b_k^2 \right)\]</span>
<p>Inline math is also possible:&nbsp;<span class="math-tex">\(c^2 = a^2 + b^2\)</span>&nbsp;</p>
<p>&nbsp;</p>
</div>

View File

@@ -15,7 +15,6 @@
<div class="ck-content">
<p>How to be a stoic from Massimo Pigliuci:</p>
<p><a href="https://www.amazon.com/gp/product/B01K3WN1BY">https://www.amazon.com/gp/product/B01K3WN1BY</a>
</p>
</div>
</div>

View File

@@ -22,7 +22,6 @@
<p>This page demonstrates two things:</p>
<ul>
<li>possibility to <a href="#root/_hidden/_help/_help_KSZ04uQ2D1St/_help_iPIMuisry3hd/_help_nBAXQFj20hS1">include one note into another</a>
</li>
<li>PDF preview - you can read PDFs directly in Trilium!</li>
</ul>

View File

@@ -14,7 +14,6 @@
<div class="ck-content">
<p>You can read some explanation on how this journal works here: <a href="https://github.com/zadam/trilium/wiki/Day-notes">https://github.com/zadam/trilium/wiki/Day-notes</a>
</p>
</div>
</div>

View File

@@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -17,7 +17,6 @@
<li>XBox</li>
<li>Candles</li>
<li><a href="https://www.amazon.ca/Anker-SoundCore-Portable-Bluetooth-Resistance/dp/B01MTB55WH?pd_rd_wg=honW8&amp;pd_rd_r=c9bb7c0f-0051-4da7-991f-4ca711a1b3e3&amp;pd_rd_w=ciUpR&amp;ref_=pd_gw_simh&amp;pf_rd_r=K10XKX0NGPDNTYYP4BS4&amp;pf_rd_p=5f1b460b-78c1-580e-929e-2878fe4859e8">Portable speakers</a>
</li>
<li>...?</li>
</ul>

View File

@@ -14,10 +14,8 @@
<div class="ck-content">
<p>Wiki: <a href="https://en.wikipedia.org/wiki/Trusted_timestamping">https://en.wikipedia.org/wiki/Trusted_timestamping</a>
</p>
<p>Bozho: <a href="https://techblog.bozho.net/using-trusted-timestamping-java/">https://techblog.bozho.net/using-trusted-timestamping-java/</a>
</p>
<p><strong>Trusted timestamping</strong> is the process of <a href="https://en.wikipedia.org/wiki/Computer_security">securely</a> keeping
track of the creation and modification time of a document. Security here

View File

@@ -16,7 +16,6 @@
<p>Miscellaneous notes done on monday ...</p>
<p>&nbsp;</p>
<p>Interesting video: <a href="https://www.youtube.com/watch?v=_eSAF_qT_FY&amp;feature=youtu.be">https://www.youtube.com/watch?v=_eSAF_qT_FY&amp;feature=youtu.be</a>
</p>
<p>&nbsp;</p>
<p>&nbsp;</p>

View File

@@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -18,7 +18,6 @@
width="209" height="300">
</figure>
<p>Maybe CodeNames? <a href="https://boardgamegeek.com/boardgame/178900/codenames">https://boardgamegeek.com/boardgame/178900/codenames</a>
</p>
</div>
</div>

View File

@@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -18,7 +18,6 @@
<li>
<label class="todo-list__label">
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">&nbsp;&nbsp;</span>
</label>
</li>
</ul>

View File

@@ -24,17 +24,14 @@
<span
class="footnote-reference" data-footnote-reference="" data-footnote-index="1"
data-footnote-id="6qz4pm021mi" role="doc-noteref" id="fnref6qz4pm021mi"><sup><a href="#fn6qz4pm021mi">[1]</a></sup>
</span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="6qz4pm021mi" role="doc-endnote" id="fn6qz4pm021mi"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="6qz4pm021mi"><sup><strong><a href="#fnref6qz4pm021mi">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

View File

@@ -26,16 +26,13 @@
been brought to its knees.<span class="footnote-reference" data-footnote-reference=""
data-footnote-index="1" data-footnote-id="o6g991vkrwj" role="doc-noteref"
id="fnrefo6g991vkrwj"><sup><a href="#fno6g991vkrwj">[1]</a></sup></span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="o6g991vkrwj" role="doc-endnote" id="fno6g991vkrwj"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="o6g991vkrwj"><sup><strong><a href="#fnrefo6g991vkrwj">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

View File

@@ -22,16 +22,13 @@
around 1450 in polished drystone walls.<span class="footnote-reference"
data-footnote-reference="" data-footnote-index="1" data-footnote-id="4prjheuho88"
role="doc-noteref" id="fnref4prjheuho88"><sup><a href="#fn4prjheuho88">[1]</a></sup></span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="4prjheuho88" role="doc-endnote" id="fn4prjheuho88"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="4prjheuho88"><sup><strong><a href="#fnref4prjheuho88">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

View File

@@ -23,16 +23,13 @@
by earthquakes.<span class="footnote-reference" data-footnote-reference=""
data-footnote-index="1" data-footnote-id="ej5sd0bakne" role="doc-noteref"
id="fnrefej5sd0bakne"><sup><a href="#fnej5sd0bakne">[1]</a></sup></span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="ej5sd0bakne" role="doc-endnote" id="fnej5sd0bakne"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="ej5sd0bakne"><sup><strong><a href="#fnrefej5sd0bakne">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

View File

@@ -26,17 +26,14 @@
<span
class="footnote-reference" data-footnote-reference="" data-footnote-index="1"
data-footnote-id="4kitkusvyi3" role="doc-noteref" id="fnref4kitkusvyi3"><sup><a href="#fn4kitkusvyi3">[1]</a></sup>
</span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="4kitkusvyi3" role="doc-endnote" id="fn4kitkusvyi3"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="4kitkusvyi3"><sup><strong><a href="#fnref4kitkusvyi3">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

View File

@@ -23,17 +23,14 @@
<span
class="footnote-reference" data-footnote-reference="" data-footnote-index="1"
data-footnote-id="o0o2das7ljm" role="doc-noteref" id="fnrefo0o2das7ljm"><sup><a href="#fno0o2das7ljm">[1]</a></sup>
</span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="o0o2das7ljm" role="doc-endnote" id="fno0o2das7ljm"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="o0o2das7ljm"><sup><strong><a href="#fnrefo0o2das7ljm">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

View File

@@ -23,16 +23,13 @@
the complex.<span class="footnote-reference" data-footnote-reference=""
data-footnote-index="1" data-footnote-id="zzzjn52iwk" role="doc-noteref"
id="fnrefzzzjn52iwk"><sup><a href="#fnzzzjn52iwk">[1]</a></sup></span>
</p>
<ol class="footnote-section footnotes" data-footnote-section="" role="doc-endnotes">
<li class="footnote-item" data-footnote-item="" data-footnote-index="1"
data-footnote-id="zzzjn52iwk" role="doc-endnote" id="fnzzzjn52iwk"><span class="footnote-back-link" data-footnote-back-link="" data-footnote-id="zzzjn52iwk"><sup><strong><a href="#fnrefzzzjn52iwk">^</a></strong></sup></span>
<div
class="footnote-content" data-footnote-content="">
<p><a href="https://www.thecollector.com/what-are-the-seven-wonders-of-the-world/">What Are the 7 Wonders of the World? (with HD Images) | TheCollector</a>
</p>
</div>
</li>

View File

@@ -1,108 +0,0 @@
import {
ActionButton, Button, LinkButton,
Admonition, Collapsible, FormGroup,
Dropdown, FormListItem, FormDropdownDivider, FormDropdownSubmenu,
NoteAutocomplete, NoteLink, Modal,
CKEditor,
useEffect, useState
} from "trilium:preact";
import { showMessage } from "trilium:api";
export default function() {
const [ time, setTime ] = useState();
const lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam accumsan eu odio non gravida. Pellentesque ornare, arcu condimentum molestie dignissim, nibh turpis ultrices elit, eget elementum nunc erat at erat. Maecenas vehicula consectetur elit, nec fermentum elit venenatis eu.";
useEffect(() => {
const interval = setInterval(() => setTime(new Date().toLocaleString()), 1000);
return () => clearInterval(interval);
}, []);
return (
<div style={{ padding: 20, display: "flex", flexDirection: "column", gap: "1em" }}>
<h1>Widget showcase</h1>
<Buttons />
<Admonition type="note">
<strong>Admonition</strong><br />
{lorem}
</Admonition>
<Collapsible title="Collapsible" initiallyExpanded>
{lorem}
</Collapsible>
<FormElements />
<NoteElements />
<ModalSample />
<DropdownSample />
</div>
);
}
function Buttons() {
const onClick = () => showMessage("A button was pressed");
return (
<>
<h2>Buttons</h2>
<div style={{ display: "flex", gap: "1em", alignItems: "center" }}>
<ActionButton icon="bx bx-rocket" text="Action button" onClick={onClick} />
<Button icon="bx bx-rocket" text="Simple button" onClick={onClick} />
<LinkButton text="Link button" onClick={onClick} />
</div>
</>
)
}
function NoteElements() {
const [ noteId, setNoteId ] = useState("");
return (
<div>
<h2>Note elements</h2>
<FormGroup name="note-autocomplete" label="Note autocomplete">
<NoteAutocomplete
placeholder="Select a note"
noteId={noteId} noteIdChanged={setNoteId}
/>
</FormGroup>
<FormGroup name="note-link" label="Note link">
{noteId
? <NoteLink notePath={noteId} showNoteIcon />
: <span>Select a note first</span>}
</FormGroup>
</div>
);
}
function ModalSample() {
const [ shown, setShown ] = useState(false);
return (
<>
<h2>Modal</h2>
<Button text="Open modal" onClick={() => setShown(true)} />
<Modal title="Modal title" size="md" show={shown} onHidden={() => setShown(false)}>
Modal goes here.
</Modal>
</>
)
}
function DropdownSample() {
return (
<>
<h2>Dropdown menu</h2>
<Dropdown text="Dropdown" hideToggleArrow>
<FormListItem icon="bx bx-cut">Cut</FormListItem>
<FormListItem icon="bx bx-copy">Copy</FormListItem>
<FormListItem icon="bx bx-paste">Paste</FormListItem>
<FormDropdownDivider />
<FormDropdownSubmenu title="Submenu">
<FormListItem>More items</FormListItem>
</FormDropdownSubmenu>
</Dropdown>
</>
)
}

View File

@@ -1,84 +0,0 @@
import {
useState,
FormCheckbox, FormDropdownList, FormFileUploadButton, FormGroup, FormRadioGroup, FormTextArea,
FormTextBox, FormToggle, Slider, RawHtml, LoadingSpinner, Icon,
} from "trilium:preact";
export default function FormElements() {
const [ checkboxChecked, setCheckboxChecked ] = useState(false);
const [ dropdownValue, setDropdownValue ] = useState("key-1");
const [ radioGroupValue, setRadioGroupValue ] = useState("key-1");
const [ sliderValue, setSliderValue ] = useState(50);
return (
<>
<h2>Form elements</h2>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: "1em" }}>
<FormGroup name="checkbox" label="Checkbox">
<FormCheckbox label="Checkbox" currentValue={checkboxChecked} onChange={setCheckboxChecked} />
</FormGroup>
<FormGroup name="toggle" label="Toggle">
<FormToggle switchOnName="Off" switchOffName="On" currentValue={checkboxChecked} onChange={setCheckboxChecked} />
</FormGroup>
<FormGroup name="dropdown" label="Dropdown">
<FormDropdownList
values={[
{ key: "key-1", name: "First item" },
{ key: "key-2", name: "Second item" },
{ key: "key-3", name: "Third item" },
]}
currentValue={dropdownValue} onChange={setDropdownValue}
keyProperty="key" titleProperty="name"
/>
</FormGroup>
<FormGroup name="radio-group" label="Radio group">
<FormRadioGroup
values={[
{ value: "key-1", label: "First item" },
{ value: "key-2", label: "Second item" },
{ value: "key-3", label: "Third item" },
]}
currentValue={radioGroupValue} onChange={setRadioGroupValue}
/>
</FormGroup>
<FormGroup name="text-box" label="Text box">
<FormTextBox
placeholder="Type something..."
currentValue="" onChange={(newValue) => {}}
/>
</FormGroup>
<FormGroup name="text-area" label="Text area">
<FormTextArea
placeholder="Type something bigger..."
currentValue="" onChange={(newValue) => {}}
/>
</FormGroup>
<FormGroup name="slider" label="Slider">
<Slider
min={1} max={100}
value={sliderValue} onChange={setSliderValue}
/>
</FormGroup>
<FormGroup name="file-upload" label="File upload">
<FormFileUploadButton
text="Upload"
onChange={(files) => {
const file = files?.[0];
if (!file) return;
showMessage(`Got file "${file.name}" of size ${file.size} B and type ${file.type}.`);
}}
/>
</FormGroup>
<FormGroup name="icon" label="Icon">
<Icon icon="bx bx-smile" />
</FormGroup>
<FormGroup name="loading-spinner" label="Loading spinner">
<LoadingSpinner />
</FormGroup>
<FormGroup name="raw-html" label="Raw HTML">
<RawHtml html="<strong>Hi</strong> <em>there</em>" />
</FormGroup>
</div>
</>
)
}

View File

@@ -0,0 +1,21 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../../../../../../../style.css">
<base target="_parent">
<title data-trilium-title>chart.js</title>
</head>
<body>
<div class="content">
<h1 data-trilium-h1>chart.js</h1>
<div class="ck-content">
<p>This is a clone of a note. Go to its <a href="../../../../../Weight%20Tracker/Implementation/JS%20code/chart.js">primary location</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,21 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../../../../../../../../../style.css">
<base target="_parent">
<title data-trilium-title>chart.js</title>
</head>
<body>
<div class="content">
<h1 data-trilium-h1>chart.js</h1>
<div class="ck-content">
<p>This is a clone of a note. Go to its <a href="../../../../../../Weight%20Tracker/Implementation/JS%20code/chart.js">primary location</a>.</p>
</div>
</div>
</body>
</html>

View File

@@ -18,7 +18,6 @@
width="209" height="300">
</figure>
<p>Maybe CodeNames? <a href="https://boardgamegeek.com/boardgame/178900/codenames">https://boardgamegeek.com/boardgame/178900/codenames</a>
</p>
</div>
</div>

View File

@@ -14,7 +14,6 @@
<div class="ck-content">
<p><a href="https://en.wikipedia.org/wiki/The_Black_Swan:_The_Impact_of_the_Highly_Improbable">https://en.wikipedia.org/wiki/The_Black_Swan:_The_Impact_of_the_Highly_Improbable</a>
</p>
<p><em><strong>The Black Swan: The Impact of the Highly Improbable</strong></em> is
a 2007 book by author and former <a href="https://en.wikipedia.org/wiki/Options_trader">options trader</a>

View File

@@ -25,7 +25,6 @@
and <a href="https://en.wikipedia.org/wiki/Apple_Inc.">Apple's</a> <a href="https://en.wikipedia.org/wiki/MacOS">macOS</a> (formerly
OS X). A version <a href="https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux">is also available for Windows 10</a>.</p>
<p><a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)">Bash on Wikipedia</a>
</p>
</div>
</div>

View File

@@ -14,7 +14,6 @@
<div class="ck-content">
<h3>Login shell</h3>
<p>As a "login shell", Bash reads and sets (executes) the user's profile
from /etc/profile and one of ~/.bash_profile, ~/.bash_login, or ~/.profile
(in that order, using the first one that's readable!).</p>
@@ -24,7 +23,6 @@
that only make sense for the initial user login. That's why all UNIX® shells
have (should have) a "login" mode.</p>
<p><em><strong>Methods to start Bash as a login shell:</strong></em>
</p>
<ul>
<li>the first character of argv[0] is - (a hyphen): traditional UNIX® shells
@@ -33,20 +31,17 @@
<li>Bash is started with the --login option</li>
</ul>
<p><em><strong>Methods to test for login shell mode:</strong></em>
</p>
<ul>
<li>the shell option <a href="http://wiki.bash-hackers.org/internals/shell_options#login_shell">login_shell</a> is
set</li>
</ul>
<p><em><strong>Related switches:</strong></em>
</p>
<ul>
<li>--noprofile disables reading of all profile files</li>
</ul>
<h3>Interactive shell</h3>
<p>When Bash starts as an interactive non-login shell, it reads and executes
commands from ~/.bashrc. This file should contain, for example, aliases,
since they need to be defined in every shell as they're not inherited from
@@ -56,13 +51,11 @@
The classic way to have a system-wide rc file is to source /etc/bashrc
from every user's ~/.bashrc.</p>
<p><em><strong>Methods to test for interactive-shell mode:</strong></em>
</p>
<ul>
<li>the special parameter $- contains the letter i (lowercase I)</li>
</ul>
<p><em><strong>Related switches:</strong></em>
</p>
<ul>
<li>-i forces the interactive mode</li>
@@ -72,7 +65,6 @@
~/.bashrc)</li>
</ul>
<h3>SH mode</h3>
<p>When Bash starts in SH compatiblity mode, it tries to mimic the startup
behaviour of historical versions of sh as closely as possible, while conforming
to the POSIX® standard as well. The profile files read are /etc/profile
@@ -82,7 +74,6 @@
file.</p>
<p>After the startup files are read, Bash enters the <a href="http://wiki.bash-hackers.org/scripting/bashbehaviour#posix_run_mode">POSIX(r) compatiblity mode (for running, not for starting!)</a>.</p>
<p><em><strong>Bash starts in sh compatiblity mode when:</strong></em>
</p>
<ul>
<li>

View File

@@ -14,7 +14,6 @@
<div class="ck-content">
<p>Documentation: <a href="http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_02.html">http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_02.html</a>
</p><pre><code class="language-text-x-sh">#!/bin/bash

View File

@@ -20,7 +20,6 @@
href="https://en.wikipedia.org/wiki/Node.js#cite_note-b1-31">[31]</a>Developers can create scalable servers without using <a href="https://en.wikipedia.org/wiki/Thread_(computing)">threading</a>,
by using a simplified model of <a href="https://en.wikipedia.org/wiki/Event-driven_programming">event-driven programming</a> that
uses callbacks to signal the completion of a task.<a href="https://en.wikipedia.org/wiki/Node.js#cite_note-b1-31">[31]</a>
</p>
</div>
</div>

View File

@@ -550,9 +550,9 @@
}
@media print{
.ck-content figure.table:not(.layout-table){
.ck-content figure.table:not(.layout-table):not(:has(> figcaption)){
display:block;
width:fit-content;
height:fit-content;
}
.ck-content figure.table:not(.layout-table) > table{
height:initial;
@@ -601,33 +601,4 @@
.ck-content .table td,
.ck-content .table th{
overflow-wrap:break-word;
}
:root{
--ck-content-table-style-spacing:1.5em;
}
.ck-content .table.table-style-align-left{
float:left;
margin-right:var(--ck-content-table-style-spacing);
}
.ck-content .table.table-style-align-right{
float:right;
margin-left:var(--ck-content-table-style-spacing);
}
.ck-content .table.table-style-align-center{
margin-left:auto;
margin-right:auto;
}
.ck-content .table.table-style-block-align-left{
margin-left:0;
margin-right:auto;
}
.ck-content .table.table-style-block-align-right{
margin-left:auto;
margin-right:0;
}
}

View File

@@ -30,8 +30,7 @@
"dependencies": {
"better-sqlite3": "12.5.0",
"html-to-text": "9.0.5",
"node-html-parser": "7.0.1",
"sucrase": "3.35.1"
"node-html-parser": "7.0.1"
},
"devDependencies": {
"@anthropic-ai/sdk": "0.71.2",

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -21,7 +21,7 @@
<p>You can easily restore the demo notes by using Trilium's built-in import
feature by importing them:</p>
<ul>
<li>Download <a href="https://github.com/TriliumNext/Trilium/raw/refs/heads/main/apps/server/src/assets/db/demo.zip">the .zip archive</a> with
<li>Download <a href="https://github.com/TriliumNext/Trilium/raw/develop/db/demo.zip">this .zip archive</a> with
the latest version of the demo notes</li>
<li>Right click on any note in your tree under which you would like the demo
notes to be imported</li>

View File

@@ -9,8 +9,8 @@
feel free to report them either via a ticket or via the Matrix.</p>
<h2>Downloading the nightly release manually</h2>
<p>Go to <a href="https://github.com/TriliumNext/Trilium/releases/tag/nightly">github.com/TriliumNext/Trilium/releases/tag/nightly</a> and
look for the artifacts starting with <code>TriliumNotes-main</code>. Choose
the appropriate one for your platform (e.g. <code>windows-x64.zip</code>).</p>
look for the artifacts starting with <code spellcheck="false">TriliumNotes-main</code>.
Choose the appropriate one for your platform (e.g. <code spellcheck="false">windows-x64.zip</code>).</p>
<p>Depending on your use case, you can either test the portable version or
even use the installer.</p>
<aside class="admonition note">

View File

@@ -15,8 +15,7 @@ class="image">
<img style="aspect-ratio:1150/27;" src="4_New Layout_image.png"
width="1150" height="27">
</figure>
<h3>Inline title</h3>
<h3>Inline title</h3>
<p>In previous versions of Trilium, the title bar was fixed at all times.
In the new layout, there is both a fixed title bar and one that scrolls
with the text. The newly introduced title is called the <em>Inline title</em> and
@@ -42,18 +41,18 @@ class="image">
width="910" height="104">
<figcaption>The fixed title bar. The title only appears after scrolling past the <em>Inline title</em>.</figcaption>
</figure>
<h3>New note type switcher</h3>
<h3>New note type switcher</h3>
<p>When a new&nbsp;<a class="reference-link" href="#root/_help_iPIMuisry3hd">Text</a>&nbsp;or&nbsp;
<a
class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>&nbsp;note is created, a note type switcher will appear below
the <em>Inline title</em>. Apart from changing the note type, it's also
possible to apply a <a href="#root/_help_KC1HB96bqqHX">template</a>.</p>
<p>The switcher will disappear as soon as a text is entered.</p>
<img src="5_New Layout_image.png"
width="735" height="143">
<h3>Note badges</h3>
<p>
<img src="5_New Layout_image.png" width="735"
height="143">
</p>
<h3>Note badges</h3>
<p>Note badges appear near the fixed note title and indicate important information
about the note such as whether it is read-only. Some of the badges are
also interactive.</p>
@@ -63,20 +62,21 @@ class="image">
</figure>
<p>The following badges are available:</p>
<ul>
<li><strong>Read-only badge</strong>, which will be shown if the note is not
<li data-list-item-id="e92dd6f75d874ac793f37db886606439d"><strong>Read-only badge</strong>, which will be shown if the note is not
editable due to either automatic read-only or manual read-only. Clicking
on the badge will temporarily edit the note (similar to the Edit <a href="#root/_help_XpOYSgsLkTJy">floating button</a>).</li>
<li><strong>Share badge</strong>, which will indicate that the current note
<li
data-list-item-id="e7bbe2a9a3ddc469e9fe1c31ec7cf1a2f"><strong>Share badge</strong>, which will indicate that the current note
is shared. The badge will also indicate if the share is on the local network
(for the desktop application without&nbsp;<a class="reference-link" href="#root/_help_cbkrhQjrkKrh">Synchronization</a>&nbsp;set
up) or publicly accessible (for the server).</li>
<li><strong>Web clip badge</strong>, which will indicate if the note was clipped
using the&nbsp;<a class="reference-link" href="#root/_help_MtPxeAWVAzMg">Web Clipper</a>.
The badge acts as a link, so it can be clicked on to navigate to the page
or right clicked for more options.</li>
<li><strong>Execute badge</strong>, for <a href="#root/_help_CdNpE2pqjmI6">scripts</a> or
<a
href="#root/_help_YKWqdJhzi2VY">saved SQL queries</a>which have an execute button or a description.</li>
<li data-list-item-id="e2f51260db16ace77feb9eddcf987cc52"><strong>Web clip badge</strong>, which will indicate if the note was clipped
using the&nbsp;<a class="reference-link" href="#root/_help_MtPxeAWVAzMg">Web Clipper</a>.
The badge acts as a link, so it can be clicked on to navigate to the page
or right clicked for more options.</li>
<li data-list-item-id="ef6bec8ce5a4f3fd249bf57a16e1e235e"><strong>Execute badge</strong>, for <a href="#root/_help_CdNpE2pqjmI6">scripts</a> or
<a
href="#root/_help_YKWqdJhzi2VY">saved SQL queries</a>which have an execute button or a description.</li>
</ul>
<p>Some of these badges replace the dedicated panels at the top of the note.</p>
<h3>Collapsible sections</h3>
@@ -86,25 +86,26 @@ class="image">
</figure>
<p>The following sections have been made collapsible:</p>
<ul>
<li><em>Promoted Attributes</em>
<li class="ck-list-marker-italic" data-list-item-id="e8add36768a67f7f5af68545beb18037f"><em>Promoted Attributes</em>
<ul>
<li>For full-height notes such as&nbsp;<a class="reference-link" href="#root/_help_grjYqerjn243">Canvas</a>,
<li data-list-item-id="e633becb9bcfb1796d6d8203ffd2ac660">For full-height notes such as&nbsp;<a class="reference-link" href="#root/_help_grjYqerjn243">Canvas</a>,
the promoted attributes are collapsed by default to make room.</li>
<li>The keyboard shortcut previously used to trigger the promoted attributes
<li
data-list-item-id="ee7cbce0e436fd7a00a176df51f5570a4">The keyboard shortcut previously used to trigger the promoted attributes
ribbon tab (which was no longer working) has been repurposed to toggle
the promoted attributes instead.</li>
</ul>
</ul>
</li>
<li><em>Edited Notes</em>, which appears for&nbsp;<a class="reference-link"
<li data-list-item-id="ece11f5c363adc3938abfd8d21b2f655d"><em>Edited Notes</em>, which appears for&nbsp;<a class="reference-link"
href="#root/_help_l0tKav7yLHGF">Day Notes</a>&nbsp;is now shown underneath the
title.
<ul>
<li>Whether the section is collapsed or not depends on the choice in&nbsp;
<li data-list-item-id="ef3f8aac2e5f49b4188acd62868b32020">Whether the section is collapsed or not depends on the choice in&nbsp;
<a
class="reference-link" href="#root/_help_4TIF1oA4VQRO">Options</a>&nbsp;→ Appearance.</li>
</ul>
</li>
<li><em>Search Properties</em>, which appears for the full&nbsp;<a class="reference-link"
<li data-list-item-id="e42393439936bb76c397de5ae6e5ecd80"><em>Search Properties</em>, which appears for the full&nbsp;<a class="reference-link"
href="#root/_help_eIg8jdvaoNNd">Search</a>&nbsp;and&nbsp;<a class="reference-link"
href="#root/_help_m523cpzocqaD">Saved Search</a>.</li>
</ul>
@@ -115,75 +116,78 @@ class="image">
<p>Here's how all the different tabs that were once part of the ribbon are
now available in the new layout:</p>
<ul>
<li>“Formatting toolbar” was relocated to the top of the page.
<li data-list-item-id="e758293ceb33f07215fcf5fc4700a4e5f">“Formatting toolbar” was relocated to the top of the page.
<ul>
<li>Instead of having one per split, now there is a single formatting toolbar
<li data-list-item-id="e8dded8673adf680dde8615934b3f857b">Instead of having one per split, now there is a single formatting toolbar
per tab. This allows more space for the toolbar items.</li>
</ul>
</li>
<li>“Owned attributes” and “Inherited attributes” were merged and moved to
<li data-list-item-id="e2c510fd7f19155ed0e05946acf7a4cd0">“Owned attributes” and “Inherited attributes” were merged and moved to
the status bar region (displayed one above the other).</li>
<li>“Basic Properties” were integrated in the&nbsp;<a class="reference-link"
<li data-list-item-id="ec032e05420954455db0884651e2d1c4e">“Basic Properties” were integrated in the&nbsp;<a class="reference-link"
href="#root/_help_8YBEPzcpUgxw">Note buttons</a>&nbsp;menu.
<ul>
<li>The only exception here is the Language combo box which can now be found
<li data-list-item-id="ebcff28c9a8d07526187559ebd5faa211">The only exception here is the Language combo box which can now be found
in the status bar (top-right of the screen).</li>
</ul>
</li>
<li>“File” and “Image” tabs
<li data-list-item-id="e0b76ace8a0be9d7bb6870398b87338ab">“File” and “Image” tabs
<ul>
<li>The buttons were moved to the right of the note title, as dedicated entries
<li data-list-item-id="e55913c9bba25585fbdcb2098a2977e0e">The buttons were moved to the right of the note title, as dedicated entries
in&nbsp;<a class="reference-link" href="#root/_help_8YBEPzcpUgxw">Note buttons</a>.</li>
<li>The info section has been merged into the <em>Note info</em> section of
<li
data-list-item-id="e749af557c7ec8307b65cebfc0b375508">The info section has been merged into the <em>Note info</em> section of
the status bar.</li>
</ul>
</ul>
</li>
<li>Edited notes
<li data-list-item-id="ee3556b093a5a07fd6d239c33d8cf2611">Edited notes
<ul>
<li>Moved underneath the title, displayed under a collapsible area and the
<li data-list-item-id="e541470be93aa45c6495efb6cd8063e47">Moved underneath the title, displayed under a collapsible area and the
notes are represented as badges/chips.</li>
<li>Whether the section is expanded or collapsed depends on the “Edited Notes
<li data-list-item-id="e16f53a81cda714958d00ca53dbb1ffa9">Whether the section is expanded or collapsed depends on the “Edited Notes
ribbon tab will automatically open on day notes” setting from Options →
Appearance.</li>
</ul>
</li>
<li>Search definition tab
<li data-list-item-id="e0fc642280dfcbc6ca7cdc995c57941f8">Search definition tab
<ul>
<li>Moved underneath the title under a collapsible area.</li>
<li>Expanded by default for new searches, collapsed for saved searches.</li>
<li data-list-item-id="e616bd30c16829e863accfcab35c41874">Moved underneath the title under a collapsible area.</li>
<li data-list-item-id="e1c2b80776b0ffe26ae69410131c1e0f2">Expanded by default for new searches, collapsed for saved searches.</li>
</ul>
</li>
<li>The Note map is now available in the Note actions menu.
<li data-list-item-id="ec4b6bd664511175e82281caf113b13b8">The Note map is now available in the Note actions menu.
<ul>
<li>Instead of opening into a panel in the ribbon, the note map now opens
<li data-list-item-id="ecd6bd6144cea4bc2148e6baaa940f9b0">Instead of opening into a panel in the ribbon, the note map now opens
in a side split (similar to the in-app help).</li>
</ul>
</li>
<li>“Note info” tab was moved to a small (i) icon in the status bar.</li>
<li>“Similar notes” tab
<li data-list-item-id="ea0cd465194ced54082c799b53a1839c0">“Note info” tab was moved to a small (i) icon in the status bar.</li>
<li
data-list-item-id="eeb0fd706272546ff31b629917b8fcb0a">“Similar notes” tab
<ul>
<li>Moved to the status bar, by going to the “Note info” section and pressing
<li data-list-item-id="eb01edf7d21f73998b99516cd46e0e321">Moved to the status bar, by going to the “Note info” section and pressing
the button to show similar notes.</li>
<li>Displayed as a fixed panel, similar to the attributes.</li>
<li data-list-item-id="e3aa6b54672681d8d90795c57372f2595">Displayed as a fixed panel, similar to the attributes.</li>
</ul>
</li>
<li>The Collection properties tab were relocated under the note title and
grouped into:
<ul>
<li>A combo box to quickly switch between views.</li>
<li>Individual settings for the current view in a submenu.</li>
</li>
<li data-list-item-id="e791313bf732b70bbf4d6f91755db2494">The Collection properties tab were relocated under the note title and
grouped into:
<ul>
<li data-list-item-id="e712f0f389eab1a329a4df3ed6d2b49aa">A combo box to quickly switch between views.</li>
<li data-list-item-id="edaba6f82803ed727489575b9c46d667c">Individual settings for the current view in a submenu.</li>
</ul>
</li>
<li data-list-item-id="e26e827650e594f717a90c0cd94c1c3a1">Some smaller ribbon tabs were converted to badges that appear near the
note title in the breadcrumb section:
<ul>
<li data-list-item-id="eaf6b3ff2c30e7167a29cb58c160c55f8">Original URL indicator for clipped web pages (<code spellcheck="false">#pageUrl</code>).</li>
<li
data-list-item-id="ee7fc72c13a93f9e5d0edc0bba6bc0b34">SQL and script execute buttons.</li>
</ul>
</li>
<li>Some smaller ribbon tabs were converted to badges that appear near the
note title in the breadcrumb section:
<ul>
<li>Original URL indicator for clipped web pages (<code>#pageUrl</code>).</li>
<li>SQL and script execute buttons.</li>
</ul>
</li>
</li>
</ul>
<aside class="admonition note">
<p>The ribbon keyboard shortcuts (e.g. <code>toggleRibbonTabClassicEditor</code>)
<p>The ribbon keyboard shortcuts (e.g. <code spellcheck="false">toggleRibbonTabClassicEditor</code>)
have been repurposed to work on the new layout, where they will toggle
the appropriate panel.</p>
</aside>
@@ -192,11 +196,13 @@ class="image">
the&nbsp;<a class="reference-link" href="#root/_help_8YBEPzcpUgxw">Note buttons</a>&nbsp;area,
with the exception of:</p>
<ul>
<li>The Edit button is displayed near the note title, as a badge.</li>
<li><em>Backlinks</em> is displayed in the status bar. When clicked, the same
<li data-list-item-id="e68c4ff8e52782bbef0104bca4b3f972a">The Edit button is displayed near the note title, as a badge.</li>
<li
data-list-item-id="e45b03477a6cb8c98327562abd551967c"><em>Backlinks</em> is displayed in the status bar. When clicked, the same
list of backlinks is displayed.</li>
<li>Relation map zoom buttons are now part of the relation map itself.</li>
<li>Export image to PNG/SVG are now in the Note actions menu, in the <em>Export as image</em> option.</li>
<li data-list-item-id="e7241aa3a6ec8dd92a3debade61439d81">Relation map zoom buttons are now part of the relation map itself.</li>
<li
data-list-item-id="e2d44d21ae8b0d77bef0327f2aaf4e050">Export image to PNG/SVG are now in the Note actions menu, in the <em>Export as image</em> option.</li>
</ul>
<h3>Changes to the sidebar</h3>
<p>The sidebar (also known as the right pane) also received some important
@@ -204,7 +210,7 @@ class="image">
<p>The previous iteration of the sidebar would appear contextually, depending
on whether there are any items to be displayed. This caused occasional
content shifts when moving between two panes in a split view. In the new
layout, the sidebar acts more like the&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;pane,
layout, the sidebar acts more like the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;pane,
remaining visible even if there is nothing to display.</p>
<p>In order to toggle the sidebar, there is a new button on the top-right
side of the screen, near the window buttons (on Windows and Linux).</p>

View File

@@ -8,48 +8,48 @@
displayed in the bottom-left of the screen.</p>
<h2>Layout and Interaction</h2>
<ul>
<li>If a note or workspace is hoisted, a badge will appear on the left-most
<li data-list-item-id="e235d35dec66a5a6e191ec5cd8af5038a">If a note or workspace is hoisted, a badge will appear on the left-most
side.
<ul>
<li>Clicking on the badge will un-hoist the note/workspace.</li>
<li data-list-item-id="e456ddbf22db13b73ffd4915431ef4edb">Clicking on the badge will un-hoist the note/workspace.</li>
</ul>
</li>
<li>The left-most icon represents the root note, or the hoisted note or workspace.
<li data-list-item-id="e1f0000e6ce3c61b41a8d9ee6ec991686">The left-most icon represents the root note, or the hoisted note or workspace.
<ul>
<li>Clicking the icon will jump to the root note.</li>
<li>Right clicking the icon will display a menu that allows opening the note
<li data-list-item-id="e224bf09301a82f6a58038c55bb4769e4">Clicking the icon will jump to the root note.</li>
<li data-list-item-id="e5e1de710e5d166aa47ba54ddd46657cb">Right clicking the icon will display a menu that allows opening the note
in a new tab, split, etc.</li>
</ul>
</li>
<li>Each segment shows the title of a note in the current note hierarchy.
<li data-list-item-id="e62deaf618aa52bb7062b1801cdaf5bf5">Each segment shows the title of a note in the current note hierarchy.
<ul>
<li>Clicking the icon will jump to that note.</li>
<li>Right clicking will open a menu with multiple options such as opening
<li data-list-item-id="e03cfdc5ebec9d3dce4503960e24fc693">Clicking the icon will jump to that note.</li>
<li data-list-item-id="e2b084f07f11586bbcfb0ec85d1fe52c2">Right clicking will open a menu with multiple options such as opening
the note in a different tab/split/window, hoisting, moving/cloning the
note, duplicating as well as changing the color of the note.</li>
</ul>
</li>
<li>Clicking the arrow next to each segment will reveal the child notes of
<li data-list-item-id="ed8b04db71853f4c0453d321de96acfdf">Clicking the arrow next to each segment will reveal the child notes of
the segment on the left.
<ul>
<li>Clicking on an icon will navigate to that particular note.</li>
<li>It's also possible to create a new child note from here.</li>
<li>The menu can optionally hide the archived notes.</li>
<li data-list-item-id="e0c88db9aafa125a06b81d9e4ab40bf8e">Clicking on an icon will navigate to that particular note.</li>
<li data-list-item-id="e04d705da35d36f06aa734b35e60e454a">It's also possible to create a new child note from here.</li>
<li data-list-item-id="e07c9f1f7bd7ea872181c7c005513ed68">The menu can optionally hide the archived notes.</li>
</ul>
</li>
<li>If the current note is deep within a hierarchy, the segments will collapse
<li data-list-item-id="ee49ef8ad47fbb227dc69ca8f976872aa">If the current note is deep within a hierarchy, the segments will collapse
into a […] button in order not to occupy too much space.
<ul>
<li>Clicking this button will display each collapsed entry as a menu item.
<li data-list-item-id="e1869d9d1b5a2f0c5def31172891a9b14">Clicking this button will display each collapsed entry as a menu item.
Clicking on it will navigate to that particular note.</li>
</ul>
</li>
<li>Right clicking on an empty space to the right of the breadcrumb (before
<li data-list-item-id="e8d793fcdf6ddc0a15f422d5ec12b05c4">Right clicking on an empty space to the right of the breadcrumb (before
the other status bar items) will reveal another menu that allows:
<ul>
<li>Toggling whether archived notes are displayed in the breadcrumb and in
<li data-list-item-id="e4d96585f45337c4bac4182db334343eb">Toggling whether archived notes are displayed in the breadcrumb and in
the note tree.</li>
<li>Copying the current note path to clipboard.</li>
<li data-list-item-id="e37105f037c1fe10fe48e428db4c7c0e7">Copying the current note path to clipboard.</li>
</ul>
</li>
</ul>

View File

@@ -7,45 +7,47 @@
<p>On the right side, specific sections will show depending on the type of
the current note.</p>
<ol>
<li>For code notes, the language mode of the note is indicated (e.g. JavaScript,
<li data-list-item-id="e7409c70ee461800fea112abc0cf9ae25">For code notes, the language mode of the note is indicated (e.g. JavaScript,
plain text), as well as allowing easy switching to another mode.</li>
<li>For text notes, the content language is displayed and can be changed,
<li
data-list-item-id="e02ade3f598cdd3dfda1abbcf9d55060b">For text notes, the content language is displayed and can be changed,
thus configuring the spell-check and the right-to-left support.
<ol>
<li>Note that this applies to the entire note and not the selection, unlike
<li data-list-item-id="e0e530605f6c359593162f582fc9113a1">Note that this applies to the entire note and not the selection, unlike
some text editors.</li>
</ol>
</li>
<li>If a note is placed in multiple places in the tree (cloned), the number
of the note paths will be displayed.
<ol>
<li>Clicking it will reveal the full list of note paths and a button to place
it somewhere else.</li>
</ol>
</li>
<li>If a note has attachments, their number will be displayed.
<ol>
<li>Clicking on it will reveal the list of attachments in a new tab.</li>
</ol>
</li>
<li>If a note is linked from other text notes (backlinks), the number of backlinks
will be displayed.
<ol>
<li>Clicking on it will show the list of notes that link to this note, as
well as an excerpt of where the note is referenced.</li>
</ol>
</li>
</li>
<li data-list-item-id="eb55ddf60b5526101fb30234178ccd4b1">If a note is placed in multiple places in the tree (cloned), the number
of the note paths will be displayed.
<ol>
<li data-list-item-id="e9fa65cb2d50422d4d715a29108268c57">Clicking it will reveal the full list of note paths and a button to place
it somewhere else.</li>
</ol>
</li>
<li data-list-item-id="e353272924ea7c0a8817b75978e74e2f4">If a note has attachments, their number will be displayed.
<ol>
<li data-list-item-id="e36057cb3080443d03fc54f373c4811f7">Clicking on it will reveal the list of attachments in a new tab.</li>
</ol>
</li>
<li data-list-item-id="ebed069f4938bd155c5e436fe92e44cea">If a note is linked from other text notes (backlinks), the number of backlinks
will be displayed.
<ol>
<li data-list-item-id="e1a3f17eeda7672abaf283f7283dd462c">Clicking on it will show the list of notes that link to this note, as
well as an excerpt of where the note is referenced.</li>
</ol>
</li>
</ol>
<p>Regardless of note type, the following items will always be displayed
if there is a note:</p>
<ol>
<li>Note info, which displays:
<li data-list-item-id="ecbd6632bd798107863b39fc23e251ae2">Note info, which displays:
<ol>
<li>The creation/modification date of the note.</li>
<li>The type and MIME of the note.</li>
<li>The note ID.</li>
<li>An estimation of the note size of the note itself and its children.</li>
<li>A button to show Similar notes.</li>
</ol>
<li data-list-item-id="e56c0d5f618b79a64326bc2ab739c2df4">The creation/modification date of the note.</li>
<li data-list-item-id="e7843741f9e99763fcf5ded821f09d789">The type and MIME of the note.</li>
<li data-list-item-id="eaf479da2bbf044be78676f7a68ded903">The note ID.</li>
<li data-list-item-id="ebbeb7fde731354c1b92951e4d45be579">An estimation of the note size of the note itself and its children.</li>
<li
data-list-item-id="e0e10095682b51783b11592065b7e28a7">A button to show Similar notes.</li>
</ol>
</li>
</ol>

View File

@@ -13,7 +13,7 @@
<li>Assign the <code>renderNote</code> <a href="#root/_help_zEY4DaJG4YT5">relation</a> to
point at the previously created code note.</li>
</ol>
<h2>Legacy scripting using jQuery</h2>
<h2>Dynamic content</h2>
<p>A static HTML is generally not enough for&nbsp;<a class="reference-link"
href="#root/_help_CdNpE2pqjmI6">Scripting</a>. The next step is to automatically
change parts of the note using JavaScript.</p>
@@ -24,7 +24,7 @@ The current date &amp; time is &lt;span class="date"&gt;&lt;/span&gt;</code></pr
<p>Now we need to add the script. Create another&nbsp;<a class="reference-link"
href="#root/_help_6f9hih2hXXZk">Code</a>, 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:</p><pre><code class="language-application-javascript-env-backend">const $dateEl = api.$container.find(".date");
note created previously; with the following content:</p><pre><code class="language-text-x-trilium-auto">const $dateEl = api.$container.find(".date");
$dateEl.text(new Date());</code></pre>
<p>Now create a render note at any place and set its <code>~renderNote</code> relation
to point to the HTML note. When the render note is accessed it will display:</p>
@@ -33,34 +33,6 @@ $dateEl.text(new Date());</code></pre>
<br>The current date &amp; time is Sun Apr 06 2025 15:26:29 GMT+0300 (Eastern
European Summer Time)</p>
</blockquote>
<h2>Dynamic content using Preact &amp; JSX</h2>
<p>As a more modern alternative to jQuery, it's possible to use Preact &amp;
JSX to render pages. Since JSX is a superset of JavaScript, there's no
need to provide a HTML anymore.</p>
<p>Here are the steps to creating a simple render note:</p>
<ol>
<li>
<p>Create a note of type&nbsp;<a class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note</a>.</p>
</li>
<li>
<p>Create a child&nbsp;<a class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>&nbsp;note
with JSX as the language.
<br>As an example, use the following content:</p><pre><code class="language-text-x-trilium-auto">export default function() {
return (
&lt;&gt;
&lt;p&gt;Hello world.&lt;/p&gt;
&lt;/&gt;
);
}</code></pre>
</li>
<li>
<p>In the parent render note, define a <code>~renderNote</code> relation pointing
to the newly created child.</p>
</li>
<li>
<p>Refresh the render note and it should display a “Hello world” message.</p>
</li>
</ol>
<h2>Refreshing the note</h2>
<p>It's possible to refresh the note via:</p>
<ul>

View File

@@ -1,46 +0,0 @@
<p>For both&nbsp;<a class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note</a>&nbsp;and
more complicated scripts, it's generally useful to split the code into
multiple&nbsp;<a class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>&nbsp;notes.</p>
<p>When a script is run, the sub-children of the script being run (or the&nbsp;
<a
class="reference-link" href="#root/_help_HcABDtFCkbFN">Render Note</a>) 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.</p>
<p>The collection of a script and its child notes is called a <em>bundle</em>.
A child note inside a bundle is called a <em>module</em>.</p>
<p>As a basic example of dependencies, consider the following note structure:</p>
<ul>
<li>
<p><em>Script with dependency</em>
</p><pre><code class="language-application-javascript-env-backend">api.log(MyMath.sum(2, 2));</code></pre>
<ul>
<li>
<p><em>MyMath</em>
</p><pre><code class="language-application-javascript-env-backend">module.exports = {
sum(a, b) {
return a + b;
}
};</code></pre>
</li>
</ul>
</li>
</ul>
<p>When <em>Script with dependency</em> is run, it will detect <em>MyMath</em> as
a submodule and provide the result of its <code>module.exports</code> object
into a global object with the same name as the note.</p>
<aside class="admonition note">
<p>If the note contains spaces or special characters, they will be stripped.
For example <code>My Nice Note!</code> becomes <code>MyNiceNote</code>.</p>
</aside>
<h2>Alternative syntax</h2>
<p>Instead of providing an object to <code>module.exports</code>, it's also
possible to add fields individually:</p><pre><code class="language-application-javascript-env-backend">module.exports.sum = (a, b) =&gt; a + b;
module.exports.subtract = (a, b) =&gt; a - b;</code></pre>
<h2>Ignoring a code script from a bundle</h2>
<p>To ignore a script from being included in a bundle (e.g. if it's unrelated
to the parent script note), apply the <code>#disableInclusion</code> label.</p>
<h2>Sharing a module across multiple bundles</h2>
<p>Modules can be reused across multiple scripts by simply cloning the shared
module between two modules (see&nbsp;<a class="reference-link" href="#root/_help_IakOLONlIfGI">Cloning Notes</a>).</p>
<p>Optionally, a separate note can be used to contain all the different reusable
modules for an easy way to discover them.</p>

View File

@@ -1,142 +1,13 @@
<p>Custom widgets are a special subset of scripts that render graphical elements
in certain parts of the application. These can be used to add new functionality
to the Trilium application.</p>
<h2>Preact with JSX vs. vanilla jQuery</h2>
<p>In older versions of Trilium, custom widgets were exclusively written
in a combination of jQuery with Trilium's internal widget architecture
(e.g., <code>BasicWidget</code>, <code>NoteContextAwareWidget</code>).</p>
<p>Starting with v0.101.0, custom widgets can also be written in JSX using
the&nbsp;<a class="reference-link" href="#root/_help_KLsqhjaqh1QW">Preact</a>&nbsp;framework.
Both legacy and Preact widgets have the same capabilities, with a single
difference:</p>
<p>It's possible to create custom widget in three possible locations where
you can display your custom content.</p>
<p>Positions are:</p>
<ul>
<li>Preact widgets are content-sized by default whereas legacy widgets need <code>this.contentSized()</code> applied
in the constructor. For more information, see the corresponding section
in&nbsp;<a class="reference-link" href="#root/_help_gMkgcLJ6jBkg">Troubleshooting</a>.</li>
</ul>
<p>Wherever possible, widget examples will be both in the legacy and Preact
format.</p>
<h2>Creating a custom widget</h2>
<ol>
<li>Create a&nbsp;<a class="reference-link" href="#root/_help_6f9hih2hXXZk">Code</a>&nbsp;note.</li>
<li>Set the language to:
<ol>
<li>JavaScript (frontend) for legacy widgets using jQuery.</li>
<li>JSX for Preact widgets. You might need to go to Options → Code to enable
the language first.</li>
</ol>
<li><code>left-pane</code>
</li>
<li>Apply the <code>#widget</code> <a href="#root/_help_HI6GBBIduIgv">label</a>.</li>
</ol>
<h2>Getting started with a simple example</h2>
<p>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.</p>
<h3>Legacy version (jQuery)</h3><pre><code class="language-text-x-trilium-auto">class HelloCenterPane extends api.BasicWidget {
constructor() {
super();
this.contentSized();
}
get parentWidget() { return "center-pane" }
doRender() {
this.$widget = $("&lt;span&gt;Center pane&lt;/span&gt;");
}
}
module.exports = new HelloCenterPane();</code></pre>
<p><a href="#root/_help_s8alTXmpFR61">Refresh the application</a> and the widget
should appear underneath the content area.</p>
<h3>Preact version</h3><pre><code class="language-text-x-trilium-auto">import { defineWidget } from "trilium:preact";
export default defineWidget({
parent: "center-pane",
render: () =&gt; &lt;span&gt;Center pane from Preact.&lt;/span&gt;
});</code></pre>
<p><a href="#root/_help_s8alTXmpFR61">Refresh the application</a> and the widget
should appear underneath the content area.</p>
<h2>Widget location (parent widget)</h2>
<p>A widget can be placed in one of the following sections of the applications:</p>
<table
class="ck-table-resized">
<colgroup>
<col style="width:15.59%;">
<col style="width:30.42%;">
<col style="width:16.68%;">
<col style="width:37.31%;">
</colgroup>
<thead>
<tr>
<th>Value for <code>parentWidget</code>
</th>
<th>Description</th>
<th>Sample widget</th>
<th>Special requirements</th>
</tr>
</thead>
<tbody>
<tr>
<th><code>left-pane</code>
</th>
<td>Appears within the same pane that holds the&nbsp;<a class="reference-link"
href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>.</td>
<td>Same as above, with only a different <code>parentWidget</code>.</td>
<td>None.</td>
</tr>
<tr>
<th><code>center-pane</code>
</th>
<td>In the content area. If a split is open, the widget will span all of the
splits.</td>
<td>See example above.</td>
<td>None.</td>
</tr>
<tr>
<th><code>note-detail-pane</code>
</th>
<td>
<p>In the content area, inside the note detail area. If a split is open,
the widget will be contained inside the split.</p>
<p>This is ideal if the widget is note-specific.</p>
</td>
<td><a class="reference-link" href="#root/_help_GhurYZjh8e1V">Note context aware widget</a>
</td>
<td>
<ul>
<li data-list-item-id="ec06332efcc3039721606c052f0d913fa">The widget must export a <code>class</code> and not an instance of the class
(e.g. <code>no new</code>) because it needs to be multiplied for each note,
so that splits work correctly.</li>
<li data-list-item-id="e8da690a2a8df148f6b5fc04ba1611688">Since the <code>class</code> is exported instead of an instance, the <code>parentWidget</code> getter
must be <code>static</code>, otherwise the widget is ignored.</li>
</ul>
</td>
</tr>
<tr>
<th><code>right-pane</code>
</th>
<td>In the&nbsp;<a class="reference-link" href="#root/_help_RnaPdbciOfeq">Right Sidebar</a>,
as a dedicated section.</td>
<td><a class="reference-link" href="#root/_help_M8IppdwVHSjG">Right pane widget</a>
</td>
<td>
<ul>
<li data-list-item-id="efe008d361e224f422582552648e1afe7">Although not mandatory, it's best to use a <code>RightPanelWidget</code> instead
of a <code>BasicWidget</code> or a <code>NoteContextAwareWidget</code>.</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p>To position the widget somewhere else, just change the value passed to <code>get parentWidget()</code> for
legacy widgets or the <code>parent</code> field for Preact. Do note that
some positions such as <code>note-detail-pane</code> and <code>right-pane</code> have
special requirements that need to be accounted for (see the table above).</p>
<h2>Launch bar widgets</h2>
<p>Launch bar widgets are similar to <em>Custom widgets</em> but are specific
to the&nbsp;<a class="reference-link" href="#root/_help_xYmIYSP6wE3F">Launch Bar</a>.
See&nbsp;<a class="reference-link" href="#root/_help_4Gn3psZKsfSm">Launch Bar Widgets</a>&nbsp;for
more information.</p>
<h2>Custom position</h2>
<li><code>center-pane</code>
</li>
<li><code>note-detail-pane</code> - located within <code>center-pane</code>,
but specific to note (split)</li>
<li><code>right-pane</code>
</li>
</ul>

View File

@@ -1,18 +1,9 @@
<h2>Classic widgets</h2>
<p>In <code>doRender()</code>:<sup><a href="#fn1saoftmefpp">[1]</a></sup>
</p><pre><code class="language-text-x-trilium-auto">this.cssBlock(`#my-widget {
<p>In <code>doRender()</code>:</p><pre><code class="language-text-x-trilium-auto">this.cssBlock(`#my-widget {
position: absolute;
bottom: 40px;
left: 60px;
z-index: 1;
}`);</code></pre>
<h2>Preact widgets</h2>
<p>See the dedicated page:&nbsp;<a class="reference-link" href="#root/_help_Sg9GrCtyftZf">CSS</a>.</p>
<ol>
<li>
<p><sup><strong><a href="#fnref1saoftmefpp">^</a></strong></sup>
</p>
<p>Reference: <a href="https://trilium.rocks/X7pxYpiu0lgU">https://trilium.rocks/X7pxYpiu0lgU</a>
</p>
</li>
</ol>
}`)</code></pre>
<hr>
<p>Reference: <a href="https://trilium.rocks/X7pxYpiu0lgU">https://trilium.rocks/X7pxYpiu0lgU</a>
</p>

View File

@@ -1,45 +0,0 @@
<p>Note context-aware widgets are a different type of widget which automatically
react to changes in the current note.</p>
<p>Important aspects:</p>
<ul>
<li>The widget must export a <code>class</code> and not an instance of the class
(e.g. <code>no new</code>) because it needs to be multiplied for each note,
so that splits work correctly.</li>
<li>Since the <code>class</code> is exported instead of an instance, the <code>parentWidget</code> getter
must be <code>static</code>, otherwise the widget is ignored.</li>
</ul>
<h2>Example displaying the current note title</h2>
<p>This is a note context-aware widget that simply displays the name the
current note.&nbsp;</p>
<h3>Classic example</h3><pre><code class="language-text-x-trilium-auto">class HelloNoteDetail extends api.NoteContextAwareWidget {
constructor() {
super();
this.contentSized();
}
doRender() {
this.$widget = $("&lt;div&gt;");
}
async refreshWithNote(note) {
this.$widget.text("Current note: " + note.title);
}
static get parentWidget() { return "note-detail-pane" }
get position() { return 10 }
}
module.exports = HelloNoteDetail;</code></pre>
<h3>Preact (v0.101.0+)</h3><pre><code class="language-text-x-trilium-auto">import { defineWidget, useNoteContext, useNoteProperty } from "trilium:preact";
export default defineWidget({
parent: "note-detail-pane",
position: 10,
render: () =&gt; {
const { note } = useNoteContext();
const title = useNoteProperty(note, "title");
return &lt;span&gt;Current note JSX: {title}&lt;/span&gt;;
}
});</code></pre>

View File

@@ -1,21 +1,23 @@
<h2>Key highlights</h2>
<ul>
<li><code>doRender</code> must not be overridden, instead <code>doRenderBody()</code> has
to be overridden.
<ul>
<li><code>doRenderBody</code> can optionally be <code>async</code>.</li>
</ul>
<li data-list-item-id="e08ebed8d78d0359c71a72be31cd95074"><code spellcheck="false">doRender</code> must not be overridden, instead
<code
spellcheck="false">doRenderBody()</code>has to be overridden.
<ul>
<li data-list-item-id="e34a4e9826ab89e0ce8ded8786a803f3a"><code spellcheck="false">doRenderBody</code> can optionally be <code spellcheck="false">async</code>.</li>
</ul>
</li>
<li><code>parentWidget()</code> must be set to <code>“rightPane”</code>.</li>
<li><code>widgetTitle()</code> getter can optionally be overriden, otherwise
the widget will be displayed as “Untitled widget”.</li>
<li data-list-item-id="e24bc184054a9c87a6c7695bb65c8d30b"><code spellcheck="false">parentWidget()</code> must be set to <code spellcheck="false">“rightPane”</code>.</li>
<li
data-list-item-id="e581cbed615c9be4f249274c5c51c6a18"><code spellcheck="false">widgetTitle()</code> getter can optionally be
overriden, otherwise the widget will be displayed as “Untitled widget”.</li>
</ul>
<h2>Example for new layout</h2>
<aside class="admonition important">
<p>This section addresses example that are tailored for the&nbsp;<a class="reference-link"
href="#root/_help_IjZS7iK5EXtb">New Layout</a>&nbsp;(available starting with
v0.101.0) where the right pane widget/sidebar is no longer shown or hidden
based on the widgets it has.&nbsp;</p>
href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_IjZS7iK5EXtb">New Layout</a>&nbsp;(available
starting with v0.101.0) where the right pane widget/sidebar is no longer
shown or hidden based on the widgets it has.&nbsp;</p>
</aside>
<h3>Title widget</h3>
<p>This is an example of a context-aware widget which displays the title
@@ -39,8 +41,7 @@
module.exports = new NoteTitleWidget();</code></pre>
<h3>Clock</h3>
<p>A simple widget which will show the current time, as an example on how
to dynamically change the content of the widget periodically.</p>
<h3>Legacy widget</h3><pre><code class="language-text-x-trilium-auto">const template = `&lt;div&gt;&lt;/div&gt;`;
to dynamically change the content of the widget periodically.</p><pre><code class="language-text-x-trilium-auto">const template = `&lt;div&gt;&lt;/div&gt;`;
class ToDoListWidget extends api.RightPanelWidget {
@@ -59,26 +60,6 @@ class ToDoListWidget extends api.RightPanelWidget {
}
module.exports = new ToDoListWidget();</code></pre>
<h3>Preact widget</h3><pre><code class="language-text-x-trilium-auto">import { defineWidget, RightPanelWidget, useEffect, useState } from "trilium:preact";
export default defineWidget({
parent: "right-pane",
position: 1,
render() {
const [ time, setTime ] = useState();
useEffect(() =&gt; {
const interval = setInterval(() =&gt; {
setTime(new Date().toLocaleString());
}, 1000);
return () =&gt; clearInterval(interval);
});
return (
&lt;RightPanelWidget id="clock-jsx" title="Clock (JSX)"&gt;
&lt;p&gt;The time is: {time}&lt;/p&gt;
&lt;/RightPanelWidget&gt;
);
}
});</code></pre>
<h2>Example for old layout</h2>
<p>Here's a widget that displays a basic message ("Hi"):</p><pre><code class="language-text-x-trilium-auto">const template = `&lt;div&gt;Hi&lt;/div&gt;`;
@@ -101,14 +82,14 @@ class HelloWorldWidget extends api.RightPanelWidget {
module.exports = new HelloWorldWidget();</code></pre>
<h3>Conditionally changing visibility</h3>
<p>In <code>refreshWithNote</code>:</p><pre><code class="language-text-x-trilium-auto">const visible = true; // replace with your own visibility logic
<p>In <code spellcheck="false">refreshWithNote</code>:</p><pre><code class="language-text-x-trilium-auto">const visible = true; // replace with your own visibility logic
this.toggleInt(visible);
this.triggerCommand("reEvaluateRightPaneVisibility");</code></pre>
<h2>Altering the position within the sidebar</h2>
<p>By default, the sidebar items are displayed in the order they are found
by the application when searching for <code>#widget</code> notes.</p>
by the application when searching for <code spellcheck="false">#widget</code> notes.</p>
<p>It is possible to make a widget appear higher or lower up, by adjusting
its <code>position</code> property:</p><pre><code class="language-text-x-trilium-auto">class MyWidget extends api.RightPanelWidget {
its <code spellcheck="false">position</code> property:</p><pre><code class="language-text-x-diff">class MyWidget extends api.RightPanelWidget {
+ get position() { return 20 };

View File

@@ -1,7 +1,7 @@
<h2>Why is my widget clipped by other UI elements</h2>
<p>For performance and layout reasons, the size of widgets in Trilium is
independent from its children. At CSS level, this means that the widget
container has <code>contain: size</code> applied to it.</p>
container has <code spellcheck="false">contain: size</code> applied to it.</p>
<p>This works well if the widget has a fixed size (or based on its parent
container), however to make a widget resize to fit its content, apply the
following change:</p><pre><code class="language-text-x-diff">class MyWidget extends api.RightPanelWidget {
@@ -12,4 +12,5 @@
+ }
}</code></pre>
<p>Alternatively apply <code>contain: none</code> to its CSS.</p>
<p>Alternatively apply <code spellcheck="false">contain: none</code> to its
CSS.</p>

View File

@@ -87,9 +87,17 @@ module.exports = new MyWidget();</code></pre>
}
module.exports = new MyWidget();</code></pre>
<p>For the list of possible values for <code>parentWidget()</code>, see&nbsp;
<a
class="reference-link" href="#root/_help_MgibgPcfeuGz">Custom Widgets</a>.&nbsp;</p>
<p><code>parentWidget()</code> can be given the following values:</p>
<ul>
<li><code>left-pane</code> - This renders the widget on the left side of the
screen where the note tree lives.</li>
<li><code>center-pane</code> - This renders the widget in the center of the
layout in the same location that notes and splits appear.</li>
<li><code>note-detail-pane</code> - This renders the widget <em>with</em> the
note in the center pane. This means it can appear multiple times with splits.</li>
<li><code>right-pane</code> - This renders the widget to the right of any opened
notes.</li>
</ul>
<p><a href="#root/_help_s8alTXmpFR61">Reload</a> the application one last time.
When you click the button, a "Hello World!" message should appear, confirming
that your widget is fully functional.</p>

View File

@@ -5,7 +5,6 @@
<p>This is an example of a note context-aware widget, which reacts to the
currently opened note and refreshes automatically as the user navigates
through the notes.</p>
<h2>Legacy widget</h2>
<p>In this example, the title of the note is displayed. It works best on
the <a href="#root/_help_x0JgW8UqGXvq">horizontal layout</a>.</p><pre><code class="language-application-javascript-env-backend">const TPL = `\
&lt;div style="
@@ -29,19 +28,4 @@ class NoteTitleWidget extends api.NoteContextAwareWidget {
}
}
module.exports = new NoteTitleWidget();</code></pre>
<h2>Preact widget (v0.101.0+)</h2><pre><code class="language-text-jsx">import { defineLauncherWidget, useActiveNoteContext } from "trilium:preact";
export default defineLauncherWidget({
render: () =&gt; {
const { note } = useActiveNoteContext();
return &lt;div style={{
display: "flex",
height: "53px",
width: "fit-content",
fontSize: "0.75em",
alignItems: "center",
flexShrink: 0
}}&gt;{note?.title}&lt;/div&gt;;
}
});</code></pre>
module.exports = new NoteTitleWidget();</code></pre>

Some files were not shown because too many files have changed in this diff Show More