@shgroup/dsh-serenity-hooks 1.67.0 → 1.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.67.0",
3
+ "version": "1.68.0",
4
4
  "main": "lib/index.js",
5
5
  "description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/官方 ACP 面/认知轨迹唤醒注册表。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档,deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
package/lib/client.js CHANGED
@@ -8,7 +8,7 @@ window.__ModuleLoader__.load({
8
8
  let react = require("react");
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
10
  //#region \0sp-css:src/client/SafeModePanel
11
- const css$5 = "/* SafeModePanel — 会话头部 Serenity 状态胶囊(v1.24.10 胶囊改版,对齐 OcgoDockEntry pill)\n * 胶囊 999px 全圆 → 点击 → 自绘 popover(340px 右上角卡片,外点/Escape 关闭)\n * popover 内:分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应)+ --sp-safe-green 统一绿家族;\n * 前缀 sp- 防冲突。 */\n\n/* ── 统一绿色变量家族(一处改全改;emerald 500~600 区间保证深色对比度) ── */\n\n.sp-root {\n --sp-safe-green: #0ba875; /* emerald-550:SAFE ON 主色 */\n --sp-safe-green-deep: #059669; /* emerald-600:渐变深端 */\n}\n\n/* ── 头部状态胶囊(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 9px; /* 无固定 height:由内容撑高(胶囊) */\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n/* 绿点 = Serenity 在线(恒亮绿,不随 SAFE 变);离线/未激活 → 灰 */\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-cardInactive {\n opacity: 0.85;\n}\n\n.sp-cardInactive .sp-brand {\n color: var(--dsw-alias-label-secondary);\n font-weight: 500;\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n/* 版本号:monospace + translateY(1px) 下沉对齐(与文字基线齐平) */\n.sp-ver {\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-family: var(--ds-font-family-code, monospace);\n transform: translateY(1px);\n}\n\n/* 1px 深灰分隔线(两侧紧凑) */\n.sp-sep {\n flex: none;\n width: 1px;\n height: 12px;\n margin: 0 2px;\n background: var(--dsw-alias-label-tertiary);\n}\n\n.sp-shield {\n display: inline-flex;\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n.sp-shield svg {\n width: 14px;\n height: 14px;\n}\n\n.sp-statusWord {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* SAFE OFF(未保护):灰——低姿态提醒,非错误(用户自己的开关选择) */\n.sp-cardUnprotected .sp-shield {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-cardUnprotected .sp-statusWord {\n color: var(--dsw-alias-label-secondary);\n}\n\n/* SAFE ON(保护中):emerald 绿——安心(盾牌实色 + SAFE 文字渐变质感) */\n.sp-cardProtected .sp-shield {\n color: var(--sp-safe-green);\n}\n\n.sp-cardProtected .sp-statusWord {\n color: var(--sp-safe-green);\n background: linear-gradient(135deg, var(--sp-safe-green) 0%, var(--sp-safe-green-deep) 100%);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* ── Mac 风格滑块(v1.24.10:轨道 24×13 / thumb 11px 灰白 #f2f4f6 无图标 / cubic-bezier 平滑) ── */\n\n.sp-switch {\n display: inline-block;\n width: 24px;\n height: 13px;\n border-radius: 999px;\n position: relative;\n flex: none;\n cursor: pointer;\n transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff {\n background: var(--dsw-alias-border-l2);\n}\n\n.sp-switchOn {\n background: var(--sp-safe-green);\n}\n\n.sp-switchThumb {\n position: absolute;\n top: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #f2f4f6; /* Mac 灰白 thumb(明暗通用) */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff .sp-switchThumb {\n left: 1px;\n}\n\n.sp-switchOn .sp-switchThumb {\n left: 12px; /* 24 - 11 - 1 */\n}\n\n/* CSS 特异性坑(S013 指南):.sp-switch:hover (0,2,0) 会覆盖 .sp-switchOn (0,1,0)\n * → ON 态 hover 绿底消失。必须同特异性且后定义补回。 */\n.sp-switch:hover {\n filter: brightness(1.08);\n}\n\n.sp-switchOn:hover {\n background: var(--sp-safe-green);\n}\n\n.sp-switchBusy {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n flex: none;\n}\n\n/* ── 自绘 popover(340px 右上角小卡片,替代官方 Modal) ── */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n}\n\n/* ── 自绘 popover(卡片,替代官方 Modal)\n v1.36.1(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):340 → **680px**(双倍)。\n v1.39.1(所有者:「下拉 dialog / 弹窗内容过宽后,会被左侧元素遮挡」):\n 🔴 根因 = **祖先 `overflow:hidden` 裁切**,不是 z-index 也不是宽度不够。\n 真机取证(CDP hit-test,1280/1600/1920 三档一致):\n · 胶囊右端 x=954.2、卡片 680 宽 ⇒ 左边界 274.2\n · 而会话中列 `pI_x6G_centerCol` / `wSkVaW_root` 都 `overflow:hidden` 且左边界 = **280**\n ⇒ 左边缘 **5.8px** 被裁掉(左边框线与圆角消失)。\n 加宽前(340px)左边界 614.2 远在 280 右侧 ⇒ **这正是 v1.36.1 加宽引入的回归**。\n 修法:**改为 `position: fixed`** —— fixed 子元素**不受祖先 overflow 裁切**;\n 坐标由 SafeModePanel 用 `getBoundingClientRect()` 算出并夹取进视口(见 .tsx 的 useLayoutEffect)。\n (本仓 client 侧**无 `react-dom` 依赖** ⇒ `createPortal` 不可用;fixed 是等效且更轻的出路。\n 已在真机验证祖先链**无** transform/filter/contain/will-change ⇒ fixed 不会被降级为相对某祖先。)\n 宽度仍走 `min(680px, 100vw-24px)`(窄视口兜底),窄屏退回单栏见文末 @media。 */\n.sp-pop {\n position: fixed; /* 不受祖先 overflow:hidden 裁切;left/top 由 JS 计算 */\n top: 0; /* ← 声明默认值,JS 会覆写(未落位时组件给 -9999 屏外值,不会闪到右下角) */\n left: 0;\n /* 🔴 2026-10-18(owner:「可以到 1000px 宽,长度更长些,允许存在滚动条」):\n 680 → **1000px**。这是 CCC 状态面板的**容器**,未来更多 CCC 内容都装在这里,\n ⇒ 宽度按\"要承载什么\"给,不按\"当前装了多少\"给。\n 窄视口仍走 `calc(100vw - 24px)` 兜底(不因追求宽度而在小屏上溢出)。 */\n width: min(1000px, calc(100vw - 24px));\n z-index: 2000;\n box-sizing: border-box;\n padding: 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 14px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);\n}\n\n/* ── 航行动画栏:宁静号心智模型(ACC 层资产 /serenity/voyage,S142 §12.44 → v1.36.1 改右栏)\n v1.36.1 改版(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):\n 动画从**背景层**改为**右栏**。这一改动解开了 v1.36.0 的取舍死结——\n 旧布局里动画垫在正文底下,必须靠 0.75 的卡片底色把它压住才读得清字;\n 分段之后两者不再争同一片像素 ⇒ 动画可以亮、正文可以干净,不必再互相妥协。\n ⚠️ 动画本身仍是「卡片模式」(词元压到 22%、铭牌层关闭),本栏只负责**给位置**,不改作品。 */\n.sp-popVoyage {\n overflow: hidden; /* 裁掉 iframe 在 14px 圆角外的溢出 */\n /* 分段后不再需要 0.75 的重压暗:压到 0.9 只压整体观感,不与正文争对比度。\n 若所有者觉得动画太亮/太暗,这仍是主旋钮(次旋钮 = iframe 的 blur)。 */\n background: rgba(6, 11, 14, 0.9);\n display: flex;\n flex-direction: row;\n /* 两栏都从**同一行高**分配(row 轴 cross = 高度):左栏按内容、右栏吃满行高。\n 行高 = 左栏内容高度,且被 max-height 封顶 ⇒ 信息长时内部滚动,动画不把行撑破。 */\n align-items: stretch;\n gap: 12px;\n padding: 12px;\n /* 🔴 2026-10-18(owner:「长度更长些,允许存在滚动条」):120 → **80**。\n 上限贴着视口走,把能给的垂直空间尽量给出去;真正超长时由**内部两栏各自滚动**消化\n (左栏 `.sp-popBody` 有 overflow-y:auto,右栏动画吃满行高)——卡片本身仍不溢出视口。 */\n max-height: calc(100vh - 80px);\n}\n/* 左栏:内容区(**主体**)—— owner 2026-10-18:「内容区作为主体,用多 panel 模式」\n 旧形态:固定 340px 的信息栏 + 吃掉剩余宽度的动画栏(动画占了大半张卡片)。\n 现在:内容区 `flex: 1` 吃掉主体,动画降为**定宽窄栏**(见 .sp-voyage)。\n ⚠️ 顺序仍由 `order` 决定(内容在左、动画在右),与 DOM 顺序解耦。 */\n.sp-popVoyage > .sp-content {\n order: 1;\n position: relative;\n z-index: 1;\n}\n/* 右栏:动画——从\"吃剩余宽度\"降为**定宽装饰条**\n owner 的原话是\"内容区作为主体\" ⇒ 装饰不该再与主体争面积。\n 240px:够看清飞船与词元,又不至于把两个 panel 挤窄。窄屏(@media)时退回全宽置于下方。 */\n.sp-voyage {\n order: 2;\n flex: none;\n width: 240px;\n min-width: 0;\n position: relative;\n pointer-events: none; /* 纯展示:不挡点击、不抢焦点 */\n overflow: hidden;\n border-radius: 10px;\n /* 卡片加高后动画也随之变长,避免\"加高只换来留白\" */\n min-height: 360px;\n}\n.sp-voyage iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n /* ⚠️ 用 iframe 自身的 filter 而**不是** backdrop-filter:iframe 内容不参与本文档的\n backdrop(跨文档不采样)⇒ backdrop-filter 对它无效,只有 filter 才是可靠的杠杆。\n v1.36.1:动画移入独立右栏后不再压在文字后面 ⇒ 磨砂可以收得很轻(1.2 → 0.6px),\n 既保留柔化,又让飞船细节(舷窗/引擎环)比旧版清楚。 */\n filter: blur(0.6px);\n}\n\n/* 窄屏(放不下两栏)⇒ 退回单栏:信息在上、动画在下(DOM 顺序即如此,无需改结构) */\n@media (max-width: 860px) {\n .sp-pop {\n /* 两栏放不下时,卡片回到\"不超过视口\"的旧行为 */\n width: min(520px, calc(100vw - 24px));\n }\n .sp-popVoyage {\n flex-direction: column;\n align-items: stretch;\n }\n .sp-popVoyage > .sp-content {\n flex: 1 1 auto;\n }\n /* 单栏时 panel 网格也退回单列(窄屏两列并排会各自只剩半屏不到的宽度) */\n .sp-panels {\n grid-template-columns: 1fr;\n }\n .sp-voyage {\n flex: none;\n width: auto;\n height: 180px;\n min-height: 0;\n }\n}\n\n.sp-popBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: calc(100vh - 140px);\n overflow-y: auto;\n}\n\n/* ── 分组(官方 settings 语言:groupTitle + rows) ── */\n\n.sp-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-groupHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.sp-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* ── safe-mode 状态徽标(owner 2026-10-18:「显示小标志即可,是个状态信息」)──────────\n 旧形态 = 盾牌 + SAFE 字样 + Mac 滑块(看起来像控件、实际可点)。\n 现在 = **不可交互**的小徽标:一个状态点 + 两三个字母。\n ⚠️ 刻意**不做 hover 反馈、不做 cursor:pointer** —— 它不是按钮。\n 这类\"长得像控件却只是信息\"的形态,正是旧版让人误以为面板能改 safe-mode 的成因。 */\n.sp-safeBadge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n flex: none;\n padding: 1px 7px;\n border-radius: 99px;\n font-size: 10.5px;\n line-height: 15px;\n font-weight: 600;\n letter-spacing: 0.03em;\n border: 1px solid transparent;\n white-space: nowrap;\n}\n.sp-safeBadgeOn {\n color: #6ee7b7;\n background: rgba(16, 185, 129, 0.12);\n border-color: rgba(16, 185, 129, 0.35);\n}\n.sp-safeBadgeOff {\n color: var(--dsw-alias-label-secondary);\n background: rgba(127, 127, 127, 0.1);\n border-color: var(--dsw-alias-border-l1);\n}\n.sp-safeDot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n flex: none;\n background: currentColor;\n}\n\n/* ── 运行环境:一行元信息(owner:「比较丑,简易一些」)──────────────\n 旧形态 = 三个带边框的 row 卡片。它们是**背景事实**(路径/模型/守卫),\n 逐行加框等于把\"不值得对焦的信息\"提到与主体同等的视觉权重。\n 现在 = 一行等宽小字,溢出省略(path 很长时靠 title 给全文)。 */\n.sp-meta {\n flex: none;\n display: flex;\n align-items: baseline;\n gap: 6px;\n min-width: 0;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n white-space: nowrap;\n overflow: hidden;\n}\n.sp-metaPath {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.sp-metaSep {\n flex: none;\n opacity: 0.45;\n}\n\n/* ── 多 panel 主体(owner:「内容区作为主体,用多 panel 模式」)──────────\n 布局契约:这一层是**主体**,所以它拿 `flex: 1`;右栏动画被压到定宽窄条。\n panel 网格两列等宽 —— 轨迹与技能并排,各自内部滚动。\n ⚠️ 用 grid 而不是 flex:两 panel 要**等宽且同高**(等高让\"多 panel\"读起来是一个整体)。 */\n.sp-content {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n gap: 10px;\n overflow: hidden;\n}\n.sp-panels {\n flex: 1 1 auto;\n min-height: 0;\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 10px;\n overflow: hidden;\n}\n.sp-panel {\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n.sp-panelHead {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.sp-panelTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n.sp-panelEmpty {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n/* panel 内列表:**自身滚动**(不把 panel 顶长),外层不再套第二层滚动 */\n.sp-list {\n margin: 0;\n padding: 0;\n list-style: none;\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.sp-item {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n padding: 4px 6px;\n border-radius: 6px;\n font-family: var(--ds-font-family-code, monospace);\n font-size: 11px;\n line-height: 16px;\n}\n.sp-item:hover {\n background: var(--dsw-alias-bg-overlay);\n}\n/* 标识列:UID 或编码。定宽让多行对齐,与目录名分离 */\n.sp-itemId {\n flex: none;\n min-width: 56px;\n max-width: 120px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.sp-itemName {\n flex: 1;\n min-width: 0;\n color: var(--dsw-alias-label-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.sp-itemNameFull {\n flex: 1 1 auto;\n}\n.sp-itemAge {\n flex: none;\n min-width: 32px;\n text-align: right;\n color: var(--dsw-alias-label-secondary);\n}\n/* 空壳标记:目录在但没有 SESSION.md ⇒ **真异常**(激活会失败),不是阶段 */\n.sp-itemWarn {\n flex: none;\n padding: 0 5px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 14px;\n color: #fff;\n background: #c2410c;\n}\n/* 已建模标记:🟢 **信息**不是缺陷(无 skill.ttl 完全正常)⇒ 中性灰,不用告警色 */\n.sp-itemTag {\n flex: none;\n padding: 0 5px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 14px;\n color: var(--dsw-alias-label-secondary);\n border: 1px solid var(--dsw-alias-border-l1);\n}\n\n/* ── 活跃轨迹清单(owner 2026-10-18 需求二)─────────────────────────\n 形态:每行 = [S### 定宽] [目录名 伸缩] [天数 定宽右对齐] [空壳标记 条件]\n · 用 mono 字体:目录名与 S### 是**标识符**,等宽才扫得快\n · 名字用 `text-overflow: ellipsis` 单行截断 + `title` 全文 —— 目录名很长时\n 宁可行内截断,也不让一行折成三四行把整个清单撑得读不下去\n · 整个列表**有上限高度 + 自身滚动**:轨迹会越攒越多,而它的容器(左栏)不该跟着无限长 */\n.sp-count {\n font-size: 11px;\n line-height: 16px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n padding: 0 6px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 99px;\n}\n\n.sp-trajList {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 2px;\n /* 清单自身封顶:活跃轨迹会越攒越多,超出后本列表滚动,不把左栏顶长 */\n max-height: 240px;\n overflow-y: auto;\n}\n\n.sp-trajItem {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n padding: 5px 8px;\n border-radius: 8px;\n font-family: var(--ds-font-family-code, monospace);\n font-size: 11px;\n line-height: 16px;\n}\n.sp-trajItem:hover {\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-trajId {\n flex: none;\n min-width: 40px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-trajName {\n flex: 1;\n min-width: 0;\n color: var(--dsw-alias-label-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sp-trajAge {\n flex: none;\n color: var(--dsw-alias-label-secondary);\n text-align: right;\n min-width: 32px;\n}\n\n/* 空壳标记:目录在但没有 SESSION.md —— 这是**真异常**(激活会失败),不是阶段。\n ⚠️ 因此用告警色,而不是任何\"状态\"色。 */\n.sp-trajWarn {\n flex: none;\n padding: 0 5px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 14px;\n color: #fff;\n background: #c2410c;\n}\n\n/* 行卡(rowCard:border + radius + padding;值与 label 分离、长内容可换行) */\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-label {\n /* 🔴 曾写死 `width: 76px` ⇒ 「CheapWorker 模型」这类长标签会**溢出到数值上**\n (nowrap + 定宽 ⇒ 文字直接压过去,不换行也不报错)。\n 改 `min-width`:短标签仍在同一列对齐,长标签自己撑开,绝不压数值。 */\n flex: none;\n min-width: 76px;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n/* 安全模式动作区 */\n.sp-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n color: var(--dsw-alias-state-error-primary);\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleDesc {\n flex: 1;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* 🔴 2026-10-14:`.sp-cheapWorkers*` / `.sp-cheapWorker*` 样式区块已随 background 模式删除\n —— 那是「运行中 cheap-worker」列表(进度文件驱动)的样式,而进度文件不再有生产者。 */\n\n/* 底部提示(配置入口引导) */\n.sp-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 10px;\n}\n";
11
+ const css$5 = "/* SafeModePanel — 会话头部 Serenity 状态胶囊(v1.24.10 胶囊改版,对齐 OcgoDockEntry pill)\n * 胶囊 999px 全圆 → 点击 → 自绘 popover(340px 右上角卡片,外点/Escape 关闭)\n * popover 内:分组(groupTitle 标题 + rowCard 行卡)——看齐官方 settings 设计语言\n * 遵循 web-styling.md:颜色只用 --dsw-alias-* 语义 token(明暗自适应)+ --sp-safe-green 统一绿家族;\n * 前缀 sp- 防冲突。 */\n\n/* ── 统一绿色变量家族(一处改全改;emerald 500~600 区间保证深色对比度) ── */\n\n.sp-root {\n --sp-safe-green: #0ba875; /* emerald-550:SAFE ON 主色 */\n --sp-safe-green-deep: #059669; /* emerald-600:渐变深端 */\n}\n\n/* ── 头部状态胶囊(可点击) ── */\n\n.sp-card {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 9px; /* 无固定 height:由内容撑高(胶囊) */\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease),\n border-color var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-card:hover {\n background: var(--dsw-alias-interactive-bg-hover);\n border-color: var(--dsw-alias-border-l2);\n}\n\n.sp-card:focus-visible {\n outline: 2px solid var(--dsw-alias-button-ghost-active-border);\n outline-offset: 1px;\n}\n\n.sp-card:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n/* 绿点 = Serenity 在线(恒亮绿,不随 SAFE 变);离线/未激活 → 灰 */\n.sp-dot {\n flex: none;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--dsw-alias-state-success-primary);\n}\n\n.sp-dotOff {\n background: var(--dsw-alias-label-dimmed);\n}\n\n.sp-cardInactive {\n opacity: 0.85;\n}\n\n.sp-cardInactive .sp-brand {\n color: var(--dsw-alias-label-secondary);\n font-weight: 500;\n}\n\n.sp-brand {\n color: var(--dsw-alias-label-primary);\n font-weight: 500;\n}\n\n/* 版本号:monospace + translateY(1px) 下沉对齐(与文字基线齐平) */\n.sp-ver {\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n font-family: var(--ds-font-family-code, monospace);\n transform: translateY(1px);\n}\n\n/* 1px 深灰分隔线(两侧紧凑) */\n.sp-sep {\n flex: none;\n width: 1px;\n height: 12px;\n margin: 0 2px;\n background: var(--dsw-alias-label-tertiary);\n}\n\n.sp-shield {\n display: inline-flex;\n width: 14px;\n height: 14px;\n flex: none;\n}\n\n.sp-shield svg {\n width: 14px;\n height: 14px;\n}\n\n.sp-statusWord {\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0;\n white-space: nowrap;\n}\n\n/* SAFE OFF(未保护):灰——低姿态提醒,非错误(用户自己的开关选择) */\n.sp-cardUnprotected .sp-shield {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-cardUnprotected .sp-statusWord {\n color: var(--dsw-alias-label-secondary);\n}\n\n/* SAFE ON(保护中):emerald 绿——安心(盾牌实色 + SAFE 文字渐变质感) */\n.sp-cardProtected .sp-shield {\n color: var(--sp-safe-green);\n}\n\n.sp-cardProtected .sp-statusWord {\n color: var(--sp-safe-green);\n background: linear-gradient(135deg, var(--sp-safe-green) 0%, var(--sp-safe-green-deep) 100%);\n -webkit-background-clip: text;\n background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n/* ── Mac 风格滑块(v1.24.10:轨道 24×13 / thumb 11px 灰白 #f2f4f6 无图标 / cubic-bezier 平滑) ── */\n\n.sp-switch {\n display: inline-block;\n width: 24px;\n height: 13px;\n border-radius: 999px;\n position: relative;\n flex: none;\n cursor: pointer;\n transition: background 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff {\n background: var(--dsw-alias-border-l2);\n}\n\n.sp-switchOn {\n background: var(--sp-safe-green);\n}\n\n.sp-switchThumb {\n position: absolute;\n top: 1px;\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: #f2f4f6; /* Mac 灰白 thumb(明暗通用) */\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n transition: left 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.sp-switchOff .sp-switchThumb {\n left: 1px;\n}\n\n.sp-switchOn .sp-switchThumb {\n left: 12px; /* 24 - 11 - 1 */\n}\n\n/* CSS 特异性坑(S013 指南):.sp-switch:hover (0,2,0) 会覆盖 .sp-switchOn (0,1,0)\n * → ON 态 hover 绿底消失。必须同特异性且后定义补回。 */\n.sp-switch:hover {\n filter: brightness(1.08);\n}\n\n.sp-switchOn:hover {\n background: var(--sp-safe-green);\n}\n\n.sp-switchBusy {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n}\n\n.sp-chev {\n color: var(--dsw-alias-label-dimmed);\n flex: none;\n}\n\n/* ── 自绘 popover(340px 右上角小卡片,替代官方 Modal) ── */\n\n.sp-root {\n position: relative;\n display: inline-flex;\n}\n\n/* ── 自绘 popover(卡片,替代官方 Modal)\n v1.36.1(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):340 → **680px**(双倍)。\n v1.39.1(所有者:「下拉 dialog / 弹窗内容过宽后,会被左侧元素遮挡」):\n 🔴 根因 = **祖先 `overflow:hidden` 裁切**,不是 z-index 也不是宽度不够。\n 真机取证(CDP hit-test,1280/1600/1920 三档一致):\n · 胶囊右端 x=954.2、卡片 680 宽 ⇒ 左边界 274.2\n · 而会话中列 `pI_x6G_centerCol` / `wSkVaW_root` 都 `overflow:hidden` 且左边界 = **280**\n ⇒ 左边缘 **5.8px** 被裁掉(左边框线与圆角消失)。\n 加宽前(340px)左边界 614.2 远在 280 右侧 ⇒ **这正是 v1.36.1 加宽引入的回归**。\n 修法:**改为 `position: fixed`** —— fixed 子元素**不受祖先 overflow 裁切**;\n 坐标由 SafeModePanel 用 `getBoundingClientRect()` 算出并夹取进视口(见 .tsx 的 useLayoutEffect)。\n (本仓 client 侧**无 `react-dom` 依赖** ⇒ `createPortal` 不可用;fixed 是等效且更轻的出路。\n 已在真机验证祖先链**无** transform/filter/contain/will-change ⇒ fixed 不会被降级为相对某祖先。)\n 宽度仍走 `min(680px, 100vw-24px)`(窄视口兜底),窄屏退回单栏见文末 @media。 */\n.sp-pop {\n position: fixed; /* 不受祖先 overflow:hidden 裁切;left/top 由 JS 计算 */\n top: 0; /* ← 声明默认值,JS 会覆写(未落位时组件给 -9999 屏外值,不会闪到右下角) */\n left: 0;\n /* 🔴 2026-10-18(owner:「可以到 1000px 宽,长度更长些,允许存在滚动条」):\n 680 → **1000px**。这是 CCC 状态面板的**容器**,未来更多 CCC 内容都装在这里,\n ⇒ 宽度按\"要承载什么\"给,不按\"当前装了多少\"给。\n 窄视口仍走 `calc(100vw - 24px)` 兜底(不因追求宽度而在小屏上溢出)。 */\n width: min(1000px, calc(100vw - 24px));\n z-index: 2000;\n box-sizing: border-box;\n padding: 14px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 14px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);\n}\n\n/* ── 航行动画栏:宁静号心智模型(ACC 层资产 /serenity/voyage,S142 §12.44 → v1.36.1 改右栏)\n v1.36.1 改版(所有者:「卡片做成双倍宽,左侧是信息,右侧是动画」):\n 动画从**背景层**改为**右栏**。这一改动解开了 v1.36.0 的取舍死结——\n 旧布局里动画垫在正文底下,必须靠 0.75 的卡片底色把它压住才读得清字;\n 分段之后两者不再争同一片像素 ⇒ 动画可以亮、正文可以干净,不必再互相妥协。\n ⚠️ 动画本身仍是「卡片模式」(词元压到 22%、铭牌层关闭),本栏只负责**给位置**,不改作品。 */\n.sp-popVoyage {\n overflow: hidden; /* 裁掉 iframe 在 14px 圆角外的溢出 */\n /* 分段后不再需要 0.75 的重压暗:压到 0.9 只压整体观感,不与正文争对比度。\n 若所有者觉得动画太亮/太暗,这仍是主旋钮(次旋钮 = iframe 的 blur)。 */\n background: rgba(6, 11, 14, 0.9);\n display: flex;\n flex-direction: row;\n /* 两栏都从**同一行高**分配(row 轴 cross = 高度):左栏按内容、右栏吃满行高。\n 行高 = 左栏内容高度,且被 max-height 封顶 ⇒ 信息长时内部滚动,动画不把行撑破。 */\n align-items: stretch;\n gap: 12px;\n padding: 12px;\n /* 🔴 2026-10-18(owner:「长度更长些,允许存在滚动条」):120 → **80**。\n 上限贴着视口走,把能给的垂直空间尽量给出去;真正超长时由**内部两栏各自滚动**消化\n (左栏 `.sp-popBody` 有 overflow-y:auto,右栏动画吃满行高)——卡片本身仍不溢出视口。 */\n max-height: calc(100vh - 80px);\n}\n/* 左栏:内容区(**主体**)—— owner 2026-10-18:「内容区作为主体,用多 panel 模式」\n 旧形态:固定 340px 的信息栏 + 吃掉剩余宽度的动画栏(动画占了大半张卡片)。\n 现在:内容区 `flex: 1` 吃掉主体,动画降为**定宽窄栏**(见 .sp-voyage)。\n ⚠️ 顺序仍由 `order` 决定(内容在左、动画在右),与 DOM 顺序解耦。 */\n/* 左栏:内容区——**定宽一列**(tab 只显示一个列表,不需要铺满)\n 🔴 2026-10-18 owner 修正后的分工:内容区占**固定列宽**,动画吃剩余空间。\n 为什么定宽而不是 flex:1:tab 里只有一个列表,铺得越宽每行留白越多、反而更难读;\n 定宽 420px 让\"标识 + 名字 + 天数\"三列都有合适密度,剩下的横向空间整体让给动画。 */\n.sp-popVoyage > .sp-content {\n order: 1;\n position: relative;\n z-index: 1;\n flex: none;\n width: 420px;\n min-width: 0;\n}\n/* 右栏:动画——**恢复为可伸缩**(owner 2026-10-18 修正:「剩下的位置还是给动画」)\n 🔵 为什么这次可以给它 flex:1 而上一版不行:上一版两 panel 并排把宽度吃光了;\n 改成 tab 后内容区只占一列 ⇒ 右侧空出来的位置本来就该是动画的。\n ⚠️ 仍然 `min-width: 0`:动画 iframe 不该把内容区挤窄(内容区是 flex:none 的一列)。 */\n.sp-voyage {\n order: 2;\n flex: 1 1 auto;\n min-width: 0;\n position: relative;\n pointer-events: none; /* 纯展示:不挡点击、不抢焦点 */\n overflow: hidden;\n border-radius: 10px;\n /* 卡片加高后动画也随之变长,避免\"加高只换来留白\" */\n min-height: 360px;\n}\n.sp-voyage iframe {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n /* ⚠️ 用 iframe 自身的 filter 而**不是** backdrop-filter:iframe 内容不参与本文档的\n backdrop(跨文档不采样)⇒ backdrop-filter 对它无效,只有 filter 才是可靠的杠杆。\n v1.36.1:动画移入独立右栏后不再压在文字后面 ⇒ 磨砂可以收得很轻(1.2 → 0.6px),\n 既保留柔化,又让飞船细节(舷窗/引擎环)比旧版清楚。 */\n filter: blur(0.6px);\n}\n\n/* 窄屏(放不下两栏)⇒ 退回单栏:信息在上、动画在下(DOM 顺序即如此,无需改结构) */\n@media (max-width: 860px) {\n .sp-pop {\n /* 两栏放不下时,卡片回到\"不超过视口\"的旧行为 */\n width: min(520px, calc(100vw - 24px));\n }\n .sp-popVoyage {\n flex-direction: column;\n align-items: stretch;\n }\n .sp-popVoyage > .sp-content {\n /* 单栏下不再定宽 420px:跟随卡片宽度 */\n flex: 1 1 auto;\n width: auto;\n }\n .sp-voyage {\n flex: none;\n width: auto;\n height: 180px;\n min-height: 0;\n }\n}\n\n.sp-popBody {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-height: calc(100vh - 140px);\n overflow-y: auto;\n}\n\n/* ── 分组(官方 settings 语言:groupTitle + rows) ── */\n\n.sp-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.sp-groupHead {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.sp-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-rows {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n/* ── safe-mode 状态徽标(owner 2026-10-18:「显示小标志即可,是个状态信息」)──────────\n 旧形态 = 盾牌 + SAFE 字样 + Mac 滑块(看起来像控件、实际可点)。\n 现在 = **不可交互**的小徽标:一个状态点 + 两三个字母。\n ⚠️ 刻意**不做 hover 反馈、不做 cursor:pointer** —— 它不是按钮。\n 这类\"长得像控件却只是信息\"的形态,正是旧版让人误以为面板能改 safe-mode 的成因。 */\n.sp-safeBadge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n flex: none;\n padding: 1px 7px;\n border-radius: 99px;\n font-size: 10.5px;\n line-height: 15px;\n font-weight: 600;\n letter-spacing: 0.03em;\n border: 1px solid transparent;\n white-space: nowrap;\n}\n.sp-safeBadgeOn {\n color: #6ee7b7;\n background: rgba(16, 185, 129, 0.12);\n border-color: rgba(16, 185, 129, 0.35);\n}\n.sp-safeBadgeOff {\n color: var(--dsw-alias-label-secondary);\n background: rgba(127, 127, 127, 0.1);\n border-color: var(--dsw-alias-border-l1);\n}\n.sp-safeDot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n flex: none;\n background: currentColor;\n}\n\n/* ── 运行环境:一行元信息(owner:「比较丑,简易一些」)──────────────\n 旧形态 = 三个带边框的 row 卡片。它们是**背景事实**(路径/模型/守卫),\n 逐行加框等于把\"不值得对焦的信息\"提到与主体同等的视觉权重。\n 现在 = 一行等宽小字,溢出省略(path 很长时靠 title 给全文)。 */\n.sp-meta {\n flex: none;\n display: flex;\n align-items: baseline;\n gap: 6px;\n min-width: 0;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n white-space: nowrap;\n overflow: hidden;\n}\n.sp-metaPath {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.sp-metaSep {\n flex: none;\n opacity: 0.45;\n}\n\n/* ── 主体:tab 容器(owner 2026-10-18 修正:「活动轨迹和 skills 使用 tab 来实现,\n 剩下的位置还是给动画」)─────────────────────────────────────────────\n 这条修正**推翻了上一版\"两 panel 并排\"的布局**,理由值得写下来(R↓):\n 并排会把内容区横着吃掉一倍宽度,动画栏只能被压成 240px 定宽窄条。\n 改成 tab 后——**同一块面积轮换显示两个列表**——内容区宽度减半,\n 省下的横向空间**正好还给动画**(见 .sp-voyage 恢复 flex:1)。\n ⇒ \"tab\" 在本面板里不只是交互选择,它**同时是布局手段**。 */\n.sp-content {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n gap: 10px;\n overflow: hidden;\n}\n.sp-tabs {\n flex: 1 1 auto;\n min-height: 0;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n overflow: hidden;\n}\n/* tab 条:贴近官方 settings 的分段控件语域(低调、无边框盒) */\n.sp-tabBar {\n flex: none;\n display: flex;\n align-items: center;\n gap: 4px;\n padding-bottom: 6px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n}\n.sp-tab {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border: 0;\n border-radius: 8px 8px 0 0;\n background: transparent;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n /* 选中态**不只依赖颜色**(色盲不友好):底部 2px 高亮条 + 文字色提亮 */\n border-bottom: 2px solid transparent;\n margin-bottom: -7px;\n}\n.sp-tab:hover {\n color: var(--dsw-alias-label-primary);\n}\n.sp-tabActive {\n color: var(--dsw-alias-label-primary);\n border-bottom-color: #10b981;\n}\n.sp-panelEmpty {\n margin: 0;\n padding: 8px 4px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n/* tab 内容列表:**自身滚动**(不把容器顶长) */\n.sp-list {\n margin: 0;\n padding: 0;\n list-style: none;\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.sp-item {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n padding: 4px 6px;\n border-radius: 6px;\n font-family: var(--ds-font-family-code, monospace);\n font-size: 11px;\n line-height: 16px;\n}\n.sp-item:hover {\n background: var(--dsw-alias-bg-overlay);\n}\n/* 标识列:UID 或编码。定宽让多行对齐,与目录名分离 */\n.sp-itemId {\n flex: none;\n min-width: 56px;\n max-width: 120px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.sp-itemName {\n flex: 1;\n min-width: 0;\n color: var(--dsw-alias-label-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.sp-itemNameFull {\n flex: 1 1 auto;\n}\n.sp-itemAge {\n flex: none;\n min-width: 32px;\n text-align: right;\n color: var(--dsw-alias-label-secondary);\n}\n/* 空壳标记:目录在但没有 SESSION.md ⇒ **真异常**(激活会失败),不是阶段 */\n.sp-itemWarn {\n flex: none;\n padding: 0 5px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 14px;\n color: #fff;\n background: #c2410c;\n}\n/* 已建模标记:🟢 **信息**不是缺陷(无 skill.ttl 完全正常)⇒ 中性灰,不用告警色 */\n.sp-itemTag {\n flex: none;\n padding: 0 5px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 14px;\n color: var(--dsw-alias-label-secondary);\n border: 1px solid var(--dsw-alias-border-l1);\n}\n\n/* ── 活跃轨迹清单(owner 2026-10-18 需求二)─────────────────────────\n 形态:每行 = [S### 定宽] [目录名 伸缩] [天数 定宽右对齐] [空壳标记 条件]\n · 用 mono 字体:目录名与 S### 是**标识符**,等宽才扫得快\n · 名字用 `text-overflow: ellipsis` 单行截断 + `title` 全文 —— 目录名很长时\n 宁可行内截断,也不让一行折成三四行把整个清单撑得读不下去\n · 整个列表**有上限高度 + 自身滚动**:轨迹会越攒越多,而它的容器(左栏)不该跟着无限长 */\n.sp-count {\n font-size: 11px;\n line-height: 16px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n font-family: var(--ds-font-family-code, monospace);\n padding: 0 6px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 99px;\n}\n\n.sp-trajList {\n margin: 0;\n padding: 0;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 2px;\n /* 清单自身封顶:活跃轨迹会越攒越多,超出后本列表滚动,不把左栏顶长 */\n max-height: 240px;\n overflow-y: auto;\n}\n\n.sp-trajItem {\n display: flex;\n align-items: baseline;\n gap: 8px;\n min-width: 0;\n padding: 5px 8px;\n border-radius: 8px;\n font-family: var(--ds-font-family-code, monospace);\n font-size: 11px;\n line-height: 16px;\n}\n.sp-trajItem:hover {\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-trajId {\n flex: none;\n min-width: 40px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-trajName {\n flex: 1;\n min-width: 0;\n color: var(--dsw-alias-label-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.sp-trajAge {\n flex: none;\n color: var(--dsw-alias-label-secondary);\n text-align: right;\n min-width: 32px;\n}\n\n/* 空壳标记:目录在但没有 SESSION.md —— 这是**真异常**(激活会失败),不是阶段。\n ⚠️ 因此用告警色,而不是任何\"状态\"色。 */\n.sp-trajWarn {\n flex: none;\n padding: 0 5px;\n border-radius: 4px;\n font-size: 10px;\n line-height: 14px;\n color: #fff;\n background: #c2410c;\n}\n\n/* 行卡(rowCard:border + radius + padding;值与 label 分离、长内容可换行) */\n.sp-row {\n display: flex;\n align-items: baseline;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-label {\n /* 🔴 曾写死 `width: 76px` ⇒ 「CheapWorker 模型」这类长标签会**溢出到数值上**\n (nowrap + 定宽 ⇒ 文字直接压过去,不换行也不报错)。\n 改 `min-width`:短标签仍在同一列对齐,长标签自己撑开,绝不压数值。 */\n flex: none;\n min-width: 76px;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n}\n\n.sp-value {\n flex: 1;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n font-family: var(--ds-font-family-code, monospace);\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n/* 安全模式动作区 */\n.sp-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sp-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 24px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n transition: background var(--ds-transition-duration-fast, 0.1s) var(--ds-ease-in-out, ease);\n}\n\n.sp-toggleLg {\n height: 30px;\n padding: 0 16px;\n font-size: 13px;\n}\n\n.sp-toggleOn {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n color: var(--dsw-alias-state-error-primary);\n border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 45%, transparent);\n}\n\n.sp-toggleOff {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-secondary);\n}\n\n.sp-toggleIcon {\n flex: none;\n}\n\n.sp-toggleDesc {\n flex: 1;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n/* 🔴 2026-10-14:`.sp-cheapWorkers*` / `.sp-cheapWorker*` 样式区块已随 background 模式删除\n —— 那是「运行中 cheap-worker」列表(进度文件驱动)的样式,而进度文件不再有生产者。 */\n\n/* 底部提示(配置入口引导) */\n.sp-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 12px;\n border: 1px dashed var(--dsw-alias-border-l2);\n border-radius: 10px;\n}\n";
12
12
  if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"SafeModePanel\"]") === null) {
13
13
  const tag = document.createElement("style");
14
14
  tag.setAttribute("data-sp-css", "SafeModePanel");
@@ -32,6 +32,9 @@ window.__ModuleLoader__.load({
32
32
  function SafeModePanel(props) {
33
33
  const [status, setStatus] = (0, react.useState)(null);
34
34
  const [open, setOpen] = (0, react.useState)(false);
35
+ /** tab 切换(2026-10-18 owner 修正:活跃轨迹 / 技能 用 tab,而不是并排两个 panel)
36
+ * ⚠️ 纯前端显示状态 —— 两份数据后端一次性都给全,切换**不触发任何请求**。 */
37
+ const [tab, setTab] = (0, react.useState)("trajectories");
35
38
  const rootRef = (0, react.useRef)(null);
36
39
  const popRef = (0, react.useRef)(null);
37
40
  const [popPos, setPopPos] = (0, react.useState)(null);
@@ -176,77 +179,80 @@ window.__ModuleLoader__.load({
176
179
  ] })
177
180
  ]
178
181
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
179
- className: cx("sp-panels"),
180
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
181
- className: cx("sp-panel"),
182
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
183
- className: cx("sp-panelHead"),
184
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
185
- className: cx("sp-panelTitle"),
186
- children: "活跃轨迹"
187
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
182
+ className: cx("sp-tabs"),
183
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
184
+ className: cx("sp-tabBar"),
185
+ role: "tablist",
186
+ "aria-label": "CCC 状态面板",
187
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
188
+ type: "button",
189
+ role: "tab",
190
+ "aria-selected": tab === "trajectories",
191
+ className: cx("sp-tab", tab === "trajectories" && "sp-tabActive"),
192
+ onClick: () => setTab("trajectories"),
193
+ children: ["活跃轨迹", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
188
194
  className: cx("sp-count"),
189
195
  children: trajectories.length
190
196
  })]
191
- }), trajectories.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
192
- className: cx("sp-panelEmpty"),
193
- children: "暂无活跃轨迹"
194
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
195
- className: cx("sp-list"),
196
- children: trajectories.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
197
- className: cx("sp-item"),
198
- children: [
199
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
200
- className: cx("sp-itemId"),
201
- title: t.uid !== null ? "UID(唯一标识符)" : "编码(未设定 UID)",
202
- children: t.uid ?? (t.sessionId || "—")
203
- }),
204
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
205
- className: cx("sp-itemName"),
206
- title: t.dirName,
207
- children: t.dirName
208
- }),
209
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
210
- className: cx("sp-itemAge"),
211
- children: [t.ageDays, "d"]
212
- }),
213
- !t.hasSessionMd && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
214
- className: cx("sp-itemWarn"),
215
- title: "目录存在但没有 SESSION.md(空壳)",
216
- children: "空壳"
217
- })
218
- ]
219
- }, t.dirName))
220
- })]
221
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
222
- className: cx("sp-panel"),
223
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
224
- className: cx("sp-panelHead"),
225
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
226
- className: cx("sp-panelTitle"),
227
- children: "技能"
228
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
197
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
198
+ type: "button",
199
+ role: "tab",
200
+ "aria-selected": tab === "skills",
201
+ className: cx("sp-tab", tab === "skills" && "sp-tabActive"),
202
+ onClick: () => setTab("skills"),
203
+ children: ["技能", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
229
204
  className: cx("sp-count"),
230
205
  children: skills.length
231
206
  })]
232
- }), skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
233
- className: cx("sp-panelEmpty"),
234
- children: "暂无技能"
235
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
236
- className: cx("sp-list"),
237
- children: skills.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
238
- className: cx("sp-item"),
239
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
240
- className: cx("sp-itemName", "sp-itemNameFull"),
241
- title: s.name,
242
- children: s.name
243
- }), s.modeled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
244
- className: cx("sp-itemTag"),
245
- title: "已建模(存在 skill.ttl)",
246
- children: "建模"
247
- })]
248
- }, s.name))
249
207
  })]
208
+ }), tab === "trajectories" ? trajectories.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
209
+ className: cx("sp-panelEmpty"),
210
+ children: "暂无活跃轨迹"
211
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
212
+ className: cx("sp-list"),
213
+ role: "tabpanel",
214
+ children: trajectories.map((t) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
215
+ className: cx("sp-item"),
216
+ children: [
217
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
218
+ className: cx("sp-itemId"),
219
+ title: t.uid !== null ? "UID(唯一标识符)" : "编码(未设定 UID)",
220
+ children: t.uid ?? (t.sessionId || "—")
221
+ }),
222
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
223
+ className: cx("sp-itemName"),
224
+ title: t.dirName,
225
+ children: t.dirName
226
+ }),
227
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
228
+ className: cx("sp-itemAge"),
229
+ children: [t.ageDays, "d"]
230
+ }),
231
+ !t.hasSessionMd && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
232
+ className: cx("sp-itemWarn"),
233
+ title: "目录存在但没有 SESSION.md(空壳)",
234
+ children: "空壳"
235
+ })
236
+ ]
237
+ }, t.dirName))
238
+ }) : skills.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
239
+ className: cx("sp-panelEmpty"),
240
+ children: "暂无技能"
241
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
242
+ className: cx("sp-list"),
243
+ role: "tabpanel",
244
+ children: skills.map((s) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
245
+ className: cx("sp-item"),
246
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
247
+ className: cx("sp-itemName", "sp-itemNameFull"),
248
+ title: s.name,
249
+ children: s.name
250
+ }), s.modeled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
251
+ className: cx("sp-itemTag"),
252
+ title: "已建模(存在 skill.ttl)",
253
+ children: "建模"
254
+ })]
255
+ }, s.name))
250
256
  })]
251
257
  })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
252
258
  className: cx("sp-panelEmpty"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.67.0",
3
+ "version": "1.68.0",
4
4
  "description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:14 个工具(container_fs/cognitive_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 认知轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/官方 ACP 面/认知轨迹唤醒注册表。适配 DSH 0.1.7-rc.1 / 0.2.0-rc.1 / 0.2.1-rc.1(三档)。",
5
5
  "license": "MIT",
6
6
  "repository": {