mirror of
https://github.com/zadam/trilium.git
synced 2025-12-24 00:59:55 +01:00
Compare commits
137 Commits
fix/implem
...
feature/mi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3595a43c2 | ||
|
|
963fcd615a | ||
|
|
fc8605a14f | ||
|
|
04fffb7ee0 | ||
|
|
86307b482f | ||
|
|
3e50262665 | ||
|
|
4e5c97d548 | ||
|
|
1185d4b10b | ||
|
|
19cd7a0cad | ||
|
|
8fda283977 | ||
|
|
6e3a020d0f | ||
|
|
2fef25e57b | ||
|
|
89ef38ba97 | ||
|
|
e96ee87472 | ||
|
|
ae83126903 | ||
|
|
a6c7610fcc | ||
|
|
d8ce0e5f16 | ||
|
|
1eebc8ff77 | ||
|
|
00592025c0 | ||
|
|
1ac7db41d3 | ||
|
|
ce84e7a861 | ||
|
|
cf039916d3 | ||
|
|
bfb3ed3ddf | ||
|
|
a4f34ce6c5 | ||
|
|
2ac3d3aaed | ||
|
|
494b99d073 | ||
|
|
8434549a9b | ||
|
|
c1e01467a5 | ||
|
|
4bd8eeb52a | ||
|
|
f4a6edbc9f | ||
|
|
0d0a1866e4 | ||
|
|
cd47e79a1b | ||
|
|
d0a83f7c05 | ||
|
|
26160b44ea | ||
|
|
9df7b04d7d | ||
|
|
b8d933d308 | ||
|
|
9021b119b2 | ||
|
|
35034fe9df | ||
|
|
2eef655ec2 | ||
|
|
57ff2f4023 | ||
|
|
df6331e3a0 | ||
|
|
b84da65a81 | ||
|
|
58e04a6f72 | ||
|
|
450bdeb39e | ||
|
|
79494e8cfe | ||
|
|
2afba34055 | ||
|
|
c391234eeb | ||
|
|
a3fca323c7 | ||
|
|
9332b9ca8f | ||
|
|
8740bf84cf | ||
|
|
1554085d7a | ||
|
|
7dd4c09057 | ||
|
|
eafd5140ea | ||
|
|
cddde353cd | ||
|
|
e4ef8f2352 | ||
|
|
2561c7ca0d | ||
|
|
b4e4950d20 | ||
|
|
a4be86dbd8 | ||
|
|
b6ca6476de | ||
|
|
db1f632859 | ||
|
|
7af8acec0f | ||
|
|
3f1b0fa71e | ||
|
|
519323292c | ||
|
|
2d6f17aeaa | ||
|
|
7507d6b385 | ||
|
|
d4fa21e7c1 | ||
|
|
608f156b82 | ||
|
|
0c965bfdf4 | ||
|
|
d407c72fae | ||
|
|
bdc0b062d5 | ||
|
|
0b912b9c7d | ||
|
|
99ac6b4df1 | ||
|
|
b0a97208a2 | ||
|
|
6e044b19c8 | ||
|
|
198dd15fb4 | ||
|
|
18f3b83827 | ||
|
|
8142b7489a | ||
|
|
7bf0a4134e | ||
|
|
29ed08d062 | ||
|
|
68dc7eedec | ||
|
|
3fc195998c | ||
|
|
b6d550f682 | ||
|
|
1f55ff536e | ||
|
|
67fb8d0354 | ||
|
|
1408b159d7 | ||
|
|
74b00e60e3 | ||
|
|
1b18a964b9 | ||
|
|
931f0a694e | ||
|
|
0d32e1f0d8 | ||
|
|
d0f91e7709 | ||
|
|
353d626d45 | ||
|
|
af67a3ba11 | ||
|
|
a867c646e4 | ||
|
|
150e2504b1 | ||
|
|
aa7ae150dc | ||
|
|
d99e08bfdd | ||
|
|
29d038c76b | ||
|
|
f1615bb4f6 | ||
|
|
0688ea7de3 | ||
|
|
af37c175a3 | ||
|
|
7567903da3 | ||
|
|
531698cafb | ||
|
|
f68f99806b | ||
|
|
c4f55395a9 | ||
|
|
444c0c6107 | ||
|
|
4da5cb43fc | ||
|
|
e6b79e83c4 | ||
|
|
6e67da7b1f | ||
|
|
9071e54bfe | ||
|
|
783b5ac8e3 | ||
|
|
f3f491d141 | ||
|
|
f8bf301d12 | ||
|
|
2c25786fa2 | ||
|
|
1093acfe45 | ||
|
|
76f054bbd5 | ||
|
|
c558255450 | ||
|
|
1e94125133 | ||
|
|
64a770175f | ||
|
|
e0416097e1 | ||
|
|
6c1b327f5f | ||
|
|
284b66acd2 | ||
|
|
dcd73ff9f9 | ||
|
|
645557b505 | ||
|
|
22a83d9f82 | ||
|
|
f64de3acca | ||
|
|
34d5793888 | ||
|
|
44ca9f457c | ||
|
|
4d7e5bc8f6 | ||
|
|
644ff07a50 | ||
|
|
41220a9d1d | ||
|
|
88945788d6 | ||
|
|
fe8f033409 | ||
|
|
eee7c49f6e | ||
|
|
d036bf0870 | ||
|
|
fa8ff4bfbf | ||
|
|
3619c0c3e4 | ||
|
|
883e32f5c9 |
@@ -17,12 +17,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@excalidraw/excalidraw": "0.18.0",
|
||||
"@fullcalendar/core": "6.1.19",
|
||||
"@fullcalendar/daygrid": "6.1.19",
|
||||
"@fullcalendar/interaction": "6.1.19",
|
||||
"@fullcalendar/list": "6.1.19",
|
||||
"@fullcalendar/multimonth": "6.1.19",
|
||||
"@fullcalendar/timegrid": "6.1.19",
|
||||
"@fullcalendar/core": "6.1.20",
|
||||
"@fullcalendar/daygrid": "6.1.20",
|
||||
"@fullcalendar/interaction": "6.1.20",
|
||||
"@fullcalendar/list": "6.1.20",
|
||||
"@fullcalendar/multimonth": "6.1.20",
|
||||
"@fullcalendar/timegrid": "6.1.20",
|
||||
"@maplibre/maplibre-gl-leaflet": "0.1.3",
|
||||
"@mermaid-js/layout-elk": "0.2.0",
|
||||
"@mind-elixir/node-menu": "5.0.1",
|
||||
@@ -75,7 +75,7 @@
|
||||
"@types/leaflet-gpx": "1.3.8",
|
||||
"@types/mark.js": "8.11.12",
|
||||
"@types/reveal.js": "5.2.2",
|
||||
"@types/tabulator-tables": "6.3.0",
|
||||
"@types/tabulator-tables": "6.3.1",
|
||||
"copy-webpack-plugin": "13.0.1",
|
||||
"happy-dom": "20.0.11",
|
||||
"script-loader": "0.7.2",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
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";
|
||||
@@ -597,8 +599,9 @@ export default class FNote {
|
||||
return "bx bx-folder";
|
||||
}
|
||||
return "bx bx-note";
|
||||
} else if (this.type === "code" && this.mime.startsWith("text/x-sql")) {
|
||||
return "bx bx-data";
|
||||
} else if (this.type === "code") {
|
||||
const correspondingMimeType = MIME_TYPES_DICT.find(m => m.mime === this.mime);
|
||||
return correspondingMimeType?.icon ?? NOTE_TYPE_ICONS.code;
|
||||
}
|
||||
return NOTE_TYPE_ICONS[this.type];
|
||||
}
|
||||
@@ -989,6 +992,10 @@ 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";
|
||||
@@ -996,7 +1003,7 @@ export default class FNote {
|
||||
|
||||
/** @returns JS script environment - either "frontend" or "backend" */
|
||||
getScriptEnv() {
|
||||
if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith("env=frontend"))) {
|
||||
if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith("env=frontend")) || this.isJsx()) {
|
||||
return "frontend";
|
||||
}
|
||||
|
||||
@@ -1018,7 +1025,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()) {
|
||||
if (!(this.isJavaScript() || this.isJsx())) {
|
||||
throw new Error(`Note ${this.noteId} is of type ${this.type} and mime ${this.mime} and thus cannot be executed`);
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ export default class DesktopLayout {
|
||||
.child(new HighlightsListWidget())
|
||||
.child(...this.customWidgets.get("right-pane"))
|
||||
)
|
||||
.optChild(isNewLayout, <RightPanelContainer customWidgets={this.customWidgets.get("right-pane")} />)
|
||||
.optChild(isNewLayout, <RightPanelContainer widgetsByParent={this.customWidgets} />)
|
||||
)
|
||||
.optChild(!launcherPaneIsHorizontal && isNewLayout, <StatusBar />)
|
||||
)
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
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, { 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";
|
||||
import toastService, { showErrorForScriptNote } from "./toast.js";
|
||||
import utils, { getErrorMessage } from "./utils.js";
|
||||
|
||||
// TODO: Deduplicate with server.
|
||||
export interface Bundle {
|
||||
@@ -14,9 +19,13 @@ export interface Bundle {
|
||||
allNoteIds: string[];
|
||||
}
|
||||
|
||||
interface Widget {
|
||||
type LegacyWidget = (BasicWidget | RightPanelWidget) & {
|
||||
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}`, {
|
||||
@@ -27,6 +36,8 @@ 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);
|
||||
|
||||
@@ -35,24 +46,14 @@ export async function executeBundle(bundle: Bundle, originEntity?: Entity | null
|
||||
return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`);
|
||||
}.call(apiContext);
|
||||
} catch (e: any) {
|
||||
const note = await froca.getNote(bundle.noteId);
|
||||
toastService.showPersistent({
|
||||
id: `custom-script-failure-${note?.noteId}`,
|
||||
title: t("toast.bundle-error.title"),
|
||||
icon: "bx bx-error-circle",
|
||||
message: t("toast.bundle-error.message", {
|
||||
id: note?.noteId,
|
||||
title: note?.title,
|
||||
message: e.message
|
||||
})
|
||||
});
|
||||
showErrorForScriptNote(bundle.noteId, t("toast.bundle-error.message", { message: e.message }));
|
||||
logError("Widget initialization failed: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -60,68 +61,99 @@ async function executeStartupBundles() {
|
||||
}
|
||||
|
||||
export class WidgetsByParent {
|
||||
private byParent: Record<string, Widget[]>;
|
||||
private legacyWidgets: Record<string, WithNoteId<LegacyWidget>[]>;
|
||||
private preactWidgets: Record<string, WithNoteId<WidgetDefinitionWithType>[]>;
|
||||
|
||||
constructor() {
|
||||
this.byParent = {};
|
||||
this.legacyWidgets = {};
|
||||
this.preactWidgets = {};
|
||||
}
|
||||
|
||||
add(widget: Widget) {
|
||||
if (!widget.parentWidget) {
|
||||
console.log(`Custom widget does not have mandatory 'parentWidget' property defined`);
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
this.byParent[widget.parentWidget] = this.byParent[widget.parentWidget] || [];
|
||||
this.byParent[widget.parentWidget].push(widget);
|
||||
if (!hasParentWidget) {
|
||||
showErrorForScriptNote(widget._noteId, t("toast.widget-missing-parent", {
|
||||
property: isPreact ? "parent" : "parentWidget"
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
get(parentName: string) {
|
||||
if (!this.byParent[parentName]) {
|
||||
return [];
|
||||
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);
|
||||
}
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
||||
getLegacyWidgets(parentName: ParentName): (BasicWidget | RightPanelWidget)[] {
|
||||
if (!this.legacyWidgets[parentName]) return [];
|
||||
|
||||
return (
|
||||
this.byParent[parentName]
|
||||
this.legacyWidgets[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();
|
||||
|
||||
for (const bundle of scriptBundles) {
|
||||
let widget;
|
||||
try {
|
||||
const scriptBundles = await server.get<Bundle[]>("script/widgets");
|
||||
|
||||
try {
|
||||
widget = await executeBundle(bundle);
|
||||
if (widget) {
|
||||
widget._noteId = bundle.noteId;
|
||||
widgetsByParent.add(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;
|
||||
}
|
||||
} 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;
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import server from "./server.js";
|
||||
import utils from "./utils.js";
|
||||
import toastService from "./toast.js";
|
||||
import linkService from "./link.js";
|
||||
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 linkService from "./link.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 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 server from "./server.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";
|
||||
import SpacedUpdate from "./spaced_update.js";
|
||||
import toastService from "./toast.js";
|
||||
import utils from "./utils.js";
|
||||
import ws from "./ws.js";
|
||||
|
||||
/**
|
||||
* A whole number
|
||||
@@ -464,6 +465,8 @@ export interface Api {
|
||||
* Log given message to the log pane in UI
|
||||
*/
|
||||
log(message: string | object): void;
|
||||
|
||||
preact: typeof preactAPI;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -533,9 +536,8 @@ 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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -562,9 +564,8 @@ 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 = []) => {
|
||||
@@ -721,6 +722,8 @@ 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 {
|
||||
|
||||
101
apps/client/src/services/frontend_script_api_preact.ts
Normal file
101
apps/client/src/services/frontend_script_api_preact.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
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
|
||||
});
|
||||
@@ -1,6 +1,10 @@
|
||||
import server from "./server.js";
|
||||
import bundleService, { type Bundle } from "./bundle.js";
|
||||
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";
|
||||
|
||||
async function render(note: FNote, $el: JQuery<HTMLElement>) {
|
||||
const relations = note.getRelations("renderNote");
|
||||
@@ -17,12 +21,34 @@ 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);
|
||||
bundleService.executeBundle(bundle, note, $scriptContainer).then(result => {
|
||||
// Render JSX
|
||||
if (bundle.html === "") {
|
||||
renderIfJsx(bundle, result, $el);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
@@ -133,11 +133,11 @@ async function call<T>(method: string, url: string, componentId?: string, option
|
||||
};
|
||||
|
||||
ipc.send("server-request", {
|
||||
requestId: requestId,
|
||||
headers: headers,
|
||||
method: method,
|
||||
requestId,
|
||||
headers,
|
||||
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 { throwError } = await import("./ws.js");
|
||||
throwError(`${statusCode} ${method} ${url} - ${message}`);
|
||||
const { logError } = await import("./ws.js");
|
||||
logError(`${statusCode} ${method} ${url} - ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
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 {
|
||||
@@ -61,6 +64,24 @@ 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[]>([]);
|
||||
|
||||
@@ -74,7 +95,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;
|
||||
});
|
||||
|
||||
@@ -717,7 +717,6 @@ 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 {
|
||||
@@ -2012,8 +2011,10 @@ body.zen .shared-info-widget,
|
||||
body.zen .ribbon-container:not(:has(.classic-toolbar-widget)),
|
||||
body.zen .ribbon-container:has(.classic-toolbar-widget) .ribbon-top-row,
|
||||
body.zen .ribbon-container .ribbon-body:not(:has(.classic-toolbar-widget)),
|
||||
body.zen .note-icon-widget,
|
||||
body.zen .title-row .icon-action,
|
||||
body.zen .note-badges > *:not(.read-only-badge),
|
||||
body.zen .ribbon-button-container,
|
||||
body.zen .inline-title,
|
||||
body.zen .promoted-attributes-widget,
|
||||
body.zen .floating-buttons-children > *:not(.bx-edit-alt),
|
||||
body.zen .action-button,
|
||||
@@ -2036,11 +2037,11 @@ body.zen #launcher-pane {
|
||||
}
|
||||
|
||||
body.zen .title-row {
|
||||
display: block !important;
|
||||
height: unset !important;
|
||||
-webkit-app-region: drag;
|
||||
padding-inline-start: env(titlebar-area-x);
|
||||
padding-inline-end: calc(100vw - env(titlebar-area-width, 100vw) + 2.5em);
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
body.zen .floating-buttons {
|
||||
@@ -2060,8 +2061,6 @@ body.zen .floating-buttons-children .button-widget {
|
||||
body.zen .note-title-widget,
|
||||
body.zen .note-title-widget input {
|
||||
font-size: 1rem !important;
|
||||
background: transparent !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
body.zen #detail-container {
|
||||
|
||||
@@ -234,6 +234,9 @@
|
||||
--right-pane-item-hover-background: #ffffff26;
|
||||
--right-pane-item-hover-color: white;
|
||||
|
||||
--bottom-panel-background-color: #11111180;
|
||||
--bottom-panel-title-bar-background-color: #3F3F3F80;
|
||||
|
||||
--scrollbar-thumb-color: #fdfdfd5c;
|
||||
--scrollbar-thumb-hover-color: #ffffff7d;
|
||||
--scrollbar-background-color: transparent;
|
||||
|
||||
@@ -232,6 +232,9 @@
|
||||
--right-pane-item-hover-background: #00000013;
|
||||
--right-pane-item-hover-color: inherit;
|
||||
|
||||
--bottom-panel-background-color: #0000000a;
|
||||
--bottom-panel-title-bar-background-color: #00000017;
|
||||
|
||||
--scrollbar-thumb-color: #0000005c;
|
||||
--scrollbar-thumb-hover-color: #00000066;
|
||||
--scrollbar-background-color: transparent;
|
||||
|
||||
@@ -128,6 +128,12 @@ body.backdrop-effects-disabled {
|
||||
font-size: 0.9rem !important;
|
||||
}
|
||||
|
||||
.dropdown-menu.tn-dropdown-menu-scrollable {
|
||||
/* Note: scrollable dropdowns does not support submenus */
|
||||
max-height: 90vh;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
body.desktop .dropdown-menu::before,
|
||||
:root .ck.ck-dropdown__panel::before,
|
||||
:root .excalidraw .popover::before,
|
||||
|
||||
@@ -653,7 +653,8 @@ body a.tn-link:focus-visible,
|
||||
}
|
||||
|
||||
body a.tn-link:hover,
|
||||
.use-tn-links a:hover {
|
||||
.use-tn-links a:hover,
|
||||
.use-tn-links a.ck-widget_selected {
|
||||
box-shadow: 0 0 0 4px var(--link-hover-background);
|
||||
--background: var(--link-hover-background);
|
||||
color: var(--link-hover-color);
|
||||
|
||||
@@ -670,16 +670,33 @@ html .note-detail-editable-text :not(figure, .include-note, hr):first-child {
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
|
||||
/* Links */
|
||||
|
||||
.ck-content a.ck-widget {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.ck-content a.ck-widget.ck-widget_selected,
|
||||
.ck-content a.ck-link_selected {
|
||||
outline: 2px solid var(--input-focus-outline-color);
|
||||
outline-offset: 2px;
|
||||
background: var(--link-hover-background);
|
||||
}
|
||||
|
||||
/* Reference link */
|
||||
|
||||
.ck-content a.reference-link,
|
||||
.ck-content a.reference-link:hover {
|
||||
/* Apply underline only to the span inside the link so it can follow the
|
||||
* target note's user defined color */
|
||||
text-decoration: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ck-content a.reference-link > span {
|
||||
.ck-content a.reference-link.use-note-color > span {
|
||||
color: var(--custom-color, inherit);
|
||||
}
|
||||
|
||||
.ck-content a.reference-link:hover > span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
@@ -1230,7 +1230,7 @@ body.layout-vertical .tab-row-widget-is-sorting .note-tab.note-tab-is-dragging .
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
body.vertical-layout #rest-pane > .classic-toolbar-widget {
|
||||
body.layout-vertical #rest-pane > .classic-toolbar-widget {
|
||||
border-start-start-radius: var(--center-pane-border-radius);
|
||||
}
|
||||
|
||||
|
||||
1
apps/client/src/translations/bg/translation.json
Normal file
1
apps/client/src/translations/bg/translation.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -817,7 +817,8 @@
|
||||
},
|
||||
"inherited_attribute_list": {
|
||||
"title": "继承的属性",
|
||||
"no_inherited_attributes": "没有继承的属性。"
|
||||
"no_inherited_attributes": "没有继承的属性。",
|
||||
"none": "无"
|
||||
},
|
||||
"note_info_widget": {
|
||||
"note_id": "笔记 ID",
|
||||
@@ -2200,5 +2201,8 @@
|
||||
"toggle": "切换右侧面板",
|
||||
"custom_widget_go_to_source": "跳转到源码",
|
||||
"empty_message": "这篇笔记没有展示内容"
|
||||
},
|
||||
"attributes_panel": {
|
||||
"title": "笔记属性"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,17 @@
|
||||
},
|
||||
"bundle-error": {
|
||||
"title": "Failed to load a custom script",
|
||||
"message": "Script from note with ID \"{{id}}\", titled \"{{title}}\" could not be executed due to:\n\n{{message}}"
|
||||
}
|
||||
"message": "Script could not be executed due to:\n\n{{message}}"
|
||||
},
|
||||
"widget-list-error": {
|
||||
"title": "Failed to obtain the list of widgets from the server"
|
||||
},
|
||||
"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}}"
|
||||
},
|
||||
"add_link": {
|
||||
"add_link": "Add link",
|
||||
@@ -818,7 +827,8 @@
|
||||
},
|
||||
"inherited_attribute_list": {
|
||||
"title": "Inherited Attributes",
|
||||
"no_inherited_attributes": "No inherited attributes."
|
||||
"no_inherited_attributes": "No inherited attributes.",
|
||||
"none": "none"
|
||||
},
|
||||
"note_info_widget": {
|
||||
"note_id": "Note ID",
|
||||
@@ -1770,7 +1780,8 @@
|
||||
"note_type_switcher_others": "Other note type",
|
||||
"note_type_switcher_templates": "Template",
|
||||
"note_type_switcher_collection": "Collection",
|
||||
"edited_notes": "Edited notes"
|
||||
"edited_notes": "Notes edited on this day",
|
||||
"promoted_attributes": "Promoted attributes"
|
||||
},
|
||||
"search_result": {
|
||||
"no_notes_found": "No notes have been found for given search parameters.",
|
||||
@@ -2203,6 +2214,9 @@
|
||||
"note_paths_title": "Note paths",
|
||||
"code_note_switcher": "Change language mode"
|
||||
},
|
||||
"attributes_panel": {
|
||||
"title": "Note Attributes"
|
||||
},
|
||||
"right_pane": {
|
||||
"empty_message": "Nothing to show for this note",
|
||||
"empty_button": "Hide the panel",
|
||||
|
||||
@@ -523,7 +523,8 @@
|
||||
},
|
||||
"toc": {
|
||||
"table_of_contents": "Sommario",
|
||||
"options": "Opzioni"
|
||||
"options": "Opzioni",
|
||||
"no_headings": "Nessun titolo."
|
||||
},
|
||||
"table_of_contents": {
|
||||
"title": "Sommario",
|
||||
@@ -556,7 +557,13 @@
|
||||
},
|
||||
"highlights_list_2": {
|
||||
"title": "Punti salienti",
|
||||
"options": "Opzioni"
|
||||
"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."
|
||||
},
|
||||
"quick-search": {
|
||||
"placeholder": "Ricerca rapida",
|
||||
@@ -1388,7 +1395,8 @@
|
||||
},
|
||||
"inherited_attribute_list": {
|
||||
"title": "Attributi ereditati",
|
||||
"no_inherited_attributes": "Nessun attributo ereditato."
|
||||
"no_inherited_attributes": "Nessun attributo ereditato.",
|
||||
"none": "nessuno"
|
||||
},
|
||||
"note_info_widget": {
|
||||
"note_id": "ID nota",
|
||||
@@ -1400,7 +1408,8 @@
|
||||
"calculate": "calcolare",
|
||||
"subtree_size": "(dimensione del sottoalbero: {{size}} in {{count}} note)",
|
||||
"title": "Nota informativa",
|
||||
"show_similar_notes": "Mostra note simili"
|
||||
"show_similar_notes": "Mostra note simili",
|
||||
"mime": "Tipo MIME"
|
||||
},
|
||||
"note_map": {
|
||||
"open_full": "Espandi completamente",
|
||||
@@ -2095,14 +2104,20 @@
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"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"
|
||||
"related_code_notes": "Schema di colori per le note del codice",
|
||||
"ui": "Interfaccia utente",
|
||||
"ui_old_layout": "Vecchio layout",
|
||||
"ui_new_layout": "Nuovo layout"
|
||||
},
|
||||
"units": {
|
||||
"percentage": "%"
|
||||
@@ -2159,13 +2174,18 @@
|
||||
"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."
|
||||
"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"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"workspace_badge": "Area di lavoro",
|
||||
"scroll_to_top_title": "Vai all'inizio della nota",
|
||||
"hoisted_badge": "Sollevato",
|
||||
"hoisted_badge_title": "Abbassato"
|
||||
"hoisted_badge_title": "Abbassato",
|
||||
"create_new_note": "Crea nuova nota secondaria",
|
||||
"empty_hide_archived_notes": "Nascondi note archiviate"
|
||||
},
|
||||
"status_bar": {
|
||||
"language_title": "Cambia lingua dei contenuti",
|
||||
@@ -2191,5 +2211,14 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1621,7 +1621,8 @@
|
||||
},
|
||||
"inherited_attribute_list": {
|
||||
"title": "継承属性",
|
||||
"no_inherited_attributes": "継承属性はありません。"
|
||||
"no_inherited_attributes": "継承属性はありません。",
|
||||
"none": "なし"
|
||||
},
|
||||
"note_map": {
|
||||
"open_full": "拡大表示",
|
||||
@@ -2200,5 +2201,8 @@
|
||||
"empty_button": "パネルを非表示",
|
||||
"toggle": "右パネルを切り替え",
|
||||
"custom_widget_go_to_source": "ソースコードへ移動"
|
||||
},
|
||||
"attributes_panel": {
|
||||
"title": "ノート属性"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1934,7 +1934,10 @@
|
||||
},
|
||||
"highlights_list_2": {
|
||||
"title": "Lista wyróżnień",
|
||||
"options": "Opcje"
|
||||
"options": "Opcje",
|
||||
"modal_title": "Konfiguracja listy wyróżnień",
|
||||
"menu_configure": "Konfiguracja listy wyróżnień...",
|
||||
"no_highlights": "Nie znaleziono wyróżnień."
|
||||
},
|
||||
"quick-search": {
|
||||
"placeholder": "Szybkie wyszukiwanie",
|
||||
|
||||
@@ -46,7 +46,10 @@
|
||||
"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."
|
||||
"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}}):"
|
||||
},
|
||||
"bulk_actions": {
|
||||
"bulk_actions": "Ações em massa",
|
||||
@@ -254,7 +257,8 @@
|
||||
"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."
|
||||
"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."
|
||||
},
|
||||
"help": {
|
||||
"noteNavigation": "Navegação de notas",
|
||||
@@ -308,7 +312,8 @@
|
||||
"other": "Outros",
|
||||
"quickSearch": "focar no campo de pesquisa rápida",
|
||||
"inPageSearch": "pesquisa na página",
|
||||
"title": "Folha de Dicas"
|
||||
"title": "Folha de Dicas",
|
||||
"editShortcuts": "Editar atalhos de teclado"
|
||||
},
|
||||
"import": {
|
||||
"importIntoNote": "Importar para a nota",
|
||||
@@ -334,7 +339,8 @@
|
||||
},
|
||||
"import-status": "Status da importação",
|
||||
"in-progress": "Importação em andamento: {{progress}}",
|
||||
"successful": "Importação concluída com sucesso."
|
||||
"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."
|
||||
},
|
||||
"include_note": {
|
||||
"dialog_title": "Incluir nota",
|
||||
@@ -349,7 +355,8 @@
|
||||
"info": {
|
||||
"modalTitle": "Mensagem informativa",
|
||||
"closeButton": "Fechar",
|
||||
"okButton": "OK"
|
||||
"okButton": "OK",
|
||||
"copy_to_clipboard": "Copiar para a área de transferência"
|
||||
},
|
||||
"jump_to_note": {
|
||||
"search_placeholder": "Pesquise uma nota pelo nome ou digite > para comandos...",
|
||||
@@ -771,7 +778,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?",
|
||||
"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.",
|
||||
"open-in-popup": "Edição rápida",
|
||||
"archive": "Ficheiro",
|
||||
"unarchive": "Desarquivar"
|
||||
@@ -789,7 +796,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 lançamento, para adicionar ou remover itens."
|
||||
"configure_launch_bar_description": "Abrir a configuração da barra de atalho, para adicionar ou remover itens."
|
||||
},
|
||||
"delete_note": {
|
||||
"delete_note": "Excluir nota",
|
||||
@@ -882,7 +889,7 @@
|
||||
"zoom_out": "Reduzir",
|
||||
"reset_zoom_level": "Redefinir Zoom",
|
||||
"zoom_in": "Aumentar",
|
||||
"configure_launchbar": "Configurar Barra de Lançamento",
|
||||
"configure_launchbar": "Configurar Barra de Atalhos",
|
||||
"show_shared_notes_subtree": "Exibir Subárvore de Notas Compartilhadas",
|
||||
"advanced": "Avançado",
|
||||
"open_dev_tools": "Abrir Ferramentas de Desenvolvedor",
|
||||
@@ -897,7 +904,9 @@
|
||||
"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."
|
||||
"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}}"
|
||||
},
|
||||
"zen_mode": {
|
||||
"button_exit": "Sair do Modo Zen"
|
||||
@@ -935,7 +944,14 @@
|
||||
"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?"
|
||||
"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"
|
||||
},
|
||||
"protected_session_status": {
|
||||
"inactive": "Clique para entrar na sessão protegida",
|
||||
@@ -979,7 +995,8 @@
|
||||
"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}} '"
|
||||
"error_cannot_get_branch_id": "Não foi possível obter o branchId para o notePath '{{notePath}} '",
|
||||
"note_revisions": "Revisões de notas"
|
||||
},
|
||||
"note_icon": {
|
||||
"change_note_icon": "Alterar ícone da nota",
|
||||
@@ -1007,7 +1024,12 @@
|
||||
"table": "Tabela",
|
||||
"geo-map": "Mapa geográfico",
|
||||
"board": "Quadro",
|
||||
"include_archived_notes": "Exibir notas arquivadas"
|
||||
"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"
|
||||
},
|
||||
"edited_notes": {
|
||||
"no_edited_notes_found": "Ainda não há nenhuma nota editada neste dia…",
|
||||
@@ -1020,7 +1042,7 @@
|
||||
"file_type": "Tipo do arquivo",
|
||||
"file_size": "Tamanho do arquivo",
|
||||
"download": "Baixar",
|
||||
"open": "Abrir",
|
||||
"open": "Abrir externamente",
|
||||
"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.",
|
||||
@@ -1040,7 +1062,8 @@
|
||||
},
|
||||
"inherited_attribute_list": {
|
||||
"title": "Atributos Herdados",
|
||||
"no_inherited_attributes": "Nenhum atributo herdado."
|
||||
"no_inherited_attributes": "Nenhum atributo herdado.",
|
||||
"none": "nenhum"
|
||||
},
|
||||
"note_info_widget": {
|
||||
"note_id": "ID da Nota",
|
||||
@@ -1051,7 +1074,9 @@
|
||||
"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."
|
||||
"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_map": {
|
||||
"open_full": "Expandir completamente",
|
||||
@@ -1111,7 +1136,8 @@
|
||||
"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"
|
||||
"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:"
|
||||
},
|
||||
"similar_notes": {
|
||||
"title": "Notas Similares",
|
||||
@@ -1192,7 +1218,13 @@
|
||||
},
|
||||
"editable_text": {
|
||||
"placeholder": "Digite o conteúdo da sua nota aqui…",
|
||||
"auto-detect-language": "Detectado automaticamente"
|
||||
"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."
|
||||
},
|
||||
"empty": {
|
||||
"search_placeholder": "buscar uma nota pelo nome",
|
||||
@@ -1299,7 +1331,8 @@
|
||||
"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."
|
||||
"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"
|
||||
},
|
||||
"native_title_bar": {
|
||||
"title": "Barra de Título Nativa (requer recarregar o app)",
|
||||
@@ -1319,11 +1352,11 @@
|
||||
"layout": "Layout",
|
||||
"layout-vertical-title": "Vertical",
|
||||
"layout-horizontal-title": "Horizontal",
|
||||
"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."
|
||||
"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."
|
||||
},
|
||||
"note_launcher": {
|
||||
"this_launcher_doesnt_define_target_note": "Este lançador não define uma nota destino."
|
||||
"this_launcher_doesnt_define_target_note": "Este atalho 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."
|
||||
@@ -1378,7 +1411,10 @@
|
||||
"title": "Editor"
|
||||
},
|
||||
"code_mime_types": {
|
||||
"title": "Tipos MIME disponíveis no dropdown"
|
||||
"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"
|
||||
},
|
||||
"vim_key_bindings": {
|
||||
"use_vim_keybindings_in_code_notes": "Atribuições de teclas do Vim",
|
||||
@@ -1498,7 +1534,13 @@
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"backup": {
|
||||
"automatic_backup": "Backup automático",
|
||||
@@ -1526,7 +1568,7 @@
|
||||
"mind-map": "Mapa Mental",
|
||||
"file": "Arquivo",
|
||||
"image": "Imagem",
|
||||
"launcher": "Lançador",
|
||||
"launcher": "Atalho",
|
||||
"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?",
|
||||
@@ -1569,7 +1611,13 @@
|
||||
},
|
||||
"highlights_list_2": {
|
||||
"title": "Lista de Destaques",
|
||||
"options": "Opções"
|
||||
"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."
|
||||
},
|
||||
"quick-search": {
|
||||
"placeholder": "Busca rápida",
|
||||
@@ -1592,23 +1640,33 @@
|
||||
"refresh-saved-search-results": "Atualizar resultados de pesquisa salvos",
|
||||
"create-child-note": "Criar nota filha",
|
||||
"unhoist": "Desafixar",
|
||||
"toggle-sidebar": "Alternar barra lateral"
|
||||
"toggle-sidebar": "Alternar barra lateral",
|
||||
"dropping-not-allowed": "Não é permitido soltar notas neste local."
|
||||
},
|
||||
"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}}'"
|
||||
"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…"
|
||||
},
|
||||
"note_title": {
|
||||
"placeholder": "digite o título da nota aqui..."
|
||||
"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"
|
||||
},
|
||||
"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 Lançamento"
|
||||
"configure_launchbar": "Configurar Barra de Atalhos"
|
||||
},
|
||||
"sql_result": {
|
||||
"no_rows": "Nenhum linha foi retornada para esta consulta"
|
||||
@@ -1630,7 +1688,8 @@
|
||||
},
|
||||
"toc": {
|
||||
"table_of_contents": "Tabela de Conteúdos",
|
||||
"options": "Opções"
|
||||
"options": "Opções",
|
||||
"no_headings": "Nenhum título."
|
||||
},
|
||||
"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>.",
|
||||
@@ -1673,22 +1732,24 @@
|
||||
"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."
|
||||
"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."
|
||||
},
|
||||
"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 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",
|
||||
"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",
|
||||
"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 lançadores visíveis",
|
||||
"move-to-available-launchers": "Mover para lançadores disponíveis",
|
||||
"duplicate-launcher": "Duplicar o lançador <kbd data-command=\"duplicateSubtree\">"
|
||||
"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\">"
|
||||
},
|
||||
"highlighting": {
|
||||
"title": "Blocos de Código",
|
||||
@@ -1722,7 +1783,8 @@
|
||||
"copy-link": "Copiar link",
|
||||
"paste": "Colar",
|
||||
"paste-as-plain-text": "Colar como texto sem formatação",
|
||||
"search_online": "Buscar por \"{{term}}\" usando {{searchEngine}}"
|
||||
"search_online": "Buscar por \"{{term}}\" usando {{searchEngine}}",
|
||||
"search_in_trilium": "Pesquisar por \"{{term}}\" no Trilium"
|
||||
},
|
||||
"image_context_menu": {
|
||||
"copy_reference_to_clipboard": "Copiar referência para a área de transferência",
|
||||
@@ -1732,7 +1794,8 @@
|
||||
"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_popup": "Edição rápida",
|
||||
"open_note_in_other_split": "Abrir nota no outro painel dividido"
|
||||
},
|
||||
"electron_integration": {
|
||||
"desktop-application": "Aplicação Desktop",
|
||||
@@ -1800,8 +1863,9 @@
|
||||
"unknown_widget": "Componente desconhecido para \"{{id}}\"."
|
||||
},
|
||||
"note_language": {
|
||||
"not_set": "Não atribuído",
|
||||
"configure-languages": "Configurar idiomas..."
|
||||
"not_set": "Nenhum idioma definido",
|
||||
"configure-languages": "Configurar idiomas...",
|
||||
"help-on-languages": "Ajuda sobre idiomas de conteúdo…"
|
||||
},
|
||||
"content_language": {
|
||||
"title": "Idiomas do conteúdo",
|
||||
@@ -1819,7 +1883,8 @@
|
||||
"button_title": "Exportar diagrama como PNG"
|
||||
},
|
||||
"svg": {
|
||||
"export_to_png": "O diagrama não pôde ser exportado como PNG."
|
||||
"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."
|
||||
},
|
||||
"code_theme": {
|
||||
"title": "Aparência",
|
||||
@@ -1838,7 +1903,11 @@
|
||||
"editorfeatures": {
|
||||
"title": "Recursos",
|
||||
"emoji_completion_enabled": "Habilitar auto-completar de Emoji",
|
||||
"note_completion_enabled": "Habilitar auto-completar de notas"
|
||||
"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`/`."
|
||||
},
|
||||
"table_view": {
|
||||
"new-row": "Nova linha",
|
||||
@@ -1863,7 +1932,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)",
|
||||
@@ -1888,7 +1957,8 @@
|
||||
"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"
|
||||
"edit-column-title": "Clique para editar o título da coluna",
|
||||
"column-already-exists": "Esta coluna já existe no quadro."
|
||||
},
|
||||
"call_to_action": {
|
||||
"next_theme_title": "Testar no novo tema do Trilium",
|
||||
@@ -1897,14 +1967,20 @@
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"units": {
|
||||
"percentage": "%"
|
||||
@@ -2047,5 +2123,102 @@
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@
|
||||
"workspace_template": "Эта заметка появится в списке доступных шаблонов при создании новой заметки, но только если будет установлен фокус на рабочую область с этим шаблоном",
|
||||
"workspace_search_home": "новые заметки поиска будут созданы как дочерние записи этой заметки, когда установлен фокус на какую-либо родительскую заметку этого рабочего пространство",
|
||||
"workspace_calendar_root": "Определяет корень календаря для каждого рабочего пространства",
|
||||
"hide_highlight_widget": "Скрыть виджет «Выделенное»",
|
||||
"hide_highlight_widget": "Скрыть виджет «Акценты»",
|
||||
"is_owned_by_note": "принадлежит заметке",
|
||||
"and_more": "... и ещё {{count}}.",
|
||||
"app_theme": "отмечает заметки CSS, которые являются полноценными темами Trilium и, таким образом, доступны в опциях Trilium.",
|
||||
@@ -750,7 +750,8 @@
|
||||
},
|
||||
"toc": {
|
||||
"table_of_contents": "Оглавление",
|
||||
"options": "Параметры"
|
||||
"options": "Параметры",
|
||||
"no_headings": "Заголовки не найдены."
|
||||
},
|
||||
"note_tree": {
|
||||
"hide-archived-notes": "Скрыть архивные заметки",
|
||||
@@ -1562,7 +1563,13 @@
|
||||
},
|
||||
"highlights_list_2": {
|
||||
"options": "Параметры",
|
||||
"title": "Список выделенного"
|
||||
"title": "Акценты",
|
||||
"modal_title": "Настроить акценты",
|
||||
"menu_configure": "Настроить акценты...",
|
||||
"no_highlights": "Акценты в тексте не найдены.",
|
||||
"title_with_count_one": "{{count}} акцент",
|
||||
"title_with_count_few": "{{count}} акцента",
|
||||
"title_with_count_many": "{{count}} акцентов"
|
||||
},
|
||||
"include_note": {
|
||||
"dialog_title": "Вставить заметку",
|
||||
@@ -1679,7 +1686,8 @@
|
||||
},
|
||||
"inherited_attribute_list": {
|
||||
"title": "Унаследованные атрибуты",
|
||||
"no_inherited_attributes": "Нет унаследованных атрибутов."
|
||||
"no_inherited_attributes": "Нет унаследованных атрибутов.",
|
||||
"none": "нет"
|
||||
},
|
||||
"note_map": {
|
||||
"title": "Карта заметок",
|
||||
@@ -1755,15 +1763,15 @@
|
||||
"enable_tray": "Включить отображение иконки в системном трее (чтобы изменения вступили в силу, необходимо перезапустить Trilium)"
|
||||
},
|
||||
"highlights_list": {
|
||||
"title": "Список выделенного",
|
||||
"title": "Акценты",
|
||||
"bold": "Жирный текст",
|
||||
"italic": "Наклонный текст",
|
||||
"underline": "Подчеркнутый текст",
|
||||
"color": "Цветной текст",
|
||||
"description": "Вы можете настроить список выделенного, отображаемый на правой панели:",
|
||||
"description": "Вы можете настроить список акцентов, отображаемый на правой панели:",
|
||||
"bg_color": "Текст с заливкой фона",
|
||||
"visibility_title": "Видимость списка выделений",
|
||||
"visibility_description": "Вы можете скрыть виджет списка выделенного, добавив атрибут #hideHighlightWidget к заметке.",
|
||||
"visibility_title": "Видимость списка акцентов",
|
||||
"visibility_description": "Вы можете скрыть виджет списка акцентов, добавив атрибут #hideHighlightWidget к заметке.",
|
||||
"shortcut_info": "Вы можете настроить сочетание клавиш для быстрого переключения правой панели (включая список выделенного) в меню Параметры -> Сочетания клавиш (название \"toggleRightPane\")."
|
||||
},
|
||||
"custom_date_time_format": {
|
||||
@@ -1808,7 +1816,7 @@
|
||||
"edit_this_note": "Редактировать заметку"
|
||||
},
|
||||
"show_highlights_list_widget_button": {
|
||||
"show_highlights_list": "Показать список выделенного"
|
||||
"show_highlights_list": "Показать список акцентов"
|
||||
},
|
||||
"zen_mode": {
|
||||
"button_exit": "Покинуть режим \"дзен\""
|
||||
@@ -2203,5 +2211,14 @@
|
||||
},
|
||||
"popup-editor": {
|
||||
"maximize": "Переключить на полный редактор"
|
||||
},
|
||||
"right_pane": {
|
||||
"custom_widget_go_to_source": "Исходный код",
|
||||
"toggle": "Переключить панель справа",
|
||||
"empty_button": "Скрыть панель",
|
||||
"empty_message": "Нечего отобразить для текущей заметки"
|
||||
},
|
||||
"attributes_panel": {
|
||||
"title": "Атрибуты заметки"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -691,7 +691,12 @@
|
||||
"convert_into_attachment_prompt": "確定要將筆記 '{{title}}' 轉換為父級筆記的附件嗎?",
|
||||
"print_pdf": "匯出為 PDF…",
|
||||
"open_note_on_server": "在伺服器上開啟筆記",
|
||||
"view_revisions": "筆記歷史版本..."
|
||||
"view_revisions": "筆記歷史版本...",
|
||||
"advanced": "進階",
|
||||
"export_as_image": "匯出為圖片",
|
||||
"export_as_image_png": "PNG (點陣)",
|
||||
"export_as_image_svg": "SVG (向量)",
|
||||
"note_map": "筆記地圖"
|
||||
},
|
||||
"onclick_button": {
|
||||
"no_click_handler": "按鈕元件'{{componentId}}'沒有定義點擊時的處理方式"
|
||||
@@ -790,7 +795,7 @@
|
||||
"file_type": "檔案類型",
|
||||
"file_size": "檔案大小",
|
||||
"download": "下載",
|
||||
"open": "打開",
|
||||
"open": "以外部程式打開",
|
||||
"upload_new_revision": "上傳新版本",
|
||||
"upload_success": "已上傳新檔案版本。",
|
||||
"upload_failed": "新檔案版本上傳失敗。",
|
||||
@@ -821,7 +826,9 @@
|
||||
"note_size_info": "筆記大小提供了該筆記儲存需求的粗略估計。它考慮了筆記及其歷史的內容。",
|
||||
"calculate": "計算",
|
||||
"subtree_size": "(子階層大小: {{size}}, 共計 {{count}} 個筆記)",
|
||||
"title": "筆記資訊"
|
||||
"title": "筆記資訊",
|
||||
"mime": "MIME 類型",
|
||||
"show_similar_notes": "顯示相似筆記"
|
||||
},
|
||||
"note_map": {
|
||||
"open_full": "展開顯示",
|
||||
@@ -884,7 +891,8 @@
|
||||
"search_parameters": "搜尋參數",
|
||||
"unknown_search_option": "未知的搜尋選項 {{searchOptionName}}",
|
||||
"search_note_saved": "搜尋筆記已儲存至 {{- notePathTitle}}",
|
||||
"actions_executed": "已執行操作。"
|
||||
"actions_executed": "已執行操作。",
|
||||
"view_options": "查看選項:"
|
||||
},
|
||||
"similar_notes": {
|
||||
"title": "相似筆記",
|
||||
@@ -1503,7 +1511,11 @@
|
||||
},
|
||||
"highlights_list_2": {
|
||||
"title": "高亮列表",
|
||||
"options": "選項"
|
||||
"options": "選項",
|
||||
"title_with_count_one": "{{count}} 處高亮",
|
||||
"modal_title": "設定高亮列表",
|
||||
"menu_configure": "設定高亮列表…",
|
||||
"no_highlights": "未找到高亮內容。"
|
||||
},
|
||||
"quick-search": {
|
||||
"placeholder": "快速搜尋",
|
||||
@@ -1539,8 +1551,13 @@
|
||||
},
|
||||
"note_title": {
|
||||
"placeholder": "請輸入筆記標題...",
|
||||
"created_on": "建立於 {{date}}",
|
||||
"last_modified": "最後修改於 {{date}}"
|
||||
"created_on": "建立於 <Value />",
|
||||
"last_modified": "修改於 <Value />",
|
||||
"note_type_switcher_label": "從 {{type}} 切換至:",
|
||||
"note_type_switcher_others": "其他筆記類型",
|
||||
"note_type_switcher_templates": "模板",
|
||||
"note_type_switcher_collection": "集合",
|
||||
"edited_notes": "編輯過的筆記"
|
||||
},
|
||||
"search_result": {
|
||||
"no_notes_found": "沒有找到符合搜尋條件的筆記。",
|
||||
@@ -1569,7 +1586,8 @@
|
||||
},
|
||||
"toc": {
|
||||
"table_of_contents": "目錄",
|
||||
"options": "選項"
|
||||
"options": "選項",
|
||||
"no_headings": "無標題。"
|
||||
},
|
||||
"watched_file_update_status": {
|
||||
"file_last_modified": "檔案 <code class=\"file-path\"></code> 最後修改時間為 <span class=\"file-last-modified\"></span>。",
|
||||
@@ -1943,8 +1961,9 @@
|
||||
"unknown_widget": "未知元件:\"{{id}}\"。"
|
||||
},
|
||||
"note_language": {
|
||||
"not_set": "不設定",
|
||||
"configure-languages": "設定語言…"
|
||||
"not_set": "未設定語言",
|
||||
"configure-languages": "設定語言…",
|
||||
"help-on-languages": "設定內容語言說明…"
|
||||
},
|
||||
"content_language": {
|
||||
"title": "內文語言",
|
||||
@@ -2011,7 +2030,7 @@
|
||||
"book_properties_config": {
|
||||
"hide-weekends": "隱藏週末",
|
||||
"display-week-numbers": "顯示週數",
|
||||
"map-style": "地圖樣式:",
|
||||
"map-style": "地圖樣式",
|
||||
"max-nesting-depth": "最大嵌套深度:",
|
||||
"raster": "柵格",
|
||||
"vector_light": "向量(淺色)",
|
||||
@@ -2068,14 +2087,19 @@
|
||||
"next_theme_title": "試用新 Trilium 主題",
|
||||
"next_theme_message": "您正在使用舊版主題,要試用新主題嗎?",
|
||||
"next_theme_button": "試用新主題",
|
||||
"dismiss": "關閉"
|
||||
"dismiss": "關閉",
|
||||
"new_layout_title": "新版面配置",
|
||||
"new_layout_button": "更多資訊"
|
||||
},
|
||||
"settings": {
|
||||
"related_settings": "相關設定"
|
||||
},
|
||||
"settings_appearance": {
|
||||
"related_code_blocks": "文字筆記中程式碼區塊的配色方案",
|
||||
"related_code_notes": "程式碼筆記的配色方案"
|
||||
"related_code_notes": "程式碼筆記的配色方案",
|
||||
"ui": "使用者介面",
|
||||
"ui_old_layout": "舊版面配置",
|
||||
"ui_new_layout": "新版面配置"
|
||||
},
|
||||
"units": {
|
||||
"percentage": "%"
|
||||
@@ -2135,6 +2159,40 @@
|
||||
"read_only_explicit": "唯讀",
|
||||
"read_only_auto": "自動唯讀",
|
||||
"shared_publicly": "公開分享",
|
||||
"shared_locally": "本地分享"
|
||||
"shared_locally": "本地分享",
|
||||
"read_only_explicit_description": "此筆記已被手動設定為唯讀。\n點擊以臨時編輯。",
|
||||
"read_only_temporarily_disabled": "臨時編輯",
|
||||
"shared_copy_to_clipboard": "複製連結至剪貼簿",
|
||||
"shared_open_in_browser": "在瀏覽器中打開連結",
|
||||
"shared_unshare": "取消分享",
|
||||
"clipped_note": "網頁擷取",
|
||||
"execute_script": "運行腳本",
|
||||
"execute_sql": "運行 SQL"
|
||||
},
|
||||
"breadcrumb": {
|
||||
"hoisted_badge": "聚焦",
|
||||
"hoisted_badge_title": "取消聚焦",
|
||||
"workspace_badge": "工作空間",
|
||||
"scroll_to_top_title": "跳轉至筆記開頭",
|
||||
"create_new_note": "新增子筆記",
|
||||
"empty_hide_archived_notes": "隱藏已歸檔的筆記"
|
||||
},
|
||||
"status_bar": {
|
||||
"language_title": "更改內容語言",
|
||||
"note_info_title": "查看筆記資訊(如日期、筆記大小)",
|
||||
"backlinks_one": "{{count}} 個反連結",
|
||||
"backlinks_title_one": "查看反連結",
|
||||
"attachments_one": "{{count}} 個附件",
|
||||
"attachments_title_one": "在新分頁中查看附件",
|
||||
"attributes_one": "{{count}} 個屬性",
|
||||
"attributes_title": "自有屬性及繼承屬性",
|
||||
"note_paths_one": "{{count}} 條路徑",
|
||||
"note_paths_title": "筆記路徑",
|
||||
"code_note_switcher": "更改語言模式"
|
||||
},
|
||||
"right_pane": {
|
||||
"empty_button": "隱藏面板",
|
||||
"toggle": "切換右側面板",
|
||||
"custom_widget_go_to_source": "跳轉至原始碼"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export default function TabHistoryNavigationButtons() {
|
||||
const legacyBackVisible = useLauncherVisibility("_lbBackInHistory");
|
||||
const legacyForwardVisible = useLauncherVisibility("_lbForwardInHistory");
|
||||
|
||||
return (isElectron() &&
|
||||
return (
|
||||
<div className="tab-history-navigation-buttons">
|
||||
{!legacyBackVisible && <ActionButton
|
||||
icon="bx bx-left-arrow-alt"
|
||||
|
||||
@@ -54,6 +54,16 @@
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
justify-content: space-between;
|
||||
|
||||
.btn {
|
||||
color: var(--bs-toast-color);
|
||||
background: var(--modal-control-button-background);
|
||||
|
||||
&:hover {
|
||||
background: var(--modal-control-button-hover-background);
|
||||
color: var(--bs-toast-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toast-progress {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
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 from "../services/toast.js";
|
||||
import toastService, { showErrorForScriptNote } from "../services/toast.js";
|
||||
import { renderReactWidget } from "./react/react_utils.jsx";
|
||||
|
||||
export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedComponent<T> {
|
||||
@@ -56,9 +57,8 @@ 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,16 +172,11 @@ export class TypedBasicWidget<T extends TypedComponent<any>> extends TypedCompon
|
||||
const noteId = this._noteId;
|
||||
if (this._noteId) {
|
||||
froca.getNote(noteId, true).then((note) => {
|
||||
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()
|
||||
})
|
||||
});
|
||||
showErrorForScriptNote(noteId, t("toast.widget-error.message-custom", {
|
||||
id: noteId,
|
||||
title: note?.title,
|
||||
message: e.message || e.toString()
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
toastService.showPersistent({
|
||||
@@ -213,7 +208,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() {
|
||||
@@ -222,7 +217,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() {
|
||||
@@ -250,9 +245,8 @@ 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() {}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
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";
|
||||
import splitService from "../../services/resizer.js";
|
||||
import type BasicWidget from "../basic_widget.js";
|
||||
import FlexContainer from "./flex_container.js";
|
||||
|
||||
export default class RightPaneContainer extends FlexContainer<RightPanelWidget> {
|
||||
export default class RightPaneContainer extends FlexContainer<BasicWidget> {
|
||||
private rightPaneHidden: boolean;
|
||||
private firstRender: boolean;
|
||||
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.note-split.type-code:not(.mime-text-x-sqlite) > .scrolling-container {
|
||||
background-color: var(--code-background-color);
|
||||
--scrollbar-background-color: var(--main-background-color);
|
||||
.note-split.type-code:not(.mime-text-x-sqlite) {
|
||||
&> .scrolling-container {
|
||||
background-color: var(--code-background-color);
|
||||
--scrollbar-background-color: var(--main-background-color);
|
||||
}
|
||||
|
||||
.inline-title,
|
||||
.title-actions {
|
||||
background-color: var(--main-background-color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
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);
|
||||
@@ -23,7 +26,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,
|
||||
@@ -75,7 +78,7 @@ export function ScriptLauncher({ launcherNote }: LauncherNoteProps) {
|
||||
text={title}
|
||||
onClick={launch}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function AiChatButton({ launcherNote }: LauncherNoteProps) {
|
||||
@@ -88,7 +91,7 @@ export function AiChatButton({ launcherNote }: LauncherNoteProps) {
|
||||
text={title}
|
||||
triggerCommand="createAiChat"
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function TodayLauncher({ launcherNote }: LauncherNoteProps) {
|
||||
@@ -114,12 +117,13 @@ export function QuickSearchLauncherWidget() {
|
||||
<div>
|
||||
{isEnabled && <LegacyWidgetRenderer widget={widget} />}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function CustomWidget({ launcherNote }: LauncherNoteProps) {
|
||||
const [ widgetNote ] = useNoteRelationTarget(launcherNote, "widget");
|
||||
const [ widget, setWidget ] = useState<BasicWidget>();
|
||||
const [ widget, setWidget ] = useState<BasicWidget | NoteContextAwareWidget | LauncherWidgetDefinitionWithType>();
|
||||
|
||||
const parentComponent = useContext(ParentComponent) as BasicWidget | null;
|
||||
parentComponent?.contentSized();
|
||||
|
||||
@@ -146,9 +150,13 @@ export function CustomWidget({ launcherNote }: LauncherNoteProps) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{widget && <LegacyWidgetRenderer widget={widget} />}
|
||||
{widget && (
|
||||
("type" in widget && widget.type === "preact-launcher-widget")
|
||||
? <ReactWidgetRenderer widget={widget as LauncherWidgetDefinitionWithType} />
|
||||
: <LegacyWidgetRenderer widget={widget as BasicWidget} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function LegacyWidgetRenderer({ widget }: { widget: BasicWidget }) {
|
||||
@@ -158,3 +166,8 @@ export function LegacyWidgetRenderer({ widget }: { widget: BasicWidget }) {
|
||||
|
||||
return widgetEl;
|
||||
}
|
||||
|
||||
function ReactWidgetRenderer({ widget }: { widget: LauncherWidgetDefinitionWithType }) {
|
||||
const El = widget.render;
|
||||
return <El />;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
}
|
||||
|
||||
a.tn-link {
|
||||
--link-hover-background: var(--icon-button-hover-background);
|
||||
|
||||
color: var(--custom-color, inherit);
|
||||
|
||||
&:hover {
|
||||
@@ -46,6 +48,19 @@
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
flex-shrink: 2;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-action {
|
||||
font-size: .9rem !important;
|
||||
|
||||
.bxs-chevron-right {
|
||||
transform: translateY(8%);
|
||||
|
||||
&::before {
|
||||
opacity: .75;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +87,15 @@
|
||||
color: var(--custom-color, inherit) !important;
|
||||
}
|
||||
|
||||
.dropdown .breadcrumb-child-list {
|
||||
/* Icon */
|
||||
li > span:first-child {
|
||||
opacity: .75;
|
||||
padding-inline-end: 4px;
|
||||
translate: none;
|
||||
};
|
||||
}
|
||||
|
||||
a.breadcrumb-last-item,
|
||||
a.breadcrumb-last-item:visited {
|
||||
text-decoration: none;
|
||||
|
||||
@@ -32,7 +32,7 @@ import { ParentComponent } from "../react/react_utils";
|
||||
|
||||
const COLLAPSE_THRESHOLD = 5;
|
||||
const INITIAL_ITEMS = 2;
|
||||
const FINAL_ITEMS = 2;
|
||||
const FINAL_ITEMS = 3;
|
||||
|
||||
export default function Breadcrumb() {
|
||||
const { note, notePath, notePaths, noteContext } = useNotePaths();
|
||||
@@ -65,8 +65,7 @@ export default function Breadcrumb() {
|
||||
? <BreadcrumbRoot noteContext={noteContext} />
|
||||
: <BreadcrumbItem index={index} notePath={item} notePathLength={notePaths.length} noteContext={noteContext} parentComponent={parentComponent} />
|
||||
}
|
||||
{(index < notePaths.length - 1 || note?.hasChildren()) &&
|
||||
<BreadcrumbSeparator notePath={item} activeNotePath={notePaths[index + 1]} {...separatorProps} />}
|
||||
<BreadcrumbSeparator notePath={item} activeNotePath={notePaths[index + 1]} {...separatorProps} />
|
||||
</Fragment>
|
||||
))
|
||||
)}
|
||||
@@ -189,10 +188,11 @@ interface BreadcrumbSeparatorProps {
|
||||
function BreadcrumbSeparator(props: BreadcrumbSeparatorProps) {
|
||||
return (
|
||||
<Dropdown
|
||||
text={<Icon icon="bx bx-chevron-right" />}
|
||||
text={<Icon icon="bx bxs-chevron-right" />}
|
||||
noSelectButtonStyle
|
||||
buttonClassName="icon-action"
|
||||
hideToggleArrow
|
||||
dropdownContainerClassName="tn-dropdown-menu-scrollable"
|
||||
dropdownOptions={{ popperConfig: { strategy: "fixed", placement: "top" } }}
|
||||
>
|
||||
<BreadcrumbSeparatorDropdownContent {...props} />
|
||||
@@ -225,7 +225,7 @@ function BreadcrumbSeparatorDropdownContent({ notePath, noteContext, activeNoteP
|
||||
</li>;
|
||||
})}
|
||||
|
||||
<FormDropdownDivider />
|
||||
{childNotes.length > 0 && <FormDropdownDivider />}
|
||||
<FormListItem
|
||||
icon="bx bx-plus"
|
||||
onClick={() => note_create.createNote(notePath, { activate: true })}
|
||||
|
||||
@@ -75,10 +75,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.note-split.type-code:not(.mime-text-x-sqlite) .inline-title {
|
||||
background-color: var(--main-background-color);
|
||||
}
|
||||
|
||||
body.prefers-centered-content .inline-title {
|
||||
margin-inline: auto;
|
||||
}
|
||||
@@ -99,58 +95,3 @@ body.prefers-centered-content .inline-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes note-type-switcher-intro {
|
||||
from {
|
||||
opacity: 0;
|
||||
} to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.note-type-switcher {
|
||||
--badge-radius: 12px;
|
||||
|
||||
position: relative;
|
||||
top: 5px;
|
||||
padding: .25em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow-x: auto;
|
||||
min-width: 0;
|
||||
gap: 5px;
|
||||
min-height: 35px;
|
||||
|
||||
>* {
|
||||
flex-shrink: 0;
|
||||
animation: note-type-switcher-intro 200ms ease-in;
|
||||
}
|
||||
|
||||
.ext-badge {
|
||||
--color: var(--input-background-color);
|
||||
color: var(--main-text-color);
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,28 +3,16 @@ import "./InlineTitle.css";
|
||||
import { NoteType } from "@triliumnext/commons";
|
||||
import clsx from "clsx";
|
||||
import { ComponentChild } from "preact";
|
||||
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
import { useLayoutEffect, useRef, useState } from "preact/hooks";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
import FNote from "../../entities/fnote";
|
||||
import attributes from "../../services/attributes";
|
||||
import froca from "../../services/froca";
|
||||
import { t } from "../../services/i18n";
|
||||
import { ViewScope } from "../../services/link";
|
||||
import { NOTE_TYPES, NoteTypeMapping } from "../../services/note_types";
|
||||
import server from "../../services/server";
|
||||
import { formatDateTime } from "../../utils/formatters";
|
||||
import NoteIcon from "../note_icon";
|
||||
import NoteTitleWidget from "../note_title";
|
||||
import 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 { useNoteContext, useNoteProperty, useStaticTooltip } from "../react/hooks";
|
||||
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"
|
||||
@@ -76,9 +64,6 @@ export default function InlineTitle() {
|
||||
<NoteTitleDetails />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EditedNotes />
|
||||
<NoteTypeSwitcher />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -142,205 +127,4 @@ 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
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
&.share-badge {--color: var(--badge-share-background-color);}
|
||||
&.clipped-note-badge {--color: var(--badge-clipped-note-background-color);}
|
||||
&.execute-badge {--color: var(--badge-execute-background-color);}
|
||||
min-width: 0;
|
||||
|
||||
.text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-badge {
|
||||
|
||||
@@ -4,8 +4,41 @@ body.experimental-feature-new-layout {
|
||||
}
|
||||
|
||||
.title-actions {
|
||||
&.visible:not(:empty) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
|
||||
&: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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "./NoteTitleActions.css";
|
||||
|
||||
import clsx from "clsx";
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import NoteContext from "../../components/note_context";
|
||||
import FNote from "../../entities/fnote";
|
||||
@@ -9,29 +9,31 @@ import { t } from "../../services/i18n";
|
||||
import CollectionProperties from "../note_bars/CollectionProperties";
|
||||
import { checkFullHeight, getExtendedWidgetType } from "../NoteDetail";
|
||||
import { PromotedAttributesContent, usePromotedAttributeData } from "../PromotedAttributes";
|
||||
import SimpleBadge from "../react/Badge";
|
||||
import Collapsible, { ExternallyControlledCollapsible } from "../react/Collapsible";
|
||||
import { useNoteContext, useNoteProperty, useTriliumEvent } from "../react/hooks";
|
||||
import { useNoteContext, useNoteLabel, useNoteProperty, useTriliumEvent, useTriliumOptionBool } from "../react/hooks";
|
||||
import NoteLink, { NewNoteLink } from "../react/NoteLink";
|
||||
import { useEditedNotes } from "../ribbon/EditedNotesTab";
|
||||
import SearchDefinitionTab from "../ribbon/SearchDefinitionTab";
|
||||
import NoteTypeSwitcher from "./NoteTypeSwitcher";
|
||||
|
||||
export default function NoteTitleActions() {
|
||||
const { note, ntxId, componentId, noteContext } = useNoteContext();
|
||||
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={clsx("title-actions", items.length > 0 && "visible")}>
|
||||
{items}
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
function SearchProperties({ note, ntxId }: { note: FNote, ntxId: string | null | undefined }) {
|
||||
function SearchProperties({ note, ntxId }: { note: FNote | null | undefined, ntxId: string | null | undefined }) {
|
||||
return (note &&
|
||||
<Collapsible
|
||||
title={t("search_definition.search_parameters")}
|
||||
@@ -64,10 +66,43 @@ function PromotedAttributes({ note, componentId, noteContext }: {
|
||||
return (note && (
|
||||
<ExternallyControlledCollapsible
|
||||
key={note.noteId}
|
||||
title={t("promoted_attributes.promoted_attributes")}
|
||||
title={t("note_title.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
|
||||
|
||||
33
apps/client/src/widgets/layout/NoteTypeSwitcher.css
Normal file
33
apps/client/src/widgets/layout/NoteTypeSwitcher.css
Normal file
@@ -0,0 +1,33 @@
|
||||
@keyframes note-type-switcher-intro {
|
||||
from {
|
||||
opacity: 0;
|
||||
} to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.note-type-switcher {
|
||||
--badge-radius: 12px;
|
||||
|
||||
position: relative;
|
||||
top: 5px;
|
||||
padding: .25em 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow-x: auto;
|
||||
min-width: 0;
|
||||
gap: 5px;
|
||||
min-height: 35px;
|
||||
|
||||
>* {
|
||||
flex-shrink: 0;
|
||||
animation: note-type-switcher-intro 200ms ease-in;
|
||||
}
|
||||
|
||||
.ext-badge {
|
||||
--color: var(--input-background-color);
|
||||
color: var(--main-text-color);
|
||||
font-size: 0.9rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
182
apps/client/src/widgets/layout/NoteTypeSwitcher.tsx
Normal file
182
apps/client/src/widgets/layout/NoteTypeSwitcher.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
import "./NoteTypeSwitcher.css";
|
||||
|
||||
import { NoteType } from "@triliumnext/commons";
|
||||
import { useEffect, useMemo, useState } from "preact/hooks";
|
||||
|
||||
import FNote from "../../entities/fnote";
|
||||
import attributes from "../../services/attributes";
|
||||
import froca from "../../services/froca";
|
||||
import { t } from "../../services/i18n";
|
||||
import { NOTE_TYPES, NoteTypeMapping } from "../../services/note_types";
|
||||
import server from "../../services/server";
|
||||
import { Badge, BadgeWithDropdown } from "../react/Badge";
|
||||
import { FormDropdownDivider, FormListItem } from "../react/FormList";
|
||||
import { useNoteBlob, useNoteContext, useNoteProperty, useTriliumEvent } from "../react/hooks";
|
||||
import { onWheelHorizontalScroll } from "../widget_utils";
|
||||
|
||||
const SWITCHER_PINNED_NOTE_TYPES = new Set<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;
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
.component.status-bar {
|
||||
contain: none;
|
||||
border-top: 1px solid var(--main-border-color);
|
||||
background-color: var(--left-pane-background-color);
|
||||
|
||||
> .status-bar-main-row {
|
||||
min-height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: var(--left-pane-background-color);
|
||||
padding-inline: 0.25em;
|
||||
font-size: 0.85em;
|
||||
|
||||
|
||||
> .breadcrumb {
|
||||
flex-grow: 1;
|
||||
--icon-button-size: 23px;
|
||||
@@ -155,11 +155,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-code-note-switcher {
|
||||
max-height: 90vh;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.backlinks-widget > .dropdown-menu {
|
||||
--menu-padding-size: .9em;
|
||||
|
||||
@@ -248,16 +243,82 @@
|
||||
|
||||
> .attribute-list {
|
||||
font-size: 0.9em;
|
||||
padding: 0.5em 0.75em;
|
||||
|
||||
.inherited-attributes-widget > div {
|
||||
padding: 0;
|
||||
font-size: 0.9em;
|
||||
.attributes-panel-label {
|
||||
opacity: .5;
|
||||
margin-inline-end: 4px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.attribute-list-editor {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.inherited-attributes-widget {
|
||||
display: inline;
|
||||
|
||||
> div {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.attribute-list-editor-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
padding-bottom: 0 !important;
|
||||
|
||||
.attribute-list-editor {
|
||||
padding-block: 0 !important;
|
||||
padding-inline: 0 100px !important ;
|
||||
}
|
||||
|
||||
.attribute-errors {
|
||||
padding: 4px 0;
|
||||
color: var(--dropdown-item-icon-destructive-color);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.ck.ck-editor__editable::after {
|
||||
/* Remove a hidden spinner that causes overflow */
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
div.similar-notes-widget div.similar-notes-wrapper {
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
button.select-button:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.bottom-panel {
|
||||
margin: 0 !important;
|
||||
padding: 0;
|
||||
|
||||
.bottom-panel-title-bar {
|
||||
display: flex;
|
||||
padding: 6px 12px;
|
||||
background: var(--bottom-panel-title-bar-background-color);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.bottom-panel-title-bar-caption {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .3pt;
|
||||
font-weight: 600;
|
||||
font-size: .85em;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-panel-content {
|
||||
border-bottom: 1px solid var(--main-border-color);
|
||||
background: var(--bottom-panel-background-color);
|
||||
padding: 8px 12px;
|
||||
max-height: 40vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -266,11 +266,15 @@ function NoteInfoValue({ text, title, value }: { text: string; title?: string, v
|
||||
);
|
||||
}
|
||||
|
||||
function SimilarNotesPane({ note, similarNotesShown }: NoteInfoContext) {
|
||||
function SimilarNotesPane({ note, similarNotesShown, setSimilarNotesShown }: NoteInfoContext) {
|
||||
return (similarNotesShown &&
|
||||
<div className="similar-notes-pane">
|
||||
<BottomPanel title={t("similar_notes.title")}
|
||||
className="similar-notes-pane"
|
||||
visible={similarNotesShown}
|
||||
setVisible={setSimilarNotesShown}
|
||||
>
|
||||
<SimilarNotesTab note={note} />
|
||||
</div>
|
||||
</BottomPanel>
|
||||
);
|
||||
}
|
||||
//#endregion
|
||||
@@ -367,15 +371,20 @@ function AttributesPane({ note, noteContext, attributesShown, setAttributesShown
|
||||
}), [ api ]));
|
||||
|
||||
return (context &&
|
||||
<div className={clsx("attribute-list", !attributesShown && "hidden-ext")}>
|
||||
<InheritedAttributesTab {...context} />
|
||||
<BottomPanel title={t("attributes_panel.title")}
|
||||
className="attribute-list"
|
||||
visible={attributesShown}
|
||||
setVisible={setAttributesShown}>
|
||||
|
||||
<span class="attributes-panel-label">{t("inherited_attribute_list.title")}</span>
|
||||
<InheritedAttributesTab {...context} emptyListString="inherited_attribute_list.none" />
|
||||
|
||||
<AttributeEditor
|
||||
{...context}
|
||||
api={api}
|
||||
ntxId={noteContext.ntxId}
|
||||
/>
|
||||
</div>
|
||||
</BottomPanel>
|
||||
);
|
||||
}
|
||||
//#endregion
|
||||
@@ -419,10 +428,10 @@ function CodeNoteSwitcher({ note }: StatusBarContext) {
|
||||
return (note.type === "code" &&
|
||||
<>
|
||||
<StatusBarDropdown
|
||||
icon="bx bx-code-curly"
|
||||
icon={correspondingMimeType?.icon ?? "bx bx-code-curly"}
|
||||
text={correspondingMimeType?.title}
|
||||
title={t("status_bar.code_note_switcher")}
|
||||
dropdownContainerClassName="dropdown-code-note-switcher"
|
||||
dropdownContainerClassName="dropdown-code-note-switcher tn-dropdown-menu-scrollable"
|
||||
>
|
||||
<NoteTypeCodeNoteList
|
||||
currentMimeType={currentNoteMime}
|
||||
@@ -439,3 +448,26 @@ function CodeNoteSwitcher({ note }: StatusBarContext) {
|
||||
);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Bottom panel
|
||||
|
||||
interface BottomPanelParams {
|
||||
children: ComponentChildren;
|
||||
title: string;
|
||||
visible: boolean;
|
||||
setVisible?: (visible: boolean) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function BottomPanel({ children, title, visible, setVisible, className }: BottomPanelParams) {
|
||||
return <div className={clsx("bottom-panel", className, {"hidden-ext": !visible})}>
|
||||
<div className="bottom-panel-title-bar">
|
||||
<span className="bottom-panel-title-bar-caption">{title}</span>
|
||||
<button class="icon-action bx bx-x" onClick={() => setVisible?.(false)} />
|
||||
</div>
|
||||
<div class={clsx("bottom-panel-content")}>
|
||||
{children}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@@ -37,7 +37,7 @@ body.experimental-feature-new-layout {
|
||||
.title-row {
|
||||
container-type: size;
|
||||
transition: border 400ms ease-out;
|
||||
|
||||
|
||||
&.note-split-title {
|
||||
border-bottom: 1px solid var(--main-border-color);
|
||||
|
||||
@@ -56,13 +56,13 @@ body.experimental-feature-new-layout {
|
||||
--note-icon-container-padding-size: 6px;
|
||||
margin-inline: 0;
|
||||
}
|
||||
|
||||
|
||||
.note-title-widget {
|
||||
--note-title-size: 18px;
|
||||
--note-title-padding-inline: 0;
|
||||
}
|
||||
|
||||
@container (max-width: 700px) {
|
||||
@container (max-width: 600px) {
|
||||
.note-title-widget {
|
||||
--note-title-size: 1.25rem;
|
||||
--note-title-padding-inline: 4px;
|
||||
@@ -80,7 +80,7 @@ body.experimental-feature-new-layout {
|
||||
.ext-badge .text {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.ext-badge {
|
||||
--size: 2em;
|
||||
width: var(--size);
|
||||
|
||||
@@ -50,16 +50,30 @@
|
||||
white-space: nowrap;
|
||||
border-radius: var(--badge-radius);
|
||||
|
||||
button,
|
||||
.btn {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ext-badge {
|
||||
border-radius: 0;
|
||||
|
||||
.text {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
|
||||
.text-inner {
|
||||
min-width: 0;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
font-size: 1.3em;
|
||||
margin-left: 0.25em;
|
||||
margin-inline-start: 0.25em;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ export function Badge({ icon, className, text, tooltip, href, ...containerProps
|
||||
fallbackPlacements: [ "bottom" ],
|
||||
animation: false,
|
||||
html: true,
|
||||
title: tooltip
|
||||
title: tooltip,
|
||||
customClass: "pre-wrap-text"
|
||||
});
|
||||
|
||||
const content = <>
|
||||
@@ -59,7 +60,10 @@ export function BadgeWithDropdown({ text, children, tooltip, className, dropdown
|
||||
<Dropdown
|
||||
className={`dropdown-badge dropdown-${className}`}
|
||||
text={<Badge
|
||||
text={<>{text} <Icon className="arrow" icon="bx bx-chevron-down" /></>}
|
||||
text={<>
|
||||
<span class="text-inner">{text}</span>
|
||||
<Icon className="arrow" icon="bx bx-chevron-down" />
|
||||
</>}
|
||||
className={className}
|
||||
{...props}
|
||||
/>}
|
||||
|
||||
@@ -2,10 +2,11 @@ import { Dropdown as BootstrapDropdown, Tooltip } from "bootstrap";
|
||||
import { ComponentChildren, HTMLAttributes } from "preact";
|
||||
import { CSSProperties, HTMLProps } from "preact/compat";
|
||||
import { MutableRef, useCallback, useEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
import { useTooltip, useUniqueName } from "./hooks";
|
||||
|
||||
type DataAttributes = {
|
||||
[key: `data-${string}`]: string | number | boolean | undefined;
|
||||
[key: `data-${string}`]: string | number | boolean | undefined;
|
||||
};
|
||||
|
||||
export interface DropdownProps extends Pick<HTMLProps<HTMLDivElement>, "id" | "className"> {
|
||||
@@ -66,14 +67,14 @@ export default function Dropdown({ id, className, buttonClassName, isStatic, chi
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
dropdown.dispose();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onShown = useCallback(() => {
|
||||
setShown(true);
|
||||
externalOnShown?.();
|
||||
hideTooltip();
|
||||
}, [ hideTooltip ])
|
||||
}, [ hideTooltip ]);
|
||||
|
||||
const onHidden = useCallback(() => {
|
||||
setShown(false);
|
||||
@@ -122,7 +123,7 @@ export default function Dropdown({ id, className, buttonClassName, isStatic, chi
|
||||
{...buttonProps}
|
||||
>
|
||||
{text}
|
||||
<span className="caret"></span>
|
||||
<span className="caret" />
|
||||
</button>
|
||||
|
||||
<ul
|
||||
@@ -130,9 +131,15 @@ 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>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Tooltip } from "bootstrap";
|
||||
import { useEffect, useRef, useMemo, useCallback } from "preact/hooks";
|
||||
import { ComponentChildren, CSSProperties } from "preact";
|
||||
import { useCallback, useEffect, useMemo, useRef } 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;
|
||||
}
|
||||
|
||||
const FormCheckbox = memo(({ name, disabled, label, currentValue, onChange, hint, containerStyle }: FormCheckboxProps) => {
|
||||
export default function FormCheckbox({ 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,6 +65,4 @@ const FormCheckbox = memo(({ name, disabled, label, currentValue, onChange, hint
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default FormCheckbox;
|
||||
}
|
||||
|
||||
@@ -105,22 +105,17 @@ export function NewNoteLink({ notePath, viewScope, noContextMenu, showNoteIcon,
|
||||
const [ archived ] = useNoteLabelBoolean(note, "archived");
|
||||
|
||||
return (
|
||||
<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>
|
||||
<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} /> </>}
|
||||
{title}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -369,7 +369,8 @@ export function useActiveNoteContext() {
|
||||
|
||||
useEffect(() => {
|
||||
setNote(noteContext?.note);
|
||||
}, [ notePath ]);
|
||||
setNotePath(noteContext?.notePath);
|
||||
}, [ notePath, noteContext?.note, noteContext?.notePath ]);
|
||||
|
||||
useTriliumEvents([ "setNoteContext", "activeContextChanged", "noteSwitchedAndActivated", "noteSwitched" ], () => {
|
||||
const noteContext = appContext.tabManager.getActiveContext() ?? undefined;
|
||||
@@ -634,7 +635,8 @@ export function useLegacyWidget<T extends BasicWidget>(widgetFactory: () => T, {
|
||||
|
||||
const renderedWidget = widget.render();
|
||||
return [ widget, renderedWidget ];
|
||||
}, [ noteContext, parentComponent, widgetFactory]);
|
||||
}, [ noteContext, parentComponent ]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
// widgetFactory() is intentionally left out
|
||||
|
||||
// Attach the widget to the parent.
|
||||
useEffect(() => {
|
||||
|
||||
@@ -9,7 +9,11 @@ import RawHtml from "../react/RawHtml";
|
||||
import { joinElements } from "../react/react_utils";
|
||||
import AttributeDetailWidget from "../attribute_widgets/attribute_detail";
|
||||
|
||||
export default function InheritedAttributesTab({ note, componentId }: Pick<TabContext, "note" | "componentId">) {
|
||||
type InheritedAttributesTabArgs = Pick<TabContext, "note" | "componentId"> & {
|
||||
emptyListString?: string;
|
||||
}
|
||||
|
||||
export default function InheritedAttributesTab({ note, componentId, emptyListString }: InheritedAttributesTabArgs) {
|
||||
const [ inheritedAttributes, setInheritedAttributes ] = useState<FAttribute[]>();
|
||||
const [ attributeDetailWidgetEl, attributeDetailWidget ] = useLegacyWidget(() => new AttributeDetailWidget());
|
||||
|
||||
@@ -63,7 +67,7 @@ export default function InheritedAttributesTab({ note, componentId }: Pick<TabCo
|
||||
/>
|
||||
)), " ")
|
||||
) : (
|
||||
<>{t("inherited_attribute_list.no_inherited_attributes")}</>
|
||||
<>{t(emptyListString ?? "inherited_attribute_list.no_inherited_attributes")}</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -283,6 +283,7 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
|
||||
return (
|
||||
<>
|
||||
{!hidden && <div
|
||||
className="attribute-list-editor-wrapper"
|
||||
ref={wrapperRef}
|
||||
style="position: relative; padding-top: 10px; padding-bottom: 10px"
|
||||
onKeyDown={(e) => {
|
||||
@@ -296,106 +297,107 @@ export default function AttributeEditor({ api, note, componentId, notePath, ntxI
|
||||
setTimeout(() => save(), 100);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CKEditor
|
||||
apiRef={editorRef}
|
||||
className="attribute-list-editor"
|
||||
tabIndex={200}
|
||||
editor={CKEditorAttributeEditor}
|
||||
currentValue={currentValue}
|
||||
config={{
|
||||
toolbar: { items: [] },
|
||||
placeholder: t("attribute_editor.placeholder"),
|
||||
mention: { feeds: mentionSetup },
|
||||
licenseKey: "GPL",
|
||||
language: "en"
|
||||
}}
|
||||
onChange={(currentValue) => {
|
||||
currentValueRef.current = currentValue ?? "";
|
||||
|
||||
const oldValue = getPreprocessedData(lastSavedContent.current ?? "").trimEnd();
|
||||
const newValue = getPreprocessedData(currentValue ?? "").trimEnd();
|
||||
setNeedsSaving(oldValue !== newValue);
|
||||
setError(undefined);
|
||||
}}
|
||||
onClick={(e, pos) => {
|
||||
if (pos && pos.textNode && pos.textNode.data) {
|
||||
const clickIndex = getClickIndex(pos);
|
||||
|
||||
let parsedAttrs: Attribute[];
|
||||
|
||||
try {
|
||||
parsedAttrs = attribute_parser.lexAndParse(getPreprocessedData(currentValueRef.current), true);
|
||||
} catch (e: unknown) {
|
||||
// the input is incorrect because the user messed up with it and now needs to fix it manually
|
||||
console.log(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
let matchedAttr: Attribute | null = null;
|
||||
|
||||
for (const attr of parsedAttrs) {
|
||||
if (attr.startIndex && clickIndex > attr.startIndex && attr.endIndex && clickIndex <= attr.endIndex) {
|
||||
matchedAttr = attr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (matchedAttr) {
|
||||
attributeDetailWidget.showAttributeDetail({
|
||||
allAttributes: parsedAttrs,
|
||||
attribute: matchedAttr,
|
||||
isOwned: true,
|
||||
x: e.pageX,
|
||||
y: e.pageY
|
||||
});
|
||||
setState("showAttributeDetail");
|
||||
} else {
|
||||
setState("showHelpTooltip");
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
setState("showHelpTooltip");
|
||||
}
|
||||
}}
|
||||
onKeyDown={() => attributeDetailWidget.hide()}
|
||||
onBlur={() => save()}
|
||||
onInitialized={() => editorRef.current?.focus()}
|
||||
disableNewlines disableSpellcheck
|
||||
/>
|
||||
|
||||
<div className="attribute-editor-buttons">
|
||||
{ needsSaving && <ActionButton
|
||||
icon="bx bx-save"
|
||||
className="save-attributes-button tn-tool-button"
|
||||
text={escapeQuotes(t("attribute_editor.save_attributes"))}
|
||||
onClick={save}
|
||||
/> }
|
||||
|
||||
<ActionButton
|
||||
icon="bx bx-plus"
|
||||
className="add-new-attribute-button tn-tool-button"
|
||||
text={escapeQuotes(t("attribute_editor.add_a_new_attribute"))}
|
||||
onClick={(e) => {
|
||||
// Prevent automatic hiding of the context menu due to the button being clicked.
|
||||
e.stopPropagation();
|
||||
|
||||
contextMenu.show<AttributeCommandNames>({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
orientation: "left",
|
||||
items: [
|
||||
{ title: t("attribute_editor.add_new_label"), command: "addNewLabel", uiIcon: "bx bx-hash" },
|
||||
{ title: t("attribute_editor.add_new_relation"), command: "addNewRelation", uiIcon: "bx bx-transfer" },
|
||||
{ kind: "separator" },
|
||||
{ title: t("attribute_editor.add_new_label_definition"), command: "addNewLabelDefinition", uiIcon: "bx bx-empty" },
|
||||
{ title: t("attribute_editor.add_new_relation_definition"), command: "addNewRelationDefinition", uiIcon: "bx bx-empty" }
|
||||
],
|
||||
selectMenuItemHandler: (item) => handleAddNewAttributeCommand(item.command)
|
||||
});
|
||||
> <div style="position: relative;">
|
||||
<CKEditor
|
||||
apiRef={editorRef}
|
||||
className="attribute-list-editor"
|
||||
tabIndex={200}
|
||||
editor={CKEditorAttributeEditor}
|
||||
currentValue={currentValue}
|
||||
config={{
|
||||
toolbar: { items: [] },
|
||||
placeholder: t("attribute_editor.placeholder"),
|
||||
mention: { feeds: mentionSetup },
|
||||
licenseKey: "GPL",
|
||||
language: "en"
|
||||
}}
|
||||
onChange={(currentValue) => {
|
||||
currentValueRef.current = currentValue ?? "";
|
||||
|
||||
const oldValue = getPreprocessedData(lastSavedContent.current ?? "").trimEnd();
|
||||
const newValue = getPreprocessedData(currentValue ?? "").trimEnd();
|
||||
setNeedsSaving(oldValue !== newValue);
|
||||
setError(undefined);
|
||||
}}
|
||||
onClick={(e, pos) => {
|
||||
if (pos && pos.textNode && pos.textNode.data) {
|
||||
const clickIndex = getClickIndex(pos);
|
||||
|
||||
let parsedAttrs: Attribute[];
|
||||
|
||||
try {
|
||||
parsedAttrs = attribute_parser.lexAndParse(getPreprocessedData(currentValueRef.current), true);
|
||||
} catch (e: unknown) {
|
||||
// the input is incorrect because the user messed up with it and now needs to fix it manually
|
||||
console.log(e);
|
||||
return null;
|
||||
}
|
||||
|
||||
let matchedAttr: Attribute | null = null;
|
||||
|
||||
for (const attr of parsedAttrs) {
|
||||
if (attr.startIndex && clickIndex > attr.startIndex && attr.endIndex && clickIndex <= attr.endIndex) {
|
||||
matchedAttr = attr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (matchedAttr) {
|
||||
attributeDetailWidget.showAttributeDetail({
|
||||
allAttributes: parsedAttrs,
|
||||
attribute: matchedAttr,
|
||||
isOwned: true,
|
||||
x: e.pageX,
|
||||
y: e.pageY
|
||||
});
|
||||
setState("showAttributeDetail");
|
||||
} else {
|
||||
setState("showHelpTooltip");
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
setState("showHelpTooltip");
|
||||
}
|
||||
}}
|
||||
onKeyDown={() => attributeDetailWidget.hide()}
|
||||
onBlur={() => save()}
|
||||
onInitialized={() => editorRef.current?.focus()}
|
||||
disableNewlines disableSpellcheck
|
||||
/>
|
||||
|
||||
<div className="attribute-editor-buttons">
|
||||
{ needsSaving && <ActionButton
|
||||
icon="bx bx-save"
|
||||
className="save-attributes-button tn-tool-button"
|
||||
text={escapeQuotes(t("attribute_editor.save_attributes"))}
|
||||
onClick={save}
|
||||
/> }
|
||||
|
||||
<ActionButton
|
||||
icon="bx bx-plus"
|
||||
className="add-new-attribute-button tn-tool-button"
|
||||
text={escapeQuotes(t("attribute_editor.add_a_new_attribute"))}
|
||||
onClick={(e) => {
|
||||
// Prevent automatic hiding of the context menu due to the button being clicked.
|
||||
e.stopPropagation();
|
||||
|
||||
contextMenu.show<AttributeCommandNames>({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
orientation: "left",
|
||||
items: [
|
||||
{ title: t("attribute_editor.add_new_label"), command: "addNewLabel", uiIcon: "bx bx-hash" },
|
||||
{ title: t("attribute_editor.add_new_relation"), command: "addNewRelation", uiIcon: "bx bx-transfer" },
|
||||
{ kind: "separator" },
|
||||
{ title: t("attribute_editor.add_new_label_definition"), command: "addNewLabelDefinition", uiIcon: "bx bx-empty" },
|
||||
{ title: t("attribute_editor.add_new_relation_definition"), command: "addNewRelationDefinition", uiIcon: "bx bx-empty" }
|
||||
],
|
||||
selectMenuItemHandler: (item) => handleAddNewAttributeCommand(item.command)
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ error && (
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useEffect, useState } from "preact/hooks";
|
||||
import FNote from "../entities/fnote";
|
||||
import attributes from "../services/attributes";
|
||||
import { t } from "../services/i18n";
|
||||
import { isElectron } from "../services/utils";
|
||||
import HelpButton from "./react/HelpButton";
|
||||
import { useNoteContext, useTriliumEvent, useTriliumOption } from "./react/hooks";
|
||||
import InfoBar from "./react/InfoBar";
|
||||
@@ -68,7 +69,11 @@ export function useShareInfo(note: FNote | null | undefined) {
|
||||
}
|
||||
});
|
||||
|
||||
return { link, linkHref, isSharedExternally: !!syncServerHost };
|
||||
return {
|
||||
link,
|
||||
linkHref,
|
||||
isSharedExternally: !isElectron() || !!syncServerHost // on server we can't reliably detect if the note is shared locally or available publicly.
|
||||
};
|
||||
}
|
||||
|
||||
function getShareId(note: FNote) {
|
||||
|
||||
@@ -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({ customWidgets }: { customWidgets: BasicWidget[] }) {
|
||||
export default function RightPanelContainer({ widgetsByParent }: { widgetsByParent: WidgetsByParent }) {
|
||||
const [ rightPaneVisible, setRightPaneVisible ] = useTriliumOptionBool("rightPaneVisible");
|
||||
const items = useItems(rightPaneVisible, customWidgets);
|
||||
const items = useItems(rightPaneVisible, widgetsByParent);
|
||||
useSplit(rightPaneVisible);
|
||||
|
||||
return (
|
||||
@@ -51,7 +51,7 @@ export default function RightPanelContainer({ customWidgets }: { customWidgets:
|
||||
);
|
||||
}
|
||||
|
||||
function useItems(rightPaneVisible: boolean, customWidgets: BasicWidget[]) {
|
||||
function useItems(rightPaneVisible: boolean, widgetsByParent: WidgetsByParent) {
|
||||
const { note } = useActiveNoteContext();
|
||||
const noteType = useNoteProperty(note, "type");
|
||||
const [ highlightsList ] = useTriliumOptionJson<string[]>("highlightsList");
|
||||
@@ -61,23 +61,38 @@ function useItems(rightPaneVisible: boolean, customWidgets: BasicWidget[]) {
|
||||
{
|
||||
el: <TableOfContents />,
|
||||
enabled: (noteType === "text" || noteType === "doc"),
|
||||
position: 10,
|
||||
},
|
||||
{
|
||||
el: <HighlightsList />,
|
||||
enabled: noteType === "text" && highlightsList.length > 0,
|
||||
position: 20,
|
||||
},
|
||||
...customWidgets.map((w, i) => ({
|
||||
el: <CustomWidget key={w._noteId} originalWidget={w as LegacyRightPanelWidget} />,
|
||||
...widgetsByParent.getLegacyWidgets("right-pane").map((widget) => ({
|
||||
el: <CustomLegacyWidget key={widget._noteId} originalWidget={widget as LegacyRightPanelWidget} />,
|
||||
enabled: true,
|
||||
position: w.position ?? 30 + i * 10
|
||||
}))
|
||||
position: widget.position
|
||||
})),
|
||||
...widgetsByParent.getPreactWidgets("right-pane").map((widget) => {
|
||||
const El = widget.render;
|
||||
return {
|
||||
el: <El />,
|
||||
enabled: true,
|
||||
position: widget.position
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
// 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 - b.position)
|
||||
.toSorted((a, b) => (a.position ?? 10) - (b.position ?? 10))
|
||||
.map(e => e.el);
|
||||
}
|
||||
|
||||
@@ -99,7 +114,7 @@ function useSplit(visible: boolean) {
|
||||
}, [ visible ]);
|
||||
}
|
||||
|
||||
function CustomWidget({ originalWidget }: { originalWidget: LegacyRightPanelWidget }) {
|
||||
function CustomLegacyWidget({ originalWidget }: { originalWidget: LegacyRightPanelWidget }) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
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 { default as VanillaCodeMirror, getThemeById } from "@triliumnext/codemirror";
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
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";
|
||||
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 { TypeWidgetProps } from "../type_widget";
|
||||
import CodeMirror, { CodeMirrorProps } from "./CodeMirror";
|
||||
|
||||
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. */
|
||||
@@ -51,7 +53,7 @@ export function ReadOnlyCode({ note, viewScope, ntxId, parentComponent }: TypeWi
|
||||
mime={note.mime}
|
||||
readOnly
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function formatViewSource(note: FNote, content: string) {
|
||||
@@ -74,6 +76,7 @@ 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,
|
||||
@@ -107,7 +110,7 @@ export function EditableCode({ note, ntxId, noteContext, debounceUpdate, parentC
|
||||
<CodeEditor
|
||||
ntxId={ntxId} parentComponent={parentComponent}
|
||||
editorRef={editorRef} containerRef={containerRef}
|
||||
mime={note.mime}
|
||||
mime={mime ?? "text/plain"}
|
||||
className="note-detail-code-editor"
|
||||
placeholder={t("editable_code.placeholder")}
|
||||
vimKeybindings={vimKeymapEnabled}
|
||||
@@ -130,7 +133,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">) {
|
||||
|
||||
128
apps/edit-docs/demo/!!!meta.json
vendored
128
apps/edit-docs/demo/!!!meta.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"formatVersion": 2,
|
||||
"appVersion": "0.99.3",
|
||||
"appVersion": "0.100.0",
|
||||
"files": [
|
||||
{
|
||||
"isClone": false,
|
||||
@@ -5507,51 +5507,7 @@
|
||||
}
|
||||
],
|
||||
"dataFileName": "chartjs-plugin-datalabe.min.js",
|
||||
"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"
|
||||
"attachments": []
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -6044,6 +6000,86 @@
|
||||
],
|
||||
"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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
20
apps/edit-docs/demo/navigation.html
vendored
20
apps/edit-docs/demo/navigation.html
vendored
@@ -540,14 +540,6 @@
|
||||
<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>
|
||||
@@ -633,6 +625,18 @@
|
||||
<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>
|
||||
|
||||
8
apps/edit-docs/demo/root/Trilium Demo.html
vendored
8
apps/edit-docs/demo/root/Trilium Demo.html
vendored
@@ -18,6 +18,7 @@
|
||||
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.
|
||||
@@ -25,17 +26,22 @@
|
||||
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
|
||||
<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>
|
||||
@@ -50,6 +56,7 @@
|
||||
</li>
|
||||
</ol>
|
||||
<p><strong>Unordered:</strong>
|
||||
|
||||
</p>
|
||||
<ul>
|
||||
<li data-list-item-id="e68bf4b518a16671c314a72073c3d900a">Item</li>
|
||||
@@ -60,6 +67,7 @@
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Block quotes</h3>
|
||||
|
||||
<blockquote>
|
||||
<p>Whereof one cannot speak, thereof one must be silent”</p>
|
||||
<p>– Ludwig Wittgenstein</p>
|
||||
|
||||
@@ -14,17 +14,22 @@
|
||||
|
||||
<div class="ck-content">
|
||||
<h2>Main characters</h2>
|
||||
|
||||
<p>… here put main characters …</p>
|
||||
<p> </p>
|
||||
<h2>Plot</h2>
|
||||
|
||||
<p>… describe main plot lines …</p>
|
||||
<p> </p>
|
||||
<h2>Tone</h2>
|
||||
|
||||
<p> </p>
|
||||
<h2>Genre</h2>
|
||||
|
||||
<p>scifi / drama / romance</p>
|
||||
<p> </p>
|
||||
<h2>Similar books</h2>
|
||||
|
||||
<ul>
|
||||
<li>…</li>
|
||||
</ul>
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
<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>
|
||||
|
||||
@@ -18,21 +18,25 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">buy milk </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 </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" checked="checked" disabled="disabled"><span class="todo-list__label__description">watch TV </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">eat ice cream </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
|
||||
}</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 <a class="reference-link" href="../Scripting%20examples/Custom%20request%20handler.js">Custom request handler</a>.</p>
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
<div class="ck-content">
|
||||
<p><span class="math-tex">\(% \f is defined as #1f(#2) using the macro \f\relax{x} = \int_{-\infty}^\infty \f\hat\xi\,e^{2 \pi i \xi x} \,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: <span class="math-tex">\(c^2 = a^2 + b^2\)</span> </p>
|
||||
<p> </p>
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<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>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<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>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<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>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description"> </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<li>XBox</li>
|
||||
<li>Candles</li>
|
||||
<li><a href="https://www.amazon.ca/Anker-SoundCore-Portable-Bluetooth-Resistance/dp/B01MTB55WH?pd_rd_wg=honW8&pd_rd_r=c9bb7c0f-0051-4da7-991f-4ca711a1b3e3&pd_rd_w=ciUpR&ref_=pd_gw_simh&pf_rd_r=K10XKX0NGPDNTYYP4BS4&pf_rd_p=5f1b460b-78c1-580e-929e-2878fe4859e8">Portable speakers</a>
|
||||
|
||||
</li>
|
||||
<li>...?</li>
|
||||
</ul>
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
|
||||
<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
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<p>Miscellaneous notes done on monday ...</p>
|
||||
<p> </p>
|
||||
<p>Interesting video: <a href="https://www.youtube.com/watch?v=_eSAF_qT_FY&feature=youtu.be">https://www.youtube.com/watch?v=_eSAF_qT_FY&feature=youtu.be</a>
|
||||
|
||||
</p>
|
||||
<p> </p>
|
||||
<p> </p>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description"> </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description"> </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description"> </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description"> </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description"> </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description"> </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
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>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description"> </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<li>
|
||||
<label class="todo-list__label">
|
||||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description"> </span>
|
||||
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -24,14 +24,17 @@
|
||||
<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>
|
||||
|
||||
@@ -26,13 +26,16 @@
|
||||
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>
|
||||
|
||||
@@ -22,13 +22,16 @@
|
||||
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>
|
||||
|
||||
@@ -23,13 +23,16 @@
|
||||
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>
|
||||
|
||||
@@ -26,14 +26,17 @@
|
||||
<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>
|
||||
|
||||
@@ -23,14 +23,17 @@
|
||||
<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>
|
||||
|
||||
@@ -23,13 +23,16 @@
|
||||
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>
|
||||
|
||||
108
apps/edit-docs/demo/root/Trilium Demo/Scripting examples/Render note with JSX/JSX.jsx
vendored
Normal file
108
apps/edit-docs/demo/root/Trilium Demo/Scripting examples/Render note with JSX/JSX.jsx
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<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>
|
||||
@@ -1,21 +0,0 @@
|
||||
<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>
|
||||
@@ -18,6 +18,7 @@
|
||||
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>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<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>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
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>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<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>
|
||||
@@ -23,6 +24,7 @@
|
||||
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
|
||||
@@ -31,17 +33,20 @@
|
||||
<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
|
||||
@@ -51,11 +56,13 @@
|
||||
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>
|
||||
@@ -65,6 +72,7 @@
|
||||
~/.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
|
||||
@@ -74,6 +82,7 @@
|
||||
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>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<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
|
||||
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
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>
|
||||
|
||||
35
apps/edit-docs/demo/style.css
vendored
35
apps/edit-docs/demo/style.css
vendored
@@ -550,9 +550,9 @@
|
||||
}
|
||||
|
||||
@media print{
|
||||
.ck-content figure.table:not(.layout-table):not(:has(> figcaption)){
|
||||
display:block;
|
||||
.ck-content figure.table:not(.layout-table){
|
||||
width:fit-content;
|
||||
height:fit-content;
|
||||
}
|
||||
.ck-content figure.table:not(.layout-table) > table{
|
||||
height:initial;
|
||||
@@ -601,4 +601,33 @@
|
||||
.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;
|
||||
}
|
||||
@@ -30,7 +30,8 @@
|
||||
"dependencies": {
|
||||
"better-sqlite3": "12.5.0",
|
||||
"html-to-text": "9.0.5",
|
||||
"node-html-parser": "7.0.1"
|
||||
"node-html-parser": "7.0.1",
|
||||
"sucrase": "3.35.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@anthropic-ai/sdk": "0.71.2",
|
||||
|
||||
Binary file not shown.
2
apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
generated
vendored
2
apps/server/src/assets/doc_notes/en/User Guide/!!!meta.json
generated
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user