chore(script/jsx): basic client-side logic to render bundles

This commit is contained in:
Elian Doran 2025-12-20 19:01:29 +02:00
parent 3619c0c3e4
commit fa8ff4bfbf
No known key found for this signature in database
2 changed files with 56 additions and 33 deletions

View File

@ -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 ScriptContext from "./script_context.js";
import server from "./server.js"; import server from "./server.js";
import toastService, { showError } from "./toast.js"; import toastService, { showError } from "./toast.js";
import froca from "./froca.js";
import utils from "./utils.js"; import utils from "./utils.js";
import { t } from "./i18n.js";
import type { Entity } from "./frontend_script_api.js";
// TODO: Deduplicate with server. // TODO: Deduplicate with server.
export interface Bundle { export interface Bundle {
@ -14,9 +16,10 @@ export interface Bundle {
allNoteIds: string[]; allNoteIds: string[];
} }
interface Widget { type LegacyWidget = (BasicWidget | RightPanelWidget) & {
parentWidget?: string; parentWidget?: string;
} };
type Widget = LegacyWidget | WidgetDefinition;
async function getAndExecuteBundle(noteId: string, originEntity = null, script = null, params = null) { async function getAndExecuteBundle(noteId: string, originEntity = null, script = null, params = null) {
const bundle = await server.post<Bundle>(`script/bundle/${noteId}`, { const bundle = await server.post<Bundle>(`script/bundle/${noteId}`, {
@ -52,7 +55,7 @@ export async function executeBundle(bundle: Bundle, originEntity?: Entity | null
async function executeStartupBundles() { async function executeStartupBundles() {
const isMobile = utils.isMobile(); 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) { for (const bundle of scriptBundles) {
await executeBundle(bundle); await executeBundle(bundle);
@ -67,13 +70,16 @@ export class WidgetsByParent {
} }
add(widget: Widget) { 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`); 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) { get(parentName: string) {

View File

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