fix: app-switcher routes through tab system + canvas fills viewport in tab pane

- App-switcher now dispatches module-select event instead of full page navigation
  for same-origin links; shell routes through TabCache for instant tab switching
- Tab pane gets height:100% in canvas-layout mode so #canvas fills the viewport
  (fixes pan/zoom not working on empty canvas background)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-02 12:35:38 -08:00
parent be271de7fb
commit a402caacd8
3 changed files with 56 additions and 0 deletions

View File

@ -324,6 +324,36 @@ export function renderShell(opts: ShellOptions): string {
document.dispatchEvent(new CustomEvent('layer-view-mode', { detail: { mode } }));
});
// ── App-switcher → tab system integration ──
// When user picks a module from the app-switcher, route through tabs
// instead of doing a full page navigation.
const appSwitcher = document.querySelector('rstack-app-switcher');
if (appSwitcher) {
appSwitcher.addEventListener('module-select', (e) => {
const { moduleId } = e.detail;
// Already on this module? No-op.
if (moduleId === currentModuleId && !tabCache) return;
if (moduleId === currentModuleId && tabCache) {
tabCache.switchTo(moduleId);
return;
}
// Add tab if not already open
if (!layers.find(l => l.moduleId === moduleId)) {
layers.push(makeLayer(moduleId, layers.length));
}
saveTabs();
tabBar.setLayers(layers);
tabBar.setAttribute('active', 'layer-' + moduleId);
if (tabCache) {
tabCache.switchTo(moduleId).then(ok => {
if (!ok) window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
});
} else {
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
}
});
}
// Expose tabBar for CommunitySync integration
window.__rspaceTabBar = tabBar;

View File

@ -254,6 +254,27 @@ export class RStackAppSwitcher extends HTMLElement {
el.addEventListener("click", (e) => e.stopPropagation());
});
// Intercept same-origin module links → dispatch event for tab system
this.#shadow.querySelectorAll("a.item").forEach((el) => {
el.addEventListener("click", (e) => {
const moduleId = (el as HTMLElement).dataset.id;
if (!moduleId) return;
// Only intercept same-origin links (skip bare-domain landing pages)
const href = (el as HTMLAnchorElement).href;
try {
const url = new URL(href, window.location.href);
if (url.origin !== window.location.origin) return;
} catch { return; }
e.preventDefault();
menu.classList.remove("open");
this.dispatchEvent(new CustomEvent("module-select", {
detail: { moduleId },
bubbles: true,
composed: true,
}));
});
});
document.addEventListener("click", () => menu.classList.remove("open"));
}

View File

@ -392,6 +392,11 @@ body[data-theme="light"] {
display: block;
}
/* When canvas is active, the tab pane must fill the viewport so #canvas height:100% works */
#app.canvas-layout > .rspace-tab-pane--active {
height: 100%;
}
.rspace-tab-loading {
display: flex;
align-items: center;