From aee772aee0ef041b0c562a37982868053c08a309 Mon Sep 17 00:00:00 2001 From: Hibryda Date: Mon, 23 Mar 2026 15:57:37 +0100 Subject: [PATCH] fix(electrobun): load CanvasAddon+ImageAddon AFTER term.open() (fixes _linkifier2 crash) --- ui-electrobun/src/mainview/Terminal.svelte | 46 +++++++++++----------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/ui-electrobun/src/mainview/Terminal.svelte b/ui-electrobun/src/mainview/Terminal.svelte index 72748bc..a73a43d 100644 --- a/ui-electrobun/src/mainview/Terminal.svelte +++ b/ui-electrobun/src/mainview/Terminal.svelte @@ -50,39 +50,41 @@ fitAddon = new FitAddon(); term.loadAddon(fitAddon); - term.loadAddon(new CanvasAddon()); + // NOTE: CanvasAddon and ImageAddon MUST be loaded AFTER term.open() + // because they access _linkifier2 which is created during open() - term.loadAddon(new ImageAddon({ - enableSizeReports: true, - sixelSupport: true, - sixelScrolling: true, - sixelPaletteLimit: 4096, - showPlaceholder: true, - })); - - // Defer open if container isn't visible yet (e.g., behind splash) - const tryOpen = () => { - try { - term.open(termEl); - fitAddon.fit(); - } catch (e) { - console.warn('[Terminal] open failed, retrying in 500ms:', (e as Error).message); - setTimeout(tryOpen, 500); + const openAndLoadAddons = () => { + term.open(termEl); + // Now safe to load addons that depend on _linkifier2 + try { term.loadAddon(new CanvasAddon()); } catch (e) { + console.warn('[Terminal] CanvasAddon failed:', (e as Error).message); } + try { + term.loadAddon(new ImageAddon({ + enableSizeReports: true, + sixelSupport: true, + sixelScrolling: true, + sixelPaletteLimit: 4096, + showPlaceholder: true, + })); + } catch (e) { + console.warn('[Terminal] ImageAddon failed:', (e as Error).message); + } + fitAddon.fit(); }; + + // Defer if container isn't visible yet (e.g., behind splash) if (termEl.offsetParent !== null) { - tryOpen(); + openAndLoadAddons(); } else { - // Container not visible — wait for it const observer = new IntersectionObserver((entries) => { if (entries[0]?.isIntersecting) { observer.disconnect(); - tryOpen(); + openAndLoadAddons(); } }); observer.observe(termEl); - // Fallback: try after 2s regardless - setTimeout(() => { observer.disconnect(); tryOpen(); }, 2000); + setTimeout(() => { observer.disconnect(); openAndLoadAddons(); }, 2000); } // ── Read cursor/scrollback settings ─────────────────────────────────