--- description: "面向 DeepSeek Harness Web GUI 的 pi-web 风格侧边栏:顶部临时会话、工作目录选择器与其下"一次只显示一个目录"的会话列表、发送前不创建任何东西的草稿输入框,以及接管右侧栏「文件」标签页的紧凑文件浏览器。" kind: "package-reference" --- # dsh-plugin-pi-ui [English](README.md) | 中文 ## 概述 本包把 Web GUI 的左侧边栏重新组织成读起来像同一套系统的几个区块,全部使用 harness 的 `--dsw-*` 设计变量,因此跟随所有内置主题: 1. **顶部:临时会话。** 最新的即用即弃会话,默认显示 5 条,**查看更多**每次再 加载 10 条。+ 按钮进入草稿态;🗑 清理没有任何会话在使用的临时目录。 2. **中间:工作目录。** 锚定在侧边栏垂直中央,紧凑下拉框用来选择当前工作的 目录——它下面的随附会话列表**永远只显示该目录**的会话。 3. **右侧栏:紧凑文件浏览器**,占据原本显示内置工作区文件视图的「文件」标签页。 贯穿三者的一条规则:**发送之前什么都不存在。** 任何"新建会话"手势都只是进入 草稿输入态;在你真正发出第一条消息之前,不会创建任何临时目录、也不会在侧边栏 多出任何会话行——发送的那一刻目录与会话才一起创建,或者复用指定目录里已有的 空白会话。 ## 目录 - [安装](#安装) - [使用](#使用) - [实现说明](#实现说明) - [延伸阅读](#延伸阅读) - [模型侧体验](#模型侧体验) - [已知限制与后续工作](#已知限制与后续工作) - [开发备注](#开发备注) ----- ## 安装 这是一个普通的树外插件包:一个 node 半部、一个浏览器半部,以及一个 loader patch。把它加进 profile 后重启 `dsh web`。 ```sh # 在已能解析该包的 profile 中: dsh plugin --profile web add dsh-plugin-pi-ui ``` 包内声明了 `dsh.bundle.patch`,所以上面这条命令会把它追加到 `dsh.profile.bundles`,其自带的 `cordis.patch.yml` 会在下次启动时生效。等价的 手写行放在 profile 自己的 patch 层: ```yaml # ~/.dsh/profiles/web/cordis.patch.yml - insert: - id: pi-ui name: 'dsh-plugin-pi-ui' ``` 两种方式**只能用一种**:同一个 row id 插入两次会导致启动失败。本地开发时用 link 依赖,改动在重启后依然有效: ```jsonc // ~/.dsh/profiles/web/package.json { "dependencies": { "dsh-plugin-pi-ui": "link:/absolute/path/to/dsh-plugin-pi-ui" } } ``` **必须重启 `dsh web`。** 浏览器半部在启动时被合成进 `window.__DSH_BOOT__`,所以 新加入的客户端插件无法出现在已经运行的服务器里(node 半部是可以热加载的)。 重启后刷新页面。 卸载时:从 `dsh.profile.bundles`(或 profile patch)里删掉该行、移除依赖、重启。 临时目录会留在磁盘上,不需要时删除 `/scratch/`。 ## 使用 ### 侧边栏一瞥 ``` [ 临时会话 1 🗑 + ] ⋮ (侧边栏垂直中央) [ 工作区 [ 📁 …/my-project ▾ ] + ] [ 该目录下的会话 … ] ``` 侧边栏展开时,随附的整宽"新建会话"按钮、搜索放大镜、添加工作区按钮都会让位, 由插件提供下表控件;折叠成 rail 时插件不绘制任何东西,随附控件全部回来。 | 控件 | 行为 | | --- | --- | | **临时会话**标题行的 + | 进入**临时会话**草稿态。点击本身不创建任何东西;发送时临时目录与会话一起创建。若会话创建失败,刚建的目录会被删除,不留孤儿目录。 | | **临时会话**标题行的 🗑 | 删除没有会话在使用的临时目录(仅当存在这样的目录时出现)。会先确认,且从不碰正在使用的目录。 | | **工作区**与其 + 之间的下拉框 | 工作目录选择器:已有目录、**打开文件夹…**、**删除工作目录…**。选中有会话的目录会打开它的空白会话(否则打开最新会话);选中还没有任何会话的目录,则进入草稿态而不是先建一个会话。 | | **工作区**行右侧的 + | 进入"**在所示目录中新建会话**"的草稿态。点击不创建任何东西;第一条消息才创建会话,若该目录已有空白会话则直接复用。 | | **删除工作目录…** | 列出所有已注册目录及其会话数,可移除选中的那个。移除只动注册表:目录本身、其中的文件与会话日志都保留,那些会话只是不再按该工作目录分组。 | **临时会话**行用于重新打开对应的 scratch 会话。临时会话刻意不出现在下拉框里 (那个控件保持为纯粹的目录选择器),也不会注册成 workspace,因此不会在持久化的 工作区注册表里越积越多。 ### 发送之前什么都不存在 任何开始手势最终都落在同一个地方:草稿输入框,而不是新会话。这样侧边栏不会再 出现没人写过的"新会话"行;临时会话的目录也只在你发送的那一刻才出现在 `/scratch/` 下。 草稿输入框就是随附的空白态卡片,只是上面叠了**两个可用的镜像控件**:覆盖在卡片 自身输入区上的可编辑输入框,以及覆盖在卡片(禁用的)发送圆钮上的发送控件。卡片 的其它一切都还是随附原样——虚线边框、附件按钮、权限选择、模型座位、发送按钮的 外观,全部由随附组件渲染。插件不加提示文字,也不添加自己的控件。打字后按 Enter 或点发送圆钮即可。 ### 会话列表永远只显示一个工作目录 **工作区**下面的区域是"某个工作目录的会话列表",一次只显示一个目录的会话——就 是选择器当前显示的那个:当前会话所属工作区优先,否则是你上次选定的目录。临时 会话不携带工作目录,因此既不会改变这个列表,也不会出现在其中。没有开关、也没有 "显示全部"状态:选择器本身就是选择动作。 选择器属于你,而不是属于当前会话:上次选定的目录记在浏览器里,所以进出临时会话 永远不会把它清空。搜索(区块标题行的放大镜)不受列表显示范围影响,仍然搜索全部 会话。 ### 设置 **设置 → 通用**新增两个开关: - **文件浏览器**——用本插件的浏览器替换右侧栏内置的文件视图;关掉立即恢复内置 视图,无需刷新。 - **工作目录切换器**——本插件侧边栏 chrome 的总开关。 两者都只在内存中,刷新后重置。这是有意为之:要持久化就应该走设置服务,而不是 客户端本地存储。 ## 实现说明 ### 为什么用浮层实现 随附侧边栏在其浏览区域上方没有声明任何座位,而品牌行里的座位 (`sidebar.brand.mark` / `sidebar.brand.name`)都是已被随附包占用的 `single` 单元——注册进去会遮蔽产品品牌,而且两者都在一个"开始会话"的 `