diff --git a/CLAUDE.md b/CLAUDE.md index a92c45d7..e0257451 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -171,6 +171,7 @@ Service Worker (后台执行) 20. **同步数据格式一致性**:`tasks.json` 结构是 `{ completedTasks: Task[] }` 不是数组,画板文件是 `board_{id}.json` 不是 `.drawnix` 21. **RPC 超时与重连**:关键 RPC 调用(如工作流提交)需设置合理超时(15-30秒),超时时主动重新初始化 SW 连接并重试 22. **降级路径功能一致性**:降级到主线程直接调用 API 时,必须保持与 SW 模式相同的功能行为(如 LLM API 日志记录),否则调试工具会漏掉这些调用 +23. **SW 初始化统一入口**:使用 `swChannelClient.ensureReady()` 作为统一入口,避免在各处重复初始化逻辑;任务队列用 `swTaskQueueService.initialize()` ### React 规则 diff --git a/docs/CODING_RULES.md b/docs/CODING_RULES.md index ce62bfb2..0191a233 100644 --- a/docs/CODING_RULES.md +++ b/docs/CODING_RULES.md @@ -1458,6 +1458,52 @@ const submitToSW = async (workflow) => { **原因**: Service Worker 的 `workflowHandler` 需要收到 `TASK_QUEUE_INIT` 消息后才会初始化。如果在 SW 初始化前提交工作流,消息会被暂存到 `pendingWorkflowMessages`,等待配置到达。若配置永远不到达(如 `swTaskQueueService.initialize()` 未被调用),工作流就永远不会开始执行,步骤状态保持 `pending`。 +### Service Worker 初始化统一入口 + +**场景**: 需要确保 SW 客户端已初始化并配置好后再进行 API 调用 + +❌ **错误示例**: +```typescript +// 错误:在各处重复 SW 初始化逻辑 +async function callSWApi() { + if (shouldUseSWTaskQueue()) { + if (!swChannelClient.isInitialized()) { + const settings = geminiSettings.get(); + if (settings.apiKey && settings.baseUrl) { + await settingsManager.waitForInitialization(); + await swChannelClient.initialize(); + await swChannelClient.init({ + geminiConfig: { apiKey: settings.apiKey, baseUrl: settings.baseUrl }, + videoConfig: { baseUrl: settings.baseUrl }, + }); + } + } + // 使用 SW... + } +} +``` + +✅ **正确示例**: +```typescript +// 正确:使用统一入口 +async function callSWApi() { + const useSW = await swChannelClient.ensureReady(); + + if (useSW) { + // 使用 SW... + } else { + // 降级到主线程... + } +} +``` + +**统一入口说明**: +- `swChannelClient.ensureReady()`: 统一的异步初始化入口,内部处理 URL 参数检查、设置验证、初始化和配置 +- `swTaskQueueService.initialize()`: 任务队列服务初始化,内部调用 `ensureReady()` + 设置 visibility 监听器 +- `shouldUseSWTaskQueue()`: 同步检查函数,仅用于判断模式,不进行初始化 + +**原因**: 分散的 SW 初始化逻辑会导致代码重复、维护困难,且容易遗漏某些检查步骤。使用统一入口可以确保所有初始化逻辑集中管理,便于维护和调试。 + ### Service Worker 更新提示在开发模式下被跳过 **场景**: 在 localhost 本地测试 Service Worker 更新提示功能 @@ -7071,3 +7117,110 @@ async deleteBoard(id: string) { - 热更新(HMR)可能导致旧的订阅被替换 - 关键业务逻辑不应依赖于"某处可能存在的订阅者" +### 自定义菜单/按钮组件的焦点样式和键盘导航 + +**场景**: 实现自定义菜单、下拉列表等交互组件时 + +❌ **错误示例**: +```scss +// 错误:没有处理焦点样式,浏览器会显示默认的蓝色边框 +.menu-item { + background-color: transparent; + border: 1px solid transparent; + // 缺少 outline 处理 +} +``` + +```tsx +// 错误:菜单没有键盘导航支持 +const Menu = ({ children }) => { + return ( +