forming / System Design Archive

Frontend Runtime Convergence

Frontend Runtime Convergence 把 MyBlog 当前的 Astro SSR、React islands、inline scripts、custom events、Pagefind、OpenList shell、Pinterest shell、localStorage cache 和 Runtime APIs 收束到一个显式的前端 Runtime Kernel 合同里,先统一 command、keyboard、overlay、drawer、focus 和 storage classification,再逐步迁移具体 owner。

Runtime KernelLegacy Inline RuntimeReact IslandsCommand BusKeyboard LayerOverlay StackDrawer IntentsFocus RestoreStorage ClassificationcmdkMotionFloating UI
Inspiration

借鉴对象

  • Linear runtime discipline
  • Raycast command model
  • AFFiNE workspace runtime
  • Arc Browser layering
  • React Aria focus semantics
Rejected

拒绝的方案

  • 继续新增自由 inline script,因为首页已经有大型 Inline Script Empire,局部补丁会扩大 hidden coupling。
  • 让 Ctrl/Cmd+K 继续由 Command Palette 和 fallback search 双重拥有,因为这是 Runtime Split Brain。
  • 继续用 window.dispatchEvent 作为长期 integration layer,因为事件名无类型、无 owner、无 fallback 合同。
  • 把 localStorage 新 key 写成 source of truth,因为它只能是 preference、cache、legacy migration 或临时本地 authority。
  • 只安装 Zustand、Radix、Vaul 或 React Flow 就声称升级,因为没有迁移 surface 和浏览器证据的依赖只能算 installed / not migrated。
Runtime

运行方式

  • 规范入口是 README.md 与本 Architecture Codex 条目;前端运行时收束不再维护独立 docs 文档。
  • P0 合同包是 packages/runtime-kernel,当前 dependency-free,定义 command、overlay、drawer、keyboard、authority、storage classification 和 small runtime plugin protocol;apps/web/src/lib/runtime/bridge.ts 是第一条生产适配路径,把 React islands 的 action 统一转成 runtime:command / runtime:overlay-* / runtime:drawer-*;packages/runtime-kernel/src/storage.ts 是 canonical browser storage registry。
  • 当前没有 active runtime-migration.json、packages/runtime-overlay 或 packages/runtime-store;overlay、drawer、focus 和 Escape 的消费实现仍在 legacy inline runtime + React islands,但入口已经开始统一到 runtime intent。
  • packages/runtime-kernel 不替代 packages/runtime-contract;前者管前端交互意图,后者管 API transport envelope。
  • packages/runtime-kernel 不替代 packages/object-model;前者管 runtime intent,后者管 KnowledgeObject identity 和 relation。
  • 当前 active libraries 是 cmdk、motion 和 @floating-ui/react;Zustand、Radix UI primitives、Vaul、React Flow 已安装但尚未迁移任何 runtime owner。
  • P1 已部分落地:HomeCommandPalette hydrate 后设置 html[data-home-command-ready="true"];home inline Ctrl/Cmd+K fallback 只在该 ready 标记不存在时运行。
  • HomeCommandPalette 的 search/openlist/pinterest action 已改由 runtime bridge 派发 runtime:command;BaseLayout 只保留 shell DOM 与模块入口,OpenList/Pinterest 的按钮、Escape 和 command 消费由 apps/web/src/lib/runtime/shell-overlays.ts 拥有,并通过 apps/web/src/lib/runtime/shell-runtime.ts 生成 runtime:overlay-open / runtime:overlay-close detail;旧 openlist-embed-open / pinterest-embed-open 只作为兼容桥保留。
  • 首页 inline runtime 把 search.open、reader.open、reader.close、home-search-open、reader-command、快捷键、URL 参数、sidebar memory 和 search-result click 收束成 runtime:overlay-* 或 runtime:drawer-*,并从 apps/web/src/lib/runtime/home-runtime.ts 注入 RUNTIME_EVENT_NAMES、LEGACY_RUNTIME_BRIDGES 和 intent detail builders,避免首页继续散落裸 runtime 事件字符串与 detail 结构;最终只有 search overlay 和 article drawer 的消费点调用真实 open/close 实现。
  • MyBlog plugin protocol 取 Astro/Vite lifecycle、Quartz transformer/filter/emitter、unified plugin 和 Obsidian manifest 的最小交集:manifest + scopes + contributions + optional setup;插件只声明 commands、overlays、drawers、storage、resources 等 contribution,不拥有 Markdown、MySQL、OpenList、KnowledgeObject 或 deployment truth。
  • Book Drawer Reader island 已同时监听 runtime:drawer-open / runtime:drawer-close drawer=book 和 legacy emptyinkpot:book-drawer-*;首页打开 book drawer 时先派发 runtime drawer intent,再派发 legacy bridge,避免 hydrate 时序丢事件。
  • knowledgeStorageKeys、book storage keys、visual manifest cache 与 build-version reload keys 已开始引用 RUNTIME_STORAGE_KEYS;reading history、bookmarks、highlights、annotations、seals、stickers、book progress/location 被标记为 legacy-migration,reader theme / visual settings / book settings 是 preference,build version / book recent / visual manifest 是 cache。
  • BaseLayout 与 HoverPreviewSystem 已把 content-settings-applied / runtime-folders-applied 收束到 runtime event registry,同时继续广播 legacy event 兼容旧消费者。
  • tools/validate-frontend-runtime-contract.mjs 已加入 emptyinkpot-* literal registry guard:新 storage key 必须进入 packages/runtime-kernel/src/storage-keys.mjs,非 storage 事件必须在 allowlist 中说明。
  • legacy bridge events 暂时允许:home-search-open、openlist-embed-open、pinterest-embed-open、reader-command、emptyinkpot:book-drawer-open、emptyinkpot:book-drawer-close。
  • 新增全局快捷键、overlay、drawer、custom event 或 localStorage key 前,必须同步更新 frontend-runtime-audit 和 frontend-runtime-convergence。
Tradeoff

取舍

  • 先写合同不会立刻减少源码中的 inline script,但能阻止新运行时继续散开,并给后续迁移提供验收边界。
  • 逐个 owner 迁移比大爆炸慢,但能保留现有首页、Reader、OpenList、Pinterest 和 Graph 的可达行为。
  • Runtime Kernel 增加一个治理层,但它不拥有数据 truth,因此不会和 MySQL、OpenList、Directus、Meilisearch 或 Immich 抢 authority。
  • 保留 legacy bridge events 会短期留下重复路径,但比一次性删除 custom event 更不容易破坏用户交互。
Future Direction

后续方向

  • P1 让 HomeCommandPalette 成为唯一 Ctrl/Cmd+K owner,把 fallback search 改成 runtime command。
  • P2 把 OpenList shell、Pinterest shell、Article Drawer 和 Book Drawer 纳入统一 overlay stack 与 focus restore 规则。
  • 下一次真实 cutover 可以评估 Book Drawer shell -> Vaul,但必须先在代码里实现 owner 切换、浏览器证据和回滚路径;Reader memory / highlights 仍由 MySQL Runtime Truth 拥有。
  • P3 在第一个 owner 迁移时引入 Zustand 或同级 store;优先迁移 overlay stack、command state 和 reader shell state。
  • Graph 只有在 KnowledgeObject contract、search authority 和 drawer navigation 稳定后才迁移到 React Flow。
  • 生成 machine listener inventory,按 source file、event type、selector 和 owner 输出运行时清单。