Skip to content

refactor(app): 拆分 App 组合层的 surface 与协调 hook - #259

Merged
GeekMai90 merged 1 commit into
mainfrom
codex/engineering-optimization
Aug 19, 2026
Merged

refactor(app): 拆分 App 组合层的 surface 与协调 hook#259
GeekMai90 merged 1 commit into
mainfrom
codex/engineering-optimization

Conversation

@GeekMai90

Copy link
Copy Markdown
Owner

做了什么

App.tsx叶子职责拆到各 feature 自己的边界,并把跨功能协调抽成 app 级 hook。行为逐字保持不变。

src/app/App.tsx3265 → 2532 行

Surface(lazy 边界从 app 下沉到所属 feature)

feature 新增
library SidebarContextMenuLibraryImportDialogsLibraryMaintenanceDialogsProjectDraftDialogsHost
publishing DocumentPublishingDialogsPublishingTargetDialogProjectPublishingSettingsHost
assistant / editor / media / settings AiAssistantPanelHostDocumentPropertyManagerDialogHostImageSourceDialogHostSettingsDialogHostKeyboardShortcutsDialogHost

app/lazySurfaces.ts 只保留首屏编辑器画布与 DEV 设计画廊,不再充当全局 surface registry。

协调 hook

  • appuseManualDocumentSaveuseGlobalSearchNavigationuseAiActionTargetNavigationuseNativeMenuBindings
  • featureuseSheetSelectionuseLiveDocumentProjectionuseAiContentGenerators
  • workspaceSelection 新增纯规则 resolveGlobalSearchNavigationTarget

两处刻意的边界选择

1. 引导页不再依赖删除状态树

写作库弹窗按“是否已有内容”拆成两个:

  • LibraryImportDialogs(Markdown 导入 + 快速记录)→ onboarding 首屏与主界面共用
  • LibraryMaintenanceDialogs(移动、图片清理、项目/分组/文稿/废纸篓确认)→ 仅主界面

否则引导页会挂上“清空废纸篓”这类它根本不该存在的路径。

2. effect 执行顺序与重构前逐字一致

useSheetSelectionuseWorkspaceNavigation 之前调用,保证“选择裁剪”effect 仍先于 repair effect 运行——否则删除文稿/切换写作库时会多一帧选中态闪烁。对 selectSheet 的前向依赖使用仓库已有的 ref-latest 范式(同 openMarkdownImportRef),不是新发明。

工程工具

  • scripts/project-health.mjs只读报告,不是门禁:按 SRP 拆成 collectHealthReport(纯统计)+ renderHealthReport(呈现);测试断言结构化结果而非控制台文案,随 test:release 执行,永远不会让 npm run check 变红
  • vitest 通过 src/testSetup.ts 声明 IS_REACT_ACT_ENVIRONMENT
  • shadcn 移到 devDependencies(纯 CLI,零运行时引用;CI 用 npm ci --legacy-peer-deps 仍会安装)

验证

门禁 结果
check:architecture ✅ GEB 契约对齐
tsc --noEmit
eslint --max-warnings=0
prettier --check + cargo fmt --check
vitest run ✅ 235 文件 / 1101 用例
test:release ✅ 28 用例
build:web + check:bundle ✅ 1131.8 KiB 初始 JS(+0.2 KiB)

搬运保真度做了逐字比对:右键菜单 240 行 JSX 的全部中文标签与分支条件不变;resolveGlobalSearchNavigationTargetrequestListScroll 与旧内联逻辑等价。ConfirmDialog / MoveSheetDialog / QuickCaptureDialog / UnusedImageCleanupDialog / SettingsDialog / AiAssistantPanel 等 lazy chunk 全部保持独立分包,code-splitting 未退化。

已知的后续工作(本 PR 不做)

  1. 7 个 Host 是同一段 20 行模板的复制 → 应抽 createLazySurface 工厂
  2. useAiActionTargetNavigation / useManualDocumentSaveuseCallback 依赖里含每次都变的数组与内联箭头,memo 永不命中 → 应抽 useEventCallback 统一 ref-latest 范式(会连带影响 useAppShortcuts,风险面不同,单独提交)
  3. App.tsx 仍是 2532 行,全仓第一。本次拆的全是叶子,约 40 个 useState 构成的跨功能状态所有权一根没动。下一刀该切状态,不该切 JSX。

🤖 Generated with Claude Code

Split App.tsx's leaf responsibilities into feature-owned boundaries and
app-level coordination hooks, keeping behaviour byte-for-byte identical.

App.tsx: 3265 -> 2532 lines.

Surfaces (lazy boundaries move from app into the owning feature):
- library: SidebarContextMenu, LibraryImportDialogs, LibraryMaintenanceDialogs,
  ProjectDraftDialogsHost
- publishing: DocumentPublishingDialogs, PublishingTargetDialog,
  ProjectPublishingSettingsHost
- assistant/editor/media/settings: AiAssistantPanelHost,
  DocumentPropertyManagerDialogHost, ImageSourceDialogHost, SettingsDialogHost,
  KeyboardShortcutsDialogHost
- app/lazySurfaces.ts keeps only the first-paint editor canvas and DEV galleries

Coordination hooks:
- app: useManualDocumentSave, useGlobalSearchNavigation,
  useAiActionTargetNavigation, useNativeMenuBindings
- features: useSheetSelection, useLiveDocumentProjection, useAiContentGenerators
- workspaceSelection gains resolveGlobalSearchNavigationTarget as a pure rule

Boundaries chosen deliberately:
- LibraryImportDialogs (Markdown import + quick capture) is shared with the
  onboarding screen; LibraryMaintenanceDialogs (move / image cleanup / trash
  confirmations) mounts only in the main workspace, so onboarding no longer
  depends on the delete and cleanup state tree.
- useSheetSelection is called before useWorkspaceNavigation so the selection
  pruning effect keeps running ahead of the repair effects, preserving the
  pre-refactor effect order; the forward reference to selectSheet uses the
  existing ref-latest paradigm.

Tooling:
- scripts/project-health.mjs is a read-only report split into
  collectHealthReport (pure) and renderHealthReport (presentation); its test
  asserts structured results and runs with test:release. It is a report, not a
  gate, and never fails npm run check.
- vitest declares IS_REACT_ACT_ENVIRONMENT via src/testSetup.ts
- shadcn moves to devDependencies (CLI only, no runtime import)

Verified: check:architecture, tsc, eslint --max-warnings=0, prettier,
cargo fmt, vitest (235 files / 1101 tests), test:release, build:web +
check:bundle (1131.8 KiB initial, +0.2 KiB; all lazy chunks still split).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@GeekMai90
GeekMai90 merged commit 8fd2ae2 into main Aug 19, 2026
1 check passed
@GeekMai90
GeekMai90 deleted the codex/engineering-optimization branch August 19, 2026 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant