From e8379541cc9f615bc8ddfc21951e920b593c49ef Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Tue, 24 Mar 2026 17:21:52 -0700 Subject: [PATCH] fix(tabs): strip duplicate shell elements from canvas tab injection The canvas.html body contained , , and elements that weren't being stripped by extractCanvasContent (the tab-row regex failed due to extra children). When injected via TabCache, these duplicate elements interfered with the shell's tab management, causing tabs to appear wiped. Fixes: - Server: robust div-counting strip for rstack-tab-row + explicit strips for space-settings and history-panel - Client: DOM-based safety strip in TabCache.extractContent() Co-Authored-By: Claude Opus 4.6 --- modules/rspace/mod.ts | 31 +++++++++++++++++++++++++++++-- shared/tab-cache.ts | 4 ++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/modules/rspace/mod.ts b/modules/rspace/mod.ts index 9e82633..13fe383 100644 --- a/modules/rspace/mod.ts +++ b/modules/rspace/mod.ts @@ -36,6 +36,29 @@ routes.get("/api/meta", async (c) => { * Strips the shell chrome (header, tab-bar, welcome overlay) that renderShell provides, * and returns just the canvas-specific DOM + inline styles + module scripts. */ +/** Strip a
and all its nested children by counting open/close tags. */ +function stripNestedDiv(html: string, className: string): string { + const re = new RegExp(`]*class="[^"]*${className}[^"]*"[^>]*>`); + const match = html.match(re); + if (!match || match.index === undefined) return html; + + let depth = 1; + let pos = match.index + match[0].length; + while (depth > 0 && pos < html.length) { + const nextOpen = html.indexOf("", pos); + if (nextClose === -1) break; + if (nextOpen !== -1 && nextOpen < nextClose) { + depth++; + pos = nextOpen + 4; + } else { + depth--; + pos = nextClose + 6; + } + } + return html.substring(0, match.index) + html.substring(pos); +} + function extractCanvasContent(html: string): { body: string; styles: string; scripts: string } { // Extract inline