debug(shell): add console logging to trace tab-switch failure for rspace
Adds diagnostic console.log to layer-switch handler, TabCache.switchTo, fetchAndInject, and reconcileRemoteLayers to identify why clicking the rspace tab closes all tabs and shows the dashboard. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
433833da0c
commit
677a69645e
|
|
@ -844,10 +844,12 @@ export function renderShell(opts: ShellOptions): string {
|
|||
|
||||
// Reconcile remote layer changes (shared by BroadcastChannel + Automerge)
|
||||
function reconcileRemoteLayers(remoteLayers) {
|
||||
console.log('[shell] reconcileRemoteLayers: remote=' + remoteLayers.length + ', local=' + layers.length + ', current=' + currentModuleId);
|
||||
// Guard: never let remote sync wipe all tabs when we have an active module.
|
||||
// Empty remote layers indicate a CRDT initial state or sync race, not
|
||||
// an intentional "close everything" action.
|
||||
if (remoteLayers.length === 0 && currentModuleId) {
|
||||
console.log('[shell] BLOCKED empty remote layers (keeping local)');
|
||||
// Keep local layers intact — remote has nothing useful
|
||||
tabBar.setLayers(layers);
|
||||
return;
|
||||
|
|
@ -936,6 +938,7 @@ export function renderShell(opts: ShellOptions): string {
|
|||
// This prevents the visual desync where the tab highlights before content loads.
|
||||
tabBar.addEventListener('layer-switch', (e) => {
|
||||
const { layerId, moduleId } = e.detail;
|
||||
console.log('[shell] layer-switch:', moduleId, 'layerId:', layerId, 'tabCache:', !!tabCache, 'layers:', layers.length);
|
||||
currentModuleId = moduleId;
|
||||
saveTabs();
|
||||
// Update settings panel to show config for the newly active module
|
||||
|
|
@ -943,15 +946,20 @@ export function renderShell(opts: ShellOptions): string {
|
|||
if (sp) sp.setAttribute('module-id', moduleId);
|
||||
if (tabCache) {
|
||||
tabCache.switchTo(moduleId).then(ok => {
|
||||
console.log('[shell] switchTo result:', ok, 'for', moduleId);
|
||||
if (ok) {
|
||||
tabBar.setAttribute('active', layerId);
|
||||
} else {
|
||||
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
||||
const url = window.__rspaceNavUrl(spaceSlug, moduleId);
|
||||
console.log('[shell] switchTo failed, navigating to:', url);
|
||||
window.location.href = url;
|
||||
}
|
||||
}).catch(() => {
|
||||
}).catch((err) => {
|
||||
console.error('[shell] switchTo error:', err);
|
||||
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
||||
});
|
||||
} else {
|
||||
console.log('[shell] no tabCache, navigating to:', window.__rspaceNavUrl(spaceSlug, moduleId));
|
||||
window.location.href = window.__rspaceNavUrl(spaceSlug, moduleId);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -101,14 +101,19 @@ export class TabCache {
|
|||
/** Switch to a module tab within the current space. Returns true if handled client-side. */
|
||||
async switchTo(moduleId: string): Promise<boolean> {
|
||||
const key = this.paneKey(this.spaceSlug, moduleId);
|
||||
if (moduleId === this.currentModuleId && this.panes.has(key)) return true;
|
||||
if (moduleId === this.currentModuleId && this.panes.has(key)) {
|
||||
console.log("[TabCache] switchTo", moduleId, "→ already current + cached");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.panes.has(key)) {
|
||||
console.log("[TabCache] switchTo", moduleId, "→ cached, showing pane");
|
||||
this.showPane(this.spaceSlug, moduleId);
|
||||
this.updateUrl(this.spaceSlug, moduleId);
|
||||
return true;
|
||||
}
|
||||
|
||||
console.log("[TabCache] switchTo", moduleId, "→ NOT cached, fetching…");
|
||||
return this.fetchAndInject(this.spaceSlug, moduleId);
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +170,7 @@ export class TabCache {
|
|||
/** Fetch a module page, extract content, and inject into a new pane */
|
||||
private async fetchAndInject(space: string, moduleId: string): Promise<boolean> {
|
||||
const navUrl = (window as any).__rspaceNavUrl;
|
||||
if (!navUrl) return false;
|
||||
if (!navUrl) { console.warn("[TabCache] fetchAndInject: no __rspaceNavUrl"); return false; }
|
||||
const url: string = navUrl(space, moduleId);
|
||||
|
||||
// For cross-space fetches, use the path-based API to stay same-origin
|
||||
|
|
@ -177,11 +182,14 @@ export class TabCache {
|
|||
fetchUrl = `/${space}/${moduleId}`;
|
||||
}
|
||||
} catch {
|
||||
console.warn("[TabCache] fetchAndInject: URL resolve failed for", url);
|
||||
return false;
|
||||
}
|
||||
|
||||
const app = document.getElementById("app");
|
||||
if (!app) return false;
|
||||
if (!app) { console.warn("[TabCache] fetchAndInject: no #app"); return false; }
|
||||
|
||||
console.log("[TabCache] fetchAndInject:", fetchUrl, "for", moduleId);
|
||||
|
||||
// Show loading spinner
|
||||
const loadingPane = document.createElement("div");
|
||||
|
|
@ -197,13 +205,16 @@ export class TabCache {
|
|||
signal: AbortSignal.timeout(10_000),
|
||||
});
|
||||
if (!resp.ok) {
|
||||
console.warn("[TabCache] fetchAndInject: HTTP", resp.status, "for", fetchUrl);
|
||||
loadingPane.remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
const html = await resp.text();
|
||||
console.log("[TabCache] fetchAndInject: got", html.length, "bytes for", moduleId);
|
||||
const content = this.extractContent(html);
|
||||
if (!content) {
|
||||
console.warn("[TabCache] fetchAndInject: extractContent returned null for", moduleId, "— HTML has #app:", html.includes('id="app"'));
|
||||
loadingPane.remove();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -233,8 +244,10 @@ export class TabCache {
|
|||
this.updateUrl(space, moduleId);
|
||||
this.updateCanvasLayout(moduleId);
|
||||
|
||||
console.log("[TabCache] fetchAndInject: SUCCESS for", moduleId);
|
||||
return true;
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error("[TabCache] fetchAndInject: CATCH for", moduleId, err);
|
||||
loadingPane.remove();
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue