From 952dc634b4b80eea636fff0af9580ab42c31bc22 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 18:24:10 +0200 Subject: [PATCH 01/15] fix(launch_bar): wrong tooltip in sync status (closes #8266) --- .../src/widgets/launch_bar/SyncStatus.tsx | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/apps/client/src/widgets/launch_bar/SyncStatus.tsx b/apps/client/src/widgets/launch_bar/SyncStatus.tsx index f5919f912b..651b89c075 100644 --- a/apps/client/src/widgets/launch_bar/SyncStatus.tsx +++ b/apps/client/src/widgets/launch_bar/SyncStatus.tsx @@ -1,12 +1,14 @@ -import { useEffect, useRef, useState } from "preact/hooks"; import "./SyncStatus.css"; -import { t } from "../../services/i18n"; -import clsx from "clsx"; -import { escapeQuotes } from "../../services/utils"; -import { useStaticTooltip, useTriliumOption } from "../react/hooks"; -import sync from "../../services/sync"; -import ws, { subscribeToMessages, unsubscribeToMessage } from "../../services/ws"; + import { WebSocketMessage } from "@triliumnext/commons"; +import clsx from "clsx"; +import { useEffect, useRef, useState } from "preact/hooks"; + +import { t } from "../../services/i18n"; +import sync from "../../services/sync"; +import { escapeQuotes } from "../../services/utils"; +import ws, { subscribeToMessages, unsubscribeToMessage } from "../../services/ws"; +import { useStaticTooltip, useTriliumOption } from "../react/hooks"; type SyncState = "unknown" | "in-progress" | "connected-with-changes" | "connected-no-changes" @@ -53,29 +55,29 @@ export default function SyncStatus() { const spanRef = useRef(null); const [ syncServerHost ] = useTriliumOption("syncServerHost"); useStaticTooltip(spanRef, { - html: true - // TODO: Placement + html: true, + title: escapeQuotes(title) }); return (syncServerHost &&
{ if (syncState === "in-progress") return; sync.syncNow(); }} > {hasChanges && ( - + )}
- ) + ); } function useSyncStatus() { From d8275e7ea88631100ea57853613400c729c806ff Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 18:24:25 +0200 Subject: [PATCH 02/15] fix(server): in-app help not working in dev mode --- apps/server/src/routes/assets.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/routes/assets.ts b/apps/server/src/routes/assets.ts index c68b64bc18..a6efe543b0 100644 --- a/apps/server/src/routes/assets.ts +++ b/apps/server/src/routes/assets.ts @@ -38,8 +38,8 @@ async function register(app: express.Application) { base: `/${assetUrlFragment}/` }); app.use(`/${assetUrlFragment}/`, (req, res, next) => { - if (req.url.startsWith("/images/")) { - // Images are served as static assets from the server. + if (req.url.startsWith("/images/") || req.url.startsWith("/doc_notes/")) { + // Images and doc notes are served as static assets from the server. next(); return; } From e4432e6feb70106ac551affdf66114d181d89961 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 18:32:00 +0200 Subject: [PATCH 03/15] fix(code): scrolling to end triggers on all splits --- apps/client/src/widgets/type_widgets/code/Code.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/client/src/widgets/type_widgets/code/Code.tsx b/apps/client/src/widgets/type_widgets/code/Code.tsx index b3c8686fae..3c12a8036b 100644 --- a/apps/client/src/widgets/type_widgets/code/Code.tsx +++ b/apps/client/src/widgets/type_widgets/code/Code.tsx @@ -180,7 +180,8 @@ export function CodeEditor({ parentComponent, ntxId, containerRef: externalConta resolve(refToJQuerySelector(containerRef)); }); - useTriliumEvent("scrollToEnd", () => { + useTriliumEvent("scrollToEnd", ({ ntxId: eventNtxId }) => { + if (eventNtxId !== ntxId) return; const editor = codeEditorRef.current; if (!editor) return; editor.scrollToEnd(); From 4caca56e3b4598191d74ebfe7176db3729e557a5 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 18:33:10 +0200 Subject: [PATCH 04/15] fix(code): scrolling to end changes the viewport --- packages/codemirror/src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/codemirror/src/index.ts b/packages/codemirror/src/index.ts index f77342455d..ae6142c1de 100644 --- a/packages/codemirror/src/index.ts +++ b/packages/codemirror/src/index.ts @@ -188,8 +188,6 @@ export default class CodeMirror extends EditorView { const endPos = this.state.doc.length; this.dispatch({ selection: EditorSelection.cursor(endPos), - effects: EditorView.scrollIntoView(endPos, { y: "end" }), - scrollIntoView: true }); } From e8e7568bdcc9f8df91230fcd09cac2f8b4698d3a Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 18:48:49 +0200 Subject: [PATCH 05/15] fix(breadcrumb): text overflow when width is constrained --- apps/client/src/widgets/layout/Breadcrumb.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/client/src/widgets/layout/Breadcrumb.css b/apps/client/src/widgets/layout/Breadcrumb.css index 18f88ac0c8..c32bc50dac 100644 --- a/apps/client/src/widgets/layout/Breadcrumb.css +++ b/apps/client/src/widgets/layout/Breadcrumb.css @@ -19,6 +19,9 @@ --link-hover-background: var(--icon-button-hover-background); color: var(--custom-color, inherit); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; &:hover { color: var(--custom-color, inherit); From 092a60fdd990e6db10e4809bdd041d25a09869e0 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 18:50:52 +0200 Subject: [PATCH 06/15] fix(codemirror): missing Node globals from linter --- packages/codemirror/src/extensions/eslint.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/codemirror/src/extensions/eslint.ts b/packages/codemirror/src/extensions/eslint.ts index 4a8e81580a..c3a498ed25 100644 --- a/packages/codemirror/src/extensions/eslint.ts +++ b/packages/codemirror/src/extensions/eslint.ts @@ -21,7 +21,7 @@ export async function lint(mimeType: string) { if (mimeType === "application/javascript;env=frontend") { globals = { ...globals, ...globalDefinitions.jquery }; } else if (mimeType === "application/javascript;env=backend") { - + globals = { ...globals, ...globalDefinitions.nodeBuiltin }; } const config: (Linter.LegacyConfig | Linter.Config | Linter.Config[]) = [ From 47601cd1da86c9d12e34ca8d6168b97ff6649f88 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 18:55:28 +0200 Subject: [PATCH 07/15] fix(active_content_badges): not refreshing on type or mime update --- .../src/widgets/layout/ActiveContentBadges.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/client/src/widgets/layout/ActiveContentBadges.tsx b/apps/client/src/widgets/layout/ActiveContentBadges.tsx index fcccd00f29..8d0cf20c75 100644 --- a/apps/client/src/widgets/layout/ActiveContentBadges.tsx +++ b/apps/client/src/widgets/layout/ActiveContentBadges.tsx @@ -9,7 +9,7 @@ import { openInAppHelpFromUrl } from "../../services/utils"; import { BadgeWithDropdown } from "../react/Badge"; import { FormDropdownDivider, FormListItem } from "../react/FormList"; import FormToggle from "../react/FormToggle"; -import { useNoteContext, useTriliumEvent } from "../react/hooks"; +import { useNoteContext, useNoteProperty, useTriliumEvent } from "../react/hooks"; import { BookProperty, ViewProperty } from "../react/NotePropertyMenu"; const NON_DANGEROUS_ACTIVE_CONTENT = [ "appCss", "appTheme" ]; @@ -213,6 +213,8 @@ function ActiveContentToggle({ note, info }: { note: FNote, info: ActiveContentI function useActiveContentInfo(note: FNote | null | undefined) { const [ info, setInfo ] = useState(null); + const noteType = useNoteProperty(note, "type"); + const noteMime = useNoteProperty(note, "mime"); function refresh() { let type: ActiveContentInfo["type"] | null = null; @@ -224,13 +226,13 @@ function useActiveContentInfo(note: FNote | null | undefined) { return; } - if (note.type === "render") { + if (noteType === "render") { type = "renderNote"; isEnabled = note.hasRelation("renderNote"); - } else if (note.type === "webView") { + } else if (noteType === "webView") { type = "webView"; isEnabled = note.hasLabel("webViewSrc"); - } else if (note.type === "code" && note.mime === "application/javascript;env=backend") { + } else if (noteType === "code" && noteMime === "application/javascript;env=backend") { type = "backendScript"; for (const backendLabel of [ "run", "customRequestHandler", "customResourceProvider" ]) { isEnabled ||= note.hasLabel(backendLabel); @@ -239,11 +241,11 @@ function useActiveContentInfo(note: FNote | null | undefined) { canToggleEnabled = true; } } - } else if (note.type === "code" && note.mime === "application/javascript;env=frontend") { + } else if (noteType === "code" && noteMime === "application/javascript;env=frontend") { type = "frontendScript"; isEnabled = note.hasLabel("widget") || note.hasLabel("run"); canToggleEnabled = note.hasLabelOrDisabled("widget") || note.hasLabelOrDisabled("run"); - } else if (note.type === "code" && note.hasLabelOrDisabled("appTheme")) { + } else if (noteType === "code" && note.hasLabelOrDisabled("appTheme")) { isEnabled = note.hasLabel("appTheme"); canToggleEnabled = true; } @@ -270,7 +272,7 @@ function useActiveContentInfo(note: FNote | null | undefined) { } // Refresh on note change. - useEffect(refresh, [ note ]); + useEffect(refresh, [ note, noteType, noteMime ]); useTriliumEvent("entitiesReloaded", ({ loadResults }) => { if (loadResults.getAttributeRows().some(attr => attributes.isAffecting(attr, note))) { From cee16dc3dc106d384b8064d15d9a0d6a1832a57d Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 19:04:14 +0200 Subject: [PATCH 08/15] feat(render): display syntax errors inside render note --- apps/client/src/services/render.ts | 40 ++++++++++++++++++------------ 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/client/src/services/render.ts b/apps/client/src/services/render.ts index f09d26532d..2c7371ba3b 100644 --- a/apps/client/src/services/render.ts +++ b/apps/client/src/services/render.ts @@ -12,26 +12,34 @@ async function render(note: FNote, $el: JQuery, onError?: (e: unkno $el.empty().toggle(renderNoteIds.length > 0); - for (const renderNoteId of renderNoteIds) { - const bundle = await server.post(`script/bundle/${renderNoteId}`); + try { + for (const renderNoteId of renderNoteIds) { + const bundle = await server.post(`script/bundle/${renderNoteId}`); - const $scriptContainer = $("
"); - $el.append($scriptContainer); + const $scriptContainer = $("
"); + $el.append($scriptContainer); - $scriptContainer.append(bundle.html); + $scriptContainer.append(bundle.html); - // async so that scripts cannot block trilium execution - bundleService.executeBundle(bundle, note, $scriptContainer) - .catch(onError) - .then(result => { - // Render JSX - if (bundle.html === "") { - renderIfJsx(bundle, result, $el).catch(onError); - } - }); + // async so that scripts cannot block trilium execution + bundleService.executeBundle(bundle, note, $scriptContainer) + .catch(onError) + .then(result => { + // Render JSX + if (bundle.html === "") { + renderIfJsx(bundle, result, $el).catch(onError); + } + }); + } + + return renderNoteIds.length > 0; + } catch (e) { + if (typeof e === "string" && e.startsWith("{") && e.endsWith("}")) { + onError?.(JSON.parse(e)); + } else { + onError?.(e); + } } - - return renderNoteIds.length > 0; } async function renderIfJsx(bundle: Bundle, result: unknown, $el: JQuery) { From fcc740d592ca38f30c59d064cf2106c7a4cfc702 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 19:11:33 +0200 Subject: [PATCH 09/15] fix(render): toast when execution fails --- apps/client/src/services/render.ts | 2 +- apps/client/src/services/server.ts | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/client/src/services/render.ts b/apps/client/src/services/render.ts index 2c7371ba3b..ece40306f7 100644 --- a/apps/client/src/services/render.ts +++ b/apps/client/src/services/render.ts @@ -14,7 +14,7 @@ async function render(note: FNote, $el: JQuery, onError?: (e: unkno try { for (const renderNoteId of renderNoteIds) { - const bundle = await server.post(`script/bundle/${renderNoteId}`); + const bundle = await server.postWithSilentInternalServerError(`script/bundle/${renderNoteId}`); const $scriptContainer = $("
"); $el.append($scriptContainer); diff --git a/apps/client/src/services/server.ts b/apps/client/src/services/server.ts index 381c58a3cf..fb1e598ec2 100644 --- a/apps/client/src/services/server.ts +++ b/apps/client/src/services/server.ts @@ -73,6 +73,10 @@ async function post(url: string, data?: unknown, componentId?: string) { return await call("POST", url, componentId, { data }); } +async function postWithSilentInternalServerError(url: string, data?: unknown, componentId?: string) { + return await call("POST", url, componentId, { data, silentInternalServerError: true }); +} + async function put(url: string, data?: unknown, componentId?: string) { return await call("PUT", url, componentId, { data }); } @@ -111,6 +115,7 @@ let maxKnownEntityChangeId = 0; interface CallOptions { data?: unknown; silentNotFound?: boolean; + silentInternalServerError?: boolean; // If `true`, the value will be returned as a string instead of a JavaScript object if JSON, XMLDocument if XML, etc. raw?: boolean; } @@ -143,7 +148,7 @@ async function call(method: string, url: string, componentId?: string, option }); })) as any; } else { - resp = await ajax(url, method, data, headers, !!options.silentNotFound, options.raw); + resp = await ajax(url, method, data, headers, options); } const maxEntityChangeIdStr = resp.headers["trilium-max-entity-change-id"]; @@ -155,10 +160,7 @@ async function call(method: string, url: string, componentId?: string, option return resp.body as T; } -/** - * @param raw if `true`, the value will be returned as a string instead of a JavaScript object if JSON, XMLDocument if XML, etc. - */ -function ajax(url: string, method: string, data: unknown, headers: Headers, silentNotFound: boolean, raw?: boolean): Promise { +function ajax(url: string, method: string, data: unknown, headers: Headers, opts: CallOptions): Promise { return new Promise((res, rej) => { const options: JQueryAjaxSettings = { url: window.glob.baseApiUrl + url, @@ -190,7 +192,9 @@ function ajax(url: string, method: string, data: unknown, headers: Headers, sile // don't report requests that are rejected by the browser, usually when the user is refreshing or going to a different page. rej("rejected by browser"); return; - } else if (silentNotFound && jqXhr.status === 404) { + } else if (opts.silentNotFound && jqXhr.status === 404) { + // report nothing + } else if (opts.silentInternalServerError && jqXhr.status === 500) { // report nothing } else { await reportError(method, url, jqXhr.status, jqXhr.responseText); @@ -200,7 +204,7 @@ function ajax(url: string, method: string, data: unknown, headers: Headers, sile } }; - if (raw) { + if (opts.raw) { options.dataType = "text"; } @@ -299,6 +303,7 @@ export default { get, getWithSilentNotFound, post, + postWithSilentInternalServerError, put, patch, remove, From bf302a84a9004c0ee7ac136ff231d7b5c78250ef Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 19:15:43 +0200 Subject: [PATCH 10/15] feat(content_renderer): display render errors --- apps/client/src/services/content_renderer.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/client/src/services/content_renderer.ts b/apps/client/src/services/content_renderer.ts index aca5d3efe3..148d59acd0 100644 --- a/apps/client/src/services/content_renderer.ts +++ b/apps/client/src/services/content_renderer.ts @@ -15,7 +15,7 @@ import protectedSessionService from "./protected_session.js"; import protectedSessionHolder from "./protected_session_holder.js"; import renderService from "./render.js"; import { applySingleBlockSyntaxHighlight } from "./syntax_highlight.js"; -import utils from "./utils.js"; +import utils, { getErrorMessage } from "./utils.js"; let idCounter = 1; @@ -62,7 +62,10 @@ export async function getRenderedContent(this: {} | { ctx: string }, entity: FNo } else if (type === "render" && entity instanceof FNote) { const $content = $("
"); - await renderService.render(entity, $content); + await renderService.render(entity, $content, (e) => { + const $error = $("
").addClass("admonition caution").text(typeof e === "string" ? e : getErrorMessage(e)); + $content.empty().append($error); + }); $renderedContent.append($content); } else if (type === "doc" && "noteId" in entity) { From a5806c0d1d8f4ea958e891653ddefcb15e9ffc45 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 19:22:12 +0200 Subject: [PATCH 11/15] feat(render): intercept bundle errors --- apps/client/src/services/bundle.ts | 16 +++++++++------- apps/client/src/services/render.ts | 4 ++-- apps/client/src/types.d.ts | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/apps/client/src/services/bundle.ts b/apps/client/src/services/bundle.ts index d33ba76a0a..7cee01812b 100644 --- a/apps/client/src/services/bundle.ts +++ b/apps/client/src/services/bundle.ts @@ -2,7 +2,6 @@ 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"; @@ -38,15 +37,18 @@ async function getAndExecuteBundle(noteId: string, originEntity = null, script = export type ParentName = "left-pane" | "center-pane" | "note-detail-pane" | "right-pane"; -export async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $container?: JQuery) { +export async function executeBundleWithoutErrorHandling(bundle: Bundle, originEntity?: Entity | null, $container?: JQuery) { const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity, $container); + return await function () { + return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`); + }.call(apiContext); +} +export async function executeBundle(bundle: Bundle, originEntity?: Entity | null, $container?: JQuery) { try { - return await function () { - return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`); - }.call(apiContext); - } catch (e: any) { - showErrorForScriptNote(bundle.noteId, t("toast.bundle-error.message", { message: e.message })); + return await executeBundleWithoutErrorHandling(bundle, originEntity, $container); + } catch (e: unknown) { + showErrorForScriptNote(bundle.noteId, t("toast.bundle-error.message", { message: getErrorMessage(e) })); logError("Widget initialization failed: ", e); } } diff --git a/apps/client/src/services/render.ts b/apps/client/src/services/render.ts index ece40306f7..fb92722a68 100644 --- a/apps/client/src/services/render.ts +++ b/apps/client/src/services/render.ts @@ -2,7 +2,7 @@ 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 { type Bundle, executeBundleWithoutErrorHandling } from "./bundle.js"; import froca from "./froca.js"; import server from "./server.js"; @@ -22,7 +22,7 @@ async function render(note: FNote, $el: JQuery, onError?: (e: unkno $scriptContainer.append(bundle.html); // async so that scripts cannot block trilium execution - bundleService.executeBundle(bundle, note, $scriptContainer) + executeBundleWithoutErrorHandling(bundle, note, $scriptContainer) .catch(onError) .then(result => { // Render JSX diff --git a/apps/client/src/types.d.ts b/apps/client/src/types.d.ts index 2e2a36e6ee..f7673901c1 100644 --- a/apps/client/src/types.d.ts +++ b/apps/client/src/types.d.ts @@ -119,7 +119,7 @@ declare global { setNote(noteId: string); } - var logError: (message: string, e?: Error | string) => void; + var logError: (message: string, e?: unknown) => void; var logInfo: (message: string) => void; var glob: CustomGlobals; //@ts-ignore From f4e82acc67a986c045d997d69173ffb565d8be64 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 19:46:46 +0200 Subject: [PATCH 12/15] feat(render): add error boundary --- .../src/services/{render.ts => render.tsx} | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) rename apps/client/src/services/{render.ts => render.tsx} (72%) diff --git a/apps/client/src/services/render.ts b/apps/client/src/services/render.tsx similarity index 72% rename from apps/client/src/services/render.ts rename to apps/client/src/services/render.tsx index fb92722a68..e996412f58 100644 --- a/apps/client/src/services/render.ts +++ b/apps/client/src/services/render.tsx @@ -1,4 +1,4 @@ -import { h, VNode } from "preact"; +import { Component, h, VNode } from "preact"; import type FNote from "../entities/fnote.js"; import { renderReactWidgetAtElement } from "../widgets/react/react_utils.jsx"; @@ -6,7 +6,9 @@ import { type Bundle, executeBundleWithoutErrorHandling } from "./bundle.js"; import froca from "./froca.js"; import server from "./server.js"; -async function render(note: FNote, $el: JQuery, onError?: (e: unknown) => void) { +type ErrorHandler = (e: unknown) => void; + +async function render(note: FNote, $el: JQuery, onError?: ErrorHandler) { const relations = note.getRelations("renderNote"); const renderNoteIds = relations.map((rel) => rel.value).filter((noteId) => noteId); @@ -27,7 +29,7 @@ async function render(note: FNote, $el: JQuery, onError?: (e: unkno .then(result => { // Render JSX if (bundle.html === "") { - renderIfJsx(bundle, result, $el).catch(onError); + renderIfJsx(bundle, result, $el, onError).catch(onError); } }); } @@ -42,7 +44,7 @@ async function render(note: FNote, $el: JQuery, onError?: (e: unkno } } -async function renderIfJsx(bundle: Bundle, result: unknown, $el: JQuery) { +async function renderIfJsx(bundle: Bundle, result: unknown, $el: JQuery, onError?: ErrorHandler) { // Ensure the root script note is actually a JSX. const rootScriptNoteId = await froca.getNote(bundle.noteId); if (rootScriptNoteId?.mime !== "text/jsx") return; @@ -55,7 +57,23 @@ async function renderIfJsx(bundle: Bundle, result: unknown, $el: JQuery VNode, {}); + const UserErrorBoundary = class UserErrorBoundary extends Component { + constructor(props: object) { + super(props); + this.state = { error: null }; + } + + componentDidCatch(error: unknown) { + onError?.(error); + this.setState({ error }); + } + + render() { + if ("error" in this.state && this.state?.error) return; + return this.props.children; + } + }; + const el = h(UserErrorBoundary, {}, h(result as () => VNode, {})); renderReactWidgetAtElement(closestComponent, el, $el[0]); } From c139ff776c325dd4be80db155c630b1be641b515 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 17 Feb 2026 22:24:42 +0200 Subject: [PATCH 13/15] chore(render): address requested changes --- apps/client/src/services/render.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/client/src/services/render.tsx b/apps/client/src/services/render.tsx index e996412f58..682efa8871 100644 --- a/apps/client/src/services/render.tsx +++ b/apps/client/src/services/render.tsx @@ -37,7 +37,11 @@ async function render(note: FNote, $el: JQuery, onError?: ErrorHand return renderNoteIds.length > 0; } catch (e) { if (typeof e === "string" && e.startsWith("{") && e.endsWith("}")) { - onError?.(JSON.parse(e)); + try { + onError?.(JSON.parse(e)); + } catch (e) { + onError?.(e); + } } else { onError?.(e); } @@ -69,7 +73,7 @@ async function renderIfJsx(bundle: Bundle, result: unknown, $el: JQuery Date: Tue, 17 Feb 2026 22:53:48 +0200 Subject: [PATCH 14/15] fix(webview): doesn't activate note context on click in browser --- .../src/widgets/type_widgets/WebView.tsx | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/apps/client/src/widgets/type_widgets/WebView.tsx b/apps/client/src/widgets/type_widgets/WebView.tsx index dd3ab43c9e..b624a62800 100644 --- a/apps/client/src/widgets/type_widgets/WebView.tsx +++ b/apps/client/src/widgets/type_widgets/WebView.tsx @@ -1,7 +1,8 @@ import "./WebView.css"; -import { useCallback, useState } from "preact/hooks"; +import { useCallback, useEffect, useRef, useState } from "preact/hooks"; +import appContext from "../../components/app_context"; import FNote from "../../entities/fnote"; import attributes from "../../services/attributes"; import { t } from "../../services/i18n"; @@ -17,7 +18,7 @@ import { TypeWidgetProps } from "./type_widget"; const isElectron = utils.isElectron(); const HELP_PAGE = "1vHRoWCEjj0L"; -export default function WebView({ note }: TypeWidgetProps) { +export default function WebView({ note, ntxId }: TypeWidgetProps) { const [ webViewSrc ] = useNoteLabel(note, "webViewSrc"); const [ disabledWebViewSrc ] = useNoteLabel(note, "disabled:webViewSrc"); @@ -29,15 +30,36 @@ export default function WebView({ note }: TypeWidgetProps) { return ; } - return ; + return isElectron + ? + : ; } -function WebViewContent({ src }: { src: string }) { - if (!isElectron) { - return