diff --git a/server/shell.ts b/server/shell.ts index 3c66c027..0b3c6c75 100644 --- a/server/shell.ts +++ b/server/shell.ts @@ -1267,6 +1267,42 @@ export function renderShell(opts: ShellOptions): string { } }); + tabBar.addEventListener('layers-close-all', () => { + // Track all closed modules so server merge doesn't resurrect them + layers.forEach(l => _closedModuleIds.add(l.moduleId)); + // Clean up space layer refs + const token = document.cookie.match(/encryptid_token=([^;]+)/)?.[1]; + layers.forEach(l => { + if (l.spaceSlug && l._spaceRefId && token) { + fetch('/api/spaces/' + encodeURIComponent(spaceSlug) + '/nest/' + encodeURIComponent(l._spaceRefId), { + method: 'DELETE', + headers: { 'Authorization': 'Bearer ' + token }, + }).catch(() => {}); + } + }); + // Remove all cached panes + if (tabCache) { + layers.forEach(l => tabCache.removePane(l.moduleId)); + tabCache.hideAllPanes(); + } + layers = []; + saveTabs(); + // Show the dashboard + const dashboard = document.querySelector('rstack-user-dashboard'); + if (dashboard) { + dashboard.style.display = ''; + if (dashboard.refresh) dashboard.refresh(); + if (dashboard.setOpenTabs) dashboard.setOpenTabs([]); + } + const app = document.getElementById('app'); + if (app) app.classList.remove('canvas-layout'); + tabBar.setAttribute('active', ''); + tabBar.setLayers([]); + currentModuleId = ''; + var dashUrl = window.location.hostname.endsWith('.rspace.online') ? '/' : '/' + spaceSlug; + history.pushState({ dashboard: true, spaceSlug: spaceSlug }, '', dashUrl); + }); + tabBar.addEventListener('layer-reorder', (e) => { const { layerId, newIndex } = e.detail; const oldIdx = layers.findIndex(l => l.id === layerId); @@ -1482,6 +1518,9 @@ export function renderShell(opts: ShellOptions): string { tabBar.addEventListener('layer-close', (e) => { sync.removeLayer(e.detail.layerId); }); + tabBar.addEventListener('layers-close-all', () => { + sync.getLayers().forEach(l => sync.removeLayer(l.id)); + }); tabBar.addEventListener('layer-reorder', (e) => { const { layerId, newIndex } = e.detail; const all = sync.getLayers(); // already sorted by order @@ -1822,6 +1861,7 @@ export function renderExternalAppShell(opts: ExternalAppShellOptions): string { tabBar.addEventListener('layer-switch', (e) => { saveTabs(); window.location.href = window.__rspaceNavUrl(spaceSlug, e.detail.moduleId); }); tabBar.addEventListener('layer-add', (e) => { const { moduleId } = e.detail; if (!layers.find(l => l.moduleId === moduleId)) layers.push(makeLayer(moduleId, layers.length)); saveTabs(); window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId); }); tabBar.addEventListener('layer-close', (e) => { const { layerId } = e.detail; tabBar.removeLayer(layerId); layers = layers.filter(l => l.id !== layerId); saveTabs(); if (layerId === 'layer-' + currentModuleId && layers.length > 0) window.location.href = window.__rspaceNavUrl(spaceSlug, layers[0].moduleId); }); + tabBar.addEventListener('layers-close-all', () => { layers = []; tabBar.setLayers([]); tabBar.setAttribute('active', ''); saveTabs(); var dashUrl = window.location.hostname.endsWith('.rspace.online') ? '/' : '/' + spaceSlug; window.location.href = dashUrl; }); tabBar.addEventListener('layer-reorder', (e) => { const { layerId, newIndex } = e.detail; const oldIdx = layers.findIndex(l => l.id === layerId); if (oldIdx === -1 || oldIdx === newIndex) return; const [moved] = layers.splice(oldIdx, 1); layers.splice(newIndex, 0, moved); layers.forEach((l, i) => l.order = i); saveTabs(); tabBar.setLayers(layers); }); } diff --git a/shared/components/rstack-tab-bar.ts b/shared/components/rstack-tab-bar.ts index bcea794d..7d83216e 100644 --- a/shared/components/rstack-tab-bar.ts +++ b/shared/components/rstack-tab-bar.ts @@ -15,6 +15,7 @@ * layer-switch — fired when user clicks a tab { detail: { layerId, moduleId } } * layer-add — fired when user clicks + to add a layer * layer-close — fired when user closes a tab { detail: { layerId } } + * layers-close-all — fired when user clicks close-all button (no detail) * layer-reorder — fired on drag reorder { detail: { layerId, newIndex } } * view-toggle — fired when switching flat/stack view { detail: { mode } } * flow-select — fired when a flow is clicked in stack view { detail: { flowId } } @@ -426,6 +427,7 @@ export class RStackTabBar extends HTMLElement {