Appearance
功能规格说明:桌面端文件面板
本面板为「源码/文本」视图:
.html/.htm路径的点击在桌面端优先进入预览面板渲染(见 desktop-preview.md「本地 HTML 文件预览」),该预览失败时回退本面板;本面板自身不因该故事改变行为。
用户场景与测试
用户故事:文件面板(优先级:P1)
作为 CodeWave IDE 桌面端用户,我希望点击 read/edit/write 工具中的文件路径时在应用内打开一个文件面板查看文件内容(默认只读,面板内改文件见「文件面板内编辑与保存」),以便无需离开对话即可核对 agent 读写过的文件。远程 SSH 会话是引入该面板的契机——远程路径无法在本地打开(当前会报「打开文件失败」,甚至误打开同名文件);面板统一承载本地与远程的文件查看,本地会话额外提供「在默认应用中打开」以便跳出到外部编辑器。
为什么是这个优先级:远程会话点击工具路径当前会在本地尝试打开而报「打开文件失败」,是远程会话体验的直接缺陷;文件面板补齐该缺口,并统一本地/远程的文件查看入口。
独立测试:远程会话中点击 read 工具路径,验证 file 面板从远端读取并展示内容(行号、长行水平滚动、代码高亮);本地会话点击同一路径验证 file 面板从本地读取并展示,且「在默认应用中打开」按钮可用;点击另一条消息的路径验证在该 file tab 内新增/激活对应的文件标签并显示;通过空态入口/「+」菜单/菜单栏「面板 → 文件」打开 file tab(未加载文件时显示占位),菜单栏项再次触发即关闭;点击含 . / .. 段的相对路径,验证按规范绝对路径读取、回填路径与面板标题均无冗余 .. 残留;点击工具条「复制文件内容」验证剪贴板为该文件完整内容、改到 markdown 文件验证复制的是源文本、进入编辑态改几行再复制验证拿到的是草稿而不是磁盘旧内容、图片与读取失败的文件验证没有这颗按钮。
验收场景:
- 假设用户处于远程主机会话(host 非 local),当点击 read 工具的文件路径,则在右上角打开 file 面板,从远端读取该文件并显示内容,且不得出现「打开文件失败」系统消息。
- 假设用户处于本地主机会话,当点击 read 工具的文件路径,则在右上角打开 file 面板,从本地读取该文件并显示内容。
- 假设用户处于任意主机会话,当点击 edit/write 工具的文件路径,则 file 面板展示该文件的内容(edit 展示新版本)。
- 假设用户处于任意主机会话,当点击带起止行信息的文件提及/ContextTag,则 file 面板打开后滚动定位到对应行区间。
- 假设本地 file 面板已打开,当点击标题栏的「在默认应用中打开」按钮,则通过系统默认应用打开该本地文件(远程会话隐藏该按钮)。
- 假设file 面板已打开并显示文件 A,当用户点击另一条消息的文件 B 路径,则在该 file tab 内按「文件面板已打开文件的标签」打开 B(新增文件标签或激活已打开的同名标签),A 的标签保留;面板保持原位(不得因再次点击而移动行/位置)。
- 假设file 面板已打开,当再次点击当前已显示文件的路径,则重新读取并刷新内容。
- 假设file 面板已打开,当点击标题栏的复制按钮,则将完整文件路径(远程为远端路径)复制到剪贴板。
- 假设file 面板已打开,当用户关闭 file tab,则该文件查看视图关闭,不残留读取进程或端口转发资源;折叠(头部按钮)仅隐藏槽位并保留 tab(见 desktop-right-panel.md「右侧面板 · 展开/折叠、空间守卫与欢迎页共存」)。
- 假设分屏中有多个会话,当切换分屏焦点,则各分屏的 file 面板打开状态与文件内容相互独立(复用面板组按会话隔离规则)。
- 假设file 面板宽度较窄,当文件行超出面板宽度,则内容水平滚动而非折行,行号列固定不随水平滚动。
- 假设打开的是 markdown 文件,当面板加载完成,则按渲染后的 markdown 展示(复用现有 marked 渲染管线),而非纯文本。
- 假设打开的是代码文件,当面板加载完成,则按扩展名匹配进行轻量语法高亮;未知扩展名回退纯文本。
- 假设文件过大或为二进制,当面板读取该文件,则截断/拒读并给出提示,不得阻塞渲染或乱码展示。
- 假设打开的是图片(本地或远端,如
previewImage),当面板加载完成,则在面板内联展示图片预览(渲染后的图片,而非 base64 文本或裂图)。 - 假设文件不存在或不可读(远端或本地),当面板读取失败,则在面板内显示错误提示,不弹「打开文件失败」系统消息。
- 假设用户通过空态入口/「+」菜单打开 file tab(file 为唯一实例入口,open-or-activate:已有 file tab 只激活不新建),当file tab 已打开但尚未加载任何文件且当前对话没有文件,则显示「点击消息中的文件路径查看」占位;对话中已有文件时改为展示文件清单(见「文件面板列出当前对话的文件」)。
- 假设file 面板已打开或未打开,当用户点击菜单栏「面板 → 文件」菜单项,则按 desktop-right-panel.md「边界情况 · 菜单栏『面板』项与快捷键」的开/关切换语义处理:无 file tab 时打开/激活唯一 file tab(未加载过文件时显示文件清单或占位提示),已有 file tab 时关闭;行为与预览/差异/终端面板菜单项一致。文件面板不设快捷键(与 Claude Code Desktop 一致,且
Ctrl+Shift+F与 Windows 输入法简繁切换冲突)。 - 假设远端主机的 MIME 类型识别不可用(如缺少
file命令或版本不支持常用探测参数),当面板打开扩展名为常见图片格式(png/jpg/jpeg/gif/webp/bmp/ico/svg)的远端文件,则仍按扩展名识别为图片并内联展示,不得误报「二进制文件无法在面板中显示」。 - 假设file 面板内联展示图片(本地或远端,含消息中的图片),当右键点击图片并选择「复制图片」,则图片被复制到系统剪贴板,可在其他应用或聊天输入框直接粘贴。
- 假设打开请求携带的路径含
./..段(如消息中`../sibling-repo/src/a.ts`按会话 workdir 归并后),当面板读取并回填,则宿主按规范绝对路径(折叠.与..、无残留冗余段)读取,且回填给面板的fileView.path同为规范路径——面板标题显示规范路径(而非…/proj/../sibling-repo/src/a.ts这类含..的串),复制路径、标题相对化、自动刷新比对均基于该规范路径;本地与远端会话行为一致,路径不存在时仍按场景 16 在面板内提示错误。归一化为纯词法折叠,不解析符号链接。 - 假设打开的 markdown 内含指向本地文件的链接(相对路径如
[规格](020-mini-app/spec.md)/[上文](../core/a.md),也含绝对路径与file:形式,可带#锚点、?query与百分号编码),当用户点击该链接,则按当前打开文件所在目录解析成绝对路径,并沿用点击消息里文件路径的同一条链路在面板内打开(存在则展示内容,不存在按场景 16 在面板内提示错误),且主窗口不得发生任何导航(链接是渲染进应用文档的,未拦截的点击会把界面换成 Chromium 错误页);外链(http:/https:/mailto:等非本地文件协议)与页内#锚点不归面板处理,保持各自默认行为。 - 假设file 面板已打开并显示了内容(代码、配置、日志、markdown 等文本文件,本地或远端会话均算),当用户点击工具条的「复制文件内容」,则把面板当前显示的完整内容写入系统剪贴板,并给一次窗口级提示「内容已复制」;剪贴板写入失败时提示「复制失败,请手动复制」,不得静默吞掉结果。该按钮与「复制路径」并列但语义独立:一颗复制内容、一颗复制路径,两者互不替代(对齐 Claude 桌面端文件查看器的
Copy file contents)。 - 假设面板没有打开文件、文件正在加载、读取失败,或显示的是图片,当用户查看工具条,则不出现「复制文件内容」按钮——图片的复制走场景 20 的「复制图片」右键,没有内容可复制时给一颗点了没反应的按钮不如不给。markdown 文件复制的是源文本而不是渲染后的文字;内容被截断时只复制已加载的部分,并在成功提示里标注「文件已截断,仅复制已加载内容」,不让用户以为拿到了完整文件。
- 假设面板处于编辑态,当用户点击「复制文件内容」,则复制的是编辑器里的当前内容(含尚未保存的修改),而不是磁盘上的旧内容;复制不改变编辑态,也不触发保存。
用户故事:文件面板列出当前对话的文件(优先级:P1)
作为 CodeWave IDE 桌面端用户,我希望打开文件面板时先看到这次对话读写过哪些文件,以便不必翻回消息流去找文件路径——在对话场景下打开文件面板,意图大概率就是看这次对话里的文件。
为什么是这个优先级:面板原先只能靠点击消息里的路径打开文件,主动打开是一个空面板。清单直接从消息流派生(成功的 read/write/edit 工具调用),不新增宿主协议,因此对本地与远端会话同样成立;点开仍在同一个 file tab 内,交互与点消息里的路径一致。
独立测试:在一个读写过若干文件的对话中通过「+」菜单打开 file tab,验证正文列出这些文件、最近的在前、点任一行即在面板中打开;点工具条「返回文件列表」回到清单;再点同一文件仍切回文件内容;在一个还没读写过文件的对话中打开 file tab,验证仍是占位提示;让 agent 读写一个新文件,验证它出现在清单最前。
验收场景:
- 假设当前对话还没有任何成功的文件读写、也还没打开过文件,当用户打开 file tab,则正文显示空态提示「打开的文件会显示在这里」(面板为空),不出现空清单与清单标题;空态文案与引导见「文件面板已打开文件的标签」场景 14。
- 假设当前对话已成功 read/write/edit 过文件,当用户通过「+」菜单、菜单栏「面板 → 文件」或空态入口打开 file tab,则正文直接列出这些文件(一行一个、只显示文件名,最近的在前),不再显示占位提示。
- 假设清单已展示,当用户点击某一行,则在同一个 file tab 内打开该文件的内层文件标签(与点击消息里的文件路径同一套语义:
.html进预览面板、其余进源码/文本视图,先显示加载中)。 - 假设file 面板正在显示某个文件且当前对话有文件清单,当用户点击工具条上的「返回文件列表」,则正文回到清单、工具条回到「文件」标题;此时若之前那次读取的回复还在路上,落下来也不得把视图从清单拽回文件。
- 假设用户从清单退回到文件清单后再点开同一个文件,则仍正常切回该文件内容(列表态由独立的视图标志表示,不靠「没有已打开文件」来推导,也不清空已打开的文件标签)。
- 假设同一个文件在对话中被多次读取或改写,当清单展示,则该文件只占一行,位置取最近一次命中;文件名保持最近一次调用的写法。
- 假设工具的调用仍在执行中(streaming/running)或已失败,当清单展示,则该文件不出现在清单里(清单只反映已成功落地的读写)。
- 假设两个不同目录下的文件同名,当清单展示,则这两行改用相对路径显示以区分,其余唯一文件名仍显示文件名;每行悬浮提示给完整相对路径。
- 假设清单里没有用户想看的文件,当用户使用目录树顶部的过滤框,则按「文件面板按名字查找」在工作区内查找并打开文件——清单不承担跨目录浏览的职责。
- 假设对话中发布了创作集(artifact),当清单展示,则不列出该 artifact:artifact 是服务端 URL,本机没有对应文件;发布它的本地源文件若被 read/write/edit 过,仍作为普通文件出现。
- 假设清单已展示,当agent 在对话中成功读写一个新文件,则该文件出现在清单最前,无需关闭再打开面板。
- 假设用户切换到另一个会话(或分屏中的另一个 pane),当其 file tab 展示清单,则清单按该会话自己的消息派生,不残留上一个会话的文件。
- 假设处于远端主机会话,当清单展示并点开某行,则列出与本地同样成立,文件按远端读取与展示(清单本身是前端从消息流派生,不依赖本地文件系统)。
用户故事:文件面板自动刷新(优先级:P1)
作为 CodeWave IDE 桌面端用户,我希望 agent 对面板当前显示的文件执行 write/edit 工具且成功后,面板自动重新读取该文件并展示最新内容,以便在 agent 修改文件后无需手动再次点击路径即可核对最新结果。
为什么是这个优先级:agent 修改文件是高频操作;若面板停留在旧内容,用户核对时会误以为修改未生效,反复手动点击路径既打断流程也容易遗漏。自动刷新让「工具完成 → 面板即最新」无需额外操作,与 diff 面板的已读/最新语义一致。
独立测试:打开 file 面板显示文件 A → 在对话中让 agent 对文件 A 执行 write/edit 并成功 → 面板自动更新为最新内容且不滚动跳位;agent 对文件 B 执行 write/edit 或对文件 A 执行失败 → 面板保持不变;以含 ../ 的相对路径打开文件 A 后触发 write/edit,验证仍能自动刷新。
验收场景:
- 假设file 面板已打开显示文件 A,当agent 对文件 A 执行 write 工具且成功完成,则面板自动重新读取文件 A 并展示最新内容,用户无需再次点击路径。
- 假设file 面板已打开显示文件 A,当agent 对文件 A 执行 edit 工具且成功完成(含同一文件多次 edit),则面板自动刷新为最新内容。
- 假设file 面板已打开显示文件 A,当agent 对文件 B(非当前显示文件)执行 write/edit 并成功,则面板保持文件 A 不变,不因其他文件的写操作而刷新。
- 假设file 面板已打开显示文件 A,当agent 对文件 A 的 write/edit 执行失败或被权限拒绝,则面板不刷新,保持失败前的内容。
- 假设file 面板已打开显示文件 A,当write/edit 工具仍在执行中(streaming/running 阶段),则面板不提前刷新,仅在工具完成(end)后刷新一次。
- 假设file 面板已打开显示文件 A 且自动刷新触发,当刷新完成,则面板内容替换为最新内容,滚动位置不产生明显跳动(软刷新语义,与再次点击同一路径一致)。
- 假设用户通过含
./..段的路径打开文件 A(如`../sibling-repo/src/a.ts`),当 agent 随后对同一文件 A 执行 write/edit 并成功,则面板照常自动刷新:面板的打开路径与工具的displayPath/绝对路径都按同一套规范折叠(工具侧已由path.resolve归一)后可比对命中,不得因一侧是…/proj/../sibling-repo/src/a.ts、另一侧是…/sibling-repo/src/a.ts而比对失败导致不刷新。
用户故事:文件面板按名字查找(目录树的过滤框,优先级:P2)
作为 CodeWave IDE 桌面端用户,我希望在目录树顶部的输入框里打几个字就能把工作区里匹配的文件列出来、点一条即打开,以便不必沿着目录一层层展开去找。过滤走输入框 @ 文件搜索的既有数据流(requestFileSuggestions → fileSuggestionsResponse),范围与 @ 一致(锚定会话工作根,本地与远端通用),并同样排除依赖与构建产物目录(node_modules、dist、.next 等,按目录名判定),与目录树、差异面板同一口径——因为它覆盖整个工作区、不依赖树展开到哪一层,所以它同时承担「按名字打开任意文件」的职责,面板里只保留这一个查找入口。
为什么是这个优先级:有了目录树之后,「按名字找文件」若还留着独立的搜索弹层就是两条并行路径;把过滤放进树顶部,输入一次同时满足「浏览时快速定位」和「知道名字直接打开」。数据流与去重逻辑均已存在可复用,宿主零改动,改动集中在共享 webview 侧;@ 提及继续用同一数据流与原来的建议下拉组件,行为不变。
独立测试:打开 file tab(目录树已展示),在树顶部输入框打文件名片段,验证树区域切换为匹配结果列表、点击与键盘选中后打开该文件、清空输入后目录树恢复且展开状态不变;输入无匹配片段验证「未找到匹配的文件」;隐藏文件树后再显示验证过滤框随之回来;回归输入框 @ 文件搜索验证行为不变。
验收场景:
- 假设file 面板已打开且目录树已展示,当用户查看目录树顶部,则有一个过滤输入框(占位提示「筛选文件…」,本产品不做文件内容搜索故不带
?前缀说明),空输入时下方仍是正常目录树。 - 假设用户在过滤框输入至少 1 个字符,当停止输入约 200ms,则发送
requestFileSuggestions请求,树区域切换为匹配文件的结果列表(目录项不展示——目录走树浏览),每项显示文件名与其所在相对目录,同名文件可区分。 - 假设过滤框已有输入,当用户清空内容或按 Esc,则结果列表消失、目录树恢复,且恢复的是过滤前的展开状态(不重置为全部折叠)。
- 假设结果列表已展示,当按 ↓/↑ 移动选中项并按 Enter 确认,则在面板中打开该文件(按「文件面板已打开文件的标签」新增或激活文件标签,先显示加载中、带行定位),过滤框清空、区域回到目录树。
- 假设结果列表已展示,当点击某个结果项,则等效于键盘 Enter 选中(打开文件并清空过滤)。
- 假设搜索请求尚未返回,当用户继续输入,则过期请求的响应被忽略(按 requestId 去重),仅最新关键字的结果生效,不出现结果闪烁覆盖。
- 假设输入的关键字无匹配,当结果区域展示,则显示「未找到匹配的文件」空态提示,过滤框保持可编辑。
- 假设处于远端主机会话,当在过滤框选中远端文件,则面板从该远端主机读取并展示文件内容(打开请求携带会话对应的 host,路径为远端路径)。
- 假设分屏中存在多个会话,当在某一分屏的面板中过滤,则结果锚定该分屏所属会话的工作目录;各分屏面板的过滤状态相互独立,broadcast 响应按各自 requestId 隔离,不串扰。
- 假设用户隐藏了文件树,当再次显示文件树,则过滤框随之回来(过滤框属于树的一部分,树隐藏时一并隐藏);重新显示时回到目录树形态,不保证保留隐藏前的过滤内容。
- 假设输入框
@文件搜索正被使用,当本功能上线,则其行为不变(下拉朝上、键盘导航、插入语义均保持原样;面板不再使用该下拉组件,组件本身仍服务于@)——交互形态不变,文件范围另见场景 12。 - 假设工作区里存在
node_modules、dist、.next等依赖或构建产物目录,当用户在过滤框输入关键字,则这些目录下的文件不参与匹配——按目录名判定,与仓库是否配置.gitignore、文件是否被 git 跟踪无关;同一张排除表也作用于输入框@文件搜索(两者共用同一条数据流)。 - 假设用户在工作区里新建或删除了文件,当约 3 秒后再在过滤框搜索该名字,则新建的文件出现在结果里、已删除的文件不再出现;不要求输入停留在过滤框,间隔期间不产生额外的文件系统监听。
用户故事:文件拖拽上传(优先级:P1)
作为 CodeWave IDE 桌面端用户,我希望把本地文件直接拖入对话面板任意位置即可上传到当前对话,以便无需先点「+」菜单再选文件,上传成功后文件路径以标签形式出现在输入框里待发送。
为什么是这个优先级:文件上传是对话中高频操作,拖拽是桌面端最自然的文件输入方式;现有上传只能通过「+」菜单逐次选择,拖拽大幅降低操作成本。
独立测试:将本地文件拖入对话面板(标题栏/消息列表/输入框均触发),验证全屏拖拽遮罩出现、拖离消失、松开后文件上传且路径标签插入当前分屏输入框;双分屏下分别拖入两个 pane 验证互不串扰。
验收场景:
- 假设用户从系统文件管理器拖拽一个或多个文件,当文件拖入对话面板范围(标题栏、消息列表、输入框任一区域),则面板出现全屏拖拽遮罩,提示「释放以上传文件」及支持多文件的说明。
- 假设拖拽遮罩已出现,当用户将文件拖离面板范围,则遮罩消失(拖拽计数防抖,快速进出不误触发)。
- 假设拖拽遮罩已出现且文件位于面板内,当用户松开鼠标,则遮罩清除、文件开始上传,上传成功后文件路径以标签形式插入当前对话输入框(与「+」菜单上传插入行为一致)。
- 假设文件拖入面板,当拖拽进行中,则不拦截正常消息输入等操作(输入框可继续输入,拖拽仅改变面板显示状态)。
- 假设桌面端存在多个分屏(pane),当用户将文件拖入某个分屏,则该分屏独立响应拖拽(遮罩显示在该分屏内),上传成功回包按 paneId 路由,仅目标分屏的输入框插入路径标签,其他分屏不串扰。
- 假设用户拖入的文件读取失败,当上传流程报错,则显示既有错误提示(如「读取文件失败」),不残留拖拽遮罩。
- 假设用户拖拽的不是文件而是会话条目或分屏标题(用于并排分屏/重排布局),当其经过对话面板,则不显示上传遮罩,仅显示分屏重排的插入位置指示——两类拖拽的视觉反馈互不混淆。
用户故事:文件面板内编辑与保存(优先级:P1)
作为 CodeWave IDE 桌面端用户,我希望在文件面板里直接修改并保存本地会话的文件,以便改一行配置、改一句文案这类小事不必跳出到编辑器、再回到对话里让 agent 代改——面板先把文件「看」全了,接着就该能「改」。对齐 Claude 桌面端 Code 标签页的文件查看器:查看器本身可编辑,保存按内容哈希做乐观并发校验,磁盘上的文件被别人改过时拒绝覆盖,把「覆盖 / 重新加载」交给用户。
为什么是这个优先级:面板目前只读,用户改一个文件的成本是「切到外部编辑器 → 找到路径 → 改 → 回来」,或者「发一条消息让 agent 改 → 等一轮工具调用」;前者打断对话上下文,后者把一次人工改动写成对话历史。本故事复用既有读取链路(openFile → desktopFileContent)加一条写回链路即可闭合,不引入新的目录浏览能力。
独立测试:本地会话打开一个工作目录内的 .ts 文件 → 点「编辑」→ 改内容 → Cmd/Ctrl+S 保存 → 验证正文回到只读且为新内容、磁盘文件确已修改;再次进入编辑改内容后点「取消」→ 验证弹「放弃未保存的修改?」、选「继续编辑」草稿还在、选「放弃」回到磁盘内容;编辑中点击另一条消息的文件路径 → 验证先被确认框拦下;编辑中让 agent 对该文件执行 edit 并成功 → 验证草稿不被自动刷新覆盖、保存时提示冲突并可选择覆盖或重新加载;远端会话与工作目录外的文件 → 验证没有编辑入口。
验收场景:
- 假设当前分屏处于本地会话、面板显示的文件位于该会话工作目录内、且内容为完整的文本(未被截断、非二进制、非图片),当用户点击工具条的「编辑」按钮,则面板进入编辑态:正文变为可编辑的文本区(保留等宽字体与行号),工具条右端换成「取消」与「保存」,焦点进入正文,用户可直接修改内容。
- 假设文件不满足场景 1 的条件(远端 SSH 会话、路径在工作目录之外、内容被截断、二进制、图片、读取失败、空内容),当用户查看工具条,则不出现「编辑」按钮,面板保持只读——编辑不为这些文件开特例,也不给「改了却存不回去」的入口。
- 假设面板处于只读态,当用户点击或双击正文,则不得进入编辑:进入编辑是显式动作(工具条按钮),避免误触把浏览态变成草稿态。
- 假设面板处于编辑态且内容已被修改,当用户点击「保存」或按 Cmd/Ctrl+S,则把当前草稿写回该文件:成功后退出编辑态、正文回到只读形态并展示刚写入的内容(高亮与行号照常),并给出一次「已保存」的轻量反馈;内容与磁盘一致(未做修改)时保存直接退出编辑态、不写盘。
- 假设面板处于编辑态且有未保存修改,当用户点击「取消」,则弹出确认(标题「放弃未保存的修改?」,按钮「继续编辑」/「放弃」):选「继续编辑」回到编辑态且草稿原样保留,选「放弃」丢弃草稿、正文回到磁盘上的当前内容。
- 假设面板处于编辑态且有未保存修改,当用户试图离开该文件——点击消息里的另一个文件路径、从文件清单或工具条搜索里打开另一个文件、关闭 file tab、切换到另一个会话或分屏、切换工作目录——则必须先弹出场景 5 的同一个确认:选「放弃」才执行原操作,选「继续编辑」则什么都不变(既不静默丢弃草稿,也不静默吞掉用户的离开操作)。
- 假设面板处于编辑态且磁盘上的该文件已被他人改动(agent 的 write/edit 工具成功、或用户在外部编辑器保存),当用户保存,则不得覆盖磁盘内容:面板提示「文件已在磁盘上更新」,并给出「覆盖(写入我的版本)」与「重新加载(丢弃我的修改)」两个动作;选覆盖则磁盘变为用户草稿,选重新加载则草稿丢弃、正文为磁盘最新内容。
- 假设面板处于编辑态且有未保存修改,当agent 对同一文件执行 write/edit 并成功(即触发「文件面板自动刷新」的同一时机),则自动刷新不得覆盖草稿、不得把用户踢出编辑态;面板按场景 7 的提示告知文件已在磁盘上更新,由用户决定覆盖或重新加载。
- 假设面板处于只读态,当agent 对当前显示的文件执行 write/edit 并成功,则沿用「文件面板自动刷新」的既有语义自动重读并展示最新内容,行为不变。
- 假设用户在编辑态保存,当写盘失败(文件不可写、权限不足、目录已被删除、磁盘已满等),则面板显示可读的失败原因、草稿完整保留且仍处于编辑态,用户可重试或取消;不得静默丢弃修改,也不得在写盘失败时显示「已保存」。
- 假设面板处于只读态且文件是 markdown(当前展示的是渲染结果),当用户点击「编辑」,则进入 markdown 源文本的编辑,保存成功后回到既有的渲染态并展示最新渲染结果(渲染态本身不可编辑)。
- 假设面板处于编辑态,当用户查看正文,则不提供行评论 / markdown 块评论入口(评论锚定的是只读正文的行区间);退出编辑态后评论入口按原样恢复。
- 假设分屏中有多个分屏/会话,当它们分别进入编辑态,则各分屏的草稿相互独立:切换分屏焦点、切换一级 tab 不丢草稿、不串台。
- 假设用户在面板里保存成功,则该写入对 agent 侧等价于外部修改(与用户在外部编辑器保存一致),后续工具调用按既有陈旧性校验处理,不由面板代为保证;面板自己的后续读取按「文件面板自动刷新」与「再次点击同一路径即重读」的既有语义拿到最新内容。
- 假设应用重载或退出,则不承诺草稿持久化:未保存的修改不跨重载/重启保留,重载后面板回到磁盘内容。
用户故事:文件面板工作区目录树(优先级:P1)
作为 CodeWave IDE 桌面端用户,我希望文件面板左侧有一条会话工作区的目录树,点目录就地展开、点文件在面板内打开,以便不必等这个文件在消息里出现过、也不必凭记忆猜文件名,就能浏览并查看项目里的任意源码。
为什么是这个优先级:面板现有两个入口都依赖「已经知道文件名」——点消息里的路径、或在搜索框里打片段。「浏览这个目录里有什么」是看源码的常规动作,缺它就只能跳出到外部编辑器,面板就退化成「看 agent 碰过的文件」。对齐 Claude 桌面端 Code 标签页的文件树:左侧与内容并排、可拖宽、可隐藏、展开目录才列目录(懒加载)、方向键导航、单层条目过多时截断并提示。
独立测试:打开 file tab 验证左侧出现会话工作目录的树、根目录条目已列出且子目录未预展开;展开一个目录验证此刻才发起列目录、加载中显示占位、失败显示可重试提示;点击文件验证在面板内打开且树中该行高亮;隐藏/显示树、拖宽验证内容区随之变化,把面板拖窄到 400px 以下验证树自动让位、拖回来验证自动恢复;用方向键在树中移动、展开折叠;在多层目录下验证缩进与「在文件树中显示」的定位与那次高亮、重复点同一个文件每次都重新高亮;打开一个工作根之外的临时文件验证工具条上没有这个动作;筛选框里有字时点击验证先清筛选再定位;远端主机会话重复以上,验证列目录与读取都走该远端主机。
验收场景:
- 假设file tab 已打开,当用户查看面板正文,则左侧显示一条会话工作根的目录树(根行即工作根的最后一段;根 = 会话打开时的目录,agent 用 EnterWorktree 切进工作树时随之换根、ExitWorktree 时切回,agent 在 shell 里
cd不换根),树顶部另有一个过滤输入框,见「文件面板按名字查找」;右侧为文件内容/清单/占位;树与内容之间可拖拽调整树宽,宽度被记住(按会话);树可整体隐藏与再显示,隐藏后内容区占满面板;树收起后在内容区左缘留一条窄的拖拽带,拖出来即恢复显示并把宽度拖到目标值。 - 假设目录树与内容并排,当用户拖宽树或面板本身变窄,则树列宽度必须给正文留出保底宽度(160px):拖到超过这个上限就先顶住,正文不被压到更窄——树是并排的一列,不是覆盖层。
- 假设面板正文区宽度不足 400px 且有打开的文件,当渲染布局,则树自动让位(不显示,正文占满),但不改写用户的手动显隐选择:面板宽度恢复到 400px 以上时树自动回来;此时工具条上的树开关呈不可用态并说明「面板过窄,加宽后显示」,而不是点击后毫无反应。一个文件都没打开、正文只是清单或占位时,树是这一格的主界面,仍按用户开关显示(不受宽度限制)。
- 假设目录树已展示,当用户展开一个尚未展开过的目录,则此刻才向宿主请求该目录的直接子项(懒加载),加载中在子层显示占位行,失败时显示可读的失败提示且该目录保持可重试态(再次点击重新请求);已加载过的目录再次展开不重复请求。
- 假设目录树列出某一层,当用户查看排序与内容,则目录在前、文件在后,同类按名称排序;版本库元数据与系统垃圾(
.git/.svn/.hg等版本控制目录、.DS_Store/Thumbs.db/desktop.ini一类)不出现;单层条目超过上限(200)只显示前 200 个,末尾给一行「还有 N 项未显示」。 - 假设目录树已展示,当用户点击目录行,则该目录就地展开或折叠(不跳转、不清空右侧),已展开的目录行给打开态图标;当点击文件行,则按「文件面板已打开文件的标签」的语义在面板内打开该文件。
- 假设目录树已获得焦点,当用户按 ↑/↓ 移动、Home/End 跳到首尾、→ 展开或进入首个可展开子项、← 折叠或跳回父目录、按首字母,则焦点按对应规则移动,折叠态与展开态同步到
aria-expanded,读屏可播报「展开目录 {name}」/「折叠目录 {name}」。 - 假设面板当前显示某个文件且它在工作根之内,当用户查看目录树,则该文件所在行处于选中态;若它被折叠在同层之外、或已经滚出可视区,点工具条的「在文件树中显示」逐级展开父目录并把它滚到可视区,并给该行一次约 1.2 秒的强调描边作为「已定位」的反馈——行本身就是选中态,没有这次高亮时「本来就看得见」与「刚刚定位过去」在画面上完全相同;重复点同一个文件每次都重新给这次反馈。
- 假设面板当前显示某个文件,当用户查看工具条的路径,则路径按目录段展示,每一段可点开同一级的同类条目下拉(目录段列出该层的目录与文件、文件段列出同目录的其它文件)——用于横向换文件而不必回到树;路径过长时中间段折叠为「…」,键盘 ←/→ 可在段间移动。
- 假设用户处于远端主机会话,当查看目录树并展开目录、点击文件,则列目录与读文件都经该远端主机的会话连接完成,路径为远端路径;远端不可达时树显示可读的失败提示与重试,不影响面板已显示的内容。
- 假设文件面板工作目录内的某个目录为空,当用户展开它,则在子层显示「空目录」提示,不显示空白。
- 假设分屏中有多个会话,当各自打开目录树,则根路径、展开状态、树宽相互独立,切换分屏焦点不共享展开状态。
- 假设用户打开目录树,当查看目录行与文件行的可执行操作,则只有「展开/折叠」与「打开」——不提供新建、重命名、删除、移动等文件管理动作,也不因右键给出这些动作(见非目标)。
- 假设会话工作目录已被删除或不可读,当面板加载目录树,则显示可读的错误提示,搜索与按路径打开仍按各自既有语义工作,不由树的存在而阻断。
- 假设面板当前显示的文件不在会话工作根之内(
/tmp下的临时图片、工作树之外的路径、或根已经换了而标签还开着),当用户查看工具条,则不出现「在文件树中显示」——树里永远不会有这一行,给一个点了毫无反应的入口不如不给(与场景 3 同一取向:不可用要有说明,没有说明就不要露出入口);这类文件仍可正常查看、评论与编辑,只是不进树。 - 假设目录树的筛选框里已经有内容(树画的是搜索命中列表而不是树本身),当用户点击「在文件树中显示」,则先清空筛选回到树形态,再按场景 8 展开并定位——否则展开只作用在没被画出来的行上,点击看起来毫无反应。
用户故事:文件面板已打开文件的标签(优先级:P1)
作为 CodeWave IDE 桌面端用户,我希望文件面板顶部有一条「已打开文件」的标签栏,打开新文件是新增一个标签而不是顶掉当前文件,以便在两个文件之间来回对照(比如改完 A 去核对 B),而不必反复回到清单里点。
为什么是这个优先级:现有语义是单文件替换(点另一个路径即顶掉当前文件),一次只能看一个文件;一旦有了目录树,浏览行为会连续打开多个文件,替换语义会让「刚看的那个」随手消失。对齐 Claude 桌面端 Code 标签页:文件在面板内部有独立标签栏(Open files),与一级 tab 栏(面板类型维度)分开——一级 tab 仍然只有唯一一个 file tab,标签只显示面板名「文件」。
独立测试:依次从目录树打开两个文件,验证出现两个标签且都能点回;在一级 tab 栏验证 file tab 仍只有「文件」一个、标签不显示文件名;关闭当前标签验证落到左邻、全部关闭后回到文件清单/占位;打开超过面板宽度的文件数验证标签栏先横向滚动、再收进「更多文件」下拉;同名不同目录的两个文件验证标签只在文件名后追加能区分彼此的最短目录后缀(不是整条相对路径);从消息里点同一文件验证激活既有标签而非新增;右键标签验证「关闭/关闭其他/关闭右侧/全部关闭」;删掉一个已打开的文件验证标签带「已删除」标记;分别验证「一个文件都没打开」与「有标签但未激活」两种空态文案;点击已激活标签验证重读刷新。
验收场景:
- 假设面板未打开任何文件,当用户从目录树、搜索框或消息里的文件路径打开文件 A,则面板顶部出现文件标签栏并新增标签 A 并激活,正文显示 A;再次打开文件 B 则标签栏新增 B(A 的标签保留),不替换 A。
- 假设标签栏已有 A,当用户再次打开 A(从树、面包屑或消息路径),则激活既有标签并重新读取刷新内容,不新增重复标签。
- 假设标签栏存在,当用户查看一级 tab 栏,则一级 tab 仍只有唯一一个 file tab、标签文案仍为「文件」——具体文件名只出现在面板内部的标签栏,不上浮到一级 tab(与 desktop-right-panel.md「一级 Tab 栏与实例语义」场景 7 一致)。
- 假设标签栏存在,当用户查看某个标签,则标签由「文件类型图标 + 文件名」组成,目录部分不占据标签宽度(悬浮给该文件的相对路径,位于工作目录之外时回落绝对路径);同名文件来自不同目录时,这些标签在文件名之后各追加一段灰色小字——能把这几个同名文件区分开的最短目录后缀(如
index.ts src与index.ts lib),其余唯一文件名仍只显示文件名;同名文件连所在目录都相同(含都在工作目录根上)而无法区分时不追加后缀。 - 假设标签栏存在,当用户查看标签的可用动作,则提供关闭当前、关闭其他、关闭右侧、全部关闭;关闭当前标签后激活其左邻,
Delete/Backspace关闭当前标签,Ctrl+Shift+←/→左右移动标签。 - 假设已打开文件的总宽在面板宽度内放不下,当用户查看标签栏,则标签栏先横向滚动、新打开的标签自动滚入可视区;仍不足以容纳时,超出可视范围的标签收进标签栏右端的「更多文件」下拉,菜单按顺序列出被收起文件的名字与所在目录,点选即激活对应标签。
- 假设用户关闭了全部文件标签,当关闭完成,则面板回到文件清单(对话有文件时)或占位提示(无文件时),一级 file tab 保持打开、不因此关闭面板。
- 假设面板处于编辑态且有未保存修改,当用户点击另一个文件标签,则先按「文件面板内编辑与保存」场景 6 的确认处理,选「继续编辑」则停留在原标签。
- 假设面板有多个标签,当用户切换标签,则每个标签各自保留自己的滚动位置与最近一次的行定位;切换不丢失、不串到其它标签。
- 假设分屏中有多个会话,当各自打开文件,则标签列表按会话隔离,切换分屏焦点不共享标签。
- 假设标签栏存在,当用户右键某个标签(或点标签上的更多操作),则菜单提供「关闭」「关闭其他」「关闭右侧」「全部关闭」,其语义与场景 5 一致;菜单弹在鼠标指针处(不是面板或窗口的某个固定角),指针贴视口右缘/下缘放不下时翻到指针的左上侧,任何情况下不出视口。
- 假设用户点击当前已激活的标签,当点击发生,则该文件重新读取刷新(相当于再次打开),不清空标签、不切走焦点。
- 假设某个已打开文件在磁盘上被删除(用户或 agent 操作),当用户查看标签栏与内容区,则该标签带「已删除」标记,激活它时内容区提示「文件已不存在」并提供「关闭标签」动作,不静默把标签移除、也不把它当作空内容展示。
- 假设面板处于无激活文件的状态,当用户查看内容区,则按两种空态区分文案——一个文件都没打开时提示「打开的文件会显示在这里」并引导「点击左侧目录树中的文件,或点击消息里的文件路径」;有标签但都没有激活时提示「未选择文件」并引导「点击上方的文件标签,或点击消息里的文件路径」。
用户故事:文件面板内查找与跳行(优先级:P2)
作为 CodeWave IDE 桌面端用户,我希望在面板里按 Ctrl/Cmd+F 就地搜索当前文件、按行号直接跳转,以便在大文件里定位,不必为了找一处而跳出到外部编辑器。
为什么是这个优先级:面板读的是源码,动辄上千行(截断上限 2000 行);现在只能靠肉眼滚动或离开面板。对齐 Claude 桌面端文件查看器:查找条支持大小写、整词、正则,带匹配计数与上一个/下一个,另有跳行输入。
独立测试:打开一个多行文件,按快捷键打开查找条、输入片段验证计数与高亮、按 Enter/Shift+Enter 在匹配间移动、切换大小写/整词/正则验证结果随之变化、无匹配验证提示;关闭查找条验证高亮清除;输入行号跳转验证滚动定位、越界验证提示;进入编辑态验证同一快捷键在编辑器内可用。
验收场景:
- 假设面板显示某个文本文件(只读态或编辑态),当用户按
Ctrl/Cmd+F,则就地打开查找条并把焦点放进输入框,不打开新的面板或对话框。 - 假设查找条已打开且用户输入关键字,当停止输入,则显示匹配计数(当前第几个 / 共几个)并高亮全部匹配,当前匹配滚动到可视区;再次按
Enter或点击「下一个」前进、Shift+Enter或「上一个」后退,到末尾时循环。 - 假设查找条已打开,当用户切换「区分大小写」「整词匹配」「正则表达式」,则结果与计数即时更新;正则非法时给出可读提示且不匹配任何内容。
- 假设查找条已打开且关键字无匹配,当查看查找条,则显示「无匹配结果」且计数为 0,不隐藏查找条。
- 假设查找条已打开,当用户按
Esc或点击关闭,则查找条关闭、全部匹配高亮清除、焦点回到正文,滚动位置停留在当前匹配。 - 假设面板显示的是 markdown 渲染结果或图片,当用户按
Ctrl/Cmd+F,则沿用渲染/图片视图的既有行为(不把整份文件切回纯文本去找)。 - 假设查找条已打开,当用户输入行号(纯数字)或「行:列」,则滚动定位到该处并高亮该行;输入超出范围时提示有效范围,不移动视图。
- 假设文件被截断展示(超过 2000 行)或存在超长行,当查找执行,则只在已加载的内容内匹配,并在结果区提示「超长行未参与搜索」这类说明,不静默漏报。
- 假设面板切换标签或换文件,当查找条仍打开,则查找条保持打开但对新文件重新计数,高亮不残留到内容之外。
- 假设面板处于编辑态,当用户使用查找或跳行,则编辑态复用编辑器自带的能力,光标与选区随跳转更新,不破坏撤销历史。
用户故事:文件面板评论(优先级:P3)
作为用户,我希望在文件面板里用与差异面板完全相同的方式评论:右键某行(或选中若干连续行、点选区末尾浮出的评论按钮)展开输入框写下评论,评论连同文件路径、行号(或行区间)与该处内容一起追加到消息输入框,以便我对一个已存在的文件(不限于本次改动)也能精准指出「要改哪一处」,连续评论多处后统一发给 agent。代码/文本文件按行 / 行区间评论;markdown 文件渲染成 HTML 后没有行号,因此按块(标题、段落、列表项、引用块、表格单元格、围栏代码块)评论,两者是同一份追加格式、同一套评论框。
为什么是这个优先级:把评论从「看改动」扩展到「读文件」——差异面板只能评论本次改动过的行,而用户常常要在长文件里指出问题,那一处并不在本次 diff 中;追加格式与无痕语义沿用差异面板的既有约定,不引入新模型(P3)。入口与 Claude 桌面端的文件查看器一致(右键 添加评论 + 选中浮出的按钮):行号列因此不必为评论按钮预留常驻槽位,行号贴左缘,窄面板下正文的可用宽度也一并回来。markdown 按块补上是同一入口的自然延伸:它恰恰是「让 agent 改这份文档的这一段」最常见的对象,而渲染后没有行号可用。
独立测试:点击消息中的文件路径打开文件面板 → 右键某行验证在指针位置弹出「添加评论」→ 点击在行下展开评论框 → 输入评论并添加 → 验证评论连同行号与该行内容追加到消息输入框(不直接发送、面板上不留痕)→ 在另一行重复添加第二条 → 验证两条评论都在输入框中 → 再选中连续若干行、点选区末尾浮出的评论按钮,验证追加内容按行区间(第 N–M 行)表达;再把同一面板切到 markdown 文件,悬停某个标题与某个段落验证块外侧出现评论按钮 → 点击在块下展开评论框 → 提交后验证追加内容含文件路径与该块文本片段、且不出现行号;切到图片文件验证不出现任何评论入口。
验收场景:
- 假设文件面板已打开一个代码/文本文件(非 markdown、非图片),当用户在代码区右键某行,则在指针位置弹出菜单,其中一项为「添加评论」;点击后在该行下方展开行内评论框(含文本输入框与添加按钮),被评论行保持可见。行号列不为评论按钮预留常驻槽位,行号紧贴面板左缘。
- 假设用户在代码区选中连续的若干行文本(跨行选区),当松开鼠标,则在选区末尾浮出评论按钮(不遮挡被选中的文字,滚动时跟着选区走),点击后按选区首末行展开评论框;选区被清除或点击别处后按钮消失。行号列宽度随该文件实际行数自适应(不按固定位数预留),同一文件内各行宽度一致、正文左缘是一条直线;行数变多时列才随之变宽。
- 假设用户不确定右键入口,当查看代码区工具条,则工具条上有「评论」按钮:点击时若存在选区则评论选区首末行,否则评论最近一次点击(或右键)的那一行;两者都没有时按钮呈不可用态并说明「点击代码行后可在此评论」,而不是点击后毫无反应。
- 假设用户在评论框输入评论并点击添加按钮(或回车),则不得直接发送给 agent,必须将评论追加到消息输入框中,追加内容包含:文件路径、行号或行区间、该处内容(或片段)、评论文本;输入框已有内容时必须保留并在其后追加;评论框关闭;不得在文件面板内留存或回显该评论(无痕,与差异面板一致);追加文本的标签与差异面板统一为「代码评论」,两个面板共用同一份追加格式,仅位置描述不同(差异面板带增删前缀与行内容片段,文件面板带单侧行号/行区间与行内容片段)。
- 假设文件面板打开的是图片,当查看该文件,则不提供任何评论入口(图片既没有行也没有块),文件本身的只读查看行为不受影响。
- 假设评论框已展开,当用户按 Esc 或点击取消,则评论框关闭且不提交;用户可继续右键或选中其他行添加评论,无需开关任何模式即可连续评论多行。
- 假设面板发生文件切换(同一 tab 换路径)、内容刷新、会话/工作目录切换或文件读取失败,则展开中的评论框必须关闭、未提交的草稿丢弃,防止行内容变化后界面状态不一致。
- 假设文件内容被截断(超过单文件行数上限,只显示前 N 行),当用户评论,则评论只能落在已显示的行上,行号以面板当前显示的行号为准;截断提示与评论入口共存,不互相遮挡。
- 假设文件面板处于空态(未打开文件)、加载中、读取失败或只显示图片,当渲染这些状态,则不得出现评论入口、评论框或工具条上的「评论」按钮。
- 假设文件面板打开的是 markdown 文件,当用户鼠标悬停某个块(标题、段落、列表项、引用块、表格单元格、围栏代码块),则该块外侧(内容左缘之外)必须出现评论按钮(markdown 没有行号,沿用的是悬停块浮出按钮的形态,不跟随代码区的右键/选区入口);悬停另一块时按钮跟随到新块,移出内容区则按钮消失,但鼠标从块移向该按钮的整段路径(含两者之间的空隙)上按钮不得消失——否则永远点不到;点击必须在该块下方展开同一个行内评论框,块内容保持可见、不被遮挡。提交后追加内容包含文件路径与该块渲染后的文本(截断到可读长度)而不含行号——渲染块与源行不对应,不得按块反推行号。嵌套时以最外层块为准(列表项里的段落、引用块里的标题都归该列表项 / 引用块),同一时刻只存在一个展开中的评论框;应用内连续评论多块、输入框既有内容保留、不在面板留存评论等语义与场景 4/6 完全一致。
非目标(明确排除)
- 远端会话的编辑:远端 SSH 会话的 file 面板继续保持只读(写能力不跨 SSH 桥),要改远端文件仍走 agent 工具或 IDE 的远端能力——对齐 Claude 桌面端「远端会话不可保存」。
- 创作集(artifact)的展示与操作:artifact 是服务端 URL,本机没有对应文件,故 file 面板不列它;仅当发布它的本地源文件被对话读写过时,才以普通文件出现在清单里。
- 本地会话的 file 面板不替代编辑器:本地会话的轻量修改由面板内的编辑态承载,但它是「顺手改一处」的入口而非编辑器替代品:多光标、格式化、代码折叠、重构等能力不做,重活仍通过「在默认应用中打开」跳出到外部编辑器/IDE(面板内的「查找与跳行」见「文件面板内查找与跳行」,不含替换)。
- 自动保存:保存一律是显式动作(工具条「保存」/ Cmd-Ctrl+S),不做输入即落盘的自动保存与后台写盘——写盘时机必须由用户决定,否则会与 agent 的写入、外部编辑器的保存互相踩踏。
- 面板内的文件管理:不做新建、重命名、删除、移动文件(目录树只读浏览);面板内的写操作只有「把当前文件的草稿写回该文件」,且只写工作目录内的路径(工作目录之外的路径即便能打开也只读)。
- 多文件同时编辑与草稿持久化:面板可以同时打开多个文件(各占一个文件标签),但同一时刻只编辑一个文件(切到别的文件即需放弃草稿);草稿不落盘、不跨重载/重启恢复,不做「每个打开的文件各留一份草稿」的模型。
- 插件端的 file 面板:VSCE/JetBrains 不提供该面板,其
openFile由 IDE 自身处理(含 IDE 的 Remote-SSH 能力)。 - 语法高亮的语言覆盖:仅覆盖常用语言子集(highlight.js common 集),未覆盖语言纯文本展示。
- 跨文件全文检索:目录树与查找条都只服务「浏览与看某一个文件」;不做工作区级的内容搜索(按文件内容在整仓范围内找命中),该需求仍由外部编辑器/IDE 承担。
- 编辑器显示设置:不提供 word wrap / tab 宽度 / 显示空白字符 / 滚动越界等的可配置项——折行与缩进外观固定(与只读视图一致),需要这些设置请跳出到外部编辑器。
- 符号跳转与大纲:不做符号列表、跳转定义、引用查找等语言级导航。