小类随手记

pi 插件:随时查看工作区文件变更(git-changes)

基于 pi 的 overlay + shortcut + 状态栏扩展机制,实现随时按 ctrl+shift+g 浮动查看 git 变更文件列表,支持 Enter 填入路径、e 键 nvim 编辑、y 键复制到剪贴板。

背景

pi 写代码时,AI 经常批量修改文件。时间久了容易忘了改过哪些文件、是否有遗漏。我希望能随时一键查看工作区文件的变更状态——像 git status 但不用切出去、不用手指离开键盘。

之前写过 pi 的 CustomEditor 扩展把光标改成竖线。这次更进一步:利用 pi 的 overlay(浮动框) + shortcut(快捷键) + status bar(状态栏) 三项能力,做了一个完整的 Git 变更查看器。

直接体验

你可以直接运行下面的命令安装本文的插件的最新版本,下文的描述可能会有过期

1
pi install npm:@joyanhui/pi-ext-git-changes

需求

  1. 随时可看——一键唤起浮动面板,列出所有变更文件
  2. 分类清晰——区分 Staged / Unstaged / Untracked / Conflict
  3. 快速操作——
    • Enter:文件路径填入输入框(交给 AI 处理)
    • e:用 nvim 打开编辑/查看
    • y:复制完整文件路径到系统剪贴板
  4. 无感提示——状态栏常驻显示变更计数,提醒有未提交的改动

实现方法

整体架构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
┌─ pi Extension API ──────────────────────────────────┐
                                                      
  pi.registerCommand("/changes")  ──┐                 
  pi.registerShortcut("ctrl+shift+g") ─┤              
                                                     
               ┌─────────────────────▼──────────────┐ 
                    ctx.ui.custom({ overlay })       
                    GitChangesOverlay                
                                                     
                  render()  带边框的浮动面板        
                  handleInput()  ↑↓ e y Enter Esc  
                  done()  返回 action + file       
               └─────────────────────────────────────┘ 
                                                      
  pi.on("tool_result")    refreshStatus()            
  ctx.ui.setStatus()      footer "git:📝3"           
└──────────────────────────────────────────────────────┘

关键 API

API用途
pi.registerShortcut("ctrl+shift+g", ...)全局快捷键,任何位置一键唤起
ctx.ui.custom(component, { overlay: true })渲染浮动框,叠加在当前 UI 之上
ctx.ui.setStatus(key, text)footer 状态栏常驻显示
ctx.ui.setEditorText(text)将文本放入输入框
pi.exec("git", ["status", "--porcelain"])获取 git 变更(异步,复用 pi 进程管理)
matchesKey(data, Key.escape)TUI 键盘事件匹配(兼容各种终端协议)
truncateToWidth() / visibleWidth()ANSI-aware 字符串截断/宽度计算

浮动框渲染

浮动框使用 Unicode box-drawing 字符绘制边框(╭╮╰╯│─),内部按分类分组渲染:

  • 文件行padToWidth() 补齐到统一宽度,保证右边框对齐
  • 选中行高亮显示( 前缀 + accent 色)
  • 状态标签按类别着色:绿 [+]=staged、黄 [*]=modified、红 [-]=deleted、灰 [?]=untracked
  • 底部提示栏显示全部快捷键

剪贴板:OSC 52

y 键复制文件路径到系统剪贴板不依赖 xclip / wl-copy 等外部工具,而是通过 OSC 52(Operating System Command 52)终端转义序列:

1
2
3
4
function osc52Copy(text: string): void {
  const b64 = Buffer.from(text).toString("base64");
  process.stdout.write(`\x1b]52;c;${b64}\x1b\\`);
}

现代终端(Kitty、Ghostty、WezTerm、Alacritty、Foot、iTerm2)均支持,零依赖、跨平台。

nvim 编辑:spawnSync

e 键通过 Node.js spawnSyncstdio: "inherit" 方式启动 nvim,让 nvim 直接接管终端:

1
2
import { spawnSync } from "node:child_process";
spawnSync("nvim", [file], { stdio: "inherit" });

退出 nvim 后控制权自动回到 pi,TUI 会重绘恢复。

完整代码

https://github.com/joyanhui/pi-extension/tree/main/pi-ext-git-changes

效果

浮动框

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
╭───────────────────────────────────────────────╮
│  📦 Staged (2)                                │
│     [+] modules/ai/cfg_pi/extensions/git-c... │
│  ▶ [+] modules/ai/cfg_pi/settings.json        │ ← 当前选中
│                                               │
│  📝 Unstaged (1)                              │
│     [*] flake.nix                             │
│                                               │
│  ❓ Untracked (1)                              │
│     [?] new-script.sh                         │
│                                               │
│  ↑↓ move  Enter→editor  e=edit(nvim)  y=yank  Esc=close
╰───────────────────────────────────────────────╯

状态栏

1
2
C-S-g:📦2📝1❓1          ← 有变更时(accent + dim 色)
C-S-g:✓                  ← 干净时(accent + dim 色)

操作速查

按键效果
ctrl+shift+g全局唤起浮动框
Enter文件路径填入输入框,交给 AI
envim 打开文件编辑/查看
y复制完整路径到系统剪贴板(OSC 52)
Esc关闭浮动框

总结

这次扩展利用了 pi 的三个扩展能力:

  1. Overlayctx.ui.custom + overlay: true)——浮动框不破坏主 UI 状态,查看完即关
  2. Shortcutpi.registerShortcut)——ctrl+shift+g 与全局键位不冲突(比 ctrl+shift+u preset、ctrl+shift+p plan mode 等默认未占用)
  3. Status barctx.ui.setStatus)——变更计数无感常驻,配合 tool_result 事件自动刷新

结合 bar-cursor 扩展 和 NixOS home-manager 的 activation 脚本软链接管理,pi 的整个配置都是声明式、可追溯的。TypeScript 扩展放到 ~/.pi/agent/extensions/ 目录就自动加载,/reload 热更新,开发体验很流畅。


另见:

comments powered by Disqus
Theme Stack