# 配置和适配说明 普通使用者无需修改以下配置。通过 Harness 的标准 bundle 机制加载插件;管理页的启停选择是插件是否加载的依据。 ## 配置 | 字段 | 默认 | 范围 / 含义 | | --- | --- | --- | | chatWidth | 400 | 280–800,初始宽度,像素 | | chatHeight | 540 | 240–1000,初始高度,像素 | | edgeGap | 20 | 0–64,与工作区边缘的间隔,像素 | | rememberGeometry | true | 在浏览器 localStorage 中记录窗口几何信息 | | compactIdleSeconds | 90 | 0–3600,紧凑条闲置回收秒数;0 关闭自动回收 | 实际尺寸限制在可见工作区内。记录的几何信息会优先于初始尺寸;双击右下角可恢复配置尺寸并保留当前位置,双击标题栏可恢复配置尺寸和默认位置。若当前位置无法容纳恢复后的尺寸,会调整位置以保持小窗可见。 配置由宿主入口校验,经 `/dsh-full-view/api/config` 传给客户端。非法配置在加载时拒绝。插件卸载后移除该路由。 ## 适配范围 宿主加载 `lib/index.js`,客户端通过 `window.__ModuleLoader__` 加载 `lib/client.js`。客户端依赖 `layout`、`sidebarRight`、`sessions` 和 `uiSession` 服务。 macOS 宿主首列全屏标签栏为窗口控件预留 `--dsh-dockkit-strip-inline-start: 88px`,系统原生全屏改为 10px。仅在 frame 带插件完整视图标记、没有 `[data-sidebar-collapsed]` 且目标为全屏首列 dock 时,以 `10px !important` 去掉侧栏已避让后的冗余留白。侧栏收起时退出这条覆盖,直接服从宿主的 88px / 系统全屏 10px 规则,不复制窗口按钮坐标或改写宿主属性。浮动面板、其他列和普通分栏保留宿主规则。 适配器通过 `data-rightbar-col`、`data-rightbar-fullscreen`、`data-sidebar-right-panel` 等语义标记找到原聊天容器,并检查会话身份匹配。它改变原聊天容器的定位,不重建会话、编辑器或 iframe。返回分栏使用面板自身的按钮。 宿主更改上述标记或相邻结构时,需要更新适配器。目前针对 layout / sidebar-right 0.2.0-rc.2 验证;未支持旧源码中的 details 布局。 小窗层级为 50,在原工作面板之上、DockKit 原有浮窗层级 60 之下。宿主 `[data-shell-overlay]` 保持原层级 20,本插件不再整体抬高公共父层。`dsh-selection-explain` 在 body 管理自己的 fixed 根,层级 70、空白区域 pointer-events:none;其子窗口与按钮仍由该插件管理。`dsh-recent-sessions` 的 body 浮层为 19,低于公共父层与解读根。宿主模型菜单自身 portal 到 body、层级 1100,继续高于这些插件界面;未声称所有宿主弹窗组合均已验证。 完整视图只公布自有 frame 的 `data-dsh-full-view` 状态,不再通过 CSS 控制解读胶囊。解读观察该状态并在自己的根上设置 `data-full-view`,由自己的样式隐藏胶囊;退出或完整视图卸载后自行恢复。解读根公开 `data-dsh-floating-owner="explanation"`,聊天通过该归属识别解读的入口、面板、历史、语音卡及内部控件,保留旧版本 `.dsh-sel-*` 选择器兼容。点击解读或其 iframe 不收起聊天;普通工作区点击仍遵循原收起规则。两个插件各自创建与清理自己的节点,解读槽的稳定 ref 在卸载时摘除自有根、重新挂载时复用原节点。 跨仓库验证(先构建三个相邻仓库):`node scripts/test-plugin-layers.mjs` 执行实际客户端的独立 DOM 集成;`node scripts/smoke-plugin-layers.mjs` 在 Chrome 中检查实际层叠与命中、空白穿透、全屏往返、连续 Esc 和卸载。后者最近会话采用真实构建 CSS 与列表结构夹具,宿主菜单采用已核对的 1100 层级;仅模拟数据与模块 / 槽服务,不连接真实 Harness。无头虚拟时钟用 timer 推进 rAF 调度,此检查不验证动画时序。 Esc 共存通过完整视图自有 frame 上的 `data-dsh-active-floating-window` 协作,值为 chat / explanation / none。完整视图捕获 pointerdown,记录最后点击的窗口;focusin 与非 Esc 键盘操作支持 Tab 和继续输入,标题、消息、空白及解读同级 historyList 都属于窗口。点击两窗外或聚焦外部 iframe 清为 none;宿主 portal 菜单保留打开它的窗口。Esc 本身不更新活动归属,不用事件目标反推用户最后点击。聊天收起 / 隐藏、换会话清为 none,程序归还编辑焦点不重新激活;退出 / 卸载删除标记和监听。解读在捕获阶段读取活动标记,活动聊天仍展开时将 Esc 留给聊天,否则退出自己的一层并阻止同一次事件继续传播。聊天在冒泡阶段依照活动标记决定是否忽略解读弹窗,保留其他菜单、输入法和审批保护。两端不移动对方节点或拦改对方监听,普通分栏保留解读原全局 Esc;未升级的完整视图没有活动标记时解读兼容旧键盘焦点判断。 标题栏增加 `[data-dsh-pin-chat]` 图钉 toggle,统一使用 26px 点击区域和 15px 图标,`aria-pressed` 表达固定状态;选中后使用主题色及淡色背景,不加悬停 title。默认不固定,只阻止 expanded 的 outsidePointer 和外部 iframe windowBlur 自动收起,不阻止网页获得焦点、不拦截网页事件。显式「−」、外缘、Esc、返回分栏保持原行为;手动隐藏再恢复保留本会话固定,退出 / 卸载或切换会话清除固定,不持久化到部署配置。 标题采用可键盘聚焦的普通 div(role=group),保留方向键移动和无悬停 title;移除专用拖拽手柄。标题栏统一接收非 button 区域冒泡的 pointer 事件并捕获到 toolbar,标题文字后代、留白及间距均使用同一拖动路径。操作按钮及其 SVG 后代不启动拖动;标题不再匹配 button:hover,背景透明、box-shadow 为 none,标题栏也不添加阴影。保留键盘 focus-visible 轮廓及整个小窗原有阴影。 紧凑态 `[data-dsh-full-view-edge]` 及其 `[data-dsh-edge-side]` 使用 grab 光标,frame 标记 `[data-dsh-fv-dragging]` 时改为 grabbing;同时设置 pointer capture 的外缘按钮本身及可命中的后代,避免捕获期间退回按钮光标。输入区、上边缘收球按钮及原生操作按钮不覆盖,左右缩放区域保留 ew-resize。 卸载和热重载会清理插件的工具栏、样式、几何标记、事件与观察器,保留宿主原有内容及布局状态。 ## 紧凑输入条(0.1.4) 通过 `data-composer-seat`、`data-composer-card`、`data-input-scroll` 找到原生输入区,给控制行及其两组控件附加临时标记,用网格将其与原编辑器排成一行。模型显示图标,权限文字收成图标,按钮原有 aria-label 和菜单保留。没有复制编辑器或转发输入事件。 进入完整视图默认收起,隐藏消息、标题栏及输入框下方详情,保留输入区、附件与必要提示。观察输入区实际高度,空输入条至少 48px;多行文本和附件自动增高。展开与收起保持底部位置,移动保存展开尺寸所对应的位置。 紧凑输入条的上下外缘各 12px(向外 6px、向内 6px),左右 e/w 缩放区域宽 8px(向外 2px、向内 6px),top/bottom 为 0,覆盖整个内框高度;缩放 z-index=4 高于外缘的 3,顶部中央收球按钮为 5。compact 的 overflow:visible 使用 !important,防止宿主较高优先级裁切圆角外的命中区。输入路径继续保留自身裁切,expanded 仍使用原外框裁切和原 6px 外缘。compact 用 :has([data-dsh-edge-side]:hover) 仅在拖动区悬停时加深原两层阴影,box-shadow 以 160ms ease 过渡;输入、操作按钮、缩放区和收球按钮不会匹配这条反馈,减少动态效果规则禁用过渡。中间区域让原编辑器和按钮正常操作。移动超过 4px 按拖动处理,随后点击不切换;键盘 Enter / 空格仍使用原生按钮行为。展开后右下角保留独立的缩放区域。 宿主替换输入框节点时重新附加标记,退出或卸载删除全部临时标记。未提供上述输入区标记的宿主回退到原有标题栏收起方式。 ## 0.1.6 适配说明 交互采用紧凑、展开、隐藏三种状态,操作栏显示与消息区展开是两个独立条件。鲸鱼恢复入口附着在工作区,避免被原聊天的 inert 隔离。缩放与移动共用受约束的几何计算;鼠标取消恢复起始偏好,视口变化只限制呈现,不写回偏好。 标题只负责移动:按住拖动或用方向键调整位置,单击不改变形态;外缘切换紧凑 / 展开;左上角「−」始终隐藏整个小窗,保留恢复入口。减号的辅助名称及悬停提示都表示隐藏,不再使用 aria-expanded。 主操作图标使用统一的 18px 尺寸,权限图标的包裹元素同步调整,模型脑形 mask 复用同一个尺寸变量,下拉箭头保留宿主的较小尺寸。移除 pointerenter / pointerleave 标题栏触发。用户 focusin 或再次 pointerdown 原编辑器直接进入 expanded;程序归还焦点用同步保护避免收起后反复展开。标题栏只在完整小窗中显示,不增加紧凑高度。 紧凑输入路径的宿主背景设为透明,避免原会话的方形白底覆盖外框圆角;不通过 overflow:hidden 裁切菜单。模型按钮的原 SVG 仅以临时属性隐藏,使用 currentColor 的脑形 SVG mask 显示替代图标,保留原按钮、下拉箭头和事件。宿主重绘按钮时重新适配,退出或卸载移除临时属性并恢复原图标。 只适配当前顶层会话的 composer,排除内嵌子会话。审批、提问、计划确认的原生标记触发展开;轨迹视图的 overlay 标记不参与待办判定。原输入区被替换时优先展示替代组件。退出与停用还原原有辅助属性,移除视觉视口监听和未完成的点击计时器。 具体行为、参考依据和无法现场验证的边界见 [交互设计说明](interaction-design.md)。 ## 运行状态与鲸鱼入口(0.1.6) 读取 `uiSession.sessionStatus` 的当前会话 running / pendingInteraction,借用 `sessions.binding(id)`,不延长会话生命周期。订阅该 binding 的 session 与 eventSource,取最后一个 `turn/start` 的 event.time;已有匹配 `turn/end` 的旧回合不能成为下一回合的计时起点。缺失起始记录时显示“处理中…”。 在原 `data-input-scroll` 内按占位文字位置覆盖低对比度耗时,与按钮同一行,不增加卡片高度,不复制原编辑器、不弹出新内容通知。只有当前会话正在运行、没有待办且输入为空时显示;输入草稿时让出位置,清空后重新显示。完成、等待确认或退出完整视图停止秒级计时。切换会话或 binding 代际会释放旧订阅;停用释放所有订阅及计时器。不存储会话内容。 40px 圆形恢复按钮只显示 Harness 原生鲸鱼尾巴,无可见文字及悬停标题,辅助名称保留“恢复聊天”。运行时读取已安装宿主 `ChatView.module.css` 样式中的 APNG mask,直接使用原生动画;静止时使用宿主 RunningWhaleTail 的原生 SVG fallback 路径(ui-chat 0.2.0-rc.2,viewBox 16×16、strokeWidth 1),保持 24px 图标尺寸,由浏览器按屏幕像素密度渲染矢量轮廓。原路径及 DeepSeek 的 MIT 许可保留于 whale.js,构建产物也保留许可。运行 APNG 为 28×28 位图,按宿主原生 14px 图标尺寸显示,避免在 2x 屏幕中再次放大到 48px。遵从 prefers-reduced-motion 和 forced-colors,恢复和拖动继续使用原交互。动画仍直接读取宿主样式,不随插件打包;静止路径来源明确固定于已验证的宿主版本,宿主更新图案时需同步适配。 ## 聚焦展开、隐藏统计与形态过渡(0.1.6) `data-dsh-full-view-input-footer` 在整个浮窗状态下隐藏,退出或卸载后标记移除,普通分栏的原统计恢复。聚焦或点击当前顶层会话的原编辑器直接展开;原生菜单和输入法仍由现有保护逻辑处理。隐藏入口恢复时直接进入 expanded。 用 CSS transition 在 280ms 内同步顶部位置与高度,保持底部锚点;圆角 240ms,标题 opacity / discrete display 160ms。紧凑输入路径固定在浮窗底部,减少形态变化时的输入位置跳动。隐藏面板通过 opacity、轻微 transform 与 visibility 过渡,交互状态立即 inert;鲸鱼入口反向淡入 / 淡出。CSS 负责中途反向衔接,没有延后写入旧状态的动画计时器。拖动禁用过渡以立即跟随,系统 prefers-reduced-motion 下关闭全部形态动画。 ## 紧凑条主动收起 `data-dsh-compact-recycle` 是原浮窗的独立按钮,仅有原 composer 的紧凑状态显示。位于上边缘中间,48×18px 命中区内显示 28×3px 横条;透明定位不占输入网格宽度。层级高于外缘和缩放区域,不绑定拖动、外缘切换或双击复位。按钮保留辅助名称和焦点环,不设置 title。 单击或原生按钮的 Enter / 空格触发 `setMode('hidden')`,复用连续缩入和鲸鱼恢复;清理可能尚未完成的外缘单击计时。待回应和输入法组词时禁用并校验点击状态。展开后隐藏,仍由标题栏减号收起;退出、卸载和重新挂载时移除旧按钮,不复制输入框。自动回收配置为 0 时,主动入口仍可用。 ## 紧凑条闲置回收 只在具备原生输入区的 compact 状态设置一次性计时器,默认 90 秒。用户在原聊天内的 pointerdown / pointermove / keydown / wheel / input 重新计时;流式 DOM 变化和任务状态订阅只维持当前截止时间。展开、隐藏、菜单、拖动、中文组合输入及审批 / 提问 / 计划确认取消计时,恢复可回收状态后重新开始完整周期。到期再次检查当前状态,调用原隐藏过渡并保留工作区焦点,鲸鱼图形继续读取真实任务状态。退出、卸载和会话切换清理计时及 IME 状态;0 禁用回收。标题与工具栏空白不注册单击切换,标题仍支持拖动、键盘移动及双击复位。 ## 浮窗滚动条与输入框对齐 宿主 `[data-conversation-scroll]` 同时包含消息和 composer,原样式有 `scrollbar-gutter: stable` 和 2px 右边距。紧凑形态的输入路径使用全宽定位,会释放这块预留空间,导致展开 / 收起时输入框宽度变化。浮窗标记范围内,聊天及 `[data-input-scroll]` 使用 `scrollbar-width: none`、`scrollbar-gutter: auto` 和零尺寸 WebKit 滚动条;聊天滚动容器右边距改为 0。保留宿主 overflow 和原滚动节点,滚动与长草稿编辑仍由 Harness 处理。退出完整视图或卸载后覆盖失效,分栏恢复宿主滚动条与间距。 ### 鲸鱼球状态 通过宿主 `uiSession.sessionStatus` 读取运行、`pendingInteraction.kind` 和完成提醒;等待回应优先于运行,运行优先于完成,最后为空闲。标题在当前主会话被保留时会清除 `completionUnread`,因此同时核对最新匹配的 `turn/start` / `turn/end`,区分真实回合完成;完成点是否显示还需通过未读判断。新的运行和切换会话不会继承之前的完成状态。 运行状态保留动画标记和辅助标签,但隐藏小点;待回应优先于运行,仍显示提醒点。6px 小点位于 40px 球内的 top/right 7px。颜色直接引用原生 StateDot 的 `--dsw-alias-label-tertiary`(ongoing)、`--dsw-alias-state-success-primary`(done)、`--dsw-alias-state-warn-primary`(warning)、`--dsw-alias-state-idle-primary`(idle)。状态点不拦截鼠标,不显示文字或悬停标题,辅助标签区分等待回答、审批和计划确认。 原本隐藏的会话出现待办时保留鲸鱼球;点击恢复展开原控件,并优先聚焦待办的输入控件或操作按钮。紧凑条遇到待办仍展开。已展开待办处理前禁止再次隐藏,处理后恢复原形态。显示时 visibility 立即生效,隐藏时延迟至淡出结束,避免动画开始时拒绝输入焦点。 ### 连续缩入与反向恢复 `src/client/morph.js` 实现用户选定的「连续缩入」;440ms,采用与独立预览一致的三次缓出。收起时前 24% 淡出内容,7% 后开始外框尺寸和位置插值,65% 后显示原鲸鱼球。恢复时鱼尾先淡出,外框展开,后 24% 恢复内容和输入焦点。 外框为插件拥有的空装饰层,不复制消息、不重建编辑器,也不重新挂载原聊天。内容保持原宽高,以平移和裁切跟随外框,所以文字不会压缩或重排。隐藏保留原来的紧凑 / 完整呈现形态,只改变交互与可见性;原聊天立即 inert。动画结束清理所有临时属性、样式和装饰层,恢复时才归还原输入或待办焦点。 连续反向操作以当前帧的尺寸、位置和透明度为起点。工作区尺寸变化安全结束动画;切换会话直接取消旧动画,避免旧回调抢新会话焦点。自动回收不抢工作区焦点。退出完整视图、卸载或热重载取消帧回调和所有临时层。系统减少动态效果、无 matchMedia 的环境直接切换。原有高度 / 顶部过渡仅用于完整小窗与紧凑条切换,不再叠加隐藏、恢复的旧淡出动画。 ### 附件与输入栏排列 宿主附件轨道保留在输入编辑器之前。浮窗 composer 使用自动行排列和 dense 填充:附件及前置辅助区各自占整行,编辑器与两侧操作区填入后续一行的对应列。窄窗口先放附件,再放跨列编辑器,最后排列两侧按钮。不再把编辑器或按钮强制定位到第一行;删除最后一个附件后自然回到无附件的紧凑高度,返回分栏还原宿主布局。 ### 完成小点的未读判断 `completionId` 来自匹配回合的 turn 与结束时间,`completionUnread` 保留宿主明确提供的未读信息。首次进入不把历史已结束回合自动当作未读;在紧凑 / 球状态下观察到新的结束身份时,生成本回合完成提醒。打开完整小窗、切换到该会话或结果在展开小窗中完成,均视为已查看。已查看后再收起或重复接收同一个结束事件,不重新点亮。新回合结束才生成新的提醒。 会话已完成这一事实保留,但已查看的完成点直接隐藏,不替换成空闲灰点。运行与待回应标识继续按原优先级显示。未读记录仅在插件内存中按会话区分,不修改宿主状态、不保存聊天内容;退出当前完整视图清理该视图的记录,再进入以宿主当前未读状态为准。