mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-09 15:58:34 +00:00
9a7d4decff
* feat: 终端命令自动补全系统 实现类似 WindTerm/Fish 的终端命令自动补全功能,不依赖机器学习: - 历史命令持久化存储:按主机分组,频率+时间衰减排序,跨会话共享 - 前缀匹配引擎:支持精确前缀匹配和模糊匹配(首字符+连续字符+词边界加权) - Prompt 检测器:识别 bash/$、zsh/%、fish/> 等常见 prompt 模式,排除 vim/less 等程序 - Ghost Text 插件:xterm.js 自定义 addon,光标后灰色行内建议,→ 接受全部,Ctrl+→ 接受一词 - 弹出补全菜单:浮动列表 UI,↑↓ 导航,Tab/Enter 选中,Esc 关闭,来源标记(h/c/s/o/a) - @withfig/autocomplete 集成:600+ 命令规范的子命令、选项、参数补全 - 上下文感知:解析命令行 token,根据当前位置提供对应类型的补全 - 用户配置:启用/禁用、Ghost Text、弹出菜单、防抖延迟、最小字符数等 - 快速打字防误触:检测打字速度,快速输入时抑制建议 - 输入防抖 100ms,异步匹配不阻塞 UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 补全菜单混合展示历史命令和 spec 子命令 - 输入已知命令名(如 docker)时即使没有空格也预览子命令 - 历史命令条数从 8 降为 5,留空间给 spec 建议 - 修复 wordIndex === 0 时 spec 补全被跳过的问题 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 补全菜单在终端底部时向上展开 当光标在终端下方、空间不足时,弹出菜单向上展开(底边对齐光标行), 避免溢出终端区域。列表顺序和选中逻辑不变——最可能的选项始终在顶部, 用户初始向下选择。参考 Termius 的做法。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 补全菜单跟随终端主题 + Enter 直接执行命令 1. 补全菜单颜色从终端主题动态派生(color-mix),不再硬编码色值, 确保与任何主题视觉一致 2. 在弹出菜单中按 Enter 选择命令时,直接插入并发送 \r 执行, 无需用户再按一次回车 3. Tab/鼠标点击仍然只插入不执行(保留选择后编辑的能力) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 修复 PR review 发现的全部 20 个问题 功能修复: - #1 修复 selectAndExecute 导致命令双重录入历史:用 suppressNextEnterRecordRef 标志位让 handleInput 的 Enter 分支跳过已经录入过的命令 - #2 修复 Prompt 末尾 $ 误判:重写 findPromptBoundary 为从左到右逐字符扫描, 排除 $HOME/$PATH 等变量引用(检查 $ 前是否有空格、是否在 token 内部) - #6 快速打字检测实际生效:快速打字时 debounce 延迟翻倍(200ms),等用户停顿 - #8 resolveSpecContext 处理带参数的 option(如 --name value): 识别 option 的 args 字段,自动跳过下一个 token - #9 Ghost text 位置随终端滚动/渲染更新:注册 term.onRender 回调 - #13 Escape 键不再拦截 vi-mode:仅在 popup 可见时消费 Escape, ghost text 显示时不拦截(ghost text 是被动的,不应阻止 shell 交互) - #14 所有 setState 统一使用 EMPTY_STATE 常量,不再遗漏 expandUpward 字段 架构修复: - #3 消除 CustomEvent 通信:改为 onAcceptText 回调注入, Terminal.tsx 直接传 writeToSession 回调给 hook, 删除 createXTermRuntime 中约 20 行 listener 代码和 cleanupAutocompleteListener 字段 - #7 xterm 私有 API 访问集中到 xtermUtils.ts:getCellDimensions 统一入口, 带缓存机制,仅在首次访问或 terminal 切换时触发 DOM 测量 - #16 删除 getCommandNameSuggestions 中多余的动态自导入 await import("./figSpecLoader") 性能修复: - #5 合并 ghost text 和 popup 的查询路径:删除独立的 getInlineSuggestion, fetchSuggestions 只调一次 getCompletions,ghost text 取 completions[0] - #10 preloadCommonSpecs 分批加载(每批 8 个,requestIdleCallback 间隔), 延迟 200ms 启动,且检查 enabled 才执行 - #11 scoreEntry 改为 scoreEntryAt(entry, now),now 在查询开始时缓存一次 - #15 scrollIntoView 从 smooth 改为 instant,消除快速导航动画排队 - #19 loadSpec 添加 in-flight 去重(inFlightLoads Map),同一 spec 并发加载只触发一次 import - #20 存储满时淘汰改为按 score 排序后保留前半,而非按插入顺序 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 修复二审发现的全部 10 个问题 功能修复: - #1(高) insertSuggestion 改用实时 detectPrompt 而非过时的 lastPromptRef, 修复用户继续打字后 Tab 选择建议导致字符重复插入的 bug - #2(中) handleInput Enter 录入历史优先用实时 detectPrompt, 修复快速打字场景下 recordCommand 记录不完整命令 - #9 suppressNextEnterRecordRef 添加 100ms 安全超时清除,防止 flag 残留 - #10 getNextWord 从 index 1 开始搜索分隔符,修复 ghost text 以 / 开头时 一次接受全部而非逐段的问题 性能修复: - #3(中) GhostTextAddon 注册 term.onResize 调用 invalidateCellDimensionCache, 确保 resize/字体变化后 cell 尺寸缓存正确失效 - #4 updatePosition 缓存 lastLeft/lastTop,位置无变化时跳过 DOM 写入; 字体属性移到 show() 中只设置一次,不再每帧写 6 个 style - #5 统一 clearState() 函数替代所有 setState({...EMPTY_STATE}), 带 popupVisible 守卫避免无效 re-render - #6 hasSpec 中 specs.includes() 改为 Set.has(),O(1) 查找 架构修复: - #7 Terminal.tsx 中 autocompleteAcceptTextRef 去掉多余的 useCallback 包装 - #8 删除 AutocompletePopup 的 onClose 死代码 prop Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: popup 默认不选中任何项,用户按 ↑/↓ 后才选中 修复输入 ls 等简单命令时回车误执行联想结果的问题: - selectedIndex 初始为 -1(无选中),Enter 直接执行用户输入的命令 - 用户按 ↑/↓ 导航后 selectedIndex >= 0,此时 Enter 才执行选中的建议 - Tab 仍然可以直接接受第一条建议(主动接受行为) - Enter 无选中时关闭 popup 并让按键透传到终端 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fig spec 改为从静态资源 fetch 加载,修复生产构建中补全不工作 根因:@vite-ignore 动态 import 在 Electron 生产构建中无法解析 node_modules 路径(app:// 协议只能访问 dist/ 目录)。 修复方案(与 Monaco 编辑器相同的模式): - 新增 scripts/copy-fig-specs.cjs,prebuild 时将全部 739 个 fig spec 从 node_modules/@withfig/autocomplete/build/ 复制到 public/fig-specs/ - Vite 自动将 public/ 内容复制到 dist/,app:// 协议可以正常访问 - figSpecLoader.ts 改用 fetch + Blob URL + dynamic import 加载 spec, 同时保留 @vite-ignore import 作为 fallback(兼容 dev 模式) - public/fig-specs 加入 .gitignore(构建时生成,不进版本控制) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: ESLint 忽略 public/fig-specs 目录(第三方生成代码) 与 public/monaco 相同的处理方式——这些是从 node_modules 复制的 第三方构建产物,不应被项目 ESLint 规则检查。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 输入完整子命令名时展示其选项(如 git commit 显示 --message 等) 当 currentToken 完全匹配一个子命令时(如 "git commit" 中的 "commit"), 导航进入该子命令并展示其 options 和 sub-subcommands 作为预览。 之前的逻辑因为 name !== currentToken 过滤掉了完全匹配的项, 且 resolveSpecContext 的 consumedTokens 不包含当前 token, 导致停留在父级而看不到子级的选项。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 修复 fig spec index.js 解析失败导致补全不工作 根因:index.js 格式为 var e=[...],diffVersionedCompletions=[...]; 正则 /var\s+\w+\s*=\s*(\[[\s\S]*?\]);/ 要求 ] 后紧跟 ;, 但第一个数组后面是 , 不是 ;,导致非贪婪匹配跳到第二个 ];, 捕获了两个数组拼在一起,JSON.parse 失败,spec 列表为空。 修复:改用 indexOf 找第一个 [ 和对应的 ],直接截取子串解析。 fig spec 的 index 是简单的字符串平坦数组,无嵌套括号。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fig spec 改用 URL 直接 dynamic import,移除 fetch+Blob 方案 fetch + Blob URL + import() 方案可能被 Electron CSP 策略阻止。 改为直接用完整 URL 做 dynamic import: - dev: import("http://localhost:5173/fig-specs/git.js") - prod: import("app://./fig-specs/git.js") 两种环境下动态 import 都能正常解析模块,无需 fetch 中间步骤。 同时简化 getAvailableSpecs 也用同样方式,移除 fetch+正则解析。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fig spec 改为通过 Electron IPC 加载,彻底解决 dev/prod 加载问题 之前的方案(静态文件 + dynamic import / fetch + Blob URL)都因为 Vite dev server 对 .js 文件的模块转换和 Electron CSP 限制而失败。 新方案:通过 main process 的 Node.js require() 加载 fig spec, 通过 IPC 传给 renderer: - main.cjs: 添加 netcatty:figspec:list 和 netcatty:figspec:load handler - preload.cjs: 暴露 listFigSpecs() 和 loadFigSpec() API - figSpecLoader.ts: 通过 window.netcatty bridge 调用 IPC 优势: - main process 直接访问 node_modules,dev 和 production 都可靠 - 无需复制文件到 public/、无需 @vite-ignore hack - spec 数据通过 IPC 序列化传输,无 CSP 限制 - 删除了 scripts/copy-fig-specs.cjs 和 public/fig-specs/ 相关代码 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: main process fig spec 加载改用 import() 替代 require() @withfig/autocomplete 是 ESM 包("type": "module"), CommonJS 的 require() 无法加载 ESM 模块会抛 ERR_REQUIRE_ESM。 改用 dynamic import() 在 async handler 中加载。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: fig spec 加载用 pathToFileURL 绕过 package.json exports 限制 @withfig/autocomplete 的 exports 字段只允许 import "." 和 "./dynamic", Node.js 严格遵守 exports map 拒绝解析 build/git.js 等子路径。 改为手动拼接文件绝对路径 + pathToFileURL 转换为 file:// URL 后 import, 完全绕过 Node.js 的 package exports 限制。 同时修复 promptDetector 不再 trim 尾部空格(用 cursorX 确定实际输入长度), 确保 "git commit " 的尾部空格被保留,触发空 token 显示选项列表。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: 补全菜单添加详情面板 + 清理调试日志 - 选中或悬停补全项时,右侧显示详情面板(类似 VS Code IntelliSense) - 显示完整命令名、来源类型标签(Option/Subcommand/History 等) - 显示完整的描述文本(不再截断) - source 标记移到左侧,与描述分离,更易读 - 悬停和键盘选中都能触发详情面板 - 向上展开时详情面板也正确对齐 - 清理所有临时调试 console.log Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: 清理全部调试日志 移除 autocomplete 模块中所有临时 console.log 调试语句, 仅保留 figSpecLoader 中的 console.warn 用于真实错误报告。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: 三审问题修复 — 移除多余 prop、过滤子路径 spec、防路径遍历 1. 移除 Terminal.tsx 传给 AutocompletePopup 的多余 onClose prop 2. getCommandNameSuggestions 过滤含 / 的 spec 名(aws/s3 等不是直接命令) 3. figspec:load IPC handler 添加 .. 路径遍历检查 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 5 个问题全部修复 1. [P1] fuzzy 匹配建议不以 userInput 开头时,用 Ctrl+U 清行再写入完整命令, 避免 substring 截断产生损坏的命令行 2. [P2] Ghost addon 初始化改用 polling 等待 termRef,解决首次挂载时 termRef.current 为 null 导致 ghost text 永远不激活的问题 3. [P2] popup overlay 改为 pointer-events-none 透传,仅 popup 自身设 pointer-events: auto,不再阻止终端区域的鼠标交互 4. [P2] getCompletions 异步返回后重新 detectPrompt 校验输入是否已变, 丢弃过时的补全结果避免覆盖新状态 5. [P2] prompt 检测支持折行:当 line.isWrapped 时向上回溯查找 prompt 行, 拼接多行内容作为完整 userInput Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第二轮 3 个问题修复 1. [P2] broadcast 模式下 autocomplete 插入也触发广播 — onAcceptText 回调中调用 onBroadcastInputRef 通知其他 session 2. [P2] 支持无尾随空格的 prompt(如 cmd.exe C:\path>)— prompt 字符后允许直接是行尾,boundary 为 i+1 3. [P2] 光标移动 escape 序列(Left/Home/End)清除过时建议 — 不再静默忽略,改为 clearState() 清除 popup 和 ghost text Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第三轮 3 个问题修复 1. [P2] commandBufferRef 处理 Ctrl+U 清行 — fuzzy 匹配发送 \x15 时 重置 buffer,避免 onCommandExecuted 记录错误的拼接命令 2. [P2] fetchVersionRef 递增计数器废弃过时异步结果 — clearState/Escape 关闭 popup 时 bump version,getCompletions 返回后检查 version 匹配, 防止已关闭的 popup 被旧请求重新打开 3. [P2] prompt scanLimit 从 80 提高到 200 — 支持包含 git branch、 kube context、长路径的 prompt,超过 80 列不再失效 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第四轮 3 个问题修复 1. [P1] 拒绝绝对路径 — figspec:load IPC handler 检查 commandName 不以 / 或 \ 开头,防止 path.join 丢弃前缀导致任意 JS 执行 2. [P1] cmd.exe prompt > 后不要求空格 — 对 > ❯ ➜ › 等 prompt 字符 不强制要求后跟空格,支持 C:\src>dir 格式 3. [P2] serial line mode 下 autocomplete 走 serialLineBufferRef — 在串口 lineMode 时不直接 writeToSession,而是缓冲到 line buffer 并处理 local echo,与正常按键输入行为一致 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第五轮 — translateToString(false) 保留尾部空格 translateToString(true) 会 trim 行尾空格,导致 cursorX 截取的 userInput 与实际行内容不一致。改为 translateToString(false) 保留 原始空格,确保 "git commit " 的尾部空格被正确保留用于触发选项补全。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: 设置页添加自动补全开关(启用/Ghost Text/弹出菜单) 在终端设置页末尾新增「自动补全」区域,包含三个开关: - 启用自动补全:总开关 - 行内建议(Ghost Text):光标后灰色建议文本 - 弹出菜单:浮动补全列表 子开关在总开关关闭时 disabled。中英文 i18n 翻译齐全。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第六轮 3 个问题修复 1. [P1] 光标不在行尾时禁止补全 — 检测 cursorX 后方是否有字符, 有则 clearState 不显示建议,避免 mid-line 插入导致文本重复 2. [P2] Enter 录入历史改为先尝试实时 detectPrompt,失败则 fallback 到 lastPromptRef 缓存,应对高延迟 SSH 下 buffer 未回显的情况 3. [P2] fuzzy 替换在 Windows host 上用退格清行而非 Ctrl+U — cmd.exe/PowerShell 不支持 Ctrl+U,改为发送 \b 退格序列 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第七轮 — commandBuffer 退格处理 + 接受后历史记录 1. [P2] commandBufferRef 处理 \b 退格 — Windows fuzzy 替换用退格 清行时正确移除 buffer 末尾字符,避免记录拼接错误的命令 2. [P3] lastAcceptedCommandRef 追踪接受的补全文本 — Tab/→ 接受后 立即 Enter 时用追踪值录入历史,不依赖可能未回显的 buffer Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第八轮 — 历史记录准确性 + 设置同步 1. [P2] 用户继续编辑后清除 lastAcceptedCommandRef — Tab 接受 "git status" 后追加 " --short" 再 Enter 时记录完整编辑后的命令 2. [P2] Ghost text →/Tab 接受路径也设置 lastAcceptedCommandRef — 确保所有接受路径在快速 Enter 时都能准确记录命令 3. [P2] autocomplete 设置加入 SYNCABLE_TERMINAL_KEYS — 跨设备同步时保留自动补全偏好 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第九轮 — REPL 误识别 + 本地终端 OS 检测 1. [P1] local terminal 的 hostOs 改用 navigator.platform 检测实际 OS, 避免 Windows 上 fallback 到 "linux" 导致 Ctrl+U 清行失败 2. [P2] 回退 > 无条件接受改动,恢复要求 > 后跟空格或行尾 — 避免 python >>>、mysql>、sqlite> 等 REPL 被误识别为 shell prompt 3. 新增 REPL NON_PROMPT_PATTERNS:>>>(python)和 word>(mysql/redis) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第十轮 4 个问题修复 1. [P1] cmd.exe prompt C:\path> — 对 > 特判:前面是 \ 或 / 时允许无空格, 避免误匹配 REPL(python>>>、mysql>)的同时支持 Windows cmd prompt 2. [P2] serial lineMode autocomplete 不再 early return — fall through 到 共享的 commandBuffer/broadcast 更新逻辑 3. [P2] serial 字符模式 + localEcho 时 autocomplete 插入文本也本地回显 4. [P3] 运行时关闭 autocomplete 时调用 clearState() 清除已显示的 popup Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第十一轮 — option args、PS2 误识别、bridge 缓存 1. [P2] resolveSpecContext 返回 option 的 args — 当光标在 option 参数 位置时(如 git archive --format |),返回该 option 的 args 而非 subcommand 的 args,使 tar/zip 等枚举值能正确补全 2. [P2] 排除 bare > 作为 shell prompt — bash PS2 续行提示 > 加入 NON_PROMPT_PATTERNS,避免在多行命令续行和 REPL 中误触发补全 3. [P3] bridge 不存在时不缓存 null — preload 时 bridge 可能未就绪, 缓存 null 会永久禁用该命令的 spec 补全 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第十二轮 — prompt 检测取最后一个分隔符 Starship/Powerlevel10k 等 prompt 包含多个 prompt 字符 (如 ➜ repo git:(main) $),之前在第一个 ➜ 就停了, 把后续 prompt 文本当成用户输入。 改为收集所有候选 prompt 边界,返回最后一个。确保 "➜ repo git:(main) $ ls" 中 userInput 正确为 "ls"。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: Codex review 第十三轮 — prompt 搜索范围限制 + cmd.exe 路径 1. [P2] prompt 扫描限制在行前 60% — 避免 "echo foo > bar" 中的 重定向符 > 被当作 prompt 结束(prompt 不会出现在行尾部分) 2. [P3] cmd.exe 路径检测扩展 — 除了 \ / 前缀,也检测行首是否有 驱动器号 (X:) 模式,支持 C:\Users\me> 等标准 Windows prompt P1 (高延迟 SSH buffer 滞后) 和 P2 (Enter 时 stale prompt) 属于 prompt 检测方案的固有局限,根本解决需要 OSC 133 Shell Integration, 不在本 PR 范围内。已有 lastAcceptedCommandRef fallback 缓解。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>