Skip to content

feat(dsh-plugin): better-sidebar 集成、会话关停与归档清理、悬浮窗修复 - #130

Open
BB-fat wants to merge 11 commits into
mainfrom
feat/optimize-dsh-plugin
Open

feat(dsh-plugin): better-sidebar 集成、会话关停与归档清理、悬浮窗修复#130
BB-fat wants to merge 11 commits into
mainfrom
feat/optimize-dsh-plugin

Conversation

@BB-fat

@BB-fat BB-fat commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

概述

优化 dsh 插件的观察视图(observation overlay),共 9 个 commit:better-sidebar 集成、会话生命周期管理(关停/归档清理)、悬浮窗 bug 修复。

改动

better-sidebar 集成

  • 检测到 dsh-better-sidebar 插件后(cordis 服务探测,未安装时行为完全不变),追踪视图从悬浮卡片迁入侧边栏单例 tab(browserskill:observation),保留 Document PiP 弹出
  • tab 品牌化:标题 "Browser Skill" + 产品 logo,与内置 browser tab 区分
  • 新会话出现时自动打开(content seed 触发面板展开);面板折叠时新会话重新聚焦;面板展开时绝不抢夺用户当前 tab
  • 按 DSH 会话隔离可见性:tab 只显示当前会话(及其血缘子代理)开启的 bsk 会话,badge 与自动打开同步过滤;悬浮卡片/PiP 保持全局视图
  • 修复页面加载时 sidebar store 异步初始化导致自动打开丢失的竞态
  • 去掉状态行底边框,消除与 shell 顶栏的错位线及暖色边框冲突
  • store 增加引用计数(acquire/release),悬浮窗/sidebar tab/集成 fiber 重叠时互不掐断 SSE

会话生命周期

  • 追踪视图关停会话:操作栏新增 stop 按钮,轻量二次确认(点击武装为红底 ✓,3 秒过期,再点执行);新宿主路由 POST /bsk-observation/stop(强制显式 sessionId,daemon 已遗忘的死会话幂等成功,先杀在途命令避免挂起拖住关停)
  • 归档自动清理:DSH 会话归档时自动关停其开启的 bsk 会话——启动时记录归属(调用方会话 + header.parentSession 血缘祖先),监听 workspace registry 的 domain/changed 差分触发;取消归档后再归档会重新清理

悬浮窗修复

  • 修复悬浮窗展开时左下/右下角无法拖拽变形:resize 热区(z-index 2)被底部工具栏(z-index 3)完全压住,提升到 4

验证

  • 162 个测试全过(新增约 30 个:四角 resize、sidebar 注册/自动打开/不抢焦点/可见性过滤、关停确认交互、归档清理差分、宿主 stopSession 语义)
  • typecheck / biome / stylelint 干净;client bundle 通过平台模块纯度门禁
  • 已在真实 profile 中通过 client-HMR 热加载逐项人工验证(tab 展示、自动打开、PiP、品牌化、关停、归档清理)

BB-fat added 11 commits August 19, 2026 13:56
…gable

The expanded observation overlay's bottom-left (sw) and bottom-right (se)
resize handles sat at z-index 2, below the full-width bottom actions bar
(z-index 3) inside the card's stacking context, so every pointer event in
the bottom corners landed on the toolbar and resize never started. The top
corners worked only because the header is unpositioned. Raise the corner
hit targets to z-index 4 so all four corners receive pointerdown again.
…-sidebar tab

When the dsh-better-sidebar plugin is installed, its client publishes a
betterSidebar cordis service; the observation tracking view now moves from
the floating card into a single-instance sidebar tab registered through
that service (detected via ctx.inject, so profiles without the sidebar
plugin keep the floating overlay untouched):

- observation-sidebar.tsx: tab descriptor (globe icon, live session-count
  badge, single instance) + ObservationSidebarTab rendering the shared
  OverlayBody without card chrome; auto-opens the tab when the first
  browser session appears but never re-focuses an already-open tab; the
  Document PiP pop-out upgrade stays available from inside the tab.
- sidebar-mode.ts: carrier flag the floating overlay subscribes to — while
  sidebar mode is active the card/capsule hides (an open PiP window keeps
  its portal), and it resumes the moment the sidebar service unloads.
- observation-view.ts: the view model (snapshot/focus/pin/ticker) and the
  PiP plumbing extracted from ObservationOverlay so both carriers share
  them; focusOf/statusOf move here and are re-exported.
- observation-store.ts: refcounted acquire/release so overlapping carriers
  (overlay, tab, integration fiber) never kill each other's SSE feed.
- OverlayBody is exported; the header only shows the move cursor when the
  carrier actually drags (data-draggable).
The sidebar already ships a built-in "browser" tab, so retitle the
observation tab to "Browser Skill" and swap the remixicon globe for the
BSK product mark (apps/extension/assets/logo.png downscaled to 32px and
inlined as a data URL — 2.4 kB, no route or asset serving needed).
…n the status row

Two live-reported issues with the sidebar carrier:

- Auto-open appeared not to work: the type-only openTab minted the tab but
  never expanded a collapsed panel, so nothing surfaced for the user. The
  open now carries an inert content seed (path), which the sidebar treats
  as a content open — it expands the hosting panel and lands the tracking
  view in sight.
- The status header's bottom border sat ~7px above the shell header's
  hairline (tab strip 34px + compact 28px card header vs the builtin
  tabs' 36px first rows), breaking the window-wide line at the panel
  boundary. In the sidebar the header now follows the builtin first-row
  metrics (36px, matching paddings); the floating card keeps its compact
  header.
…ons behind a collapsed panel

When the observation tab already exists but the panel is collapsed, a new
browser session now issues the same content open: the sidebar dedupe-
focuses the existing tab AND expands the panel (documented openTab
semantics), so the tracking view resurfaces exactly like the floating
card did. While the panel is open, an existing tab is still never
re-focused.
…rder

Pixel analysis of the user's window showed the line still stepped ~6px at
the panel boundary — the shell conversation header's height varies per
window (35-38px), so no fixed row height can land a border on it — and
the BSK warm border tint (oklch hue 60) visibly clashes with the
sidebar's neutral hairlines. Borderless in the sidebar, the status row
reads as plain chrome and the stage's own background separates it; the
floating card keeps its bordered compact header.
…with a two-click confirm

Every carrier of the observation view (floating card, better-sidebar tab,
PiP) now offers a stop button next to interrupt, driven by a lightweight
armed-state confirm: the first click flips the button to a solid-red check
(3s expiry, auto-disarm when the focus vanishes), the second executes —
a stray single click can never close an Agent Window.

- host: ObservationService.stopSession kills in-flight tool children so a
  hung command never delays the stop, funnels the stop through the
  session's keyed queue like the tool path, treats a daemon-forgotten
  session (dead strip entries) as an idempotent success, and removes the
  registry + observation entries; POST /bsk-observation/stop requires an
  explicit sessionId (destructive calls never default to "current").
- client: store.stopSession posts the stop; the session's removal arrives
  through the existing SSE remove event, so all carriers update at once.
…n it is archived

Archiving a DSH conversation hides it from every surface, but any Agent
Windows it opened kept running with no way back to them. The plugin now
records which conversation started each bsk session — the calling agent's
session id plus every ancestor along the seed lineage (a subagent's
browsers belong to the root conversation too) — and watches the workspace
registry's domain/changed broadcast for newly archived ids, stopping
those sessions through the same kill/queue/teardown path as the overlay's
stop button. Re-archiving after an unarchive cleans up again; headless
compositions (no workspace domain) degrade to a no-op.
…sation's own bsk sessions

The sidebar tab used to show every bsk session in the profile. Each
observation entry now carries the owning DSH conversations (the starting
agent's session plus its seed-lineage ancestors, stamped at addSession),
and the sidebar surface filters by them:

- the tab body (focus view + strip) shows only sessions started by the
  sidebar's own conversation or its descendants; the floating card and
  PiP keep the global view; untracked (orphan) sessions stay global-only
- the tab badge counts only sessions visible to its conversation
- auto-open fires only when a session visible to the ACTIVE conversation
  appears, and re-evaluates on sidebar state changes (subscribeState) —
  which also covers the store's async init after page load and
  conversation switches
…ests

- drop the tab-icon data-URL assertion (cosmetic constant, no logic)
- drop the sidebar PiP round-trip (already covered through the shared
  usePip path by the overlay's PiP test)
- fold the feed-lifetime check into the registration lifecycle test
- fold disarm-silence, empty-identity, and empty-ownership guards into
  their parent behavior tests

No coverage lost: 156 tests (was 162, −6 cases).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant