@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 +19 -0
- package/CHANGELOG.zh-CN.md +19 -0
- package/README.md +1 -1
- package/lib/client.js +79 -24
- package/package.json +1 -1
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`.
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -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)
|
|
14
14
|
[](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
|
|
1376
|
-
function
|
|
1377
|
-
|
|
1378
|
-
|
|
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
|
-
|
|
1395
|
-
|
|
1396
|
-
const
|
|
1397
|
-
if (
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
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
|
-
|
|
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
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
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
|
|
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: "
|
|
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 =
|
|
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 {
|