# 附录:dsh-better-sidebar 0.19.1 的 DOM 契约审计(逐行证据) > **来源与性质**:本文件是 2026-09-17「壁纸插件 × better-sidebar 适配」核查时,由子代理对 > **第三方包** `dsh-better-sidebar@0.19.1` 做的**只读**结构审计(审计对象=npm tarball 解包目录 > `/tmp/dsb-0191/package`,sha512 见 `docs/BETTER-SIDEBAR-COMPAT.md` §1.1)。每条结论都带 > `file:line`;写 "not found" 的地方=该解包里查不到证据(而不是"猜")。 > **用途**:我们 `bsCompat` 的选择器锚点依据(哪些属性/类名是稳定的、哪些在 0.19 被删)。 > **不构成**对该包任何代码的复用或再分发;本仓库不打包它的任何产物。 > 上游仓库:https://github.com/omdsh-dev/DSH-better-sidebar (MIT)。 --- # dsh-better-sidebar 0.19.1 — DOM contract: bottom workbench, native right sidebar, session header Audit target: `/tmp/dsb-0191/package` (dsh-better-sidebar 0.19.1, extracted; read-only, nothing modified). Priority: `src/client/**` (TSX) > `lib/client*.js` (built) > `README.md`. Every claim carries `file:line`; "not found" = no evidence in this extraction. Base path for all citations is `/tmp/dsb-0191/package/` (abbreviated below as the relative path). No 0.16.x/0.18.x artifact exists in the extraction, so era-migration answers are stated as 0.19.1 status + README-history evidence only. ## A. Root / host elements | Region | JSX tag | ALL `data-*` on it (name → value) | CSS-module class expression | Evidence | |---|---|---|---|---| | Plugin mount host (root of every region below) | `div` created imperatively, appended to `document.body` | `data-dsh-better-sidebar` → `''` (always) | none — selected only by the attribute; no `panelHost` class exists anywhere (`grep -rn panelHost src` = 0 hits) | `src/client/index.tsx:307` (createElement), `:308` (setAttribute), `:309` (appendChild), `:311` (React root renders `RenderBoundary`+`Sidebar`) | | Panel-host layer (containing block for the panel) | `div` | `data-dsh-panel-host` → `''` (always); `data-dsh-panel-host-degraded` → `''` only in degraded mode (page-level transform self-check) | none (styling by `:global([data-dsh-panel-host])`) | `src/client/Sidebar.tsx:713` (main), `:611` (no-session early return); degraded write `src/client/index.tsx:276`, cleared `:272`, `:290`; CSS `src/client/sidebar.module.css:35`, `:66` | | Bottom workbench panel root | `div` (ref `bottomRef`) | `data-dsh-panel` → `''`; `data-dsh-bottom-panel` → `''`; `data-dragging` → `'true'` only while the top edge is dragged | `clsx(css.bottomPanel, !state.bottomOpen && css.bottomPanelHidden)` | `src/client/Sidebar.tsx:726-746` (attrs `:729`, `:730`, `data-dragging` `:745`); built confirmation `lib/client.js:16455-16468` (`"data-dsh-panel": true, "data-dsh-bottom-panel": true, "data-dragging": draggingBottom \|\| void 0`) | | Panel resize strip (top edge) | `div` | none | `clsx(css.bottomResize, draggingBottom && css.bottomResizeActive)` | `src/client/Sidebar.tsx:747-774` | | Panel collapse button (inside strip's right end) | `button` | none (`aria-label` only) | `css.bottomClose` | `src/client/Sidebar.tsx:781-788` | | Panel body (hosts the workbench) | `div` | none | `css.panelBody` | `src/client/Sidebar.tsx:790`; CSS `src/client/sidebar.module.css:104` | | Workbench root (split tree) | `div` | none | `css.workbench` | `src/client/split-pane.tsx:317`; built `lib/client.js` (`css_default.workbench` ×1) | | Split container / child | `div` | none | `clsx(css.split, isRow ? css.splitRow : css.splitCol)` / `css.splitChild` | `src/client/split-pane.tsx:271`, `:281` | | Pane (leaf) root | `div` | `data-dsh-pane` → **leaf id string** (`leaf.id`, a plugin-generated pane id) | `clsx(css.pane, dropZone !== null && css.paneDrop)` | `src/client/split-pane.tsx:174-176`; built `lib/client.js:14858` (`"data-dsh-pane": leaf.id`) | | Pane drop overlay | `div` (conditional) | none | `clsx(css.dropOverlay, css['drop'+Zone])` | `src/client/split-pane.tsx:197` | | Pane content + per-tab wrapper | `div` | none | `css.paneContent`; `clsx(css.paneTab, inactive && css.paneTabHidden)` | `src/client/split-pane.tsx:227`, `:229-232` | | Empty-pane cards | `div` / `button` | none | `css.paneEmptyCards` / `css.paneCard` | `src/client/split-pane.tsx:129`, `:134` | | **Tab bar root** | `div` | **none — no `data-dsh-*` attribute at all** | `clsx(css.tabBar, dragOver && css.tabBarDrop)` | `src/client/TabBar.tsx:164-165`; built `lib/client.js:14531` (same `clsx`, no attribute keys in the `jsx("div", …)` props) → only the build-hashed class identifies it | | Tab bar scroll list / tab / title / close / “+” | `div`,`div`,`span`,`button`,`button` | none | `css.tabList`; `clsx(css.tab, tabActive, pinnedTab)`; `css.tabTitle`; `css.tabClose`; `css.tabBarPlus` | `src/client/TabBar.tsx:185`, `:189-191`, `:234`, `:237`, `:273` | | Right sidebar column | **not rendered by this plugin** — DSH host markup (native `dsh-client-ui-dockkit`, host class `.paneBody`); the plugin only *styles* it via `:global` and injects bodies | n/a | plugin side: `css.nativeTabHost` wrapper only | `src/client/native/index.ts:157-167` (registers into host slots `sidebar.right.pane.tab`, `sidebar.right.pane.tab.title`); `src/client/native/surface.ts:63-148` (writes via `ctx.sidebarRight*`); `src/client/sidebar.module.css:2040-2056` (host `.paneBody` contract); README:4, :53, :325 | | Plugin's tab-body host inside the native column | `div` (via `createElement`) | `data-dsh-native-tab-host` → `''` (both the live and the orphan-fallback body) | `css.nativeTabHost` | `src/client/native/tab-adapter.tsx:297` (orphan branch), `:308-309` (live branch); CSS `src/client/sidebar.module.css:2051` | | Session header / title bar (chat header) | plugin contributes one `button` into DSH's header *utilities* slot; the header itself is host markup | `data-dsh-bottom-toggle` → `''` (always); `data-active` → `'true'` iff panel open, otherwise absent | `css.toggleButton` | registration `src/client/sidebar/bottom-toggle.tsx:23-28` (slot `conversation.session.header.utilities`, `order: 10`); button `src/client/Sidebar.tsx:819-829` (attrs `:822`, `:823`); built `lib/client.js:16549` | | Document-level markers written by the plugin | `body` / `html` (DSH host elements) | `body[data-dsh-title-bar-compat]` → `''` when strip > 0; `html` style `--dsh-title-bar-strip: `; `body[data-dsh-sidebar-dragging]` → `''` while dragging; `body[data-dsh-tab-dragging]` → `''` while a tab drag is live | n/a | `src/client/Sidebar.tsx:241-249`, `:242` (var), `:520` (var removal), `:524-525`; `src/client/TabBar.tsx:56-57`; CSS consumers `src/client/layout.css:54`, `src/client/sidebar.module.css:166-167`, `:1860-1864` | | Pane-content sub-regions (no data hooks) | `div` | none | `css.editorHeader` (`EditorHost.tsx:412`, `TextEditor.tsx:437`, `OrphanedTab.tsx:18`); `css.browserBar` (`BrowserView.tsx:166`); `css.terminalWrap` + `css.terminal` (`TerminalView.tsx:410`, `:434`) | same lines; xterm DOM styled through `:global(.xterm)` at `src/client/sidebar.module.css:1885` | ## B. Every `data-dsh-*` attribute emitted by 0.19.1 Own elements (plugin-rendered markup): | Attribute | Value / condition | Site | Meaning | |---|---|---|---| | `data-dsh-better-sidebar` | `''`, always | `src/client/index.tsx:308` | Marks the plugin's own body-level mount host (one per page) | | `data-dsh-panel-host` | `''`, always | `src/client/Sidebar.tsx:713`, `:611` | Fixed viewport-sized containing block for the panel | | `data-dsh-panel-host-degraded` | `''` only in degraded (page-transform) mode | `src/client/index.tsx:276` | Switches host to `position:absolute` + per-frame viewport sync | | `data-dsh-panel` | `''`, always | `src/client/Sidebar.tsx:729` | Marks the single docked panel inside the host | | `data-dsh-bottom-panel` | `''`, always | `src/client/Sidebar.tsx:730` | Marks the bottom workbench panel specifically | | `data-dsh-pane` | leaf id string | `src/client/split-pane.tsx:176` | Identifies each workbench pane (one per leaf) | | `data-dsh-bottom-toggle` | `''`, always (+ `data-active='true'` when open) | `src/client/Sidebar.tsx:822-823` | The expand/collapse button in the session header | | `data-dsh-native-tab-host` | `''`, always | `src/client/native/tab-adapter.tsx:297`, `:309` | Full-height flex wrapper around a tab body inside DSH's native right column | | `data-dsh-revealed` | `'true'` only when the row is the reveal target, else absent | `src/client/FileTree.tsx:649`, `:681` | Marks explorer rows highlighted by “show in folder” | | `data-dsh-html-segment` | `''`, always | `src/client/MarkdownHtml.tsx:204` | Marks a sanitized HTML block segment | | `data-dsh-md-toc-panel` | `''`, always (panel open + ≥ min headings) | `src/client/md-toc.tsx:113` | Markdown outline popover root | | `data-dsh-md-toc` | `''`, always | `src/client/md-toc.tsx:133` | Outline toggle button | | `data-dsh-preset-css` | preset id, e.g. `dsh-desktop`; else tag not created | `src/client/Sidebar.tsx:92`+`:264`; preset ids `src/client/shell-presets.ts:57` | Identity tag on the injected preset `