Skip to content

Commit dea1a6a

Browse files
committed
perf(app): mount conversation dock on first open
1 parent 6353601 commit dea1a6a

2 files changed

Lines changed: 41 additions & 16 deletions

File tree

‎app/src/App.tsx‎

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,16 @@ const KnowledgePage = lazy(() => import("./pages/KnowledgePage"));
4141
// v0.18.0:学习目标页(意图层——独立 Tab,零叙事元素)
4242
const GoalsPage = lazy(() => import("./pages/GoalsPage"));
4343
// REQ-274(v0.19.4):全局 AI 对话面板(丙案——按需唤起 + 内容保活)
44-
import AiConversationDock from "./components/AiConversationDock";
44+
// 批 2 包体治理:从静态 import 改为按需 import,并在它之前加一道「首开挂载」闸门。
45+
// @ai-context: 原语义(见文件末尾 dock 渲染处的注释)是「常驻挂载——开合仅切 display,
46+
// 选中态/后台任务保活」。本任务**只**把「首次打开之前」变成不挂载:dockOpen 为真过
47+
// 一次 ⇒ dockMounted 永为真 ⇒ 之后的 open/close 仍是纯 display 切换,保活语义逐字保留
48+
// (控制方 2026-09-12 裁决 2:唯一被允许的行为差异就是首访挂载时序)。
49+
// @ai-context: 收益来自它的静态依赖链 AiConversationDock → TaskConversationView →
50+
// ChatMessageMarkdown → rehype-katex + katex/dist/katex.min.css。实测这条链把
51+
// vendor-katex + vendor-md(79,916 + 50,618 = 130,534 字节 gzip)钉在首屏;摘掉后
52+
// 首屏 gzip 227.08 → 92.79 kB(预算 200 kB,来源 docs/standards/performance.md:28)。
53+
const AiConversationDock = lazy(() => import("./components/AiConversationDock"));
4554
import AppErrorBoundary from "./components/AppErrorBoundary";
4655
// 批 2 包体治理:两个窗口变体面板从静态 import 改为按需 import(各自独立 chunk)。
4756
// @ai-context: 这两个面板只在**自己的窗口**里渲染(App() 顶部的 ?float=1 / ?overlay=1 早返回),
@@ -181,6 +190,15 @@ function MainShell() {
181190
const [focusChatTaskId, setFocusChatTaskId] = useState<number | null>(null);
182191
// REQ-274:对话面板开合 + 「在对话页继续」直达会话(消费后清空)
183192
const [dockOpen, setDockOpen] = useState(false);
193+
// 批 2:首次打开之前的挂载闸门(打开过即常驻,见 import 处的 @ai-context)。
194+
// @ai-context: 用 effect 而不是「渲染期 if (dockOpen) setDockMounted(true)」——渲染期改
195+
// state 会让 React 19 多跑一轮渲染(StrictMode 下更明显);代价只是首开首帧 dock 仍
196+
// 缺席(本来也要等它的 chunk 下载),无观感回归。与 mountedPages 同一模式。
197+
// 副作用:仅置真、无回落路径 ⇒ 一旦挂载过就永不卸载(TD-004 保活)。
198+
const [dockMounted, setDockMounted] = useState(false);
199+
useEffect(() => {
200+
if (dockOpen) setDockMounted(true);
201+
}, [dockOpen]);
184202
const [focusChatId, setFocusChatId] = useState<number | null>(null);
185203
// 全局采集状态(ADR-007:与页面解耦,徽标常驻导航栏)。
186204
// 批 2b:capturing/recovering/paused 三份本地状态删除——采集控制单一状态源
@@ -473,20 +491,27 @@ function MainShell() {
473491
<SettingsPage active={page === "settings"} />
474492
</PageSlot>
475493
</main>
476-
{/* REQ-274:全局 AI 对话面板(常驻挂载——开合仅切 display,选中态/后台任务保活) */}
477-
<AiConversationDock
478-
open={dockOpen}
479-
onClose={() => setDockOpen(false)}
480-
onOpenChat={(chatId) => { setFocusChatId(chatId); setPage("chat"); }}
481-
onOpenTaskInChat={(taskId) => { setFocusChatTaskId(taskId); setPage("chat"); }}
482-
onOpenSessions={(id) => { setFocusSessionId(id); setPage("sessions"); }}
483-
onOpenNote={(id) => openNotePlain(id)}
484-
onOpenRefineWorkbench={(sessionId, taskId) => {
485-
setFocusSessionId(sessionId);
486-
setFocusRefineTaskId(taskId);
487-
setPage("sessions");
488-
}}
489-
/>
494+
{/* REQ-274:全局 AI 对话面板(常驻挂载——开合仅切 display,选中态/后台任务保活)。
495+
批 2:闸门只加在**首开之前** —— dockMounted 一旦为真永不复位,此后 open/close
496+
仍是纯 display 切换(保活语义逐字保留);独立 Suspense(fallback=null) 与 PageSlot
497+
同理由:chunk 首次到达前不渲染任何东西,加载失败由外层 AppErrorBoundary 兜底。 */}
498+
{dockMounted && (
499+
<Suspense fallback={null}>
500+
<AiConversationDock
501+
open={dockOpen}
502+
onClose={() => setDockOpen(false)}
503+
onOpenChat={(chatId) => { setFocusChatId(chatId); setPage("chat"); }}
504+
onOpenTaskInChat={(taskId) => { setFocusChatTaskId(taskId); setPage("chat"); }}
505+
onOpenSessions={(id) => { setFocusSessionId(id); setPage("sessions"); }}
506+
onOpenNote={(id) => openNotePlain(id)}
507+
onOpenRefineWorkbench={(sessionId, taskId) => {
508+
setFocusSessionId(sessionId);
509+
setFocusRefineTaskId(taskId);
510+
setPage("sessions");
511+
}}
512+
/>
513+
</Suspense>
514+
)}
490515
</div>
491516
);
492517
}

‎docs/standards/line-limit-exemptions.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@
3333
| app/src-tauri/src/commands.rs | 523 | 命令装配域(AppState + 通用命令 + 导入管线编排);登记值 466 过期快照——2026-09-09 实测纠偏(含批 7 delete_note 结果契约 +12;600 硬限内压线) | 若再增长:导入管线命令拆至 commands_import.rs(既有登记计划) |
3434
| app/src-tauri/src/video_profile_tests.rs | 522 | 档案测试域(12 档案断言矩阵 + 检测投票 + JSON 校准 + v0.13.6 领域记忆独立通道/旧 JSON 零迁移用例 + 审查回归(烘焙迁移/单字种子守卫))单模块 #[path] 挂载 | 若再增长:档案矩阵拆至 video_profile_data_tests.rs |
3535
| app/src-tauri/src/engine.rs | 521 | 引擎池句柄与同步 API(双 worker 编排 + ADR-009 设备状态 + M7 心跳/失败/缓存计数 + 有界等待变体);三维复审 #5 超时排空机制(drain_asr/ocr_backlog)与 #3 ASR_FILE_TIMEOUT 文件级超时常量接入后,worker 主循环与请求协议按登记计划拆至 engine_worker.rs(见文末"已拆分"注记)回归本值 | 若再增长:排空机制与同步 API 变体拆至 engine_request.rs |
36+
| app/src/App.tsx | 519 | 根装配(页面切换/焦点跨页直达状态机/全局事件监听)+ v0.20.5 行动页 + v0.20.10 批 5 复习页 Tab/深链/保活挂载(f702d876)——登记值 363 过期,实测纠偏 | 若再增长:焦点跨页直达 state 族拆至 useFocusRouting.ts |
3637
| app/src/components/GroupSidebar.tsx | 519 | v0.20.12 批 7(REQ-316)拖拽归组/ⓘ 弹层移组清理留痕透传(509→519;登记值过期纠偏) | 若再增长:体系引用拉取与徽标聚合拆至 useGroupSystemLinks.ts hook |
3738
| app/src/components/LiveActivityPanel.tsx | 517 | 实时活动面板:会话状态/转录流/OCR 预览/控制区多状态面板内聚(前端审查登记) | 若再增长:转录流与 OCR 预览拆至 LiveTranscriptStream.tsx / LiveOcrPreview.tsx |
3839
| app/src-tauri/src/ai_client.rs | 505 | v0.11.6 M1(AiClient::from_provider / from_settings_with_store / is_fallbackable / fallback_provider_ids)+ 2026-09-11 DeepSeek V4.1 适配(chat_plain 探活路径 / build_plain_payload / json 前置条件兜底接线 / thinking 策略落点 / 4xx 错误体透出——纯策略与提取逻辑已拆至 ai_request_policy.rs)——Provider 解析与错误分类内聚于 AiClient 域,构造入口与降级链纯函数同文件便于单测。**旧登记 322 为过期快照,本次按实测纠偏** | 若再增长:fallback_provider_ids 拆至 ai_fallback.rs;payload 构造族拆至 ai_payload.rs |
3940
| app/src/components/GroupSidebar.test.tsx | 497 | 覆盖串组场景——切换 ⓘ 弹层目标组时表单态必须重置(key=group.id修复):组 A 改了判类(未确认)→ 点组 B ⓘ → 弹层显示 B 且判类下拉回到 B.kind(防把 A 的选择误用到 B——路径: 改判误操作)。(自动摘取,待细化) | 若再增长:按职责拆分 |
40-
| app/src/App.tsx | 494 | 根装配(页面切换/焦点跨页直达状态机/全局事件监听)+ v0.20.5 行动页 + v0.20.10 批 5 复习页 Tab/深链/保活挂载(f702d876)——登记值 363 过期,实测纠偏 | 若再增长:焦点跨页直达 state 族拆至 useFocusRouting.ts |
4141
| app/src-tauri/src/db_sop.rs | 490 | v0.20.3(REQ-296/297)SOP 三表数据域(模板/run/步骤/保鲜/聚合)+ 审查加固(保链更新/幂等守卫/步数计数)内聚 | 若再增长:run 执行族拆至 db_sop_run.rs |
4242
| app/src-tauri/src/live_session_persist.rs | 488 | 定稿落库域(persist_final/digest_merged/handle_final_event)+ P2 flush_tail_and_persist(停止/暂停共用尾句落库)内聚 | 若再增长:flush_tail_and_persist 与 digest_merged 拆至 live_session_persist_tail.rs |
4343
| app/src-tauri/src/db_sessions.rs | 482 | 会话仓储;批 4 delete_sessions_batch + 审查修复轮 3(transaction() 改造)——2026-09-09 实测纠偏(登记值 394 过期) | 若再增长:recent_ocr_texts 等建议查询拆至 db_sessions_queries.rs |

0 commit comments

Comments
 (0)