@michengai/dsh-codex-ui 0.2.86 → 0.2.88

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,41 @@
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.88 — 2026-08-26
8
+
9
+ Companion release: `@michengai/dsh-codex-suite-installer@0.1.5`.
10
+
11
+ ### Changed
12
+
13
+ - Replaced the lightweight **Settings → Connectors** tool list with the complete `dsh-mcp-connector` marketplace and connection manager whenever that optional external plugin is installed.
14
+ - Kept the existing **Connectors** home/sidebar action as the single entry point and suppressed the external plugin's separate launcher in both expanded and compact Codex sidebar layouts.
15
+ - Preserved the original current-session MCP tool directory as an automatic fallback when `dsh-mcp-connector` is absent or its Web UI is unavailable.
16
+
17
+ ### Compatibility
18
+
19
+ - Added a same-origin, source-validated Prompt bridge that creates or reuses a workspace session and places the selected marketplace Prompt into its draft.
20
+ - Synchronized the embedded marketplace with the DSH-selected light or dark theme without modifying the externally maintained plugin or making it a required dependency.
21
+ - Updated the lightweight installer to pin Codex UI `0.2.88`; the legacy aggregate Suite remains private and unpublished.
22
+
23
+ Published packages: [`@michengai/dsh-codex-ui@0.2.88`](https://www.npmjs.com/package/@michengai/dsh-codex-ui/v/0.2.88) and [`@michengai/dsh-codex-suite-installer@0.1.5`](https://www.npmjs.com/package/@michengai/dsh-codex-suite-installer/v/0.1.5).
24
+
25
+ ## 0.2.87 — 2026-08-26
26
+
27
+ Companion release: `@michengai/dsh-codex-suite-installer@0.1.4`.
28
+
29
+ ### Fixed
30
+
31
+ - Restored Chinese labels for the built-in permission presets in Settings and Chat with an exact-match compatibility adapter that leaves custom presets, prose, icons, and Host menu layout untouched.
32
+ - Made **Open in File Explorer** bypass in-app file previews, close its menu immediately, and open Windows Explorer in the foreground and maximized.
33
+ - Restyled conversation and workspace rename fields to match the native settings form, including helper copy, accessible labels, and a visible focus state.
34
+ - Kept the Codex UI **About** section at the bottom of Settings even when third-party plugins add later sections.
35
+
36
+ ### Compatibility
37
+
38
+ - Updated the lightweight installer to pin Codex UI `0.2.87`, IM Connect `0.1.24`, and Automation `0.1.15`; the legacy aggregate Suite remains private and unpublished.
39
+
40
+ Published packages: [`@michengai/dsh-codex-ui@0.2.87`](https://www.npmjs.com/package/@michengai/dsh-codex-ui/v/0.2.87) and [`@michengai/dsh-codex-suite-installer@0.1.4`](https://www.npmjs.com/package/@michengai/dsh-codex-suite-installer/v/0.1.4).
41
+
7
42
  ## 0.2.86 — 2026-08-25
8
43
 
9
44
  Companion release: `@michengai/dsh-codex-suite-installer@0.1.3`.
@@ -4,6 +4,41 @@
4
4
 
5
5
  本日志记录 DSH Codex UI 及其一键安装器的最近发布;更早的变更可查看 [Git 提交历史](https://github.com/MichengAI/dsh-codex-ui/commits/main)。
6
6
 
7
+ ## 0.2.88 — 2026-08-26
8
+
9
+ 配套版本:`@michengai/dsh-codex-suite-installer@0.1.5`。
10
+
11
+ ### 调整
12
+
13
+ - 安装外部可选插件 `dsh-mcp-connector` 时,用其完整的市场与连接管理界面替换轻量的“设置 → 连接器”工具列表。
14
+ - 首页/侧边栏继续以“连接器”作为唯一入口,展开与收缩状态下均不再显示外部插件单独注入的启动入口。
15
+ - 未安装 `dsh-mcp-connector` 或其 Web UI 不可用时,自动回退到原有的当前会话 MCP 工具目录。
16
+
17
+ ### 兼容性
18
+
19
+ - 增加同源且校验消息来源的 Prompt 桥接,可创建或复用工作区会话,并把市场中的 Prompt 写入新会话草稿。
20
+ - 让内嵌市场跟随 DSH 当前选择的明暗主题;不修改外部插件,也不将其变为强制依赖。
21
+ - 轻量安装器同步固定 Codex UI `0.2.88`;旧聚合 Suite 继续保持私有且不发布。
22
+
23
+ 发布包:[`@michengai/dsh-codex-ui@0.2.88`](https://www.npmjs.com/package/@michengai/dsh-codex-ui/v/0.2.88) 与 [`@michengai/dsh-codex-suite-installer@0.1.5`](https://www.npmjs.com/package/@michengai/dsh-codex-suite-installer/v/0.1.5)。
24
+
25
+ ## 0.2.87 — 2026-08-26
26
+
27
+ 配套版本:`@michengai/dsh-codex-suite-installer@0.1.4`。
28
+
29
+ ### 修复
30
+
31
+ - 恢复设置与 Chat 中三个内置权限预设的中文显示;兼容逻辑只精确替换权限控件文案,不改动自定义预设、正文、图标或宿主菜单布局。
32
+ - “在资源管理器中打开”不再被应用内文件预览拦截;点击后立即收起菜单,并在前台最大化打开 Windows 资源管理器。
33
+ - 将会话和工作区重命名输入框调整为原生设置表单样式,补充辅助说明、无障碍名称和清晰的焦点状态。
34
+ - 即使第三方插件继续注册设置分区,也保证 Codex UI 自带的“关于”入口始终位于最底部。
35
+
36
+ ### 兼容性
37
+
38
+ - 轻量安装器同步固定 Codex UI `0.2.87`、IM Connect `0.1.24` 和 Automation `0.1.15`;旧聚合 Suite 继续保持私有且不发布。
39
+
40
+ 发布包:[`@michengai/dsh-codex-ui@0.2.87`](https://www.npmjs.com/package/@michengai/dsh-codex-ui/v/0.2.87) 与 [`@michengai/dsh-codex-suite-installer@0.1.4`](https://www.npmjs.com/package/@michengai/dsh-codex-suite-installer/v/0.1.4)。
41
+
7
42
  ## 0.2.86 — 2026-08-25
8
43
 
9
44
  配套版本:`@michengai/dsh-codex-suite-installer@0.1.3`。
package/dist/client.js CHANGED
@@ -1334,8 +1334,9 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1334
1334
  .dcu-wb-rename-actions{display:flex;justify-content:flex-end;gap:8px}
1335
1335
  .dcu-wb-delete-button{color:var(--dsw-alias-state-error-primary)!important}
1336
1336
  .dcu-wb-delete-copy{margin:0;color:var(--dcu-sidebar-secondary);font-size:13px;line-height:20px}
1337
- .dcu-wb-rename-input{width:100%;height:44px;border:1px solid var(--dcu-sidebar-border);border-radius:22px;padding:7px 14px;outline:0;background:transparent;color:var(--dcu-sidebar-primary);font:inherit}
1338
- .dcu-wb-rename-input:focus{border-color:var(--dsw-alias-button-info-fill)}
1337
+ .dcu-wb-rename-input{box-sizing:border-box;width:100%;height:36px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;outline:0;background:var(--dsw-alias-background-secondary);color:var(--dsw-alias-label-primary);font:14px/20px var(--dsw-font-family,inherit);caret-color:var(--dsw-alias-state-business-primary);transition:border-color 120ms ease,box-shadow 120ms ease}
1338
+ .dcu-wb-rename-input:hover{border-color:color-mix(in srgb,var(--dsw-alias-label-primary) 32%,var(--dsw-alias-border-l2))}
1339
+ .dcu-wb-rename-input:focus{border-color:var(--dsw-alias-state-business-primary);box-shadow:0 0 0 1px color-mix(in srgb,var(--dsw-alias-state-business-primary) 28%,transparent)}
1339
1340
  `;
1340
1341
  const runningStyles = `.dcu-wb-running{flex:none;width:12px;height:12px;border:2px solid color-mix(in srgb,var(--dcu-sidebar-secondary) 25%,transparent);border-top-color:var(--dcu-sidebar-secondary);border-radius:50%;animation:dcu-wb-spin .8s linear infinite}@keyframes dcu-wb-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.dcu-wb-running{animation:none}}`;
1341
1342
  const typographyStyles = `.dcu-wb{font:14px/20px var(--dcu-font,var(--dsw-font-family))}.dcu-wb-section-label{color:var(--dcu-sidebar-tertiary);font-size:13px;font-weight:400;letter-spacing:.02em}.dcu-wb-project-title{font-size:14px;line-height:20px;font-weight:400;color:var(--dcu-sidebar-primary)}.dcu-wb-session-title{font-size:14px;line-height:20px;font-weight:400;color:var(--dcu-sidebar-secondary)}.dcu-wb-session.dcu-wb-selected .dcu-wb-session-title,.dcu-wb-session:hover .dcu-wb-session-title{color:var(--dcu-sidebar-primary)}.dcu-wb-empty{color:var(--dcu-sidebar-tertiary);font-size:13px;line-height:18px}.dcu-wb-nochat{padding:0 8px 4px 28px;color:var(--dcu-sidebar-tertiary);font-size:14px;line-height:20px}`;
@@ -1605,6 +1606,11 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1605
1606
  await (0, _deepseek_ai_dsh_client_ui_primitives.writeClipboard)(value);
1606
1607
  });
1607
1608
  };
1609
+ const openPathImmediately = (path) => {
1610
+ setMenu(void 0);
1611
+ setHeaderMenu(void 0);
1612
+ run("open-path", () => openPath(path));
1613
+ };
1608
1614
  const toggleGroup = (key, defaultOpen = true) => {
1609
1615
  setExpanded((current) => ({
1610
1616
  ...current,
@@ -1816,7 +1822,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
1816
1822
  setPinnedWorkspaceIds((ids) => togglePinnedWorkspace(ids, workspace.workspaceId));
1817
1823
  setMenu(void 0);
1818
1824
  }
1819
- if (id === "openPath") run("open-path", () => openPath(workspace.path));
1825
+ if (id === "openPath") openPathImmediately(workspace.path);
1820
1826
  if (id === "delete") {
1821
1827
  setDeleteTarget({
1822
1828
  id: workspace.workspaceId,
@@ -2040,7 +2046,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2040
2046
  setMenu(void 0);
2041
2047
  }
2042
2048
  if (action === "fork") run("fork", () => forkSession(id));
2043
- if (action === "openPath" && path !== void 0) run("open-path", () => openPath(path));
2049
+ if (action === "openPath" && path !== void 0) openPathImmediately(path);
2044
2050
  if (action === "copyPath") copy(path);
2045
2051
  if (action === "copyTitle") copy(session.displayTitle);
2046
2052
  if (action === "copyId") copy(id);
@@ -2380,7 +2386,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2380
2386
  setMenu(void 0);
2381
2387
  }
2382
2388
  if (action === "fork") run("fork", () => forkSession(id));
2383
- if (action === "openPath" && session.cwd !== void 0) run("open-path", () => openPath(session.cwd));
2389
+ if (action === "openPath" && session.cwd !== void 0) openPathImmediately(session.cwd);
2384
2390
  if (action === "copyPath") copy(session.cwd);
2385
2391
  if (action === "copyTitle") copy(title);
2386
2392
  if (action === "copyId") copy(id);
@@ -2417,7 +2423,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2417
2423
  setError(void 0);
2418
2424
  }
2419
2425
  if (action === "fork") run("fork", () => forkSession(id));
2420
- if (action === "openPath" && path !== void 0) run("open-path", () => openPath(path));
2426
+ if (action === "openPath" && path !== void 0) openPathImmediately(path);
2421
2427
  if (action === "copyPath") copy(path);
2422
2428
  if (action === "copyTitle") copy(session.displayTitle);
2423
2429
  if (action === "copyId") copy(id);
@@ -2446,6 +2452,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2446
2452
  },
2447
2453
  closeLabel: t("sessions.close"),
2448
2454
  title: renameTarget?.kind === "workspace" ? t("workspace.rename") : t("sessions.rename"),
2455
+ description: t("sessions.renameDescription"),
2449
2456
  footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2450
2457
  className: "dcu-wb-rename-actions",
2451
2458
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
@@ -2463,6 +2470,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2463
2470
  }),
2464
2471
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2465
2472
  className: "dcu-wb-rename-input",
2473
+ "aria-label": renameTarget?.kind === "workspace" ? t("workspace.rename") : t("sessions.rename"),
2466
2474
  value: renameDraft,
2467
2475
  autoFocus: true,
2468
2476
  onFocus: (event) => {
@@ -2675,6 +2683,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2675
2683
  },
2676
2684
  closeLabel: t("sessions.close"),
2677
2685
  title: t("sessions.rename"),
2686
+ description: t("sessions.renameDescription"),
2678
2687
  footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2679
2688
  className: "dcu-wb-rename-actions",
2680
2689
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
@@ -2692,6 +2701,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
2692
2701
  }),
2693
2702
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2694
2703
  className: "dcu-wb-rename-input",
2704
+ "aria-label": t("sessions.rename"),
2695
2705
  value: renameDraft,
2696
2706
  autoFocus: true,
2697
2707
  onFocus: (event) => {
@@ -3233,6 +3243,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
3233
3243
  .dcu-menu-icon{display:grid;place-items:center start;width:20px;height:20px}.dcu-menu-icon svg,.dcu-footer-link svg{display:block;width:16px;height:16px;color:var(--dcu-sidebar-icon)}.dcu-menu button:disabled{color:var(--dcu-sidebar-secondary);cursor:default;opacity:1}.dcu-menu button:disabled svg{color:var(--dcu-sidebar-secondary)}
3234
3244
  .dcu-extension-items{display:grid;gap:1px;margin:1px 0 4px 28px}.dcu-extension-items button{min-height:32px;color:var(--dcu-sidebar-secondary);font-size:13px;font-weight:400}
3235
3245
  .dcu-workspaces{display:flex;min-height:0;flex:1;flex-direction:column;margin-top:2px;padding-top:8px;border-top:1px solid var(--dcu-sidebar-border)}.dcu-workspaces.dcu-workspaces-tabs{padding-top:0;border-top:0}.dcu-im-tabs{display:flex;gap:16px;margin:0 8px 12px;padding:0;border-bottom:1px solid var(--dcu-sidebar-border)}.dcu-im-tab{appearance:none;border:0;background:transparent;color:var(--dcu-sidebar-secondary);padding:8px 0 7px;font:14px/22px var(--dcu-font);font-weight:500;cursor:pointer}.dcu-im-tab[data-on=true]{color:var(--dcu-sidebar-primary);font-weight:600;box-shadow:inset 0 -2px 0 currentColor}.dcu-native-workspaces{display:flex;min-height:0;flex:1}.dcu-native-workspaces>*{min-width:0;flex:1}.dcu-native-workspaces .ima-tabs,.dcu-native-workspaces [role=tablist]{display:none!important}.dcu-foot{display:grid;width:var(--dcu-sidebar-wide-width);gap:4px;padding:8px 6px 12px;border-top:1px solid var(--dcu-sidebar-border);transition:opacity 120ms ease-out,transform 160ms cubic-bezier(.16,1,.3,1)}.dcu-root.dcu-collapsing>.dcu-foot{opacity:0;transform:translateX(-4px);pointer-events:none}.dcu-footer-actions:empty,.dcu-settings-seat:empty{display:none}.dcu-settings-seat>button{width:100%;min-height:36px;padding-left:4px!important;color:var(--dcu-sidebar-navigation);font:14px/20px var(--dcu-font);font-weight:400}.dcu-compact{width:100%;align-items:flex-start;overflow:hidden;padding:10px 0 8px}.dcu-compact-nav{display:flex;flex:1;min-height:0;flex-direction:column;align-items:center;gap:2px;overflow:auto;padding:6px 0}.dcu-compact .dcu-icon{width:36px;height:36px;flex:none}.dcu-compact .dcu-foot{width:36px;margin-top:auto;margin-left:10px;padding:8px 0;border-top:0}.dcu-compact .dcu-settings-seat{width:36px;overflow:hidden}.dcu-compact .dcu-settings-seat>button{display:grid;place-items:center;width:36px;min-height:36px;padding:0!important;font-size:0!important;line-height:0}.dcu-compact .dcu-settings-seat>button svg{width:16px;height:16px}.dcu-compact .dcu-footer-link{display:flex;justify-content:center;width:36px;padding:0;font-size:0}.dcu-compact .dcu-footer-link svg{width:16px;height:16px}
3246
+ .dcu-native-workspaces{flex-direction:column}.dcu-native-workspaces>[data-mcp-connector-top-mount=true]{flex:none}.dcu-root .mcpConnectorLauncher,.dcu-root [data-mcp-connector-top-mount=true]{display:none!important}
3236
3247
  .dcu-dependency-notice{margin:0 10px 8px;border:1px solid var(--dcu-sidebar-border);border-radius:8px;padding:8px;color:var(--dcu-sidebar-secondary);font-size:12px;line-height:18px}
3237
3248
  [role="dialog"][aria-labelledby]{width:min(1000px,calc(100vw - 48px));max-width:calc(100vw - 48px);height:min(800px,calc(100vh - 48px))}
3238
3249
  .dcu-search-scrim{position:fixed;z-index:10020;inset:0;display:flex;justify-content:center;align-items:flex-start;padding:72px 20px;background:color-mix(in srgb,#000 48%,transparent);animation:dcu-search-scrim-in 140ms ease-out}.dcu-search-dialog{width:min(560px,100%);max-height:min(640px,calc(100vh - 120px));overflow:auto;border:1px solid var(--dcu-sidebar-border);border-radius:16px;padding:10px;background:var(--dsw-specific-menu);box-shadow:var(--dsw-shadow-lv4);animation:dcu-search-dialog-in 180ms cubic-bezier(.16,1,.3,1)}@keyframes dcu-search-scrim-in{from{opacity:0}to{opacity:1}}@keyframes dcu-search-dialog-in{from{opacity:0;transform:translateY(-6px) scale(.985)}to{opacity:1;transform:none}}.dcu-search-input{margin-bottom:8px}.dcu-search-section{padding:6px 0}.dcu-search-title{padding:0 8px 4px;color:var(--dcu-sidebar-tertiary);font-size:12px;font-weight:600}.dcu-search-row{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:12px;width:100%;min-height:34px;border:0;border-radius:8px;padding:6px 8px;background:transparent;color:var(--dcu-sidebar-primary);font:inherit;text-align:left;cursor:pointer}.dcu-search-row:hover,.dcu-search-row[data-active=true]{background:var(--dcu-sidebar-hover)}.dcu-search-main{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dcu-search-detail{max-width:160px;overflow:hidden;color:var(--dcu-sidebar-tertiary);font-size:12px;text-overflow:ellipsis;white-space:nowrap}.dcu-search-empty{padding:18px 8px;color:var(--dcu-sidebar-tertiary);font-size:13px}@media (prefers-reduced-motion:reduce){.dcu-expanded-shell,.dcu-root.dcu-compact .dcu-compact-shell,.dcu-foot,.dcu-search-scrim,.dcu-search-dialog{animation:none;transition:none}}
@@ -4180,16 +4191,82 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4180
4191
  }
4181
4192
  //#endregion
4182
4193
  //#region src/client/ConnectorsSection.tsx
4194
+ const MCP_CONNECTOR_UI = "/mcp-connector/ui/";
4195
+ const PROMPT_REQUEST_TYPE = "mcp-connector:start-session";
4196
+ const PROMPT_RESULT_TYPE = "mcp-connector:start-session-result";
4183
4197
  const stylesheet$1 = `
4184
- .dcu-connectors{color:var(--dsw-alias-label-primary)}.dcu-connectors h2{margin:0;font-size:18px}.dcu-connectors p{margin:6px 0 18px;color:var(--dsw-alias-label-secondary);font-size:12px}.dcu-connector-list{overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:9px}.dcu-connector{padding:12px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dcu-connector:last-child{border-bottom:0}.dcu-connector-head{display:flex;align-items:center;gap:8px;font-weight:650}.dcu-connector-meta{margin:3px 0 8px;color:var(--dsw-alias-label-tertiary);font-size:11px}.dcu-connector-tool{padding:5px 0 0 24px;color:var(--dsw-alias-label-secondary);font-size:12px}.dcu-connector-tool span{display:block;margin-top:1px;color:var(--dsw-alias-label-tertiary);font-size:11px}.dcu-connector-empty{padding:20px 8px;color:var(--dsw-alias-label-secondary);text-align:center}
4198
+ .dcu-connectors{color:var(--dsw-alias-label-primary)}.dcu-connectors h2{margin:0;font-size:18px}.dcu-connectors p{margin:6px 0 18px;color:var(--dsw-alias-label-secondary);font-size:12px}.dcu-connector-frame{display:block;width:100%;height:clamp(420px,calc(100vh - 160px),700px);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-base);color-scheme:light dark}.dcu-connector-list{overflow:hidden;border:1px solid var(--dsw-alias-border-l2);border-radius:9px}.dcu-connector{padding:12px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dcu-connector:last-child{border-bottom:0}.dcu-connector-head{display:flex;align-items:center;gap:8px;font-weight:650}.dcu-connector-meta{margin:3px 0 8px;color:var(--dsw-alias-label-tertiary);font-size:11px}.dcu-connector-tool{padding:5px 0 0 24px;color:var(--dsw-alias-label-secondary);font-size:12px}.dcu-connector-tool span{display:block;margin-top:1px;color:var(--dsw-alias-label-tertiary);font-size:11px}.dcu-connector-empty{padding:20px 8px;color:var(--dsw-alias-label-secondary);text-align:center}
4185
4199
  `;
4200
+ const frameLightTheme = `:root{color-scheme:light;--bg:#f8f9fb;--card:#fff;--line:#e5e7eb;--text:#111827;--text-2:#4b5563;--muted:#9ca3af;--accent:#4f46e5;--accent-hover:#4338ca;--accent-light:#eef2ff;--ok:#059669;--ok-bg:#ecfdf5;--warn:#d97706;--warn-bg:#fffbeb;--bad:#dc2626;--bad-bg:#fef2f2;--shadow:0 1px 3px rgba(0,0,0,.08),0 1px 2px rgba(0,0,0,.06);--shadow-lg:0 10px 25px rgba(0,0,0,.1)}`;
4201
+ const frameDarkTheme = `:root{color-scheme:dark;--bg:#111318;--card:#1b1e25;--line:#30343d;--text:#f3f4f6;--text-2:#c4c8d0;--muted:#8e96a3;--accent:#818cf8;--accent-hover:#6366f1;--accent-light:#252750;--ok:#34d399;--ok-bg:#0d3027;--warn:#fbbf24;--warn-bg:#35280b;--bad:#f87171;--bad-bg:#3b171b;--shadow:0 1px 3px rgba(0,0,0,.35);--shadow-lg:0 16px 35px rgba(0,0,0,.45)}`;
4186
4202
  function isConnector(value) {
4187
4203
  if (value === null || typeof value !== "object") return false;
4188
4204
  const item = value;
4189
4205
  return typeof item.name === "string" && Array.isArray(item.tools) && item.tools.every((tool) => tool !== null && typeof tool === "object" && typeof tool.name === "string" && typeof tool.description === "string");
4190
4206
  }
4191
- /** 设置内的 MCP 连接器目录,只暴露名称、工具数和工具说明。 */
4192
- function ConnectorsSection({ sessionStore, t }) {
4207
+ function isPromptRequest(value) {
4208
+ if (value === null || typeof value !== "object") return false;
4209
+ const item = value;
4210
+ return item.type === PROMPT_REQUEST_TYPE && typeof item.requestId === "string" && item.requestId !== "" && typeof item.prompt === "string" && item.prompt.trim() !== "";
4211
+ }
4212
+ function syncFrameTheme(frame) {
4213
+ const doc = frame?.contentDocument;
4214
+ if (doc === null || doc === void 0) return;
4215
+ let style = doc.head.querySelector("style[data-michengai-host-theme]");
4216
+ if (style === null) {
4217
+ style = doc.createElement("style");
4218
+ style.dataset.michengaiHostTheme = "true";
4219
+ doc.head.append(style);
4220
+ }
4221
+ style.textContent = document.body.hasAttribute("data-ds-dark-theme") ? frameDarkTheme : frameLightTheme;
4222
+ }
4223
+ function ConnectorMarket({ startPromptSession }) {
4224
+ const frameRef = (0, react.useRef)(null);
4225
+ (0, react.useEffect)(() => {
4226
+ const onMessage = (event) => {
4227
+ const frameWindow = frameRef.current?.contentWindow;
4228
+ if (event.origin !== window.location.origin || frameWindow === null || frameWindow === void 0 || event.source !== frameWindow) return;
4229
+ if (!isPromptRequest(event.data)) return;
4230
+ const { requestId, prompt } = event.data;
4231
+ const reply = (ok, message) => {
4232
+ frameWindow.postMessage({
4233
+ type: PROMPT_RESULT_TYPE,
4234
+ requestId,
4235
+ ok,
4236
+ message
4237
+ }, window.location.origin);
4238
+ };
4239
+ startPromptSession(prompt).then(() => {
4240
+ reply(true, "已带入新会话");
4241
+ }, (error) => {
4242
+ reply(false, error instanceof Error ? error.message : String(error));
4243
+ });
4244
+ };
4245
+ const syncTheme = () => {
4246
+ syncFrameTheme(frameRef.current);
4247
+ };
4248
+ const observer = new MutationObserver(syncTheme);
4249
+ observer.observe(document.body, {
4250
+ attributes: true,
4251
+ attributeFilter: ["data-ds-dark-theme"]
4252
+ });
4253
+ window.addEventListener("message", onMessage);
4254
+ return () => {
4255
+ observer.disconnect();
4256
+ window.removeEventListener("message", onMessage);
4257
+ };
4258
+ }, [startPromptSession]);
4259
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
4260
+ ref: frameRef,
4261
+ className: "dcu-connector-frame",
4262
+ src: MCP_CONNECTOR_UI,
4263
+ title: "MCP连接器",
4264
+ onLoad: () => {
4265
+ syncFrameTheme(frameRef.current);
4266
+ }
4267
+ });
4268
+ }
4269
+ function NativeConnectorList({ sessionStore, t }) {
4193
4270
  const sessionId = (0, react.useSyncExternalStore)(sessionStore.subscribe, () => sessionStore.getSnapshot().current);
4194
4271
  const [connectors, setConnectors] = (0, react.useState)([]);
4195
4272
  const [state, setState] = (0, react.useState)("loading");
@@ -4215,46 +4292,74 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4215
4292
  controller.abort();
4216
4293
  };
4217
4294
  }, [sessionId]);
4295
+ if (sessionId === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4296
+ className: "dcu-connector-empty",
4297
+ children: t("connectors.openSession")
4298
+ });
4299
+ if (state === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4300
+ className: "dcu-connector-empty",
4301
+ children: t("connectors.loading")
4302
+ });
4303
+ if (state === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4304
+ className: "dcu-connector-empty",
4305
+ children: t("connectors.failed")
4306
+ });
4307
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4308
+ className: "dcu-connector-list",
4309
+ children: [connectors.map((connector) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
4310
+ className: "dcu-connector",
4311
+ children: [
4312
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4313
+ className: "dcu-connector-head",
4314
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLinkOutline16, { size: 16 }), connector.name]
4315
+ }),
4316
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4317
+ className: "dcu-connector-meta",
4318
+ children: t("connectors.toolCount", { count: connector.tools.length })
4319
+ }),
4320
+ connector.tools.map((tool) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4321
+ className: "dcu-connector-tool",
4322
+ children: [tool.name, tool.description !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: tool.description })]
4323
+ }, tool.name))
4324
+ ]
4325
+ }, connector.name)), connectors.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4326
+ className: "dcu-connector-empty",
4327
+ children: t("connectors.empty")
4328
+ })]
4329
+ });
4330
+ }
4331
+ /** 安装 dsh-mcp-connector 时显示完整市场,否则回退到当前会话的 MCP 工具目录。 */
4332
+ function ConnectorsSection({ sessionStore, startPromptSession, t }) {
4333
+ const [marketAvailable, setMarketAvailable] = (0, react.useState)();
4334
+ (0, react.useEffect)(() => {
4335
+ const controller = new AbortController();
4336
+ fetch(MCP_CONNECTOR_UI, {
4337
+ method: "HEAD",
4338
+ cache: "no-store",
4339
+ signal: controller.signal
4340
+ }).then((response) => {
4341
+ if (!controller.signal.aborted) setMarketAvailable(response.ok);
4342
+ }).catch(() => {
4343
+ if (!controller.signal.aborted) setMarketAvailable(false);
4344
+ });
4345
+ return () => {
4346
+ controller.abort();
4347
+ };
4348
+ }, []);
4218
4349
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
4219
4350
  className: "dcu-connectors",
4220
4351
  "aria-label": t("connectors.title"),
4221
- children: [
4222
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: stylesheet$1 }),
4352
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: stylesheet$1 }), marketAvailable === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4353
+ className: "dcu-connector-empty",
4354
+ children: t("connectors.loading")
4355
+ }) : marketAvailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConnectorMarket, { startPromptSession }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
4223
4356
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: t("connectors.title") }),
4224
4357
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("connectors.description") }),
4225
- sessionId === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4226
- className: "dcu-connector-empty",
4227
- children: t("connectors.openSession")
4228
- }) : state === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4229
- className: "dcu-connector-empty",
4230
- children: t("connectors.loading")
4231
- }) : state === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4232
- className: "dcu-connector-empty",
4233
- children: t("connectors.failed")
4234
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4235
- className: "dcu-connector-list",
4236
- children: [connectors.map((connector) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
4237
- className: "dcu-connector",
4238
- children: [
4239
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4240
- className: "dcu-connector-head",
4241
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLinkOutline16, { size: 16 }), connector.name]
4242
- }),
4243
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4244
- className: "dcu-connector-meta",
4245
- children: t("connectors.toolCount", { count: connector.tools.length })
4246
- }),
4247
- connector.tools.map((tool) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4248
- className: "dcu-connector-tool",
4249
- children: [tool.name, tool.description !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: tool.description })]
4250
- }, tool.name))
4251
- ]
4252
- }, connector.name)), connectors.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4253
- className: "dcu-connector-empty",
4254
- children: t("connectors.empty")
4255
- })]
4358
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NativeConnectorList, {
4359
+ sessionStore,
4360
+ t
4256
4361
  })
4257
- ]
4362
+ ] })]
4258
4363
  });
4259
4364
  }
4260
4365
  //#endregion
@@ -4327,6 +4432,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4327
4432
  "sessions.markRead": "标记为已读",
4328
4433
  "sessions.unread": "未读",
4329
4434
  "sessions.rename": "重命名会话",
4435
+ "sessions.renameDescription": "保持简短且易于识别",
4330
4436
  "sessions.archive": "归档会话",
4331
4437
  "sessions.delete": "删除会话",
4332
4438
  "sessions.deleteDescription": "将永久删除会话“{name}”及其全部记录,此操作不可恢复。",
@@ -4449,6 +4555,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4449
4555
  "sessions.markRead": "Mark as read",
4450
4556
  "sessions.unread": "Unread",
4451
4557
  "sessions.rename": "Rename conversation",
4558
+ "sessions.renameDescription": "Keep it short and easy to recognize.",
4452
4559
  "sessions.archive": "Archive conversation",
4453
4560
  "sessions.delete": "Delete conversation",
4454
4561
  "sessions.deleteDescription": "This permanently deletes “{name}” and all of its records. This cannot be undone.",
@@ -4505,6 +4612,30 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4505
4612
  "turns.untitled": "Untitled prompt",
4506
4613
  "turns.jump": "Jump to turn {index}: {summary}"
4507
4614
  };
4615
+ //#endregion
4616
+ //#region src/client/host-open-path.ts
4617
+ const OPEN_IN_EXPLORER_ENDPOINT = "/api/michengai/codex-ui/open-in-explorer";
4618
+ /** “在资源管理器中打开”属于明确的系统动作,不应被侧边栏文件预览器接管。 */
4619
+ async function openPathInHost(connection, path, fetcher = fetch) {
4620
+ let foregroundResponse;
4621
+ try {
4622
+ foregroundResponse = await fetcher(OPEN_IN_EXPLORER_ENDPOINT, {
4623
+ method: "POST",
4624
+ headers: { "content-type": "application/json" },
4625
+ body: JSON.stringify({ path })
4626
+ });
4627
+ } catch {}
4628
+ if (foregroundResponse?.ok) return;
4629
+ if (foregroundResponse !== void 0 && foregroundResponse.status !== 404 && foregroundResponse.status !== 501) {
4630
+ const payload = await foregroundResponse.json().catch(() => ({}));
4631
+ throw new Error(typeof payload.error === "string" ? payload.error : "无法在前台打开资源管理器。");
4632
+ }
4633
+ const response = await connection.api.host.openPath({ path });
4634
+ if (!response.result.ok) throw new Error(`path open failed: ${response.result.error.message}`);
4635
+ }
4636
+ //#endregion
4637
+ //#region src/client/settings-nav-icons.ts
4638
+ const ABOUT_SETTINGS_NAV_ORDER = "2147483647";
4508
4639
  const SETTINGS_NAV_ICON_HTML = {
4509
4640
  experts: "<path fill=\"currentColor\" d=\"M11.0307 5.46369C11.0305 3.78995 9.6734 2.43357 7.99961 2.43357C6.32601 2.43379 4.96972 3.79009 4.96949 5.46369C4.96949 7.13748 6.32587 8.49455 7.99961 8.49477C9.67354 8.49477 11.0307 7.13762 11.0307 5.46369ZM12.3163 5.46369C12.3163 7.84777 10.3837 9.78042 7.99961 9.78042C5.61572 9.7802 3.68288 7.84763 3.68288 5.46369C3.6831 3.07993 5.61586 1.14718 7.99961 1.14695C10.3836 1.14695 12.3161 3.0798 12.3163 5.46369Z\"/><path fill=\"currentColor\" d=\"M8.00002 10.3316C11.7343 10.3316 14.1864 11.8997 15.0387 14.4445L14.4292 14.6483L13.8197 14.8531C13.1955 12.9893 11.3673 11.6182 8.00002 11.6182C4.63277 11.6182 2.80455 12.9893 2.18031 14.8531L1.5708 14.6483L0.961304 14.4445C1.81368 11.8997 4.26579 10.3316 8.00002 10.3316Z\"/>",
4510
4641
  skills: "<path fill=\"currentColor\" d=\"M12.5113 15.4067C12.4395 15.6249 12.1308 15.6249 12.059 15.4067L11.643 14.1416C11.454 13.567 11.0033 13.1164 10.4288 12.9274L9.16369 12.5113C8.94544 12.4395 8.94544 12.1308 9.16369 12.059L10.4288 11.643C11.0033 11.454 11.454 11.0033 11.643 10.4288L12.059 9.16369C12.1308 8.94544 12.4395 8.94544 12.5113 9.16369L12.9274 10.4288C13.1164 11.0033 13.567 11.454 14.1416 11.643L15.4067 12.059C15.6249 12.1308 15.6249 12.4395 15.4067 12.5113L14.1416 12.9274C13.567 13.1164 13.1164 13.567 12.9274 14.1416L12.5113 15.4067Z\"/><path fill=\"currentColor\" d=\"M9.02246 0.546878C9.9822 0.546878 10.7564 0.545403 11.374 0.612307C12.0042 0.680586 12.5515 0.826244 13.0273 1.17188C13.3052 1.37376 13.5501 1.61868 13.752 1.89649C14.0975 2.37225 14.2432 2.91984 14.3115 3.54981C14.3784 4.16727 14.377 4.94206 14.377 5.90137V8.51367C13.9611 8.29533 13.5071 8.13985 13.0273 8.06055V5.90137C13.0273 4.9121 13.0259 4.22322 12.9688 3.69532C12.9129 3.18044 12.8098 2.89782 12.6592 2.69043C12.5406 2.52724 12.3966 2.38326 12.2334 2.26465C12.026 2.11404 11.7437 2.0109 11.2285 1.95508C10.7005 1.89789 10.0122 1.89649 9.02246 1.89649H6.55371C5.56395 1.89649 4.87569 1.89787 4.34766 1.95508C3.83242 2.01092 3.55022 2.11398 3.34278 2.26465C3.17953 2.38329 3.03564 2.52719 2.91699 2.69043C2.76642 2.89782 2.66325 3.18042 2.60742 3.69532C2.55027 4.22322 2.54883 4.9121 2.54883 5.90137V10.0986C2.54883 11.0878 2.55031 11.7768 2.60742 12.3047C2.66326 12.8196 2.76642 13.1032 2.91699 13.3105C3.03558 13.4736 3.17966 13.6178 3.34278 13.7363C3.5502 13.8869 3.83265 13.9901 4.34766 14.0459C4.87568 14.1031 5.56398 14.1035 6.55371 14.1035H8.08399C8.27443 14.6025 8.55077 15.0585 8.89551 15.4541H6.55371C5.59402 15.4541 4.81976 15.4546 4.20215 15.3877C3.57204 15.3194 3.02468 15.1738 2.54883 14.8281C2.27111 14.6263 2.02606 14.3813 1.82422 14.1035C1.47883 13.6278 1.33293 13.08 1.26465 12.4502C1.19783 11.8327 1.19922 11.0579 1.19922 10.0986V5.90137C1.19922 4.94206 1.1978 4.16727 1.26465 3.54981C1.33295 2.91984 1.47867 2.37225 1.82422 1.89649C2.02613 1.61864 2.27098 1.37379 2.54883 1.17188C3.02472 0.826181 3.57197 0.6806 4.20215 0.612307C4.81976 0.545393 5.594 0.546877 6.55371 0.546878H9.02246ZM9.19629 9.14649H4.5459V7.84571H9.19629V9.14649ZM11.0303 6.10645H4.5459V4.80567H11.0303V6.10645Z\"/>",
@@ -4534,9 +4665,20 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4534
4665
  function applySettingsNavIcon(button) {
4535
4666
  const id = settingsNavIconId(button.textContent ?? "");
4536
4667
  if (id === void 0) return false;
4668
+ let changed = false;
4669
+ if (id === "about") {
4670
+ if (!button.hasAttribute("data-dcu-settings-about-last")) {
4671
+ button.setAttribute("data-dcu-settings-about-last", "");
4672
+ changed = true;
4673
+ }
4674
+ if (button.style.getPropertyValue("order") !== "2147483647" || button.style.getPropertyPriority("order") !== "important") {
4675
+ button.style.setProperty("order", ABOUT_SETTINGS_NAV_ORDER, "important");
4676
+ changed = true;
4677
+ }
4678
+ }
4537
4679
  const svg = button.querySelector("svg");
4538
- if (svg === null) return false;
4539
- if (svg.getAttribute("data-dcu-nav-icon") === id) return false;
4680
+ if (svg === null) return changed;
4681
+ if (svg.getAttribute("data-dcu-nav-icon") === id) return changed;
4540
4682
  svg.setAttribute("data-dcu-nav-icon", id);
4541
4683
  svg.setAttribute("viewBox", id === "archive" ? "0 0 20 20" : id === "about" ? "0 0 14 14" : "0 0 16 16");
4542
4684
  svg.setAttribute("fill", "none");
@@ -4581,6 +4723,105 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4581
4723
  };
4582
4724
  }
4583
4725
  //#endregion
4726
+ //#region src/client/permission-labels.ts
4727
+ const PERMISSION_LABELS = [
4728
+ {
4729
+ aliases: [
4730
+ "Read Only",
4731
+ "preset.readOnly",
4732
+ "只读"
4733
+ ],
4734
+ en: "Read Only",
4735
+ zh: "只读"
4736
+ },
4737
+ {
4738
+ aliases: [
4739
+ "Workspace Write",
4740
+ "preset.workspaceWrite",
4741
+ "工作区写入"
4742
+ ],
4743
+ en: "Workspace Write",
4744
+ zh: "工作区写入"
4745
+ },
4746
+ {
4747
+ aliases: [
4748
+ "Full access",
4749
+ "preset.fullAccess",
4750
+ "完全访问"
4751
+ ],
4752
+ en: "Full access",
4753
+ zh: "完全访问"
4754
+ }
4755
+ ];
4756
+ const INTERACTIVE_SELECTOR = "button, [role=\"menuitem\"], [role=\"menuitemradio\"], [role=\"option\"]";
4757
+ function targetLabel(value, activeLocale) {
4758
+ const definition = PERMISSION_LABELS.find((item) => item.aliases.includes(value));
4759
+ if (definition === void 0) return void 0;
4760
+ return activeLocale.toLowerCase().startsWith("zh") ? definition.zh : definition.en;
4761
+ }
4762
+ function applyPermissionLabel(element, activeLocale) {
4763
+ const document = element.ownerDocument;
4764
+ const showText = document.defaultView?.NodeFilter.SHOW_TEXT ?? 4;
4765
+ const walker = document.createTreeWalker(element, showText);
4766
+ let changed = false;
4767
+ let node = walker.nextNode();
4768
+ while (node !== null) {
4769
+ const value = node.nodeValue ?? "";
4770
+ const trimmed = value.trim();
4771
+ const next = targetLabel(trimmed, activeLocale);
4772
+ if (next !== void 0 && next !== trimmed) {
4773
+ node.nodeValue = value.replace(trimmed, next);
4774
+ changed = true;
4775
+ }
4776
+ node = walker.nextNode();
4777
+ }
4778
+ return changed;
4779
+ }
4780
+ /** 只修正权限选择控件里的内置预设名称,不改动正文或自定义权限名称。 */
4781
+ function localizePermissionLabels(root, activeLocale) {
4782
+ let changed = 0;
4783
+ const rootElement = root.nodeType === 1 ? root : void 0;
4784
+ const elements = rootElement?.matches(INTERACTIVE_SELECTOR) === true ? [rootElement, ...rootElement.querySelectorAll(INTERACTIVE_SELECTOR)] : [...root.querySelectorAll(INTERACTIVE_SELECTOR)];
4785
+ for (const element of elements) if (applyPermissionLabel(element, activeLocale)) changed += 1;
4786
+ return changed;
4787
+ }
4788
+ /** 兼容宿主设置页和 Chat 中由官方组件直接输出的英文权限名称。 */
4789
+ function observePermissionLabels(locale) {
4790
+ if (typeof document === "undefined" || document.body === null) return () => {};
4791
+ let applying = false;
4792
+ let frame;
4793
+ const apply = () => {
4794
+ if (applying) return;
4795
+ applying = true;
4796
+ const active = locale.getSnapshot?.().active ?? document.documentElement.lang ?? window.navigator.language ?? "zh";
4797
+ try {
4798
+ localizePermissionLabels(document, active);
4799
+ } finally {
4800
+ applying = false;
4801
+ }
4802
+ };
4803
+ const schedule = () => {
4804
+ if (frame !== void 0) return;
4805
+ frame = window.requestAnimationFrame(() => {
4806
+ frame = void 0;
4807
+ apply();
4808
+ });
4809
+ };
4810
+ apply();
4811
+ const observer = new MutationObserver(schedule);
4812
+ observer.observe(document.body, {
4813
+ childList: true,
4814
+ characterData: true,
4815
+ subtree: true
4816
+ });
4817
+ const unsubscribe = locale.subscribe?.(schedule) ?? (() => {});
4818
+ return () => {
4819
+ observer.disconnect();
4820
+ unsubscribe();
4821
+ if (frame !== void 0) window.cancelAnimationFrame(frame);
4822
+ };
4823
+ }
4824
+ //#endregion
4584
4825
  //#region src/client/conversation-dom.ts
4585
4826
  /**
4586
4827
  * DSH 尚未公开会话滚动容器和消息锚点服务;把兼容读取集中在这里,方便
@@ -4828,7 +5069,9 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4828
5069
  "sessions",
4829
5070
  "workspaces",
4830
5071
  "layout",
4831
- "locale"
5072
+ "locale",
5073
+ "connection",
5074
+ "conversation"
4832
5075
  ];
4833
5076
  function hasDeleteSession(value) {
4834
5077
  return value !== null && typeof value === "object" && "deleteSession" in value && typeof value.deleteSession === "function";
@@ -4840,8 +5083,11 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4840
5083
  en
4841
5084
  }), "michengai-codex-ui: dictionaries");
4842
5085
  const t = ctx.locale.bind(NS);
5086
+ const connection = ctx.get("connection");
5087
+ const openPath = (path) => openPathInHost(connection, path);
4843
5088
  ctx.effect(() => observeSlimSidebar(), "michengai-codex-ui: slim sidebar");
4844
5089
  ctx.effect(() => observeSettingsNavIcons(), "michengai-codex-ui: settings nav icons");
5090
+ ctx.effect(() => observePermissionLabels(ctx.locale), "michengai-codex-ui: permission labels");
4845
5091
  ctx.effect(() => observeConversationHeader(), "michengai-codex-ui: conversation header");
4846
5092
  const companionSlots = createCompanionTabSource(ctx.slots);
4847
5093
  ctx.slots.inject("sidebar", () => ctx.slots.register({
@@ -4884,7 +5130,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4884
5130
  deleteSession,
4885
5131
  forkSession,
4886
5132
  renameSession,
4887
- openPath: (path) => ctx.workspaces.openPath(path),
5133
+ openPath,
4888
5134
  companionSlots
4889
5135
  })
4890
5136
  }, CodexSidebar));
@@ -4913,6 +5159,21 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4913
5159
  const result = await registry.deleteSession(sessionId);
4914
5160
  if (!result.ok) throw new Error(result.error?.message ?? t("sessions.deleteUnavailable"));
4915
5161
  };
5162
+ const startConnectorPromptSession = async (promptText) => {
5163
+ const prompt = promptText.trim();
5164
+ if (prompt === "") throw new Error("Prompt 不能为空");
5165
+ const workspaces = ctx.workspaces.list.getSnapshot();
5166
+ const currentSessionId = ctx.sessions.list.getSnapshot().current;
5167
+ const targetWorkspaceId = (currentSessionId === void 0 ? void 0 : workspaces.items.find((workspace) => workspace.sessionIds.includes(currentSessionId))?.workspaceId) ?? workspaces.recentWorkspaceId;
5168
+ if (targetWorkspaceId === void 0) throw new Error("请先选择一个工作空间,再使用示例 Prompt");
5169
+ const sessionId = await ctx.workspaces.connectWorkspace(targetWorkspaceId);
5170
+ const conversation = ctx.get("conversation");
5171
+ if (conversation === void 0) throw new Error("DSH 对话服务尚未就绪,请稍后重试");
5172
+ const binding = ctx.sessions.binding(sessionId);
5173
+ if (binding === void 0) throw new Error("新会话尚未就绪,请稍后重试");
5174
+ conversation.input.for(binding.ctx).setDraft(prompt);
5175
+ ctx.sessions.open(sessionId);
5176
+ };
4916
5177
  ctx.slots.inject("sidebar.workspaces", () => ctx.slots.register({
4917
5178
  name: "sidebar.workspaces",
4918
5179
  priority: -1,
@@ -4922,7 +5183,7 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4922
5183
  deleteSession,
4923
5184
  deleteWorkspace: (workspaceId) => ctx.workspaces.delete(workspaceId),
4924
5185
  forkSession,
4925
- openPath: (path) => ctx.workspaces.openPath(path),
5186
+ openPath,
4926
5187
  openSession: (sessionId) => {
4927
5188
  ctx.sessions.open(sessionId);
4928
5189
  },
@@ -4956,13 +5217,14 @@ header [data-dcu-title-folder] svg,header [data-dcu-title-more] svg{display:bloc
4956
5217
  icon: "connector",
4957
5218
  inject: () => ({
4958
5219
  sessionStore: ctx.sessions.list,
5220
+ startPromptSession: startConnectorPromptSession,
4959
5221
  t
4960
5222
  })
4961
5223
  }, ConnectorsSection));
4962
5224
  ctx.slots.inject("settings.section", () => ctx.slots.register({
4963
5225
  name: "settings.section",
4964
5226
  id: "about",
4965
- order: 100,
5227
+ order: Number.MAX_SAFE_INTEGER,
4966
5228
  label: () => t("about.nav"),
4967
5229
  locale: NS
4968
5230
  }, AboutSection));
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { mkdir, readFile, rename, rm, unlink, writeFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
- import { basename, dirname, join, resolve, sep } from "node:path";
4
+ import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { randomUUID } from "node:crypto";
7
7
  //#region src/dependencies.ts
@@ -529,6 +529,224 @@ function hostServices(ctx) {
529
529
  };
530
530
  }
531
531
  //#endregion
532
+ //#region src/native-explorer.ts
533
+ const WINDOWS_EXPLORER_TIMEOUT_MS = 8e3;
534
+ const WINDOWS_FOREGROUND_EXPLORER_SCRIPT = String.raw`
535
+ $ErrorActionPreference = 'Stop'
536
+
537
+ Add-Type -TypeDefinition @'
538
+ using System;
539
+ using System.Runtime.InteropServices;
540
+ public static class DcuWindowFocus {
541
+ [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
542
+ [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint pid);
543
+ [DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId();
544
+ [DllImport("user32.dll")] public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool attach);
545
+ [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int command);
546
+ [DllImport("user32.dll")] public static extern bool BringWindowToTop(IntPtr hWnd);
547
+ [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
548
+ }
549
+ '@
550
+
551
+ $shell = New-Object -ComObject Shell.Application
552
+
553
+ function Find-ExplorerWindow([string]$path) {
554
+ foreach ($item in @($shell.Windows())) {
555
+ try {
556
+ $candidate = [IO.Path]::GetFullPath([string]$item.Document.Folder.Self.Path).TrimEnd('\')
557
+ if ($candidate -ieq $path) { return $item }
558
+ } catch {}
559
+ }
560
+ return $null
561
+ }
562
+
563
+ function Open-ExplorerInForeground([string]$target) {
564
+ $target = [IO.Path]::GetFullPath($target).TrimEnd('\')
565
+ $window = Find-ExplorerWindow $target
566
+ if ($null -eq $window) {
567
+ $shell.Explore($target)
568
+ for ($attempt = 0; $attempt -lt 120 -and $null -eq $window; $attempt += 1) {
569
+ Start-Sleep -Milliseconds 25
570
+ $window = Find-ExplorerWindow $target
571
+ }
572
+ }
573
+ if ($null -eq $window) { throw 'Explorer window was not found.' }
574
+
575
+ $handle = [IntPtr]([long]$window.HWND)
576
+ $foreground = [DcuWindowFocus]::GetForegroundWindow()
577
+ $foregroundProcessId = 0
578
+ $foregroundThread = [DcuWindowFocus]::GetWindowThreadProcessId($foreground, [ref]$foregroundProcessId)
579
+ $currentThread = [DcuWindowFocus]::GetCurrentThreadId()
580
+ $attached = $foregroundThread -ne 0 -and [DcuWindowFocus]::AttachThreadInput($currentThread, $foregroundThread, $true)
581
+ try {
582
+ [void][DcuWindowFocus]::ShowWindowAsync($handle, 3)
583
+ [void][DcuWindowFocus]::BringWindowToTop($handle)
584
+ if (-not [DcuWindowFocus]::SetForegroundWindow($handle)) { throw 'Explorer could not be activated.' }
585
+ } finally {
586
+ if ($attached) { [void][DcuWindowFocus]::AttachThreadInput($currentThread, $foregroundThread, $false) }
587
+ }
588
+ }
589
+
590
+ [Console]::Out.WriteLine('READY')
591
+ [Console]::Out.Flush()
592
+ while (($line = [Console]::In.ReadLine()) -ne $null) {
593
+ if ([string]::IsNullOrWhiteSpace($line)) { continue }
594
+ $parts = $line.Split([char]9, 2)
595
+ $requestId = $parts[0]
596
+ try {
597
+ if ($parts.Count -ne 2) { throw 'Invalid request.' }
598
+ $target = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($parts[1]))
599
+ Open-ExplorerInForeground $target
600
+ [Console]::Out.WriteLine(('OK' + [char]9 + $requestId))
601
+ } catch {
602
+ $message = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes([string]$_.Exception.Message))
603
+ [Console]::Out.WriteLine(('ERR' + [char]9 + $requestId + [char]9 + $message))
604
+ }
605
+ [Console]::Out.Flush()
606
+ }
607
+ `;
608
+ /**
609
+ * 常驻的 Windows Explorer 前台激活助手。
610
+ * PowerShell 与窗口 API 只在 Host 启动时初始化一次,点击时仅发送一行路径请求。
611
+ */
612
+ var ForegroundExplorer = class {
613
+ run;
614
+ platform;
615
+ child;
616
+ startupChild;
617
+ starting;
618
+ stdout = "";
619
+ sequence = 0;
620
+ pending = /* @__PURE__ */ new Map();
621
+ disposed = false;
622
+ constructor(run = spawn, platform = process.platform) {
623
+ this.run = run;
624
+ this.platform = platform;
625
+ }
626
+ /** 在用户点击前完成 PowerShell 与窗口 API 初始化。 */
627
+ warmup() {
628
+ if (this.platform !== "win32") return Promise.resolve();
629
+ return this.ensureStarted().then(() => void 0);
630
+ }
631
+ async open(path) {
632
+ if (this.platform !== "win32") throw new Error("foreground Explorer is only available on Windows");
633
+ if (!isAbsolute(path)) throw new Error("Explorer path must be absolute");
634
+ if (this.disposed) throw new Error("Explorer helper has been disposed");
635
+ const child = await this.ensureStarted();
636
+ const requestId = String(++this.sequence);
637
+ const encodedPath = Buffer.from(path, "utf8").toString("base64");
638
+ return await new Promise((resolve, reject) => {
639
+ const timeout = setTimeout(() => {
640
+ this.pending.delete(requestId);
641
+ reject(/* @__PURE__ */ new Error("Explorer activation timed out"));
642
+ }, WINDOWS_EXPLORER_TIMEOUT_MS);
643
+ timeout.unref?.();
644
+ this.pending.set(requestId, {
645
+ resolve,
646
+ reject,
647
+ timeout
648
+ });
649
+ child.stdin.write(`${requestId}\t${encodedPath}\n`, (error) => {
650
+ if (error === null || error === void 0) return;
651
+ const request = this.pending.get(requestId);
652
+ if (request === void 0) return;
653
+ clearTimeout(request.timeout);
654
+ this.pending.delete(requestId);
655
+ request.reject(error);
656
+ });
657
+ });
658
+ }
659
+ dispose() {
660
+ this.disposed = true;
661
+ this.failAll(/* @__PURE__ */ new Error("Explorer helper has been disposed"));
662
+ this.startupChild?.kill();
663
+ this.child?.kill();
664
+ this.startupChild = void 0;
665
+ this.child = void 0;
666
+ this.starting = void 0;
667
+ }
668
+ ensureStarted() {
669
+ if (this.disposed) return Promise.reject(/* @__PURE__ */ new Error("Explorer helper has been disposed"));
670
+ if (this.child !== void 0) return Promise.resolve(this.child);
671
+ if (this.starting !== void 0) return this.starting;
672
+ const encoded = Buffer.from(WINDOWS_FOREGROUND_EXPLORER_SCRIPT, "utf16le").toString("base64");
673
+ this.starting = new Promise((resolve, reject) => {
674
+ const child = this.run("powershell.exe", [
675
+ "-NoLogo",
676
+ "-NoProfile",
677
+ "-NonInteractive",
678
+ "-EncodedCommand",
679
+ encoded
680
+ ], { windowsHide: true });
681
+ this.startupChild = child;
682
+ let settled = false;
683
+ const finishStart = (error) => {
684
+ if (settled) return;
685
+ settled = true;
686
+ clearTimeout(startupTimeout);
687
+ if (error !== void 0) {
688
+ this.starting = void 0;
689
+ if (this.startupChild === child) this.startupChild = void 0;
690
+ child.kill();
691
+ reject(error);
692
+ return;
693
+ }
694
+ if (this.disposed) {
695
+ this.starting = void 0;
696
+ if (this.startupChild === child) this.startupChild = void 0;
697
+ child.kill();
698
+ reject(/* @__PURE__ */ new Error("Explorer helper has been disposed"));
699
+ return;
700
+ }
701
+ if (this.startupChild === child) this.startupChild = void 0;
702
+ this.child = child;
703
+ this.starting = void 0;
704
+ resolve(child);
705
+ };
706
+ const startupTimeout = setTimeout(() => finishStart(/* @__PURE__ */ new Error("Explorer helper startup timed out")), WINDOWS_EXPLORER_TIMEOUT_MS);
707
+ startupTimeout.unref?.();
708
+ child.stdout.on("data", (chunk) => {
709
+ this.stdout += String(chunk);
710
+ let newline = this.stdout.indexOf("\n");
711
+ while (newline >= 0) {
712
+ const line = this.stdout.slice(0, newline).trimEnd();
713
+ this.stdout = this.stdout.slice(newline + 1);
714
+ if (line === "READY") finishStart();
715
+ else this.handleResponse(line);
716
+ newline = this.stdout.indexOf("\n");
717
+ }
718
+ });
719
+ child.once("error", (error) => finishStart(error));
720
+ child.once("exit", () => {
721
+ if (!settled) finishStart(/* @__PURE__ */ new Error("Explorer helper exited during startup"));
722
+ if (this.child === child) this.child = void 0;
723
+ this.failAll(/* @__PURE__ */ new Error("Explorer helper exited"));
724
+ });
725
+ });
726
+ return this.starting;
727
+ }
728
+ handleResponse(line) {
729
+ const [status, requestId, encodedMessage] = line.split(" ");
730
+ const request = requestId === void 0 ? void 0 : this.pending.get(requestId);
731
+ if (request === void 0) return;
732
+ clearTimeout(request.timeout);
733
+ this.pending.delete(requestId);
734
+ if (status === "OK") {
735
+ request.resolve();
736
+ return;
737
+ }
738
+ const message = encodedMessage === void 0 ? "Explorer activation failed" : Buffer.from(encodedMessage, "base64").toString("utf8");
739
+ request.reject(new Error(message));
740
+ }
741
+ failAll(error) {
742
+ for (const request of this.pending.values()) {
743
+ clearTimeout(request.timeout);
744
+ request.reject(error);
745
+ }
746
+ this.pending.clear();
747
+ }
748
+ };
749
+ //#endregion
532
750
  //#region src/workspace-preferences.ts
533
751
  const WORKSPACE_PREFERENCES_FILE = ".dsh-codex-ui-preferences.json";
534
752
  /** Desktop 和普通 DSH Web 共用 Profile;服务换端口或重启后目录仍保持稳定。 */
@@ -596,6 +814,7 @@ function writeWorkspacePreferences(pinnedWorkspaceIds, path = workspacePreferenc
596
814
  //#region src/index.ts
597
815
  const connectorsEndpoint = "/api/michengai/codex-ui/connectors";
598
816
  const dependenciesEndpoint = "/api/michengai/codex-ui/dependencies";
817
+ const explorerEndpoint = "/api/michengai/codex-ui/open-in-explorer";
599
818
  const preferencesEndpoint = "/api/michengai/codex-ui/preferences";
600
819
  const maxPreferencesBodyBytes = 32768;
601
820
  function headerValue(headers, name) {
@@ -656,6 +875,8 @@ const inject = [
656
875
  function apply(ctx) {
657
876
  const host = hostServices(ctx);
658
877
  ctx.effect(() => {
878
+ const foregroundExplorer = new ForegroundExplorer();
879
+ foregroundExplorer.warmup().catch((error) => ctx.logger.warn("foreground explorer warmup failed: %s", error));
659
880
  const disposeConnectors = host.webServer.register({
660
881
  kind: "exact",
661
882
  path: connectorsEndpoint,
@@ -844,10 +1065,66 @@ function apply(ctx) {
844
1065
  }
845
1066
  }
846
1067
  });
1068
+ const disposeExplorer = host.webServer.register({
1069
+ kind: "exact",
1070
+ path: explorerEndpoint,
1071
+ handler: async (request, response) => {
1072
+ if (request.method !== "POST") {
1073
+ response.writeHead(405, { allow: "POST" });
1074
+ response.end();
1075
+ return;
1076
+ }
1077
+ if (crossSiteRequest(request)) {
1078
+ response.writeHead(403, {
1079
+ "content-type": "application/json; charset=utf-8",
1080
+ "cache-control": "no-store"
1081
+ });
1082
+ response.end(JSON.stringify({ error: "已拒绝跨站请求。" }));
1083
+ return;
1084
+ }
1085
+ if (process.platform !== "win32") {
1086
+ response.writeHead(501, {
1087
+ "content-type": "application/json; charset=utf-8",
1088
+ "cache-control": "no-store"
1089
+ });
1090
+ response.end(JSON.stringify({ error: "当前平台使用系统默认打开方式。" }));
1091
+ return;
1092
+ }
1093
+ try {
1094
+ const body = JSON.parse(await readRequestBody(request));
1095
+ if (typeof body.path !== "string" || body.path.trim() === "") {
1096
+ response.writeHead(400, {
1097
+ "content-type": "application/json; charset=utf-8",
1098
+ "cache-control": "no-store"
1099
+ });
1100
+ response.end(JSON.stringify({ error: "目录路径无效。" }));
1101
+ return;
1102
+ }
1103
+ await foregroundExplorer.open(body.path);
1104
+ response.writeHead(200, {
1105
+ "content-type": "application/json; charset=utf-8",
1106
+ "cache-control": "no-store"
1107
+ });
1108
+ response.end(JSON.stringify({
1109
+ opened: true,
1110
+ foreground: true
1111
+ }));
1112
+ } catch (error) {
1113
+ ctx.logger.warn("foreground explorer open failed: %s", error);
1114
+ response.writeHead(error instanceof SyntaxError ? 400 : 503, {
1115
+ "content-type": "application/json; charset=utf-8",
1116
+ "cache-control": "no-store"
1117
+ });
1118
+ response.end(JSON.stringify({ error: error instanceof SyntaxError ? "目录路径格式无效。" : "无法在前台打开资源管理器。" }));
1119
+ }
1120
+ }
1121
+ });
847
1122
  return () => {
848
1123
  disposeDependencyInstaller();
1124
+ foregroundExplorer.dispose();
849
1125
  disposeConnectors();
850
1126
  disposeDependencies();
1127
+ disposeExplorer();
851
1128
  disposePreferences();
852
1129
  };
853
1130
  }, "michengai-codex-ui: catalogs");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@michengai/dsh-codex-ui",
3
- "version": "0.2.86",
3
+ "version": "0.2.88",
4
4
  "description": "以 Codex 风格重构 DSH Web 侧栏的独立客户端插件",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
@@ -53,6 +53,11 @@
53
53
  "platform": "web"
54
54
  }
55
55
  },
56
+ "scripts": {
57
+ "build": "tsc --noEmit && tsdown",
58
+ "typecheck": "tsc --noEmit",
59
+ "test": "pnpm run typecheck && vitest run tests/client-runtime.integration.spec.ts && tsdown && node tests/run-assertions.mjs"
60
+ },
56
61
  "peerDependencies": {
57
62
  "@deepseek-ai/cordis": ">=4.0.1 <5.0.0",
58
63
  "@deepseek-ai/dsh-client-locale": ">=0.1.0-rc.0 <0.2.0",
@@ -98,10 +103,5 @@
98
103
  "tsx": "^4.22.4",
99
104
  "typescript": "^6.0.3",
100
105
  "vitest": "^4.1.8"
101
- },
102
- "scripts": {
103
- "build": "tsc --noEmit && tsdown",
104
- "typecheck": "tsc --noEmit",
105
- "test": "pnpm run typecheck && vitest run tests/client-runtime.integration.spec.ts && tsdown && node tests/run-assertions.mjs"
106
106
  }
107
- }
107
+ }