diff --git a/apps/client/src/services/bundle.ts b/apps/client/src/services/bundle.ts index e925253ce2..e1c38d0d09 100644 --- a/apps/client/src/services/bundle.ts +++ b/apps/client/src/services/bundle.ts @@ -1,10 +1,12 @@ +import BasicWidget from "../widgets/basic_widget.js"; +import RightPanelWidget from "../widgets/right_panel_widget.js"; +import froca from "./froca.js"; +import type { Entity, WidgetDefinition } from "./frontend_script_api.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"; // TODO: Deduplicate with server. export interface Bundle { @@ -14,9 +16,10 @@ export interface Bundle { allNoteIds: string[]; } -interface Widget { +type LegacyWidget = (BasicWidget | RightPanelWidget) & { parentWidget?: string; -} +}; +type Widget = LegacyWidget | WidgetDefinition; async function getAndExecuteBundle(noteId: string, originEntity = null, script = null, params = null) { const bundle = await server.post(`script/bundle/${noteId}`, { @@ -52,7 +55,7 @@ export async function executeBundle(bundle: Bundle, originEntity?: Entity | null async function executeStartupBundles() { const isMobile = utils.isMobile(); - const scriptBundles = await server.get("script/startup" + (isMobile ? "?mobile=true" : "")); + const scriptBundles = await server.get(`script/startup${ isMobile ? "?mobile=true" : ""}`); for (const bundle of scriptBundles) { await executeBundle(bundle); @@ -67,13 +70,16 @@ export class WidgetsByParent { } add(widget: Widget) { - if (!widget.parentWidget) { + if ("parent" in widget) { + // React-based script. + this.byParent[widget.parent] = this.byParent[widget.parent] || []; + this.byParent[widget.parent].push(widget); + } else if (widget.parentWidget) { + this.byParent[widget.parentWidget] = this.byParent[widget.parentWidget] || []; + this.byParent[widget.parentWidget].push(widget); + } else { console.log(`Custom widget does not have mandatory 'parentWidget' property defined`); - return; } - - this.byParent[widget.parentWidget] = this.byParent[widget.parentWidget] || []; - this.byParent[widget.parentWidget].push(widget); } get(parentName: string) { diff --git a/apps/client/src/services/frontend_script_api.ts b/apps/client/src/services/frontend_script_api.ts index e16670d38d..9fe5504ce5 100644 --- a/apps/client/src/services/frontend_script_api.ts +++ b/apps/client/src/services/frontend_script_api.ts @@ -1,26 +1,28 @@ -import server from "./server.js"; -import utils from "./utils.js"; -import toastService from "./toast.js"; -import linkService from "./link.js"; +import { dayjs } from "@triliumnext/commons"; +import { formatLogMessage } from "@triliumnext/commons"; +import { VNode } from "preact"; + +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 { 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 +466,18 @@ export interface Api { * Log given message to the log pane in UI */ log(message: string | object): void; + + /** + * 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): void; +} + +export interface WidgetDefinition { + parent: "right-pane", + render: () => VNode } /** @@ -533,9 +547,9 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig return params.map((p) => { if (typeof p === "function") { return `!@#Function: ${p.toString()}`; - } else { - return p; } + return p; + }); } @@ -562,9 +576,9 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig await ws.waitForMaxKnownEntityChangeId(); return ret.executionResult; - } else { - throw new Error(`server error: ${ret.error}`); } + throw new Error(`server error: ${ret.error}`); + }; this.runOnBackend = async (func, params = []) => { @@ -721,6 +735,9 @@ function FrontendScriptApi(this: Api, startNote: FNote, currentNote: FNote, orig this.logMessages[noteId].push(message); this.logSpacedUpdates[noteId].scheduleUpdate(); }; + this.defineWidget = (definition) => { + module.exports = definition; + }; } export default FrontendScriptApi as any as {