ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Voyager 仓库贡献避坑指南:从 PR 机制到插件系统的全链路实战手册

2026/9/21 19:16:01 拓冰建站 浏览量
Voyager 仓库贡献避坑指南:从 PR 机制到插件系统的全链路实战手册 Voyager 仓库贡献避坑指南从 PR 机制到插件系统的全链路实战手册【免费下载链接】voyagerEnhancement suite for Gemini, AI Studio, Claude ChatGPT — plus a prompt manager for any websites, DeepSeek Harness included. / 面向 Gemini、AI Studio、Claude 与 ChatGPT 的增强套件其中的提示词管理器可用于任意网站如 DeepSeek Harness。项目地址: https://gitcode.com/gh_mirrors/ge/voyager本指南以仓库维护者沉淀的《Repository-Specific Traps》repo-traps.md 为主体逐条还原 Voyager面向 Gemini、AI Studio、Claude 与 ChatGPT 的浏览器增强套件在代码贡献、PR 提交、插件开发、文档维护过程中真实踩过的坑并结合src/下可验证的源码实现给出对应的正确做法。读完你将对本仓库的 squash-merge 协作模型、codex review自动评审流程、新站点必须走插件系统的架构红线、主题解析优先级、文档多语言镜像的维护边界有完整认知能显著减少一轮又一轮的 review 返工。这份清单不是空泛的社区行为准则而是每条条目都对应至少一次真实 review 教训来源 PR 编号在括号中标注且CI 一项都抓不到它们——所以它被 voyager-contribute 技能强制列为 preflight 阶段的必读材料第 5 步Read repo-traps.md。一、Git 与 PR 机制squash-merge 的连锁反应Voyager 仓库采用squash-merge压缩合并这一决策直接决定了本仓库所有 Git 操作的正确姿势也是新手最容易翻车的环节。1.1 合并后本地分支看起来没合是正常现象PR 合并后你的本地分支会显示为 unmerged 或 conflicted——这是 squash-merge 的固有特征不是操作失误。正确的处理方式是删除旧分支从最新的main重新拉取新分支绝不从旧分支重新开 PR。文档记载了真实案例PR #867 就是因为从旧分支重复开 PR被作为 self-duplicate 关闭。1.2 PR 标题就是 squash commit 的提交头由于 squash 合并会把整个 PR 压缩成一个 commitPR 标题会成为该 commit 的 header。因此标题必须符合 Conventional Commits 格式type(scope): imperative summary例如fix(sendBehavior): respect gvCtrlEnterSend during IME composition。文档明确禁止用分支名或Fixes #N作为标题#859、#865、#867 均为反面教材。这与 SKILL.md 第 3 阶段Create a Conventional Commit with a lowercase scope and a header no longer than 100 characters的规范互相印证且提交时可附Fixes #issue/Closes #issue关联 Issue。1.3 开 PR 前必须检查 diff 范围文档给出的硬性命令是git diff origin/main --statorigin/main指跟踪基础仓库main的远端也可用其他跟踪该分支的远端名。目的是排查无关 churn是否混入例如 #865 曾意外地删除了sponsors.svg仓库根目录的赞助商图片这类资产、lockfile 的意外变更在 squash 合并后会永久进入历史。注意本指令不允许使用rm/mv等破坏性命令实际排查以只读 diff 为主。1.4codex review是唯一触发自动评审的方式裸写codex不触发任何评审必须写codex review每个新的 head 触发一次rebase/force-push 后必须重新触发有评审正在运行时不要重复触发#854人类评审者在 Codex 覆盖完当前 head SHA 之后才会做最终确认——即 Codex 评审是人工评审的前置关卡。这解释了为什么 SKILL.md 要求 push 后用gh核验 PR author、base branch、commit 集、changed files 与 CI 状态确保 head 与评审快照一致。二、复用既有实现reviewer 视重复造轮子为缺陷本仓库最大的隐性规则是动手写并行逻辑前先 grep 是否已有同平台的原语primitive。reviewer 会把对既有行为的临时重写直接判为缺陷。以下是文档点名的五个关键先例及其源码位置。2.1 发送/键盘处理复用sendBehavior任何涉及Enter 发送 / 换行的新逻辑都应复用 sendBehavior 模块而不是另写一套英文-only 的选择器或无条件的 Enter 处理器#854。为什么不能重写看实现就明白它处理了多少平台细节本地化标签isSendActionButton已覆盖发送/送信/enviar 等多语言按钮标签且兼容仅图标的按钮SEND_BUTTON_SELECTORSisVisibleButton判定offsetParent ! null设置项行为受gvCtrlEnterSendGemini CtrlEnter 发送、gvAIStudioEnterSendAI Studio Enter 发送、gvSafariEnterFixSafari 双击 Enter 修复三个存储开关控制见 common.ts 中的StorageKeys平台优先级同时开启时 CtrlEnter 发送优先Enter → 换行且监听器仅在至少一个模式启用时才激活shouldBeActive()→activateListeners()/deactivateListeners()全部关闭时零 DOM 监听开销健壮性细节handleKeyDown中event.isComposing直接 return规避 IME 组合输入误触发对应 Issue 260查找发送按钮采用closest()限定容器再querySelector的受限搜索并跳过附件上传中的progress/roleprogressbar状态多行插入对 Gemini 的 Quill 编辑器ql-editor用document.execCommand(insertParagraph)优先、insertHTML(brbr)其次、模拟 ShiftEnter 兜底的三级策略。配套测试位于 sendBehavior.test.ts新增发送类逻辑时应扩充它而不是新建平行实现。2.2 文本插入chatInput的多行感知插入向输入框插入文本时必须复用 chatInput。裸用createTextNode会把多行 prompt 体压扁成单行——因为 Gemini 的 Quill 编辑器会静默丢弃通过insertText传入的\n。正确的多行插入实现insertMultilineViaExecCommand先把文本按\n分段段间用insertParagraph插入换行const segments text.split(\n); for (let i 0; i segments.length; i) { if (i 0) { let paragraphInserted false; try { paragraphInserted document.execCommand(insertParagraph, false); } catch { /* 忽略 */ } if (!paragraphInserted) return false; // …写入当前段 } }完整实现见 chatInput/index.ts。它同时维护selection光标位置并派发input事件保证数据同步。2.3 导出 CSS扩展共享样式构建器为导出功能添加样式时要扩展共享样式构建器buildKatexExportStyles位于 katexExportStyles.ts而不是把 CSS 复制粘贴进第二个导出服务#847。该工具函数目前被 PDFPrintService.ts、DeepResearchPDFPrintService.ts、ImageExportService.ts 三个导出服务共同引用——这正是共享一份、多处生效的范例任何主题修复只需改一处。2.4 导出 DOM 遍历DOMContentExtractor的单一共享遍历DOMContentExtractorDOMContentExtractor.ts约 1500 行负责从 Gemini DOM 提取富文本内容。Gemini 会把section/div/response-element等层级任意嵌套任何第二条独立的遍历代码路径都会漏掉某层结构#847 为此花了 4 轮评审。一个非常典型的实现细节是queryOutsideThoughts当用户展开 Gemini 的 thinking 面板时DOM 顺序里会在真正的回复之前出现第二个message-content元素普通的querySelector会先匹配到思考面板导致导出抓错内容。该函数遍历所有候选后排除model-thoughts, .thoughts-container, .thoughts-content内的元素见 DOMContentExtractor.ts。所以导出相关改动必须在这一个文件中扩展并同步 DOMContentExtractor.test.ts。2.5 弹层/全局监听/浮层复制gv-pm-*集成模式凡是 body 级 popover、全局事件监听、遮罩层都要照抄已有的gv-pm-*集成即 prompt-manager 系列组件沉淀的完整模式close-outside点击外部关闭处理器、teardown卸载清理、主题覆盖。例如 coachmark/index.ts 就明确注释Visual style mirrors the existinggv-pm-*body-appended popoveranchored…platformTheme 则通过注入--gv-pm-brand变量 gv-platform-themedbody 类完成平台品牌色覆盖。三、新站点功能必须走插件系统这是本仓库最核心的架构红线为 ChatGPT、Claude、DeepSeek 等新站点添加支持一律通过插件系统禁止硬编码进扩展主体#865。3.1 两类插件的分界线文档给出了清晰的两分法插件类型适用场景存放位置加载方式声明式插件CSS JSON纯样式/声明式增强src/features/plugins/catalog/经BundledCatalogPluginSource打包进扩展随 catalog 快照加载原生函数插件真正需要 JS必须运行代码的功能src/features/plugins/builtin/默认禁用 可选 host 权限 动态 content-script 注册 start/stop生命周期源码证据createDefaultPluginSources()按 tier 顺序组装三层源——BuiltinPluginSource一方原生插件→BundledCatalogPluginSource声明式快照→HostCatalogSource每 host 的远程目录只读后台刷新缓存见 defaultSources.ts。合并规则计划 D6/D20保证 builtin id 永不被远程目录覆盖远程版本不兼容引擎时回退快照并标记blockedUpdate。目录实况catalog 下有chatgpt/、claude/、deepseek/三个站点的声明式插件如sites/deepseek/plugins/wrap-code/index.test.ts 会校验每个插件 id 等于其目录名、CSS 文件真实非空、每个matches模式都落在站点匹配范围内、marketplace.json与发现结果保持同步builtin 下则有formula-copy、inputVim、claudeUsage、claudeTimeline、chatgptExport、chatgptTemporaryHandoff等原生函数插件builtin/index.ts 的注释明确Like every plugin, builtin plugins shipDISABLED by default— the user turns them on in the popup。3.2 manifest 权限红线hard stop严禁在manifest*.json对应 manifest.json、manifest.dev.json中新增静态content_scripts或必需的host_permissions。原生函数插件需要通过插件自身的 manifest 声明可选host 权限 动态content-script 注册。未经 Issue 批准就提升 manifest 权限是 hard stop#865——这与 CI 无关是评审硬门槛。3.3 生命周期完整性与注册表只增不减任何由存储开关控制的功能都需要完整生命周期页面加载时能正确start运行时切换开关能正确destroy。隐藏 UI 不等于禁用功能#854。这与 sendBehavior 中activateListeners/deactivateListeners/cleanup的对称设计一脉相承注册的 listener 通过cleanupFns数组统一摘除MutationObserver同步 disconnect。平台注册表是增量式的绝不为了给自己的平台腾位置而删除或收窄其他平台的 adapter 或测试#865。例如站点适配器 gemini.ts、aistudio.ts 各自声明独立的主题选择器只能增加不能改动别人的。四、文档与多语言镜像20 个文档表面的维护边界文档相关的陷阱最容易让人措手不及因为文档在本仓库不是一个文件而是20 个表面主 README.md九个.github/README_*.md语言镜像README_ZH.md、README_AR/ES/FR/JA/KO/PT/RU/ZH_TW十个docs/locale/文档树如 docs/zh、docs/en 等每个含 40 篇 guidedocs/public/llms.txt 与 docs/public/llms-full.txt面向 LLM 的检索入口。关键的认知盲区CI 里那个绿灯的i18n检查只比对src/locales/*/messages.json的 key 一致性见 ci.yml 中i18njob 调用的node scripts/check-locale-keys.mjs它完全覆盖不到上述 20 个文档表面#874、#853。也就是说改了文档后 CI 可能全绿但某语言镜像已经过期——这部分只能靠人工纪律。另一条硬规则永远不要删除已发布的文档路由——仓库没有重定向层。退役页面必须在原 URL 上替换为本地化说明notice而不是删文件#874。五、主题与数据正确性5.1 主题解析顺序三档优先级 冲突标记测试主题解析必须遵循固定顺序#859优先.theme-host.light-theme/.theme-host.dark-themeGemini 专属的 theme-host 元素其次泛化的body/html/data-theme标记兜底prefers-color-scheme媒体查询。源码印证Gemini 适配器声明hostSelector: .theme-host、lightSelector: .theme-host.light-theme、darkSelector: .theme-host.dark-themegemini.tsAI Studio 则用body.light-theme/body.dark-themeaistudio.ts通用解析逻辑与单测见 contentStyleTheme.test.ts。测试要求写主题相关测试时必须用冲突标记同时存在.theme-host.dark-theme和body.dark-theme但取其一而不是单个信号——因为单个信号无法证明优先级实现正确#859。主题一致性导出产物要整体一致地应用主题——在强制白色文档里出现暗色图表不算支持暗色模式#847。5.2 对话 ID 的命名空间与账户作用域Gemini 的对话 ID 是带命名空间的gemini:conv:id。任何共享代码如果直接处理裸 ID会悄悄孤立掉已存在的星标/书签数据#865。同时构造任何路由时都必须保留/u/index/...的账户作用域。5.3 提示词/文件夹数据的合并入口Prompt 与文件夹数据存在多个合并入口src/utils/merge.ts含 merge.test.ts 配套测试以及页面级的 Drive 合并。规则是数据形状data-shape变更必须路由到同一个共享合并 helper合并冲突时绝不丢弃或重命名已有用户记录#854。这样保证星标、文件夹、历史记录在不同合并路径下结果一致。5.4 回归笔记Trap / Rule / Guard 三字段涉及非平凡功能或修复时需遵循 .github/docs/REGRESSION_NOTES.md 记录 Trap/Rule/Guard 条目对应子目录 .github/docs/regressions/ 下的主题文件。该格式由 validate-regression-notes.mjs 脚本强制校验每个主题文件必须恰好一个顶层标题#每条 entry 必须有且仅有一个- **Trap:**、- **Rule:**、- **Guard:**字段且内容非空Guard 中引用的测试路径必须是仓库相对路径禁止裸文件名且引用的.github|Voyager|docs|public|scripts|src路径必须真实存在见validateGuardPaths。文档同时强调只有当引入点影响解释时才添加 commit 细节引用的任何 PR/commit 必须是真实的不能用占位符#859。六、范围纪律评审比对的是 Issue 范围最后一条常被忽视却决定成败的纪律reviewer 拿你的行为与 Issue 确认过的范围做 diff并双向拦截偏差——超范围做多了、或范围内的没做完都会被拦#854。当某个边界情况诱惑你改变产品约束时例如强制名称唯一性先在 Issue 里提问不要自作主张在 PR 描述里明确列出 non-goals非目标提交策略上的分界一行可验证的修复可以直接提 PR#876新功能需要维护者在 Issue 或当前任务指令中显式批准方案——被分配 Issueassignment或 /claim只代表选中了负责人不等于批准#865。这与 SKILL.md preflight 第 2 步新功能需要维护者对方案的显式批准完全一致。结语把这份清单变成你的提交前检查表归纳起来Voyager 的贡献者文化可以用四句话概括先 grep 再写码——sendBehavior、chatInput、buildKatexExportStyles、DOMContentExtractor、gv-pm-*是五个必须复用的前辈新站点走插件系统——catalog 放声明式 CSSJSONbuiltin 放原生函数插件manifest 权限不升级文档是 20 个表面——i18n 绿灯不代表文档同步已发布路由不删除范围即契约——一行修复直通 PR功能先获批准non-goals 写进 PR 描述。每条陷阱都来自真实 PR 的学费#854、#859、#865、#867、#874、#876而 CI 一道都抓不到它们。把它当作写代码之前、而非 review 之后的读物你的 Voyager 首次贡献将大幅减少往返轮次。【免费下载链接】voyagerEnhancement suite for Gemini, AI Studio, Claude ChatGPT — plus a prompt manager for any websites, DeepSeek Harness included. / 面向 Gemini、AI Studio、Claude 与 ChatGPT 的增强套件其中的提示词管理器可用于任意网站如 DeepSeek Harness。项目地址: https://gitcode.com/gh_mirrors/ge/voyager创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考