## 问题描述

目前 pi 启动的 resume 感觉操作很别扭，方向键控制上下移动，然后 ctrl + d 删， 能否帮我做一个插件 lazygit 那种（hjkl, d 是删除，r 是 rename）。操作 pi resume 的插件啊？

## 项目描述

一个类似 lazygit 的插件，用来管理 pi 的历史对话，主要命令有三个 /resume（恢复会话，从历史会话中选择并恢复）  /tree  （会话树导航，跳转到对话树的任意节点继续对话，某个对话详情里面的某一个界面） /new （新建一个对话），然后还有围绕着对话进行操作的命令：/name（命名会话，为当前会话设置一个易读的名称） /session（会话信息，显示会话文件路径、ID、消息数、token 用量和费用） /fork（分叉会话，从之前的用户消息分叉出一个新会话文件）/clone（克隆会话复制当前活动分支到新会话文件）/copy （复制 AI 最后一条回复到剪贴板）/export（导出会话导出为 HTML 或 JSONL 文件）/import（导入会话从 JSONL 文件导入并恢复会话）
/share（分享会话：选择 Auto / GitHub Gist / Radius 上传，Auto 优先 Gist、其次 Radius，生成可分享的链接）， 根据这些命令能够拿到信息，我觉得产品可以设计成为这样：

- 左边第一个面板是显示历史对话, 也就是 /resume 里面的那些数据
- 左边第二个面板（在第一个面板底部）是显示对话节点 也就是 /tree 里面的那些数据（根据上面选择的不同的 /resume 来确自动变化这里面的内容）
- 右边第一个面板是显示对话的详细内容（需要区分是AI的消息，还是我的消息），这个面板占据全部高度
- 搜索，放在底部不需要一个输入框。当用户输入 / 时候，底部出现 搜索：两个字样，随后用户输入内容回车进行搜索（只搜索当前focus面板中的内容），这个完全参考 lazygit 就行

聚焦到得不同的面板，不同的快捷键有不同的含义。

```txt
关系其实是：
    resume1
        - tree1
            - node1
            - node2
```

也就是随着第一个面板，用户选择聚焦不同的resume,下面的第二个面板的tree也要不停变化显示不同的可还原的节点

暂定的指定（后续可能会适配）：
~~/compact~~（2026-09-24 完成：SESSIONS 面板快捷键 `c`，见下面"左侧列表第一个面板"的 c），/settings（全局快捷键 `,`：常规 / 布局 / 快捷键编辑已做，快捷键按作用域分页，动作行 Enter 弹管理菜单改键，改键是**按键录入**——直接按下想绑的键、Enter 添加、Backspace 退回、Esc 取消（`chordFromKeypress` 反向解析，多键序列也能录），保存后实时生效；改完做**按键冲突检测**——同一个作用域里先匹配上的那条拿走按键，所以按不出来的新键（占用者排得更前、或它的键更短）会弹「抢过来 / 取消这次改动」，抢得走的（同键排在我们后面、或我们的键是别人长序列的前缀）照常写入并在落盘后提示，见 docs/keybindings.md；**Esc / Backspace 是保留键，任何作用域都绑不了**（前者面板自己占用、后者是输入与录入的编辑键，写进配置也会被丢弃并提示），单独的修饰键同理；主题选择 2026-10-13 完成：THEME 分类只有一行（右边是当前主题），Enter 弹出 pi 的主题菜单（内置 dark/light + 用户主题）；选中即切（`ctx.ui.setTheme`，由 pi 写它自己的 settings.json），失败留在面板上可重试——设置页不分草稿 / 保存，选中就落盘，面板颜色一律取自 pi 的活动主题，面板自己不保存 theme，细节见 docs/theme.md），~~/changelog~~（2026-09-24 完成：全局快捷键 `@`，见下面"通用的快捷键"）

确定不会用到的指令：
/login，/logout，/model，/thinking，/scoped-models

## 使用流程

用户安装插件之后，使用 /lazy-panel 命令可以打开一个 tui 对话框，整体布局是参考项目描述

- ~~打开时 SESSIONS 光标自动落到 pi 当前打开的会话上，TREE / CONTENT 随之显示当前对话；新对话（还没有会话文件或还没列出来）则选中第一行~~（2026-09-22 完成；C / A 切换范围后仍回到顶部）

## 快捷键操作

### 通用的快捷键

~~h/l： 参考 lazygit，h 切换到上一个面板，l 切换到下一个面板（tab 也保留为“下一个”），聚焦在不同的面板上，快捷键会有不同的表现~~
~~1/2/3： 每个面板标题前有数字标记（[1] SESSIONS、[2] TREE、[3] CONTENT），按下对应数字直接跳到该面板~~
~~C：切换到 Current Folder 模式（只做单向切换，已经是 Current 时按下无效果）~~
~~A：切换到 All 模式（只做单向切换，已经是 All 时按下无效果）~~

- ~~SESSIONS 面板标题右侧始终显示 `1/15 · Current · recent`（位置 · 模式 · 排序），C/A 两种模式都要有；宽度不够时按 `1/15 · Current` → `1/15 · Cur` → `1/15` 逐级缩短，不能整段消失~~
- ~~底部 footer 只提示能切换过去的那个：当前是 Current 就显示 `A All`，当前是 All 就显示 `C Current`~~
~~?: 快捷键提示菜单, 不同的面板提示不同的快捷键。~~
~~exit/q： 退出插件~~
~~@: 查看 pi 的 changelog，和 /changelog 命令一样~~（2026-09-24 完成：居中大弹窗用 pi-tui `Markdown` 渲染 pi 安装目录的 `CHANGELOG.md`，j / k / 方向键滚动一行，ctrl+d / ctrl+u 半页，g / G 顶部 / 底部，Esc / q / @ 关闭；和 pi 的差异：最新版本在最上面（pi 倒序、最新的在底部））
~~/: 底部出现 搜索：两个字样，随后用户输入内容回车进行搜索，只搜索当前fouce的,文本需要高亮，使用 n/N 可以上一个和下一个，具体参考lazygit的形式~~（2026-09-23 完成：三个面板各记各的关键字，输入时实时跳到原位置之后的第一个匹配，Enter 保留、Esc 取消并回到原位置；n / N 回绕；标题右侧 `2/7 matches`，footer 显示关键字和 n / N / Esc 提示；命中文字高亮、当前匹配反色；列表不过滤只跳转）

### 鼠标

核心仍是键盘，鼠标只做轻量适配（2026-09-24 完成，`ui/mouse.ts` + `ui/mouse-input.ts` + `LazyPanel.handleMouse`）：

- ~~滚轮 / 三指上下滚动指针所在的列表（只滚动视图、不移动选中项；CONTENT 按行滚），不改变焦点~~
- ~~单击列表项：焦点切到该面板并把光标移到该项；单击面板的空白 / 边框只切焦点~~
- ~~双击 SESSIONS：进入该会话（等价 Enter / resume）；双击 TREE：折叠 / 展开分支（等价 z）~~

带列表 / 多区域的弹窗也支持鼠标（2026-10-13 完成，`frame.ts` 的 `centeredRect` / `boxCell` + 各 widget 的命中查询 + `Overlay.handleMouse`）：

- ~~单击列表项 = 选中（光标 / 区域移过去），双击 = 该行的 Enter：确认框选中项、帮助执行命令、树对话框恢复节点、设置行进入编辑、设置按钮运行、上下文分类打开预览~~
- ~~滚轮 = 移动选中项（菜单式弹窗让选中项跟着滚轮走，和三个面板"只滚视图、不动选中"是有意的差异：弹窗没有独立滚动状态）；纯滚动文本框（changelog、上下文里的 prompt 预览）滚轮只滚内容~~
- ~~点击弹窗外面 / 边框：什么都不做，绝不关闭弹窗~~
- ~~只有输入框的弹窗（打标签 / 改名 / 新建 / 压缩 / 导出路径 / 导入 / 改键位录入）和会话信息弹窗没有可选项，保持不响应鼠标~~
- 画和命中用同一份几何：弹窗在 `overlay()` 里记下这次画出来的矩形，点击按它换算（和面板的 `panelGeometry` 是同一条纪律）；弹窗叠在**面板正文**上（footer 最后才 append），所以不能用终端高度重算居中

两种 TUI 模式都支持：fullscreen 由 pi 把事件派给面板；regular（默认）模式插件自己打开 SGR 鼠标上报并解析（需要终端支持 SGR，面板打开期间接管终端的选中 / 滚动，关闭后恢复）。`press` / `move` / `drag` 在任何情况下都交回终端做文本选择。

### 左侧列表第一个面板（resume）

~~/: 底部出现 搜索：两个字样，随后用户输入内容回车进行搜索，只搜索当前fouce的,文本需要高亮，使用 n/N 可以上一个和下一个，具体参考lazygit的形式~~（2026-09-23 完成：普通词只按 名称 / 首条消息预览 匹配，模型和路径只通过 `model:` / `path:` 匹配（`path:` 也认 `~/…` 写法），`name:` 只看会话名，`after:` / `before:` 看最后更新时间，`tag:` 忽略；搜索生效期间 n / N 是上下一个匹配，n 不再是新建会话）
~~j/k/方向上下键盘：上下移动~~
~~d: 删除对话~~（2026-09-23 完成：居中 Yes / No 确认框，默认停在 No，y / n 直接选；照 pi 内置 /resume 先试 `trash` 再 `unlink`，当前打开的会话拒绝删除；删完重新拉列表、光标夹回范围内）
~~r: 重命名对话(类似于进入 /resume 之后使用 ctrl + r) 修改过名字的 sessiion 面板顶部显示名字，并且看通过高亮还是什么手段，告诉用户这个对话修改过名称~~（2026-09-23 完成：居中输入框预填当前名字，空值清除；当前会话走 `pi.setSessionName`，其他会话追加 session_info 条目；改完重新拉列表、光标留在同一会话。有名字的行标题显示名字而不是首条消息，这就是"改过名"的标记；额外高亮暂不做）
~~o: Fork the session and open the fork~~（2026-09-23 完成：先弹一个 user 消息选择器让用户挑分叉点（默认停在最后一条，和 pi 内置 /fork 一致，消息多了按窗口滚动），选中后再弹 Yes / No 确认框（Esc / No 退回选择器）；确认后 fork 在那条消息之前，pi 自己把那条 prompt 填回新会话的编辑器。光标会话不是当前会话时先切过去再 fork；没有 user 消息时 footer 报 `No messages to fork from`）
~~y: 克隆会话 复制当前活动分支到新会话文件~~（2026-09-23 完成：先弹 Yes / No 确认框（虽然无损但会把用户切走），确认后 `fork(leafId, { position: "at" })` 复制活动分支到新文件并打开）
~~J/K：上下滚动右侧的聊天内容~~
~~gg: 回到列表顶部~~
~~G: 跳转到列表底部~~
~~space: 多选聊天，主要是为了批量删除，fork 和 rname 不可用，如果选中多个按下了 f/r 提示用户：不能对多个对像进行操作~~（2026-09-24 完成：多选不画图标、只把选中行标题着 accent 色，标题显示 `3 selected`；d 在有选中时弹一次 `Delete N sessions?` 确认，跳过 pi 当前打开的会话，失败的留在列表和选中里、第一条错误进 footer；选中多个时 r / o / y / e / S 提示 `cannot act on multiple sessions`；Esc 先清空选中、再按才退出）
~~p: 置顶 / 取消置顶会话，置顶的会话固定在列表最上、不受排序影响（搜索和过滤仍然作用于它们，tree 不做置顶）~~（2026-09-27 完成：p 切换光标会话（或所选会话，整组）的置顶态，置顶按操作顺序固定——最后置顶的在最上（先置顶 2 再置顶 3 → 3, 2, 1），置顶会话归到列表顶部的 `── PINNED ──` 分组下（其余在 `── OTHERS ──` 下），逐行不画字形；threaded 模式下置顶会话拉平不缩进；置顶与归档统一存在 `~/.pi/agent/lazy-panel-state.json`；首次成功操作时迁移旧 `lazy-panel-pins.json`，旧文件保留。本面板删除成功后同步清理，不把暂时不可访问的外部文件误当成已删除）
~~E: 导出对话，点击快捷键之后，显示一个输入框输入输出的目录~~（2026-09-24 完成，键位是小写 `e`：先选 HTML / JSONL，再弹输入框，预填 pi 的默认路径（完整路径），输入目录就在目录里用默认文件名，已有文件先确认覆盖；任意会话都能导出，面板不关）
~~I: 导入对话，点击快捷键之后，显示一个输入框输入导入的目录~~（2026-09-24 完成：输入的是 JSONL 文件路径而不是目录（pi 的 /import 一次导入一个文件）；确认后复制进当前会话目录并切过去，面板关闭）
~~S: 分享会话 上传为私有 GitHub Gist，生成可分享的 HTML 链接，这里用大写的S, 避免和排序冲突~~（2026-09-24 完成：先确认，再用 `gh` 上传 secret gist，pi.dev 的查看链接自动复制到剪贴板并显示在 footer；2026-10-20 改为 Auto / Gist / Radius 三选菜单，显式选 Radius 未登录时在面板内接管 `/login radius`，登录成功继续分享；浏览器登录的链接复制到剪贴板、等待期间 Esc 取消登录；已登录时选 Radius 先弹“分享到 Radius / 退出登录”，退出登录确认后清除本地凭据（相当于 pi 的 `/logout radius`），网页端会话不受影响）
~~Y: 复制回复 复制 AI 最后一条回复到剪贴板~~（2026-09-23 完成：照搬 pi 的 `getLastAssistantText`——活动分支上最后一条 assistant 回复，只取 text 片段，跳过中止且没有内容的回复；面板不关，footer 报 `copied last reply` / `no assistant reply to copy`）
~~s: 对列表排序：（Threaded， Recent， Fuzzy）~~（2026-09-23 完成：循环 recent → created → title → threaded，标题右侧显示当前排序，光标跟着同一会话走。pi 的 fuzzy 只在有搜索关键字时有意义，换成了 created / title 两个没有 fork 会话也能看出效果的维度；title 按面板显示的标题排序，没名字的用首条消息预览参与排序，避免"没名字就一律沉底"的反直觉）
~~enter：点击恢复这个对话~~（对应 /resume：切过去并关闭面板，失败原因留在 footer）
~~n: 新建一个对话，显示输入框，必须输入新对话的名称（/name）,输出完成后，回车关闭这个插件，并且进入到聊天界面~~（2026-09-23 完成：居中输入框输入名字，**名字可以留空**（对应 /name 的可选 `[name]` 参数，空则不设置名称，不再强制必须输入），回车新建并关闭面板进入新对话）
v: 暂定
~~i: 弹出对话框展示 Session Info 信息，就是 /seesion 中的内容（这个对话框弹出的时候使用y，可以复制对话框中全部的内容）~~（2026-09-23 完成：居中弹窗列 Name / Model / Messages / Tokens / Cost / Created / Updated / Path / ID，y 复制全部（路径用完整路径），Esc / q 关闭）
~~u: 弹出对话框展示上下文占用详情（pi footer 那个百分比的展开视图）~~（2026-09-28 完成，随后按反馈借鉴 pi 的 npm:pi-cc-extensions 的 /context 重做：居中只读弹窗——`已用 / 窗口 · 百分比` + 彩色堆叠进度条，随后每个类别按占整个窗口的比例列一行：System prompt / Memory / Skills / Tools definition / Tool results / Context / Other / Free space（空闲），再加 Model / Messages / Cost 概要。当前会话给完整拆分：提示侧（系统提示词 / 记忆 / 技能 / 工具定义）来自 pi 运行态 `getSystemPrompt` / `getSystemPromptOptions` / `getAllTools`，对话侧（Tool results / Context）从 `buildContextEntries` 估算，总占用用 `getContextUsage` 的数字并按 pi-cc 的 resolveUsedTokens / capParts 对齐；其他会话只能从文件算对话侧、其余归 Other，窗口按 model id 反查、查不到只显示 token。y 复制全部、Esc / q 关闭；多选时拒绝。footer 不列 u，只进 ? 帮助。回车进入看某一类详情暂未做）
~~c: 压缩选中的对话（/compact），压缩完进对话~~（2026-09-24 完成：先弹居中输入框输入可选的聚焦指令（对应 /compact [instructions]，可留空用 pi 默认），回车后压缩光标会话的活动分支并进入它——`ctx.compact` 只作用于当前会话，所以其他会话先 switchSession 切过去（这就是"进对话"），再在新 ctx 上压缩；压缩要跑模型、可能十几秒，期间面板隐藏、pi 自己的 footer 显示旋转 spinner（正方形），成功后关面板、失败留 footer；选中多个会话时拒绝。不弹确认框，和 pi 自带 /compact 一致（只追加一条 compaction 条目、不删数据）)

### 对话归档（2026-10-02）

- `x` 归档 / 取消归档光标会话，支持 Space 多选后整组操作，无确认框；`X` 在同一 SESSIONS 面板切换普通 / 已归档列表，不增加第四面板或弹窗。
- 归档只影响插件列表，不移动或改写会话 JSONL，也不影响 pi 原生 `/resume`。一个文件内部所有分支共同归档，独立 fork / clone / 导入副本不继承；不是备份、删除或压缩功能。
- C/A 目录范围与归档视图正交，排序和搜索在当前可见集合上工作。先归档过滤，再置顶和 threaded 排序，避免隐藏父会话后留下错误缩进。
- 两种视图各自记住本次打开期间的光标文件、搜索词和滚动位置；切换视图 / 目录范围清空多选，重算搜索命中。归档后选最近的下一条，末尾选上一条；空列表清空 TREE / CONTENT。
- 归档同时取消置顶；取消归档不恢复置顶；归档列表按 p 提示先取消归档。允许归档当前会话，不中断它；重开面板默认普通列表并提示当前会话已归档。Enter / TREE 恢复 / 双击只进入，不自动取消归档。
- Esc 先清按键前缀、当前搜索、多选，再返回普通列表；q 直接退出。归档标题及 footer 提示清楚区分当前视图，所有文案与自定义键位同步。
- 置顶与归档原子写入同一 `lazy-panel-state.json`，旧置顶文件只读迁移、原样保留；跨进程锁内读最新状态后应用明确目标操作，防止多窗口互相覆盖。损坏状态不覆盖、暂时不可访问的会话不自动清理。锁异常 / 元数据清理失败明确提示，保存成功但解锁失败不能显示成回滚。详见快捷键文档的归档小节。

### 左侧列表第二个面板(tree)

~~/: 底部出现 搜索：两个字样，随后用户输入内容回车进行搜索，只搜索当前fouce的,文本需要高亮，使用 n/N 可以上一个和下一个，具体参考lazygit的形式~~（2026-09-22 曾改为只在 a 打开的树对话框里搜；同日又恢复；2026-09-23 完成：和对话框共用 `matchTreeRow`——每个词都要出现在 label / 正文里，`tag:` 只看 label，`role:` 只看角色，`after:` / `before:` 看时间（role 不再参与普通词匹配，和 pi /tree 不同）；折叠段里的行也算匹配，跳过去时展开它的祖先，右侧 CONTENT 跟着高亮；对话框里的搜索仍是过滤，两边互不影响）
~~j/k/方向上下键盘：上下移动~~

- ~~光标变化时右侧 CONTENT 跟着定位：对应消息框头部显示 `›` 箭头并加选中背景，同时把这条消息滚到面板顶部；节点在别的分支上时右侧改为显示“以该节点为叶子”的分支~~
- ~~树线：左侧画 pi `/tree` 一样的树形线条（`├⊟` / `└─` / `│`，⊟ 表示有子节点），活动分支上的节点文字前加 `• `，system 提示词 / compaction / branch summary 显示为 `[system]` / `[branch summary]: …` 这种标签行；层级最多画 3 层，更深的折叠成 `… `；完整的树在 a 打开的对话框里看~~（2026-09-22 起小面板改成折叠大纲：段头左侧画 `▸` / `▾` 三角、没有后代的旁支画 `─`，旁支默认折叠、活动分支展开，段内每层缩进 2 列、最多四层、更深的以 `… ` 代替，线性对话不缩进；pi 风格的树线只保留在 a 打开的对话框里）
- ~~z: 折叠 / 展开光标所在的分支段：光标在段头（`▸` / `▾` 行）上切换；在段内任意一行按下则折叠所在段并把光标移到段头（vim 的 zc）；线性对话的主干上没有可折叠的段，footer 提示 nothing to fold here~~
~~y: 复制消息内容（类似于 /tree 里面 ctrl + x 快捷键的功能）~~
~~T: 给某个节点添加 label，在面板中央弹出一个输入框（类似 lazygit commit 的弹窗；和 /tree 里面 shift+t 快捷键一致；不用 l 是为了把 l 留给全局的“下一个面板”），打标签之后 / 搜索也要能按照标签搜索~~（2026-09-22 起标签在树对话框的搜索里能搜到：普通词就匹配 label，`tag:xxx` 只看 label）
~~d/t/u/L/a: 过滤（过滤 类似 /tree ：filters ctrl+d/t/u/l/a 快捷键的功能，其中“只看有标签”用大写 L）~~（直接过滤键放在树对话框里，l 表示仅标签；小面板的 l 仍用于切换面板）
~~f: 小面板打开五行过滤菜单，依次为 d 默认 / t 无工具结果 / u 仅用户 / l 仅标签 / a 全部~~（2026-09-30：复用居中选择框，初始选中当前模式，字母直选或上下键 / Enter 确认，Esc 取消；重复选择当前模式不切回默认。过滤与完整树对话框双向同步，a 只是放大同一棵树，不重置过滤。切换过滤清空折叠，保留节点或定位最近可见祖先，并同步 CONTENT；搜索关键字仍各自独立）
~~a: 打开树对话框：顶部搜索框、中间完整的树形结构（不折叠层级）、底部快捷键提示，占满终端只留 2 列 / 1 行边距，光标停在面板当前节点上；Esc/q 关闭~~（对话框和面板共用折叠状态，折叠的段头在树线上画 `⊞`。2026-09-22 起对话框内的键按 `tree-dialog` → `tree` → `global` 的顺序解析：j/k/gg/G 移动自己的光标，y / T / Enter 和面板一样但作用于对话框选中的行（Label 弹窗、Summarize 菜单画在对话框上面），z 折叠，d/t/u/l/a 过滤（t/u/l/a 再按一次回 default；重新加载树、清空折叠，面板同步显示同一过滤并在标题里标出），`/` 聚焦顶部搜索框实时过滤（每个词都要出现在 label / 正文里，`tag:` 只看 label，`role:` 只看角色，`after:` / `before:` 看时间；2026-09-23 起 role 不再参与普通词匹配；Esc 退出搜索框、关键字和结果保留，再按 / 接着改、删光即清除，Enter 在搜索框里没有含义；搜索期间折叠全部打开、删光或关对话框后恢复），列表上 q / Esc 关闭并让面板光标跳到对话框选中的行——藏在折叠段里就展开它，右侧内容跟着高亮；对话框不做 ? 帮助，键都在底部 footer）
~~enter: 确定要恢复到这个界面上，此时展示菜单，提供三个选项（No summary，Summarize，Summarize with custom prompt）~~（对应 /tree 的 restore：居中弹出三选菜单，j/k 移动、Enter 确认、Esc 退回 tree 面板；选 custom prompt 再弹一个单行输入框输入摘要指令，Esc 退回菜单；节点在其他会话里就先切会话再恢复；光标就在活动叶子上、或 pi 设置了 `branchSummary.skipPrompt` 时不弹菜单、直接进入）

三个选项只在一点上不同：跳到目标节点后，"被放弃的那段分支"（从当前叶子往上到它和目标节点的公共祖先之间的所有消息）怎么处理。跳转本身都一样：pi 把叶子指针移到目标节点，之后发给模型的上下文变成"根 → 目标节点"这条路径；被放弃的消息仍留在会话文件里，树里还能看到、以后还能跳回去。

- No summary：只移动叶子指针，不调模型、不花钱、瞬间完成，被放弃分支上发生过的事模型完全"忘记"。适合那段分支是死胡同，或只是想回到某句话重新问。
- Summarize：pi 把被放弃分支上的消息按时间顺序序列化成文本，用当前模型写一段结构化摘要（固定格式 Goal / Constraints & Preferences / Progress（Done、In Progress、Blocked）/ Key Decisions / Next Steps，要求保留精确的文件路径、函数名和报错信息），开头加一句"用户之前探索过另一条分支，以下是那次探索的总结"，结尾附上那段分支读过和改过的文件列表。结果作为一条 `branch_summary` 条目挂在目标节点下面成为新叶子，模型继续对话时知道另一条路试过什么、有什么结论。代价是一次模型调用（输出最多 4096 token，用量计入会话总费用），要等几秒到几十秒，且必须有模型可用，否则报 `No model available for summarization`。适合那段分支做过有价值的探索或修改，想保留结论但不想把整段对话的 token 都带回来。
- Summarize with custom prompt：和 Summarize 一样，只是把输入的指令追加到默认提示词后面作为 `Additional focus: …`，默认结构仍然保留。用来指定摘要侧重点，比如"只记录和 API 改动相关的决定"或"完整保留报错堆栈"。
- 补充：目标是用户消息时，pi 把叶子移到它的父节点并把那句话填回输入框让你改了重发（摘要挂在父节点位置）；目标在当前叶子的下游（没有任何消息被放弃）时，选 Summarize 也不会调模型，等同 No summary；分支太长放不进上下文时按 `branchSummary.reserveTokens`（默认 16384）预留空间、裁掉更早的消息再总结。摘要条目在 TREE 里显示为 `system: branch summary: …`，默认过滤下隐藏，按 `a` 可见。

### 右侧第一个面板（content 只读）

这个需要区分是“我”发送的内容，还是AI返回的内容
感觉这部分不需要这么重度的操作，用户大概率只需要粗略的查看这里面的内容，所以感觉快捷键只需要：
~~j/k/方向键：用来上下滚动查看内容~~
~~y: 复制对应对话的内容直接上tree面板复制就行了，不必要在这里~~
~~gg: 回到顶部~~
~~G：回到底部~~
~~/: 底部出现 搜索：两个字样，随后用户输入内容回车进行搜索，只搜索当前fouce的,文本需要高亮，使用 n/N 可以上一个和下一个，具体参考lazygit的形式~~（2026-09-23 完成：按渲染后的正文行匹配自由文本（不支持限定词，消息框的头部不参与），跳转把该行滚到面板顶部，命中文字在缓存的排版行上叠加高亮）

~~/: 底部出现 搜索：两个字样，随后用户输入内容回车进行搜索，只搜索当前fouce的,文本需要高亮，使用 n/N 可以上一个和下一个，具体参考lazygit的形式
h/j/k/l/键盘方向键: 光标移动
v: 预览模式
y: 复制内容
yy: 复制一整行
b/e: 跳过单词
gg: 回到顶部
G：回到底部
zz: 滚动到中心
其实就是 vim 这些快速跳转的快捷键, 把常规常用的全部加上就行~~

## 使用语言

- typescript
- node.js

## 注意点

1. 搜索可以强化一下语义，可以类似于github那样：auther:xxxx 这个代表找到作者是 xxxx 的仓库，这里的顶部搜索也可以参考这种设计，例如：model:xxx 当然这个根据实际情况考虑，如果做不到就算了（我建议第一版不要做真正的 semantic search。但是可以先设计成：
/name:FilmRecall
/model:opus
/path:FilmRecall
/tag:scan
/after:2026-09-01
/before:2026-09-20）（2026-09-23：已按这套语法实现，另加 `role:`——普通词只搜 名称 / 标题 / label，模型、路径、角色、时间都要加限定词：SESSIONS 认 name: / model: / path: / after: / before:，TREE 和树对话框认 tag: / role: / after: / before:，CONTENT 只搜自由文本；不认识的 key 和解析不了的日期当普通词搜，某个面板没有的字段对应的限定词被忽略；见 docs/keybindings.md 的 Search 一节）
2. 快捷键可配置，最好能够叫用户自定义快捷键，上面不是最优解, 可以通过配置文件或者在tui修改的形式来修改快捷键，并且要允许组合建，比如用户希望使用`ctrl + d` 来删除对话，这个也是可以的
3. 每次fork和delete操作的时候都需要，弹出一个对话框让用户来手动确定。（上下结构，确认和取消（y/n））
4. 右侧聊天内容要想办法区分“我”的内容和“AI”发送的内容，并且使用 markdown 渲染，最好直接用 直接用pi原生的渲染器
5. 确定左侧 resume 面板里面展示的内容，比如聊天的时间，模型，名称，运行路径，聊天内容的简介？等等不一定要全部展示，主要还是要UI美观，tree 面板就非常简单了，就按照

```txt
  user: hi
› [我的] 22:18 assistant: Hi! 👋 I'm ready to help with your **FilmRecall** Android project — whether that's writing or refactoring code, de
```

前面 `user` 代表是用户 `assistant` 代表是AI，`：`后面是内容, `[]` 里面是标签, `22:18` 是时间。

6. 主题：面板不拥有自己的主题，颜色一律取自 pi 当前的活动主题（入口传 `ctx.ui.theme` 这个 live proxy，pi 换主题面板跟着变），设置页的 THEME 分类只有一行（当前主题），Enter 弹出 pi 的主题菜单，选中即切到 pi。所以内置主题、自定义主题（`~/.pi/agent/themes/*.json`）都直接白送，用户不用学第二套格式；面板元素对应哪些 pi token、为什么不做面板专属配色，见 docs/theme.md。

7. 弹窗的退出提示统一：标题栏右上角可以显示任意说明（角色 · 时间 / 会话名或模型 · 日期 / 位置计数），不强制塞 Esc；但只要弹窗支持 `q` 退出，底部 footer 就写 `Esc/q 关闭`（输入框等把 `q` 当正文的地方只写 Esc）。标题栏和 footer 可以同时提示，不冲突。右上角只放**完整可读**的短标识：弹窗最宽 60 列、右侧预算约 30 列，正文 / 首条消息预览塞进去只会变成 `…`，所以节点用 角色 · 时间、会话用 名字（或模型）· 更新日期（`ui/widgets/dialog-subject.ts`），fork 确认框用 `#序号/总数`。

## UI 图

```

                 25%                  75%
          ┌───────────────┬─────────────────────────────┐
          │               │                             │
          │ [1] SESSIONS  │                             │
          │               │                             │
          │               │        [3] CONTENT          │
          │               │                             │
          │               │                             │
          ├───────────────┤                             │
          │               │                             │
          │   [2] TREE    │                             │
          │               │                             │
          │               │                             │
          └───────────────┴─────────────────────────────┘

┌───────────────────┬────────────────────────────────────────┐
│                   │                                        │
│     SESSIONS      │                                        │
│                   │                                        │
│   选对话           │                                        │
│                   │                                        │
├───────────────────┤              CONTENT                   │
│                   │                                        │
│       TREE        │              看内容                     │
│                   │                                        │
│   选节点           │                                        │
│                   │                                        │
└───────────────────┴────────────────────────────────────────┘

┌──────────────────────────────────────┐
│           SESSION INFO               │
│                                      │
│ Name       FilmRecall                │
│ Model      claude-opus-4             │
│ Messages   128                       │
│ Tokens     84.2k                     │
│ Cost       $1.42                     │
│ Created    Sep 20 22:18              │
│ Updated    Sep 20 22:21              │
│ Path       ~/.pi/sessions/...        │
│                                      │
│                         [Esc] Close  │
└──────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────┐
│ CONTENT                                                                  │
├──────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  ┌─ YOU · 22:18 ─────────────────────────────────────────────────────┐   │
│  │                                                                   │   │
│  │  你帮我设计一下 FilmRecall 的 Android 项目目录结构。              │   │
│  │                                                                   │   │
│  └───────────────────────────────────────────────────────────────────┘   │
│                                                                          │
│  ┌─ ASSISTANT · 22:18 ────────────────────────────────────────────────┐   │
│  │                                                                   │   │
│  │  可以。                                                           │   │
│  │                                                                   │   │
│  │  我建议把扫描模块拆成几个部分：                                   │   │
│  │                                                                   │   │
│  │  ### 1. Scanner                                                  │   │
│  │                                                                   │   │
│  │  负责 CameraX 相机预览以及拍摄。                                  │   │
│  │                                                                   │   │
│  │  ### 2. Film Boundary Detection                                  │   │
│  │                                                                   │   │
│  │  使用 OpenCV 检测胶片边界……                                      │   │
│  │                                                                   │   │
│  └───────────────────────────────────────────────────────────────────┘   │
│                                                                          │
│                                                                          │
├──────────────────────────────────────────────────────────────────────────┤
│ NORMAL │ / Search   ? Help   l Focus   Enter Restore   y Copy   gg G    │
└──────────────────────────────────────────────────────────────────────────┘

```
