diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ce512c..469bca6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ ## 2026-04-06 +### 自动连接首屏再提速一层 + +- 自动连接成功后,`/v2/me` 和 `/v2/me/dashboard` 现在改成并行请求,不再串行等待。 +- 会话拿到以后,页面会更快进入真实项目总台和当前工作区骨架,再继续后台补齐账号、存储、Agent 控制面等重数据。 +- 这让“点开就能用”的体验更接近真实可交互,而不是长时间停在连接提示上。 + ### 自动连接工作区改成先可用后补水 - 自动连接成功后,不再把账号详情、存储、Agent 控制面、OneLiner 消息、文档等重数据全部串在首屏可用之前。 diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index d8d55c0..f89babf 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -3000,7 +3000,11 @@ async function bootstrap() { } setBusy(true, "正在同步工作区..."); try { - appState.me = await storyforgeFetch("/v2/me"); + const [me, dashboard] = await Promise.all([ + storyforgeFetch("/v2/me"), + storyforgeFetch("/v2/me/dashboard") + ]); + appState.me = me; if (requestToken !== bootstrapHydrationToken) return; if (appState.me.approval_status !== "approved" && appState.me.role !== "super_admin") { appState.dashboard = null; @@ -3013,7 +3017,6 @@ async function bootstrap() { renderAll(); return; } - const dashboard = await storyforgeFetch("/v2/me/dashboard"); if (requestToken !== bootstrapHydrationToken) return; appState.dashboard = dashboard; const preferredPlatform = getCurrentPlatformValue(); diff --git a/web/storyforge-web-v4/tests/workbench-pages.test.mjs b/web/storyforge-web-v4/tests/workbench-pages.test.mjs index d2927f1..fed47fe 100644 --- a/web/storyforge-web-v4/tests/workbench-pages.test.mjs +++ b/web/storyforge-web-v4/tests/workbench-pages.test.mjs @@ -643,6 +643,13 @@ test("bootstrap renders the workspace shell before heavy hydration completes", ( assert.match(hydration, /void loadPlatformAccount\(getAccountPlatform\(nextAccount\), nextAccountId\)/); }); +test("bootstrap fetches me and dashboard in parallel before entering the workspace shell", () => { + const bootstrap = extractBetween(APP, "async function bootstrap()", "async function markTrackingDigestRead()"); + assert.match(bootstrap, /const \[me, dashboard\] = await Promise\.all\(\[\s*storyforgeFetch\("\/v2\/me"\),\s*storyforgeFetch\("\/v2\/me\/dashboard"\)\s*\]\);/); + assert.match(bootstrap, /appState\.me = me;/); + assert.match(bootstrap, /appState\.dashboard = dashboard;/); +}); + test("bootstrap only loads live recorder runtime endpoints when the integration is reachable", () => { const hydration = extractBetween(APP, "async function hydrateWorkbenchDataAfterBootstrap(", "async function bootstrap()"); assert.match(hydration, /const liveRecorderIntegration = integrationHealth\?\.live_recorder \|\| null/);