dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
按实际运行状态排序与折叠 DSH 左侧栏:有会话开跑的工作区浮到最前,会话跑完仍留在原位,只有最近活跃的 N 个工作区保持展开;六个开关加一个数量控制各条规则。
左侧工作区的排序与展开:按「有没有在跑」和「你正在哪儿」决定。六条规则,每条一个开关,位置在
插件页 → 点开 dsh-sidebar-focus 之后的详情页里(在描述与行列表之间)。
dsh plugin --profile web add dsh-sidebar-focus
还没上 npm 时用 GitHub 源:
dsh plugin --profile web add github:wjingshan/dsh-sidebar-focus
装完刷新页面即可 —— 本插件是纯客户端(宿主半身是空的),不需要重启 dsh web。
| 设置 | 默认 | 作用 |
|---|---|---|
| 置顶最近有活动的工作区 | 开 | 有会话跑过的工作区排到最前,按最近一次活动排序。跑完不会掉回原位,只有更新的活动才把它往下挤一位。只影响显示,你手动拖出来的工作区顺序从不被改写 |
| 置顶当前会话所在的工作区 | 开 | 你正在用的那个会话(包括刚新建、还没开始跑的)所在的工作区也排到最前。新建的会话不渲染状态点,只靠上面那条永远浮不上来 |
| 自动展开正在运行的工作区 | 开 | 有会话在跑、或正在等你的工作区自动展开。宿主本身只在「当前会话所在的工作区」上这么做,这条把它扩展到所有在跑的工作区 |
| 置顶正在进行的会话 | 开 | 运行中与等待你处理的会话排到所属工作区的最前 |
| 收起没有运行的工作区 | 开 | 没有会话在跑的工作区保持折叠;刚跑完的会先留 2.5 秒再收(宿主分两步报告「跑完了」:先清 running、之后才升起完成提醒) |
| 保留未读完成提醒 | 开 | 含「已完成未查看」的工作区不自动折叠,避免刚跑完的会话被收起来 |
| 只保留最近活跃的工作区展开 | 2 | 最近有会话跑过的前 N 个工作区一直展开,其余空闲的在宽限期后收起。用 − / + 调整,范围 0–20,0 = 关掉这条 |
自动折叠最烦人的失败模式是「你点开、它立刻折回去」。所以两侧都留了 意图豁免:
豁免只在本次页面会话内有效,刷新后回到规则本身。判断依据是 aria-expanded 上不是本插件
写出的翻转——所以「搜索结果跳转」「新建会话」这类宿主自己的展开,也被当成你的意图而保留。
另外两条不参与自动折叠的例外:
overflow-y:auto 的块容器,order 对它无效;插件先把它变成
flex 列([data-wsf-rank-tree]),再用 order 给忙碌工作区 / 活动会话排名。DOM 节点从不移动,
所以 React 的 reconciliation 与内置的 FLIP 行动画都不受影响。
order 作用在 flex item 上,而每一行外面还套着 HoverCard 的 <span>,所以排名规则用
:has() 同时匹配「被包一层」与「裸行」两种形状;JS 侧同样不能假设 parentElement,
分组位置一律由 sectionOf() 向上解析。Workspace.sessionIds,也不碰你手动排序的结果。SessionNodeItem 把状态槽写成 !row.blank && …,
一个刚建、还没发消息的会话一个状态点都不渲染(data-state 根本不存在),状态快照里也还没有它,
所以「忙」这条路永远看不见它;而 aria-selected="true" 对 blank 行同样成立,正好补上这个缺口。
两条理由分别由 pinBusyWorkspaces 与 pinCurrentWorkspace 掌管,关掉一条不会影响另一条。order(每条工作区的名次都不同,静态 CSS 表达不了),从未活动过的工作区
不带 order,保持原生顺序排在后面。于是跑完不掉位,只有更新的活动才把它往下挤一位。running,之后才升起
completionUnread),在第一帧「看起来空闲」时就折,会关掉一个刚出结果、提醒还没到的工作区。
两个配套细节:宽限期到点时若没有任何 DOM 变化,是没有人来叫醒扫描的(用 wakeIn() 定时器补);
新活动挣到的名次是在扫描过程中发现的,而 ranking 算在扫描开头(用 rankDirty 补一次扫描)。ranking(lastActive 排名),
所以列表最上面那 N 个就是保持展开的那 N 个,不会出现「浮在最上面却折着」的矛盾。把它设成 0
时判定退化成原来的「空闲 + 宽限期就折」。正在运行的工作区本来就因为 blocking 不会被折,与排名无关。deriveGroups 对折叠组返回空
sessions),所以 DOM 层做不到,必须改展开状态本身。写入走宿主自己的视图 store:
ctx.get('uiWorkspace').view.setGroupExpanded(key, expanded),键就是工作区 id。useSessionStatus(running / pendingInteraction /
completionUnread,与三条判据一一对应)加 Workspace 成员表 useWorkspaces
(items[].sessionIds 说明哪些会话属于哪个工作区)。这两个事实与「有没有渲染成一行」无关,
折叠态照样成立,所以折叠中的工作区一旦开工就会被看见并展开。
两个 hook 由挂在 shell.overlay 上的零渲染桥接组件发布给 watcher(选它只因为那个席位永远挂载)。
行仍然照读——行级标记要用,快照未就绪时也要回退;两个来源取并集,任一见到在跑就不许关。data-wsf-* 标记会被移除,CSS 规则随之失效,侧栏回到原生
布局与原生折叠行为——不做「半开」状态。dsh-client-ui-workspace)| 事实 | 来源 |
|---|---|
会话列表是 div[role="tree"] | AnimatedRows.render |
工作区行是 div[data-row-key="workspace:<key>"][role="treeitem"][aria-expanded] | ProjectRowItem |
每一行都被 HoverCard 套了一层 <span>,所以行的 parentElement 不是分组容器;分组要向上找(父元素是列表列本身,或父元素是 role="group" 容器)。未分组是唯一例外——宿主对它直接返回裸行 | ProjectRowItem / HoverCard |
会话行是 div[data-row-key="session:<id>"][role="treeitem"][aria-selected],第一个子元素是该行自己的状态槽(同样被 HoverCard 包着,所以 :scope > 读不到它) | SessionNodeItem |
状态点带 data-state ∈ {ongoing, warning, done, error, idle} | ui-primitives 的 StateDot |
展开状态存在 dsh.workspace.view.v5,groupExpansion[key] 显式为 false 才折叠 | createWorkspaceViewStore / deriveGroups |
接口是 ctx.get('uiWorkspace').view.setGroupExpanded(key, expanded) | UiWorkspaceService 的 view 字段 |
| 折叠的组不渲染任何会话行,所以运行状态必须另有来源 | deriveGroups 的 GroupNode.sessions |
槽位标准钩子 useSessionStatus / useWorkspaces 提供状态快照与工作区成员 | shell.overlay 的 standardProps |
WorkspaceSnapshot.items[].sessionIds 是「谁属于哪个工作区」 | dsh-api-workspace-controller 的 WorkspaceView / WorkspaceSnapshot |
这些是构建产物的结构,不是文档化 API:宿主升级后如果结构变了,插件会安静地什么都不做
(data-wsf-* 标记无处可落),而不是弄坏侧栏。若 uiWorkspace.view.setGroupExpanded 不存在,
折叠会被整体关闭,并在该插件的详情页上显示告警、控制台打印原因——不会静默失败。
scanGroup 里的 key === '' 判断。groupExpansion[key] 会以
false 持久化。卸载本插件后它们会保持折叠——手动点开即可恢复,或在插件页里禁用这个包再刷新。role="group" 容器里,不是列表列的直接子元素,
order 在那里无效。会话级排序不受影响。ongoing」与「等待你 warning」;error 不参与。已验证:
node --check 通过;manifest 解析通过;application: applied,profile 的 dependencies 与 dsh.profile.bundles 都已登记;plugins.bundle.config 席位上出现了 dsh-sidebar-focus 这个 key
(cordis_inspect_query 可见)。插件页只在存在该 key 的注册时才渲染那块配置
(owner 的判据是 ledger.bundles.has(pkg.name)),所以这一条同时证明详情页里会出现这 6 个开关 + 1 个计数器;settings.general.item 移除,不会两处重复;shell.overlay 的实时占用者里有 dsh-sidebar-focus.status-bridge,
而该席位的 standardProps 同时提供 useSessionStatus 与 useWorkspaces。真实 GUI 实测确认(2026-10-17,用户实际操作):
− / + 计数器工作正常。包括 0.1.1 那个根因修复(sectionOf() 的三种形状 + CSS :has())在内,这些行为此前只是从宿主源码
推演出来的,现在有了真实页面上的确认。
仍未验证(没有可读的 DOM 通道;按插件规范的验证边界,未用截图或模拟 DOM 冒充):
order 对子组无效,这是已声明的限制;自查方式:刷新 Web 页面 → 打开一个正在跑的工作区(应自动展开并置顶)→ 打开一个空闲工作区
(应保持折叠,你手动展开后应保持展开)→ 控制台不应出现 [dsh-sidebar-focus] 开头的报错。