# 并入官方右侧栏:迁移说明与自研 dock 外壳退役范围 > 适用客户端:DSH Desktop 2.0.9 起的 `rightbar` 槽位;本机已核对 2.0.13 的官方侧栏包 0.1.5-rc.2 接口 > 状态:**琉璃四向 dock 承载原生官方 Seat**。新版客户端默认启用,旧版自动回退自研详细页;可在「设置 → 功能」手动切换 > 相关代码:[`src/client/liuli-dock-surface.tsx`](../src/client/liuli-dock-surface.tsx)(核心)、 > [`src/client/sidebar-right-tabs.ts`](../src/client/sidebar-right-tabs.ts)、 > [`src/client/dock-shell-frame.tsx`](../src/client/dock-shell-frame.tsx)、 > [`src/client/index.ts`](../src/client/index.ts) ## 0.0 架构修订(重要,覆盖下文早期描述) **早期做法(已废弃)**:以 `priority: -1` 遮蔽官方 `rightbar.session` 席位,让琉璃的 dock surface 占住它。**代价是官方 `ctx.sidebarRight` 彻底失效**: ```js // 官方 client.js bindService: (binding) => controller.bind(binding) // ← 官方 RightbarSeat 挂载时调用 require() { if (this.binding === void 0) throw new Error("sidebarRight: no session surface is mounted") } ``` 遮蔽 Seat ⇒ binding 永不建立 ⇒ 官方 controller 每个方法都抛 `sidebarRight: no session surface is mounted`。受害的是**官方插件**: - 对话页「交付文件」的文件按钮(`ui-deliverables` 的 `gemp6G_file`)→ `ui-chat` 的 `openFile` → `ctx.sidebarRight.openResource(url)` → 抛错 ⇒ **点击完全无反应**; - 该错误文本还会显示在界面上(用户实测反馈)。 **现在的做法**: 1. 官方 `rightbar.session` **不遮蔽** —— 官方 Seat 照常挂载,`bindService` 建立 binding, 官方导航 API 继续可调用; 2. 官方 Seat 在帧根部**常驻挂载**,即使用户把详情区域拖出 dock 树, `ctx.sidebarRight` 的绑定仍在。默认隐藏;当琉璃 dock 的「官方侧栏」标签 处于活动态时,按该标签正文的实际矩形显示原生 Seat。它以 `viewportWidth:0` 进入官方 auto-fullscreen 呈现,`canShow:false` 不分配 第二条右栏轨道;浮窗只在原生标签活动时可见; 3. 琉璃的 dock surface 由**帧层在详情卡片里直接渲染** (`dock-shell-frame.tsx` 的 `REGION_DETAILS` 分支 → ``), 于是既有官方的完整 API,又有琉璃的四边分栏 / 4 格 / 浮窗 / 跨区域拖拽; 4. 官方 API 打开的 tab 仍由官方 Tab 域持有。**轮询桥** `startOfficialTabBridge` 观察官方 `active()`:官方内建 `guide`、`files`、 `text`(以及未来未映射的原生类型)聚焦琉璃 dock 的「官方侧栏」标签, 在其中直接显示原生 Seat;琉璃自己注册的 `liuli-*` 类型则映射回原有 琉璃面板。原生文档预览不再降级成 CodeViewer,Markdown、代码、PDF、 图片、HTML、打开方式、重载和行号导航由官方原组件处理。 桥只观察**当前活动 tab**,但原生 Seat 自己持有并显示全部原生标签, 切换、关闭、资源导航和浮窗仍由官方实现。琉璃只为整棵 Seat 提供外层停靠位置。 dockkit 由上游标注为内部引擎,升级时需要核对其契约。 官方展开按钮的 CSS Module 类名在本机 2.0.13 已变化;琉璃按钮现在使用 `LiuliDockSurface.module.css` 自有样式和旧版 `PanelRightOpen/CloseIcon`, 不再硬编码官方哈希类名。默认启用「禁用右栏内部分栏」:隐藏琉璃 dockkit 与 原生 Seat 的分栏按钮,并禁止原生 tab 的拖拽分栏手势;琉璃标签在栏内边缘与 栏外的落点仍交给整窗 Dockable 布局。 设置中关闭该项即可恢复右栏内部最多 4 格的分栏。 右栏标签条以 dockkit 的 `data-dockkit-*` 稳定属性对齐整窗琉璃标签样式: 48px 标签条、8px 圆角、相同的活动底色/描边/阴影和无常驻分隔线。 初始控制器不再预置「审查」tab;没有停靠标签时显示旧版「打开标签页」入口, `+` 菜单与空状态共用带图标的功能列表。 旧设置中保存的 `official_sidebar_right:false` 与旧默认值无法区分,升级后按新版 默认启用;曾手动关闭此选项的用户需要在设置页再关闭一次。 **2.0.13 本机运行检查(2026-09-24)**:官方宿主与琉璃 surface 各 1 份; 官方 `openTab('guide')` 在琉璃标签里显示原生 8 项引导入口,进入「工作区文件」 后可见官方文件树与重新读取按钮,点击 README.md 得到官方 `text` tab 与 Markdown 渲染器。PNG 得到 Blob 图片渲染器(160×28),HTML 得到带 `sandbox="allow-scripts"` 的 Blob iframe(775×897),PDF 得到官方 PDF 渲染器和页面 canvas。可见原生面板与琉璃占位几何同为 775×973,主帧保持 2560px,只有一份官方面板,无双轨挤压。 以 `{ params: { line: 100 } }` 打开 TS 文件后,官方「代码」渲染器的正文滚动 位置到 1935px。原生形态按钮可铺满 1936×953 视口,再退回 775×894 的 标签占位;官方文件 tab 可浮出成 450×350 面板并放回,浮窗宿主保持点击穿透。 经典按钮设置默认开启时,右栏内置分栏按钮不可见、dockkit 原生 `+` 按钮为 0, 琉璃 `+` 入口与旧版页头图标各 1 份;关闭「禁用右栏内部分栏」后分栏按钮 出现,重新开启即隐藏。禁用内部分栏时将标签拖到会话区上缘,整窗 `dock-drop-indicator` 显示 `split` 落点;在右栏内边缘松手后,外层 panel 数量增加、内层 pane 数不变,源 tab 关闭。 > 以下 §0–§0.4 是现行增强方案的实现记录;§1–§7 保留了早期迁移试验的历史记录, > 其中关于遮蔽 Seat、默认关闭、官方界面负责分栏的描述不再代表当前行为。 **官方文件打开的完整链路(实测确认)**: ``` 对话页文件按钮(ui-deliverables,style 类 gemp6G_file) → ui-chat 的 openFile(path) → fileAddressFor(sessionId, cwd, path) → sessionFileAddress → ctx.sidebarRight.openResource('dsh-resource://file/session//') → 官方 layout 出现 tab { kind:"text", contentId:"dsh-resource://file/session//" } → 琉璃轮询桥捕获 → 聚焦 dock 中的「官方侧栏」标签 → 原生 text tab 直接通过官方 Tab 域加载并渲染文件 ``` `CodeViewerPanel` 仍用于琉璃自身的代码查看入口;官方文件地址不再由桥解析 并转换为该面板,而交给官方文档预览的资源导航、参数 revision 和加载器。 ## 0. 最终形态:并入官方外壳,右栏内部换成琉璃的 dockable 布局 `ui-sidebar-right` 自己就是用 `@deepseek-ai/dsh-client-ui-dockkit` 渲染的,但它把能力 **收窄**成了「右栏内左右两格」: ```js ``` 官方 README 明确写着这是**调用方的策略**而非引擎限制: > `dropZones="horizontal"` 提供左右两个半区提示……**Sidebar 使用 0.2 并在自己的 store 限制两格。通用引擎仍保留原有树与其它分割方向。** 引擎实测常量:`MAX_DOCK_PANES = 4`、`dropZones?: 'edges' | 'horizontal'`、 `DOCK_ZONES` 含 `center/top/right/bottom/left`。 因此琉璃按官方 README 点明的第二条嵌入路径(「经由自己 store 路由的嵌入方则实现同名 方法」)**用自己的 `DockController` 实现 `DockIntents`**,把 `DockSurface` 接到自己的 布局状态上,在官方外壳内拿回完整能力: | 能力 | 官方 `RightbarRoot` | 琉璃 `LiuliDockSurface` | |---|---|---| | 落区 | `horizontal`(仅左右) | **`edges`(四边,含上下分栏)** | | 分栏上限 | 2 格 | **4 格(`MAX_DOCK_PANES`)** | | 浮窗 | 有 | 有(`FloatLayer`) | | 标签条 / chip / 右键菜单 / 分隔条 / 键盘无障碍 | 有 | 有(同一套 dockkit 组件) | | tab 内容 | 官方注册表(files/documentpreview/…) | 琉璃 7 个面板 | **接入点**:不重写 root 层。`rightbar` 是 root 作用域席位(框架不注入 `sessionId`), 官方 `RightbarRoot` 用 `SessionProvider` 包住后渲染 session 作用域的 `rightbar.session` 子席位;琉璃以 `priority: -1` 注册在 **`rightbar.session`** 上 (single 席位「最低者渲染」=本 entry 生效、官方 Seat 被遮蔽),而**官方 `RightbarRoot` 照常挂载**。 **⚠️ 遮蔽官方 Seat 的连带影响(实测,必须知道)**: 官方 `ctx.sidebarRight` 控制器的每个方法都走 `this.require()`,而 `require()` 依赖 **官方 `RightbarSeat` 挂载时建立的会话绑定(`binding`)**。遮蔽该席位后 binding 永不建立: ``` ctx.sidebarRight.toggleExpanded() → "sidebarRight: no session surface is mounted" ctx.sidebarRight.isExpanded() → 恒 false → 官方 header 那枚「打开右侧边栏」按钮点了无效 ``` 因此迁移模式下**开合必须自己提供**。琉璃的做法:在 `conversation.session.header.corner` 席位(single)以 `priority: -1` 遮蔽官方 `ExpandButton`,放一枚**同款外观**的按钮(照抄官方样式类 `GzLz_G_button` / `GzLz_G_icon` 与图标 `IconPanelLeftOutline16`,官方包已把该 CSS 注入到 `