@michengai/dsh-codex-ui 0.2.102 → 0.2.103

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/CHANGELOG.md CHANGED
@@ -4,6 +4,25 @@
4
4
 
5
5
  This changelog records recent releases of DSH Codex UI and its one-click installer. Earlier changes remain available in the [Git history](https://github.com/MichengAI/dsh-codex-ui/commits/main).
6
6
 
7
+ ## 0.2.103 — 2026-09-04
8
+
9
+ Companion release: `@michengai/dsh-codex-suite-installer@0.1.20`.
10
+
11
+ ### Fixed
12
+
13
+ - Limited conversation DOM processing to affected message roots during streaming updates, avoiding repeated full-document decoration scans.
14
+ - Corrected blank-language fallback, cyclic remote-error handling, workspace-preference response versioning, About-page refresh warnings, and connector marketplace probing compatibility.
15
+ - Hardened Suite Installer YAML build-policy normalization across indentation styles and rejected Windows command-expansion characters.
16
+ - Preserved existing third-party bundle order during Suite migration, expanded managed members at the prior Suite anchor, and rejected profiles with missing or reversed base/web prerequisites instead of silently rewriting them.
17
+
18
+ ### Tests
19
+
20
+ - Added behavior coverage for cross-site dependency-route rejection, error redaction, localized refresh failures, connector probing, observer scope, cyclic errors, installer ordering, invalid bundle baselines, and Windows command quoting.
21
+
22
+ ### Dependencies
23
+
24
+ - The Suite Installer release workflow will resolve Codex UI `0.2.103` and publish the companion `0.1.20` release.
25
+
7
26
  ## 0.2.102 — 2026-09-03
8
27
 
9
28
  Companion release: `@michengai/dsh-codex-suite-installer@0.1.19`.
@@ -4,6 +4,25 @@
4
4
 
5
5
  本日志记录 DSH Codex UI 及其一键安装器的最近发布;更早的变更可查看 [Git 提交历史](https://github.com/MichengAI/dsh-codex-ui/commits/main)。
6
6
 
7
+ ## 0.2.103 — 2026-09-04
8
+
9
+ 配套版本:`@michengai/dsh-codex-suite-installer@0.1.20`。
10
+
11
+ ### 修复
12
+
13
+ - 将流式更新期间的会话 DOM 处理限制在受影响的消息根,避免反复执行全文档装饰扫描。
14
+ - 修复空语言兜底、远程错误循环引用、工作区偏好响应版本、关于页刷新失败提示和连接器市场探测兼容性。
15
+ - 强化 Suite 安装器对不同缩进 YAML 构建策略的归一化,并拒绝 Windows 命令展开字符。
16
+ - Suite 迁移时保留既有第三方 bundle 顺序,在原 Suite 锚点展开受管成员;缺少或颠倒 `base/web` 前置项时明确失败,不再静默改写。
17
+
18
+ ### 测试
19
+
20
+ - 新增依赖路由跨站拒绝、错误脱敏、本地化刷新失败、连接器探测、观察器范围、循环错误、安装器保序、无效 bundle 基线和 Windows 命令引用的行为覆盖。
21
+
22
+ ### 依赖
23
+
24
+ - Suite Installer 发布 workflow 会解析 Codex UI `0.2.103`,并发布配套的 `0.1.20` 版本。
25
+
7
26
  ## 0.2.102 — 2026-09-03
8
27
 
9
28
  配套版本:`@michengai/dsh-codex-suite-installer@0.1.19`。
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  **Rebuild the DeepSeek Harness Web sidebar, workspace tree, search, and turn navigation in a Codex-style layout**
10
10
 
11
- [简体中文](README.zh-CN.md) · [Changelog](CHANGELOG.md) · [Apache-2.0](LICENSE)
11
+ [简体中文](README.zh-CN.md) · [项目交接](docs/00-交接入口/00-阅读导航.md) · [Changelog](CHANGELOG.md) · [Apache-2.0](LICENSE)
12
12
 
13
13
  [![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](LICENSE)
14
14
  [![npm package](https://img.shields.io/npm/v/%40michengai%2Fdsh-codex-ui.svg?label=npm%20package)](https://www.npmjs.com/package/@michengai/dsh-codex-ui)
package/lib/client.js CHANGED
@@ -1236,8 +1236,11 @@ header:has([data-dcu-inline-tabs]) [class*="headerActions"]{order:2;flex:none}
1236
1236
  header [data-dcu-inline-tabs]{box-sizing:border-box;order:3;flex:none;display:flex;align-items:center;gap:0;margin:0;padding:0;height:28px;position:relative;z-index:1;overflow:hidden;border:1px solid var(--dsw-alias-border-subtle,rgba(255,255,255,.12));border-radius:8px;background:var(--dsw-alias-background-secondary,rgba(255,255,255,.025))}
1237
1237
  header:has([data-dcu-inline-tabs]) [class*="headerUtilities"]{order:4;flex:none}
1238
1238
  header [data-dcu-tab-slider]{position:absolute;left:0;top:0;height:26px;border-radius:7px;background:color-mix(in srgb,var(--dsw-alias-button-info-fill,#4c8dff) 18%,transparent);pointer-events:none;z-index:0;opacity:0;transform:translateX(0);width:0;transition:transform 240ms cubic-bezier(.16,1,.3,1),width 240ms cubic-bezier(.16,1,.3,1),opacity 160ms ease}
1239
- /* 顶栏统一使用 34px 控件带:与宿主扩展常用的 top:3px + 28px 图标按钮同心,
1240
- 外部插件按钮无需在展开/收起时改变位置。 */
1239
+ /* 顶栏统一使用 34px 控件带:与宿主扩展常用的 top:3px + 28px 图标按钮同心。
1240
+ better-sidebar 0.18.0 收起时会把开关组改到 14px(按官方 padding-top:12px 算),
1241
+ 这里只在紧凑顶栏存在时扣回 3px,不搬 DOM、不跟展开/收起跳动。 */
1242
+ body[data-dsh-sidebar-collapsed]:has([data-dcu-inline-tabs]) [data-dsh-toggle-cluster]{top:calc(3px + env(safe-area-inset-top))}
1243
+ body[data-dsh-sidebar-collapsed][data-dsh-title-bar-compat]:has([data-dcu-inline-tabs]) [data-dsh-toggle-cluster]{top:calc(var(--dsh-title-bar-strip, 40px) + 3px)}
1241
1244
  header [data-dcu-inline-tabs] [role=tab]{box-sizing:border-box;position:relative;z-index:1;height:26px;padding:3px 10px;margin:0;line-height:20px;color:var(--dsw-alias-label-tertiary);border:0;box-shadow:none;background:transparent;font-size:13px;white-space:nowrap}
1242
1245
  header [data-dcu-inline-tabs] [role=tab][aria-selected=true],header [data-dcu-inline-tabs] [role=tab][data-state=active]{color:var(--dsw-alias-button-info-fill,#4c8dff);font-weight:500}
1243
1246
  header [data-dcu-inline-tabs] [role=tab]+[role=tab]{border-left:1px solid var(--dsw-alias-border-subtle,rgba(255,255,255,.08))}
@@ -1372,10 +1375,28 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1372
1375
  style.textContent = CONVERSATION_HEADER_STYLE;
1373
1376
  doc.head.append(style);
1374
1377
  }
1375
- const CONVERSATION_DECORATION_SELECTOR = "header,[data-time-hover-root],[data-dcu-user-card],[data-dcu-user-source]";
1376
- function conversationDecorationMutation(records) {
1377
- const relevant = (node) => node instanceof Element && (node.matches(CONVERSATION_DECORATION_SELECTOR) || node.closest(CONVERSATION_DECORATION_SELECTOR) !== null || node.querySelector(CONVERSATION_DECORATION_SELECTOR) !== null);
1378
- return records.some((record) => relevant(record.target) || [...record.addedNodes].some(relevant) || [...record.removedNodes].some(relevant));
1378
+ const CONVERSATION_BUBBLE_SELECTOR = "[data-time-hover-root],[data-dcu-user-card],[data-dcu-user-source]";
1379
+ function inspectConversationNode(node, impact, includeDescendants) {
1380
+ if (!(node instanceof Element)) return;
1381
+ if (node.matches("header") || node.closest("header") !== null || includeDescendants && node.querySelector("header") !== null) impact.headerChanged = true;
1382
+ const row = node.closest("[data-time-hover-root]");
1383
+ if (row !== null) {
1384
+ impact.bubbleRoots.add(row);
1385
+ return;
1386
+ }
1387
+ if (node.matches(CONVERSATION_BUBBLE_SELECTOR) || includeDescendants && node.querySelector(CONVERSATION_BUBBLE_SELECTOR) !== null) impact.bubbleRoots.add(node);
1388
+ }
1389
+ function conversationMutationImpact(records) {
1390
+ const impact = {
1391
+ headerChanged: false,
1392
+ bubbleRoots: /* @__PURE__ */ new Set()
1393
+ };
1394
+ for (const record of records) {
1395
+ inspectConversationNode(record.target, impact, false);
1396
+ for (const node of record.addedNodes) inspectConversationNode(node, impact, true);
1397
+ for (const node of record.removedNodes) if (node instanceof Element && (node.matches("header") || node.querySelector("header") !== null)) impact.headerChanged = true;
1398
+ }
1399
+ return impact;
1379
1400
  }
1380
1401
  /** 观察会话顶栏与用户气泡;只对相关子树变更按帧合并,流式回答不会触发全文档扫描。 */
1381
1402
  function observeConversationHeader(doc = document) {
@@ -1384,6 +1405,8 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1384
1405
  restoreOfficialUserBubbles(doc);
1385
1406
  let applying = false;
1386
1407
  let frame;
1408
+ let headerPending = true;
1409
+ const bubbleRoots = /* @__PURE__ */ new Set();
1387
1410
  let watchedTabs;
1388
1411
  let stopWatchingTabs;
1389
1412
  const run = () => {
@@ -1391,17 +1414,22 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1391
1414
  if (applying) return;
1392
1415
  applying = true;
1393
1416
  try {
1394
- restoreOfficialUserBubbles(doc);
1395
- placeConversationTabs(doc);
1396
- const tabs = findConversationTablist(doc);
1397
- if (tabs !== watchedTabs) {
1398
- stopWatchingTabs?.();
1399
- watchedTabs = tabs;
1400
- stopWatchingTabs = tabs === void 0 ? void 0 : watchTabSelection(tabs);
1417
+ const roots = [...bubbleRoots];
1418
+ bubbleRoots.clear();
1419
+ for (const root of roots) restoreOfficialUserBubbles(root);
1420
+ if (headerPending) {
1421
+ headerPending = false;
1422
+ placeConversationTabs(doc);
1423
+ const tabs = findConversationTablist(doc);
1424
+ if (tabs !== watchedTabs) {
1425
+ stopWatchingTabs?.();
1426
+ watchedTabs = tabs;
1427
+ stopWatchingTabs = tabs === void 0 ? void 0 : watchTabSelection(tabs);
1428
+ }
1429
+ syncTabSlider(doc);
1430
+ decorateConversationTitle(doc);
1431
+ decorateSessionLogDownload(doc);
1401
1432
  }
1402
- syncTabSlider(doc);
1403
- decorateConversationTitle(doc);
1404
- decorateSessionLogDownload(doc);
1405
1433
  } finally {
1406
1434
  applying = false;
1407
1435
  }
@@ -1412,7 +1440,11 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1412
1440
  };
1413
1441
  sync();
1414
1442
  const observer = new MutationObserver((records) => {
1415
- if (conversationDecorationMutation(records)) sync();
1443
+ const impact = conversationMutationImpact(records);
1444
+ if (!impact.headerChanged && impact.bubbleRoots.size === 0) return;
1445
+ headerPending ||= impact.headerChanged;
1446
+ for (const root of impact.bubbleRoots) bubbleRoots.add(root);
1447
+ sync();
1416
1448
  });
1417
1449
  observer.observe(doc.body, {
1418
1450
  childList: true,
@@ -1932,10 +1964,15 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1932
1964
  }
1933
1965
  /** DSH 明确要求跨 bundle 按结构标记识别 RemoteFailure,不能使用 instanceof。 */
1934
1966
  function remoteFailureCode(value) {
1935
- const record = objectRecord(value);
1936
- if (record === void 0) return void 0;
1937
- if (record.isDSHRemoteError === true && typeof record.code === "string") return record.code;
1938
- return record.rpcError === value ? void 0 : remoteFailureCode(record.rpcError);
1967
+ const seen = /* @__PURE__ */ new Set();
1968
+ let current = value;
1969
+ while (true) {
1970
+ const record = objectRecord(current);
1971
+ if (record === void 0 || seen.has(record)) return void 0;
1972
+ seen.add(record);
1973
+ if (record.isDSHRemoteError === true && typeof record.code === "string") return record.code;
1974
+ current = record.rpcError;
1975
+ }
1939
1976
  }
1940
1977
  function diagnosticMessage(value) {
1941
1978
  if (value instanceof Error) return value.message;
@@ -5977,6 +6014,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
5977
6014
  const [installing, setInstalling] = (0, react.useState)();
5978
6015
  const [progress, setProgress] = (0, react.useState)();
5979
6016
  const [message, setMessage] = (0, react.useState)();
6017
+ const [refreshFailed, setRefreshFailed] = (0, react.useState)(false);
5980
6018
  const alive = (0, react.useRef)(true);
5981
6019
  const root = (0, react.useRef)(null);
5982
6020
  const stateRef = (0, react.useRef)("loading");
@@ -6002,9 +6040,11 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6002
6040
  if (!response.ok || !Array.isArray(payload.dependencies) || !payload.dependencies.every(isDependencyStatus)) throw new Error();
6003
6041
  setDependencies(payload.dependencies);
6004
6042
  setState("ready");
6043
+ setRefreshFailed(false);
6005
6044
  } catch (error) {
6006
6045
  if (signal?.aborted || error instanceof DOMException && error.name === "AbortError" || currentRequest !== requestId.current) return;
6007
- if (stateRef.current !== "ready") setState("failed");
6046
+ if (stateRef.current === "ready") setRefreshFailed(true);
6047
+ else setState("failed");
6008
6048
  }
6009
6049
  }, []);
6010
6050
  (0, react.useEffect)(() => {
@@ -6072,6 +6112,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6072
6112
  if (!alive.current) return;
6073
6113
  setDependencies(payload.dependencies);
6074
6114
  setState("ready");
6115
+ setRefreshFailed(false);
6075
6116
  setMessage({
6076
6117
  error: false,
6077
6118
  text: payload.autoReload === false ? t("about.restartManually") : t("about.restartRequired")
@@ -6099,6 +6140,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6099
6140
  if (!alive.current) return;
6100
6141
  setDependencies(payload.dependencies);
6101
6142
  setState("ready");
6143
+ setRefreshFailed(false);
6102
6144
  setMessage({
6103
6145
  error: false,
6104
6146
  text: payload.restartRequired === false ? t("about.upToDate") : payload.autoReload === false ? t("about.restartManually") : t("about.restartRequired")
@@ -6243,6 +6285,12 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6243
6285
  })
6244
6286
  ]
6245
6287
  }),
6288
+ refreshFailed && state === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6289
+ className: "dcu-about-message",
6290
+ "data-error": "true",
6291
+ role: "status",
6292
+ children: t("about.refreshFailed")
6293
+ }),
6246
6294
  message !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
6247
6295
  className: "dcu-about-message",
6248
6296
  "data-error": message.error,
@@ -6397,10 +6445,11 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6397
6445
  (0, react.useEffect)(() => {
6398
6446
  const controller = new AbortController();
6399
6447
  fetch(MCP_CONNECTOR_UI, {
6400
- method: "HEAD",
6448
+ method: "GET",
6401
6449
  cache: "no-store",
6402
6450
  signal: controller.signal
6403
6451
  }).then((response) => {
6452
+ response.body?.cancel().catch(() => {});
6404
6453
  if (!controller.signal.aborted) setMarketAvailable(response.ok);
6405
6454
  }).catch(() => {
6406
6455
  if (!controller.signal.aborted) setMarketAvailable(false);
@@ -6582,6 +6631,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6582
6631
  "about.dependenciesDescription": "每个配套插件都可单独安装和更新,也可一键安装缺失项并更新旧版本。可分别检查 Codex UI、专家、技能、归档、IM、定时任务和第三方插件市场。",
6583
6632
  "about.loading": "正在读取依赖安装状态…",
6584
6633
  "about.statusFailed": "暂时无法读取依赖安装状态。",
6634
+ "about.refreshFailed": "依赖状态刷新失败,当前显示上次成功读取的结果。",
6585
6635
  "about.installed": "已安装",
6586
6636
  "about.missing": "未安装",
6587
6637
  "about.updateAvailable": "可更新",
@@ -6780,6 +6830,7 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
6780
6830
  "about.dependenciesDescription": "Install or update each companion separately, or install missing plugins and update old versions in one action. Check Codex UI, expert, skill, archive, IM, scheduled-task, and marketplace plugins on their own.",
6781
6831
  "about.loading": "Loading dependency status…",
6782
6832
  "about.statusFailed": "Dependency status is temporarily unavailable.",
6833
+ "about.refreshFailed": "Dependency status refresh failed. Showing the last successful result.",
6783
6834
  "about.installed": "Installed",
6784
6835
  "about.missing": "Not installed",
6785
6836
  "about.updateAvailable": "Update available",
@@ -7024,7 +7075,11 @@ html[data-dcu-official-turn-navigator-supported=true] .dcu-turn-navigator,html:h
7024
7075
  const apply = () => {
7025
7076
  if (applying) return;
7026
7077
  applying = true;
7027
- const active = locale.getSnapshot?.().active ?? document.documentElement.lang ?? window.navigator.language ?? "zh";
7078
+ const active = [
7079
+ locale.getSnapshot?.().active,
7080
+ document.documentElement.lang,
7081
+ window.navigator.language
7082
+ ].find((value) => typeof value === "string" && value.trim() !== "") ?? "zh";
7028
7083
  const roots = [...pendingRoots];
7029
7084
  pendingRoots.clear();
7030
7085
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-codex-ui",
3
- "version": "0.2.102",
3
+ "version": "0.2.103",
4
4
  "description": "以 Codex 风格重构 DSH Web 侧栏的独立客户端插件",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {