@deepseek-ai/dsh-client-ui-settings-general 0.1.7-rc.1 → 0.2.0-rc.1

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/README.i18n.yaml CHANGED
@@ -1,6 +1,58 @@
1
- # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
- # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
- # after editing either side, bring the other along and re-record with:
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/ui-settings-general/README.md
5
- README.md: 36d3fd7b260f95cffb67977e5a6f37852bf195aa
6
- README.zh.md: 9df8fe8bc3a39fceeccfc8b08a8e18cc5eeaf752
5
+ /:
6
+ en: b12def981d836df5
7
+ zh: 9d813c1937f22c61
8
+ /deepseek-ai-dsh-client-ui-settings-general:
9
+ en: 1017c980d55e1945
10
+ zh: d3febd2062fce20f
11
+ /deepseek-ai-dsh-client-ui-settings-general/summary:
12
+ en: cd94b4a8dae32670
13
+ zh: 333b8603e77f92d7
14
+ /deepseek-ai-dsh-client-ui-settings-general/table-of-contents:
15
+ en: 2843d46e95311bce
16
+ zh: ec11bdeb5d3ce818
17
+ /deepseek-ai-dsh-client-ui-settings-general/use-this-package:
18
+ en: 3be9bf992c10bc6b
19
+ zh: b270083651e5dc43
20
+ /deepseek-ai-dsh-client-ui-settings-general/use-this-package/the-general-section:
21
+ en: 8b8a7c6287bb303b
22
+ zh: 19535112531aedd5
23
+ /deepseek-ai-dsh-client-ui-settings-general/use-this-package/opening-the-configuration-file:
24
+ en: ca2b4f5ccfbe2705
25
+ zh: bd1dc1d9879d0f0e
26
+ /deepseek-ai-dsh-client-ui-settings-general/use-this-package/onboarding-steps:
27
+ en: 27f0f6ec55f00228
28
+ zh: 69b47ff1efd81c17
29
+ /deepseek-ai-dsh-client-ui-settings-general/understand-the-implementation:
30
+ en: 41726bcd4ae37c0a
31
+ zh: d994b70ca7997da8
32
+ /deepseek-ai-dsh-client-ui-settings-general/understand-the-implementation/ledger-projections:
33
+ en: 3e3c2c72f87e8b23
34
+ zh: 12b10ba2d37c9077
35
+ /deepseek-ai-dsh-client-ui-settings-general/understand-the-implementation/connection-recovery:
36
+ en: e37fb7df883ea356
37
+ zh: c972524e56c6d6c2
38
+ /deepseek-ai-dsh-client-ui-settings-general/understand-the-implementation/document-availability:
39
+ en: 474168eef3339de0
40
+ zh: 254ea21415082dae
41
+ /deepseek-ai-dsh-client-ui-settings-general/understand-the-implementation/host-half:
42
+ en: 5d722d055516f905
43
+ zh: e7a236f0ff84f7ff
44
+ /deepseek-ai-dsh-client-ui-settings-general/further-exploration:
45
+ en: 21348b768f60f587
46
+ zh: 0c3c5b6a682ae267
47
+ /deepseek-ai-dsh-client-ui-settings-general/model-experience:
48
+ en: 3120df665299c634
49
+ zh: 1d9f365d091d0d2a
50
+ /deepseek-ai-dsh-client-ui-settings-general/model-experience/kv-cache-effect:
51
+ en: ca75c51c89c2c9b0
52
+ zh: de54f3467ef3b9be
53
+ /deepseek-ai-dsh-client-ui-settings-general/known-limitations-and-deferred-work:
54
+ en: 97dd637a320ac334
55
+ zh: faa5c512c56a128e
56
+ /deepseek-ai-dsh-client-ui-settings-general/known-limitations-and-deferred-work/dev-note:
57
+ en: b36039e49a66b8e0
58
+ zh: 306682cd90cce1db
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- Use this package to give the dsh web client a Settings panel, connection-recovery control, feature-contributed navigation, and sequential first-run onboarding. Users can open it from the sidebar, retry a failed connection immediately, and access a local configuration file when the Host makes one available on a loopback browser. Feature packages supply their own settings rows, sections, and onboarding steps; this package supplies their shared presentation and the Developer tools switch without adding onboarding copy.
12
+ Use this package to give the dsh web client a Settings panel, connection-recovery control, feature-contributed navigation, and sequential first-run onboarding. Users can open it from the sidebar, retry a failed connection immediately, and access a local configuration file when the Host makes one available on a loopback browser. Feature packages supply their own settings rows, sections, and onboarding steps; this package supplies their shared presentation and the Coding Tools switch without adding onboarding copy.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -22,24 +22,28 @@ Use this package to give the dsh web client a Settings panel, connection-recover
22
22
 
23
23
  -----
24
24
 
25
- The Settings panel uses a shared 760 × 500 layout, bounded by the viewport. Longer sections scroll inside the content column; the Account entry uses the account icon.
25
+ The Settings panel uses a shared 800 × 800 layout, bounded by the viewport. Longer sections scroll inside the content column; the Account entry uses the account icon. The panel portals beside `#root` rather than inside it, so a macOS window drag region a chrome row declares later in document order cannot swallow its controls.
26
26
 
27
27
  <a id="use-this-package"></a>
28
28
  ## Use this package
29
29
 
30
30
  Users reach the shell through the sidebar's bottom Settings control; feature plugins contribute their pages and onboarding steps through the slot ledgers this shell projects. In both the expanded sidebar and collapsed rail, the control exposes the localized Settings label as its accessible name. A pale-yellow **Disconnected** action beside Settings indicates browser offline suspension; its permanent retry glyph marks the retry action, which the Chinese outage copy also names (连接异常,刷新重试). Every recovery attempt shows the shared ongoing loader beside **Reconnecting** with one to three dots advancing every 500ms, and an attempt stays visible for at least 800ms so brief retries do not flicker. Selecting either yellow state starts an immediate retry; press feedback stays within the warning palette. Recovery changes the region to pale-green **Connected** for two seconds from the moment the green pill becomes visible. The pill fades in on appearance, fades out over 150ms on removal, and sizes to its current label. Initial startup and uninterrupted healthy operation remain silent. The shell renders the modal panel, the navigation built from `settings.section` entries, and exactly one mounted onboarding step at a time.
31
31
 
32
+ Settings uses the [frame’s shared overlay inset](../ui-layout/README.md#window-chrome-seat) and keeps at least 24px of viewport margin. Its mask leaves the Windows caption unpainted; the full-viewport layer still blocks background clicks.
33
+
32
34
  When the section navigation exceeds the panel's available height, the list scrolls independently of the settings content and keeps the Settings title fixed.
33
35
 
34
- In Desktop, the account-row update control shows availability, progress, verification, readiness, and persistent retry feedback. It shares the connection indicator’s 28px height, 13px corners, 14px icon slot, 4px icon gap, and 12px medium text with an 18px line height; update-state border, fill, and text colors remain independently defined. Retry text and its dot use the same brand blue as other update labels; numeric download progress has no ellipsis. The preload carries semantic phase, version, progress, and classified failures; the component resolves every visible and accessible string from the active `settings` locale, including after an in-application language change. Selecting an available update starts downloading; installation requires a separate shell-owned confirmation. A collapsed sidebar shows the same status as a brand-blue dot on its top expand button, including failures. Connection feedback takes priority except during shell-reported installation, when the expected backend disconnect must not hide update status. Failure restores connection feedback. Both controls share one carrier subscription; browser code cannot choose packages or authorize installation. [Desktop updates](../../../apps/desktop/README.md) owns the release workflow.
36
+ In Desktop, the account-row update control shows availability, progress, verification, readiness, and persistent retry feedback. It shares the connection indicator’s 28px height, 8px corners, 14px icon slot, 4px icon gap, and 12px medium text with an 18px line height; update-state border, fill, and text colors remain independently defined. Retry text and its dot use the same brand blue as other update labels; numeric download progress has no ellipsis. The preload carries semantic phase, version, progress, and classified failures; the component resolves every visible and accessible string from the active `settings` locale, including after an in-application language change. Selecting an available update starts downloading; installation requires a separate shell-owned confirmation. A collapsed sidebar shows the same status as a brand-blue dot on its top expand button, including failures. Connection feedback takes priority except during shell-reported installation, when the expected backend disconnect must not hide update status. Failure restores connection feedback. Both controls share one carrier subscription; browser code cannot choose packages or authorize installation. [Desktop updates](../../../apps/desktop/README.md) owns the release workflow. Checking, verifying, and preparing-to-restart tooltips show only the supplied version; without a version, they retain the state label. Desktop update tooltips preserve the complete version and prerelease suffix without adding v or V; their error summaries match the shell update dialogs.
37
+
38
+ Settings visibility and section selection live in the shell owner store. The shell supplies the effective Settings binding to the contributed launcher for menu keycaps and `aria-keyshortcuts`; the fallback button uses the same binding for hover and keyboard-focus hints and `aria-keyshortcuts`. The Settings command (`Mod+,` by default) toggles the dialog when Settings is in front or no modal is open; the sidebar control opens the same dialog. The command cannot open or close Settings while the shortcut reference or another modal is in front. Held-key repeats do nothing. Initial focus lands on the selected section in the navigation, or on the title when no sections are available, without drawing a focus outline. Tab and directional navigation retain their visible focus indicators. `Mod+/` can open the shortcut reference above Settings; `Escape` closes the top dialog and restores focus to its invoking control. Return focus omits outlines after shortcut, Escape, or pointer dismissal.
35
39
 
36
40
  ### The General section
37
41
 
38
42
  The current release version appears at the bottom of General Settings in Web and Desktop, using the build’s `DSH_CLIENT_VERSION` metadata and the active language. Partial builds without version metadata omit the row.
39
43
 
40
- The Developer tools switch controls the shared preference described by [ui-settings](../ui-settings/README.md#use-this-package). It is available in both Web and desktop, follows accepted changes immediately, and disables duplicate input while a write settles. A failed write displays localized retry guidance.
44
+ The Coding Tools switch controls the shared `ui-settings.enabled` preference described by [ui-settings](../ui-settings/README.md#use-this-package). It is available in both Web and desktop, follows accepted changes immediately, and disables duplicate input while a write settles. A failed write displays localized retry guidance.
41
45
 
42
- The General section holds the built-in Developer tools and Current version rows alongside rows registered into `settings.general.item` by feature packages. Each registrant owns its row copy and behavior. The Appearance row, for example, lives in ui-theme.
46
+ The General section holds the built-in Coding Tools and Current version rows alongside rows registered into `settings.general.item` by feature packages. Each registrant owns its row copy and behavior. The Appearance row, for example, lives in ui-theme.
43
47
 
44
48
  ### Opening the configuration file
45
49
 
@@ -54,12 +58,12 @@ The onboarding ledger projects in ascending order and mounts exactly one step at
54
58
  <a id="understand-the-implementation"></a>
55
59
  ## Understand the implementation
56
60
 
57
- The shell declares settings.launcher for an account-owned sidebar menu and retains the Settings button as its fallback. Closing the dialog returns focus to the active launcher.
61
+ The shell declares settings.launcher for an account-owned sidebar menu and retains the Settings button as its fallback. The launcher receives settingsOpen, whose false-to-true edge marks one Settings entry, so a registrant acts once per entry instead of on every re-render inside one open. Closing the dialog returns focus to the active launcher.
58
62
 
59
63
  <details>
60
64
  <summary>Implementation internals — click to expand</summary>
61
65
 
62
- The shell owns the chrome and the projections; it contributes the Developer tools and Current version rows, while feature registrants own their additional content and copy.
66
+ The shell owns the chrome and the projections; it contributes the Coding Tools and Current version rows, while feature registrants own their additional content and copy.
63
67
 
64
68
  ### Ledger projections
65
69
 
@@ -110,7 +114,7 @@ None; this package neither assembles nor sends a provider request.
110
114
 
111
115
  These limits define what the shell itself provides versus what features must supply; they are current package constraints.
112
116
 
113
- - **Additional General rows require their feature plugins** — the shell supplies Developer tools and Current version; feature plugins supply the remaining preferences.
117
+ - **Additional General rows require their feature plugins** — the shell supplies Coding Tools and Current version; feature plugins supply the remaining preferences.
114
118
  - **The Windows caption badge keeps a side-opening bubble** — `DesktopUpdateBadge` occupies `sidebar.toggle.badge` in the caption and requests `side="right"`, so the Desktop-owned menu text can cover its bubble while the sidebar is collapsed on Windows; the sidebar toggle and New Session bubbles open below the caption instead (#4688).
115
119
 
116
120
  <a id="dev-note"></a>
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- 使用本包可为 dsh Web 客户端提供 Settings 面板、连接恢复控件、由功能包贡献的导航,以及依次进行的首次运行引导。用户可以从侧边栏打开面板、立即重试失败的连接,并在宿主为回环浏览器提供本地配置文件时访问该文件。各功能包提供自己的设置行、分区和引导步骤;本包提供共享的界面展示和开发者工具开关,但不添加引导文案。
12
+ 使用本包可为 dsh Web 客户端提供 Settings 面板、连接恢复控件、由功能包贡献的导航,以及依次进行的首次运行引导。用户可以从侧边栏打开面板、立即重试失败的连接,并在宿主为回环浏览器提供本地配置文件时访问该文件。各功能包提供自己的设置行、分区和引导步骤;本包提供共享的界面展示和代码工作工具开关,但不添加引导文案。
13
13
 
14
14
  ## 目录
15
15
 
@@ -22,24 +22,28 @@ kind: "package-reference"
22
22
 
23
23
  -----
24
24
 
25
- 设置面板采用各页面共享的 760 × 500 布局,并受视口大小约束。较长的页面在内容栏内部滚动;账号入口使用账号图标。
25
+ 设置面板采用各页面共享的 800 × 800 布局,并受视口大小约束。较长的页面在内容栏内部滚动;账号入口使用账号图标。面板挂载在 `#root` 之外而不是其内部,这样在文档序上更靠后的 chrome 行所声明的 macOS 窗口拖拽区不会吞掉它的控件。
26
26
 
27
27
  <a id="use-this-package"></a>
28
28
  ## 使用本包
29
29
 
30
30
  用户通过侧边栏底部的 Settings 控件进入外壳;功能插件通过本外壳所投影的 slot 账本贡献自己的页面与引导步骤。在展开侧边栏和收起轨道中,该控件都会把本地化的 Settings 文案作为其可访问名称。Settings 右侧浅黄色的**连接异常**操作表示浏览器离线暂停;其常驻重试图形与中文文案「连接异常,刷新重试」都指明重试动作。每次恢复尝试都显示共享 ongoing loading 加**重新连接中**,其后一至三个点每 500ms 前进一次,且每次尝试至少可见 800ms,短暂重试不会闪动。选中任一黄色状态都会立即发起重试;按压反馈留在黄色色阶内。恢复后该区域变为浅绿色的**连接成功**,从绿色药丸可见起驻留 2 秒再消失。药丸出现时淡入、移除时以 150ms 淡出,宽度随当前文案自适应。首次启动与未曾中断的健康连接保持静默。外壳渲染模态面板、由 `settings.section` 条目构建的导航,以及每次只挂载一个的引导步骤。
31
31
 
32
+ 设置面板使用[框架共享的浮层起点](../ui-layout/README.zh.md#window-chrome-seat),视口边距至少为 24px。其遮罩不绘制 Windows 顶栏区域,覆盖整个视口的模态层仍阻挡背景点击。
33
+
32
34
  当分区导航超出面板可用高度时,列表独立于设置内容滚动,并保持「设置」标题固定。
33
35
 
34
- 在 Desktop 中,账户行更新控件显示可用版本、进度、验证、就绪和持久重试反馈。它与连接指示器共用 28px 高度、13px 圆角、14px 图标占位、4px 图文间距,以及 12px 中等字重文字和 18px 行高;更新状态的边框、填充和文字颜色独立定义。重试文字及其圆点与其他更新文案使用相同的品牌蓝色;数字下载进度不带省略号。预加载仅传递语义化阶段、版本、进度和失败类别;组件从当前 `settings` locale 解析全部可见与无障碍文案,并在应用内语言变化后同步更新。选择可用更新即开始下载;安装需要独立的壳拥有的确认。侧栏收起时,其顶部展开按钮以品牌蓝色圆点显示相同状态,包括失败状态。连接反馈通常优先展示,但壳报告正在安装时,预期的后端断开不能遮盖更新状态;失败后恢复连接反馈。两个控件共用一次载体订阅;浏览器代码不能选择安装包或授权安装。[Desktop 更新](../../../apps/desktop/README.zh.md)负责发布流程。
36
+ 在 Desktop 中,账户行更新控件显示可用版本、进度、验证、就绪和持久重试反馈。它与连接指示器共用 28px 高度、8px 圆角、14px 图标占位、4px 图文间距,以及 12px 中等字重文字和 18px 行高;更新状态的边框、填充和文字颜色独立定义。重试文字及其圆点与其他更新文案使用相同的品牌蓝色;数字下载进度不带省略号。预加载仅传递语义化阶段、版本、进度和失败类别;组件从当前 `settings` locale 解析全部可见与无障碍文案,并在应用内语言变化后同步更新。选择可用更新即开始下载;安装需要独立的壳拥有的确认。侧栏收起时,其顶部展开按钮以品牌蓝色圆点显示相同状态,包括失败状态。连接反馈通常优先展示,但壳报告正在安装时,预期的后端断开不能遮盖更新状态;失败后恢复连接反馈。两个控件共用一次载体订阅;浏览器代码不能选择安装包或授权安装。[Desktop 更新](../../../apps/desktop/README.zh.md)负责发布流程。检查中、校验中和准备重启状态的悬浮提示只显示已提供的版本号;没有版本号时保留状态文案。桌面更新提示完整保留版本号及预发布后缀,不额外添加 v 或 V;错误提示与桌面升级弹窗保持一致。
37
+
38
+ 设置的可见状态和分区选择由 shell owner store 保存。外壳将设置命令的有效绑定传给入口贡献,用于菜单键帽和 `aria-keyshortcuts`;回退按钮使用相同绑定提供悬停、键盘聚焦提示和 `aria-keyshortcuts`。设置位于最前方或没有模态弹窗时,设置命令(默认为 `Mod+,`)切换设置弹窗的开关;侧栏控件打开同一弹窗。快捷键速查或其他模态弹窗位于前方时,该命令不能打开或关闭设置。长按产生的重复按键不执行操作。初始焦点落在导航中选中的分区;没有可用分区时落在标题上,均不绘制焦点外轮廓线。Tab 和方向键导航保留可见焦点提示。`Mod+/` 可在设置上方打开快捷键速查;`Escape` 关闭最上层弹窗,焦点返回触发该弹窗的控件。通过快捷键、Esc 或鼠标关闭后的回焦均不绘制外轮廓线。
35
39
 
36
40
  ### 「通用」分区
37
41
 
38
42
  Web 与桌面端的通用设置底部显示当前发布版本,使用构建注入的 `DSH_CLIENT_VERSION` 元数据和当前语言。缺少版本元数据的局部构建不显示该行。
39
43
 
40
- 开发者工具开关控制 [ui-settings](../ui-settings/README.zh.md#use-this-package) 定义的共享偏好。Web 和桌面端均提供此开关,立即跟随已接受的变更,并在写入完成前禁用重复输入。写入失败时显示本地化的重试提示。
44
+ 代码工作工具开关控制 [ui-settings](../ui-settings/README.zh.md#use-this-package) 定义的共享偏好 `ui-settings.enabled`。Web 和桌面端均提供此开关,立即跟随已接受的变更,并在写入完成前禁用重复输入。写入失败时显示本地化的重试提示。
41
45
 
42
- 「通用」分区承载内置的开发者工具行与当前版本行,以及功能包注册进 `settings.general.item` 的行。每个注册方拥有自己的行文案与行为。例如「外观」行位于 ui-theme。
46
+ 「通用」分区承载内置的代码工作工具行与当前版本行,以及功能包注册进 `settings.general.item` 的行。每个注册方拥有自己的行文案与行为。例如「外观」行位于 ui-theme。
43
47
 
44
48
  ### 打开配置文件
45
49
 
@@ -54,12 +58,12 @@ Web 与桌面端的通用设置底部显示当前发布版本,使用构建注
54
58
  <a id="understand-the-implementation"></a>
55
59
  ## 理解实现
56
60
 
57
- 外壳声明 settings.launcher,供账号功能提供侧边栏菜单,并以设置按钮作为回退。关闭对话框后,焦点返回当前入口。
61
+ 外壳声明 settings.launcher,供账号功能提供侧边栏菜单,并以设置按钮作为回退。入口接收 settingsOpen,其 false→true 边沿代表一次进入设置页,因此注册方在每次进入时执行一次,而不是在同一次打开中的每次重渲染执行。关闭对话框后,焦点返回当前入口。
58
62
 
59
63
  <details>
60
64
  <summary>实现细节——点击展开</summary>
61
65
 
62
- 外壳拥有界面框架与投影,并提供开发者工具行与当前版本行;功能注册方拥有其余内容与文案。
66
+ 外壳拥有界面框架与投影,并提供代码工作工具行与当前版本行;功能注册方拥有其余内容与文案。
63
67
 
64
68
  ### 账本投影
65
69
 
@@ -110,7 +114,7 @@ Web 与桌面端的通用设置底部显示当前发布版本,使用构建注
110
114
 
111
115
  这些限制说明外壳自身提供什么、功能包必须提供什么;它们是当前包约束。
112
116
 
113
- - **额外的通用设置行需要相应功能插件**:外壳提供开发者工具与当前版本;功能插件提供其他偏好。
117
+ - **额外的通用设置行需要相应功能插件**:外壳提供代码工作工具与当前版本;功能插件提供其他偏好。
114
118
  - **Windows 顶栏徽标的气泡仍向右展开**:`DesktopUpdateBadge` 占用顶栏的 `sidebar.toggle.badge` 且请求 `side="right"`,侧栏收起时其气泡会被 Desktop 菜单文字遮挡;侧栏开关与新建会话的气泡则改在顶栏下方展开(#4688)。
115
119
 
116
120
  <a id="dev-note"></a>
package/lib/client.js CHANGED
@@ -5,10 +5,42 @@ window.__ModuleLoader__.load({
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let _deepseek_ai_dsh_client_ui_slots = require("@deepseek-ai/dsh-client-ui-slots");
8
- let react_jsx_runtime = require("react/jsx-runtime");
9
- let react = require("react");
10
8
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
11
9
  let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
10
+ let react_jsx_runtime = require("react/jsx-runtime");
11
+ let react = require("react");
12
+ let react_dom = require("react-dom");
13
+ //#region lib/types/client/shell-store.js
14
+ /** Shared settings viewing state for its mouse and command entry points. */
15
+ /**
16
+ * Declare the settings dialog state and its complete mutation API.
17
+ * @returns one root-scoped store handle for the settings shell.
18
+ */
19
+ function createSettingsShellStore() {
20
+ return (0, _deepseek_ai_dsh_client_store.defineStore)({
21
+ init: () => ({
22
+ open: false,
23
+ activeId: void 0
24
+ }),
25
+ actions: {
26
+ open: (d) => {
27
+ d.open = true;
28
+ },
29
+ close: (d) => {
30
+ d.open = false;
31
+ d.activeId = void 0;
32
+ },
33
+ select: (d, id) => {
34
+ d.activeId = id;
35
+ },
36
+ openSection: (d, id) => {
37
+ d.activeId = id;
38
+ d.open = true;
39
+ }
40
+ }
41
+ });
42
+ }
43
+ //#endregion
12
44
  //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
13
45
  function r(e) {
14
46
  var t, f, n = "";
@@ -25,7 +57,7 @@ window.__ModuleLoader__.load({
25
57
  }
26
58
  //#endregion
27
59
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-general/src/client/SettingsRoot.module.css.mjs
28
- const css$6 = ".VOzbGW_triggerRow{flex:none;align-items:center;gap:8px;width:calc(100% + 4px);margin:4px -2px;display:flex;position:relative}.VOzbGW_triggerRow.VOzbGW_railRow{width:36px;margin:8px 0 10px}.VOzbGW_trigger{box-sizing:border-box;cursor:pointer;width:auto;min-width:0;height:42px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:12px;flex:1;align-items:center;gap:8px;margin:0;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.VOzbGW_trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.VOzbGW_trigger.VOzbGW_rail{flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.VOzbGW_triggerLabel{white-space:nowrap;overflow:hidden}.VOzbGW_overlay{z-index:1000;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.VOzbGW_mask{background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute;inset:0}.VOzbGW_panel{z-index:1;width:800px;height:min(800px, calc(100vh - 2 * max(24px, var(--dsh-frame-top-clearance,24px))));background:var(--dsw-alias-bg-layer-2);max-width:calc(100vw - 48px);box-shadow:var(--dsw-elevation-prominent);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:32px;display:flex;position:relative;overflow:hidden}.VOzbGW_nav{box-sizing:border-box;flex-direction:column;flex:none;gap:18px;width:188px;padding:22px 12px 0;display:flex}.VOzbGW_navTitle{color:var(--dsw-alias-label-primary);padding:0 12px;font-size:16px;font-weight:500;line-height:24px}.VOzbGW_navList{flex-direction:column;gap:4px;display:flex;overflow-y:auto}.VOzbGW_navCell{box-sizing:border-box;cursor:pointer;height:40px;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:9px 16px 9px 12px;font-family:inherit;font-size:14px;font-weight:400;line-height:22px;display:flex}.VOzbGW_navCell:hover{background:var(--dsw-specific-sidebar-nav-item-hover)}.VOzbGW_navCell.VOzbGW_active{background:var(--dsw-specific-sidebar-nav-item-active)}.VOzbGW_navIcon{flex:none}.VOzbGW_navLabel{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.VOzbGW_content{flex-direction:column;flex:1;min-width:0;display:flex}.VOzbGW_header{box-sizing:border-box;flex:none;justify-content:space-between;align-items:flex-start;gap:8px;height:54px;padding:20px 14px 8px 10px;display:flex}.VOzbGW_actions{justify-content:flex-end;align-items:center;gap:8px;min-width:0;margin-left:auto;display:flex}.VOzbGW_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.VOzbGW_close:hover{background:var(--dsw-alias-interactive-bg-hover)}.VOzbGW_options{flex:1;min-height:0;padding:0 24px 24px;overflow-y:auto}.VOzbGW_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}[data-platform=darwin] .VOzbGW_overlay{-webkit-app-region:no-drag}";
60
+ const css$6 = ".VOzbGW_triggerRow{flex:none;align-items:center;gap:8px;width:calc(100% + 4px);margin:4px -2px;display:flex;position:relative}.VOzbGW_triggerRow.VOzbGW_railRow{width:36px;margin:8px 0 10px}.VOzbGW_trigger{box-sizing:border-box;border-radius:var(--dsw-radius-md);cursor:pointer;width:auto;min-width:0;height:42px;color:var(--dsw-alias-label-primary);background:0 0;border:none;flex:1;align-items:center;gap:8px;margin:0;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.VOzbGW_trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.VOzbGW_trigger.VOzbGW_rail{flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.VOzbGW_triggerLabel{white-space:nowrap;overflow:hidden}.VOzbGW_overlay{z-index:1000;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.VOzbGW_mask{inset:var(--dsh-frame-chrome-top,0px) 0 0;background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute}.VOzbGW_panel{z-index:1;width:800px;height:min(800px, calc(100vh - 2 * max(24px, var(--dsh-frame-overlay-top,24px))));border-radius:var(--dsw-radius-panel);background:var(--dsw-alias-bg-layer-2);max-width:calc(100vw - 48px);box-shadow:var(--dsw-elevation-prominent);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);display:flex;position:relative;overflow:hidden}.VOzbGW_panel:focus{outline:none}.VOzbGW_nav{box-sizing:border-box;flex-direction:column;flex:none;gap:18px;width:188px;padding:22px 12px 0;display:flex}.VOzbGW_navTitle{color:var(--dsw-alias-label-primary);padding:0 12px;font-size:16px;font-weight:500;line-height:24px}.VOzbGW_navList{flex-direction:column;gap:4px;display:flex;overflow-y:auto}.VOzbGW_navCell{box-sizing:border-box;border-radius:var(--dsw-radius-md);cursor:pointer;height:40px;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:none;align-items:center;gap:8px;padding:9px 16px 9px 12px;font-family:inherit;font-size:14px;font-weight:400;line-height:22px;display:flex}.VOzbGW_navCell:hover{background:var(--dsw-specific-sidebar-nav-item-hover)}.VOzbGW_navCell.VOzbGW_active{background:var(--dsw-specific-sidebar-nav-item-active)}.VOzbGW_navIcon{flex:none}.VOzbGW_navLabel{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.VOzbGW_content{flex-direction:column;flex:1;min-width:0;display:flex}.VOzbGW_header{box-sizing:border-box;flex:none;justify-content:space-between;align-items:flex-start;gap:8px;height:54px;padding:20px 14px 8px 10px;display:flex}.VOzbGW_actions{justify-content:flex-end;align-items:center;gap:8px;min-width:0;margin-left:auto;display:flex}.VOzbGW_close{border-radius:var(--dsw-radius-sm);cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-primary);background:0 0;border:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.VOzbGW_close:hover{background:var(--dsw-alias-interactive-bg-hover)}.VOzbGW_options{flex:1;min-height:0;padding:0 24px 24px;overflow-y:auto}.VOzbGW_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}";
29
61
  const tagId$6 = "@deepseek-ai/dsh-client-ui-settings-general/SettingsRoot.module.css";
30
62
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$6) + "]") === null) {
31
63
  const tag = document.createElement("style");
@@ -59,7 +91,7 @@ window.__ModuleLoader__.load({
59
91
  };
60
92
  //#endregion
61
93
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.module.css.mjs
62
- const css$5 = ".DOUpOa_indicator{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 20%, transparent);height:28px;color:var(--dsw-alias-brand-primary-new-colorprimary-new-color);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 8%, transparent);text-align:left;white-space:nowrap;cursor:pointer;border-radius:13px;flex:none;grid-template-columns:14px max-content;align-items:center;column-gap:4px;padding:0 8px;font-family:inherit;font-size:12px;font-weight:500;line-height:18px;display:inline-grid}.DOUpOa_icon{place-items:center;width:14px;height:14px;display:grid}.DOUpOa_badge{inset-inline-end:2px;corner-shape:round;background:var(--dsw-alias-brand-primary-new-colorprimary-new-color);border-radius:50%;width:6px;height:6px;position:absolute;top:2px}.DOUpOa_errorDot{corner-shape:round;background:currentColor;border-radius:50%;width:6px;height:6px}.DOUpOa_spinner{animation:1s linear infinite DOUpOa_spin}@keyframes DOUpOa_spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.DOUpOa_spinner{animation:none}}.DOUpOa_indicator[aria-disabled=true]{cursor:default}";
94
+ const css$5 = ".DOUpOa_indicator{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 20%, transparent);border-radius:var(--dsw-radius-sm);height:28px;color:var(--dsw-alias-brand-primary-new-colorprimary-new-color);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 8%, transparent);text-align:left;white-space:nowrap;cursor:pointer;flex:none;grid-template-columns:14px max-content;align-items:center;column-gap:4px;padding:0 8px;font-family:inherit;font-size:12px;font-weight:500;line-height:18px;display:inline-grid}.DOUpOa_icon{place-items:center;width:14px;height:14px;display:grid}.DOUpOa_badge{inset-inline-end:2px;corner-shape:round;background:var(--dsw-alias-brand-primary-new-colorprimary-new-color);border-radius:50%;width:6px;height:6px;position:absolute;top:2px}.DOUpOa_errorDot{corner-shape:round;background:currentColor;border-radius:50%;width:6px;height:6px}.DOUpOa_spinner{animation:1s linear infinite DOUpOa_spin}@keyframes DOUpOa_spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.DOUpOa_spinner{animation:none}}.DOUpOa_indicator[aria-disabled=true]{cursor:default}";
63
95
  const tagId$5 = "@deepseek-ai/dsh-client-ui-settings-general/DesktopUpdateIndicator.module.css";
64
96
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$5) + "]") === null) {
65
97
  const tag = document.createElement("style");
@@ -96,6 +128,10 @@ window.__ModuleLoader__.load({
96
128
  ready: t("desktop.update.ready"),
97
129
  error: t("desktop.update.retry")
98
130
  }[state.phase];
131
+ if (state.phase === "checking" || state.phase === "verifying" || state.phase === "installing") return {
132
+ label,
133
+ detail: state.version ?? label
134
+ };
99
135
  if (state.phase === "error") return {
100
136
  label,
101
137
  detail: {
@@ -196,7 +232,7 @@ window.__ModuleLoader__.load({
196
232
  * close label, sections) arrives from registrants through slots; accessible
197
233
  * names resolve from localized content (trigger: shell locale; dialog:
198
234
  * aria-labelledby the title node; close: visually-hidden slot text). Modal
199
- * open state and the active section id are component-local viewing state;
235
+ * open state and the active section id belong to the declared owner store;
200
236
  * the onboarding coordinator mounts exactly one ordered registrant while the
201
237
  * sessions-derived empty-Hero fact is active. Visible dialog chrome belongs
202
238
  * to the step, so a mounted-but-deciding step paints nothing here.
@@ -232,27 +268,16 @@ window.__ModuleLoader__.load({
232
268
  });
233
269
  }
234
270
  /**
235
- * The modal layer: full-viewport mask + centered panel. Close paths: the
271
+ * Body-portaled modal layer: full-viewport mask + centered panel. Close paths: the
236
272
  * header button, a mask click, and document-level Escape (mounted only while
237
273
  * open, so the listener lifetime is the panel's).
238
274
  */
239
275
  function SettingsPanel({ rows, renderSlot, activeId, onSelect, onClose }) {
240
276
  const active = rows.find((r) => r.id === activeId)?.id ?? rows[0]?.id;
241
277
  const titleId = (0, react.useId)();
242
- (0, react.useEffect)(() => {
243
- const onKeyDown = (e) => {
244
- if (e.key === "Escape") onClose();
245
- };
246
- document.addEventListener("keydown", onKeyDown);
247
- return () => {
248
- document.removeEventListener("keydown", onKeyDown);
249
- };
250
- }, [onClose]);
251
- const closeButton = (0, react.useRef)(null);
252
- (0, react.useEffect)(() => {
253
- closeButton.current?.focus();
254
- }, []);
255
- return (0, react_jsx_runtime.jsxs)("div", {
278
+ const panel = (0, react.useRef)(null);
279
+ (0, _deepseek_ai_dsh_client_ui_primitives.useModalLayer)(panel, true, onClose);
280
+ return (0, react_dom.createPortal)((0, react_jsx_runtime.jsxs)("div", {
256
281
  className: SettingsRoot_module_css_default.overlay,
257
282
  role: "presentation",
258
283
  children: [(0, react_jsx_runtime.jsx)("div", {
@@ -260,6 +285,9 @@ window.__ModuleLoader__.load({
260
285
  "aria-hidden": "true",
261
286
  onClick: onClose
262
287
  }), (0, react_jsx_runtime.jsxs)("div", {
288
+ ref: panel,
289
+ tabIndex: -1,
290
+ "data-shortcut-modal": "settings",
263
291
  className: SettingsRoot_module_css_default.panel,
264
292
  role: "dialog",
265
293
  "aria-modal": "true",
@@ -269,6 +297,8 @@ window.__ModuleLoader__.load({
269
297
  children: [(0, react_jsx_runtime.jsx)("div", {
270
298
  className: SettingsRoot_module_css_default.navTitle,
271
299
  id: titleId,
300
+ tabIndex: -1,
301
+ "data-modal-autofocus": active === void 0 ? "" : void 0,
272
302
  children: renderSlot("settings.header", {})
273
303
  }), (0, react_jsx_runtime.jsx)("div", {
274
304
  className: SettingsRoot_module_css_default.navList,
@@ -276,6 +306,7 @@ window.__ModuleLoader__.load({
276
306
  type: "button",
277
307
  className: clsx(SettingsRoot_module_css_default.navCell, row.id === active && SettingsRoot_module_css_default.active),
278
308
  "aria-current": row.id === active ? "true" : void 0,
309
+ "data-modal-autofocus": row.id === active ? "" : void 0,
279
310
  onClick: () => {
280
311
  onSelect(row.id);
281
312
  },
@@ -293,7 +324,6 @@ window.__ModuleLoader__.load({
293
324
  className: SettingsRoot_module_css_default.actions,
294
325
  children: renderSlot("settings.action", {})
295
326
  }), (0, react_jsx_runtime.jsxs)("button", {
296
- ref: closeButton,
297
327
  type: "button",
298
328
  className: SettingsRoot_module_css_default.close,
299
329
  onClick: onClose,
@@ -308,7 +338,7 @@ window.__ModuleLoader__.load({
308
338
  })]
309
339
  })]
310
340
  })]
311
- });
341
+ }), document.body);
312
342
  }
313
343
  /**
314
344
  * Render the settings trigger and panel.
@@ -316,29 +346,15 @@ window.__ModuleLoader__.load({
316
346
  * @returns the settings shell element tree.
317
347
  */
318
348
  function SettingsRoot(props) {
319
- const { wide, reconnect, useConnectionState, useSections, useOnboardingSteps, useSessions, renderSlot, t, useDesktopUpdate, openDesktopUpdate } = props;
320
- const [open, setOpen] = (0, react.useState)(false);
321
- const [activeId, setActiveId] = (0, react.useState)(void 0);
349
+ const { wide, reconnect, useConnectionState, useSections, useOnboardingSteps, useSessions, renderSlot, t, useDesktopUpdate, openDesktopUpdate, useStore, actions, useShortcuts } = props;
350
+ const { open, activeId } = useStore((state) => state);
351
+ const shortcut = useShortcuts((rows) => rows.find((row) => row.id === "settings.open"));
352
+ const { close, openSection } = actions;
322
353
  const [requestedOnboarding, setRequestedOnboarding] = (0, react.useState)();
323
354
  const [completedOnboarding, setCompletedOnboarding] = (0, react.useState)(() => /* @__PURE__ */ new Set());
324
355
  const [showRecovery, setShowRecovery] = (0, react.useState)(false);
325
356
  const [holdConnecting, setHoldConnecting] = (0, react.useState)(false);
326
357
  const connectingShownAt = (0, react.useRef)(void 0);
327
- const triggerRow = (0, react.useRef)(null);
328
- const triggerButton = (0, react.useRef)(null);
329
- const wasOpen = (0, react.useRef)(open);
330
- const close = (0, react.useCallback)(() => {
331
- setOpen(false);
332
- setActiveId(void 0);
333
- }, []);
334
- (0, react.useEffect)(() => {
335
- if (wasOpen.current && !open) triggerRow.current?.querySelector("button")?.focus();
336
- wasOpen.current = open;
337
- }, [open]);
338
- const openSection = (0, react.useCallback)((id) => {
339
- setActiveId(id);
340
- setOpen(true);
341
- }, []);
342
358
  const rows = useSections((s) => s);
343
359
  const desktopUpdate = useDesktopUpdate((state) => state);
344
360
  const connectionState = useConnectionState((state) => state);
@@ -353,6 +369,16 @@ window.__ModuleLoader__.load({
353
369
  if (onboardingActive) return;
354
370
  setCompletedOnboarding(/* @__PURE__ */ new Set());
355
371
  }, [onboardingActive]);
372
+ const onboardingStepSeen = (0, react.useRef)(onboardingStep);
373
+ (0, react.useEffect)(() => {
374
+ const appeared = onboardingStepSeen.current === void 0 && onboardingStep !== void 0;
375
+ onboardingStepSeen.current = onboardingStep;
376
+ if (appeared && open) close();
377
+ }, [
378
+ onboardingStep,
379
+ open,
380
+ close
381
+ ]);
356
382
  (0, react.useLayoutEffect)(() => {
357
383
  const previous = previousConnectionState.current;
358
384
  previousConnectionState.current = connectionState;
@@ -404,29 +430,36 @@ window.__ModuleLoader__.load({
404
430
  else if (showRecovery) connectionIndicator = "recovered";
405
431
  return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
406
432
  (0, react_jsx_runtime.jsxs)("div", {
407
- ref: triggerRow,
408
433
  className: clsx(SettingsRoot_module_css_default.triggerRow, !wide && SettingsRoot_module_css_default.railRow),
409
434
  children: [
410
435
  renderSlot("settings.launcher", {
411
436
  wide,
412
- openSettings: () => {
413
- setOpen(true);
414
- },
437
+ settingsOpen: open,
438
+ openSettings: actions.open,
439
+ ...shortcut?.keys.length ? { settingsShortcut: {
440
+ keys: shortcut.keys,
441
+ aria: shortcut.aria
442
+ } } : {},
415
443
  openOnboarding: (id) => {
416
- setOpen(false);
444
+ close();
417
445
  setRequestedOnboarding(id);
418
446
  }
419
- }, { fallback: (0, react_jsx_runtime.jsx)("button", {
420
- ref: triggerButton,
421
- type: "button",
422
- className: clsx(SettingsRoot_module_css_default.trigger, !wide && SettingsRoot_module_css_default.rail),
423
- "aria-label": t("trigger"),
424
- "aria-haspopup": "dialog",
425
- "aria-expanded": open,
426
- onClick: () => {
427
- setOpen(true);
428
- },
429
- children: renderSlot("settings.trigger", { wide })
447
+ }, { fallback: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
448
+ disabled: open,
449
+ label: t("trigger"),
450
+ shortcutKeys: shortcut?.keys,
451
+ children: (0, react_jsx_runtime.jsx)("button", {
452
+ type: "button",
453
+ className: clsx(SettingsRoot_module_css_default.trigger, !wide && SettingsRoot_module_css_default.rail),
454
+ "aria-label": t("trigger"),
455
+ "aria-keyshortcuts": shortcut?.aria,
456
+ "aria-haspopup": "dialog",
457
+ "aria-expanded": open,
458
+ onClick: () => {
459
+ actions.open();
460
+ },
461
+ children: renderSlot("settings.trigger", { wide })
462
+ })
430
463
  }) }),
431
464
  (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ConnectionIndicator, {
432
465
  state: wide && desktopUpdate.presentation?.phase !== "installing" ? connectionIndicator : void 0,
@@ -450,7 +483,7 @@ window.__ModuleLoader__.load({
450
483
  rows,
451
484
  renderSlot,
452
485
  activeId,
453
- onSelect: setActiveId,
486
+ onSelect: actions.select,
454
487
  onClose: close
455
488
  }),
456
489
  onboardingStep !== void 0 && renderSlot("settings.onboarding", {
@@ -627,7 +660,7 @@ window.__ModuleLoader__.load({
627
660
  function CurrentVersionRow({ t }) {
628
661
  return (0, react_jsx_runtime.jsx)("div", {
629
662
  className: CurrentVersionRow_module_css_default.row,
630
- children: t("general.currentVersion", { version: "0.1.7-rc.1" })
663
+ children: t("general.currentVersion", { version: "0.2.0-rc.1" })
631
664
  });
632
665
  }
633
666
  //#endregion
@@ -826,6 +859,7 @@ window.__ModuleLoader__.load({
826
859
  /** Simplified Chinese dictionary (the key-set source of truth). */
827
860
  const zh = {
828
861
  "trigger": "设置",
862
+ "shortcut.open": "打开设置",
829
863
  "desktop.update.available": "新版本",
830
864
  "desktop.update.checking": "正在检查更新…",
831
865
  "desktop.update.progress": "{percent}%",
@@ -833,16 +867,16 @@ window.__ModuleLoader__.load({
833
867
  "desktop.update.installing": "正在准备重启…",
834
868
  "desktop.update.ready": "安装并重启",
835
869
  "desktop.update.retry": "重试更新",
836
- "desktop.update.versionDetail": "{label} — V{version}",
837
- "desktop.update.downloadDetail": "正在下载更新:{percent}%\n目标版本:V{version}",
870
+ "desktop.update.versionDetail": "{label}:{version}",
871
+ "desktop.update.downloadDetail": "正在下载更新:{percent}%\n目标版本:{version}",
838
872
  "desktop.update.checkFailed": "检查更新失败,请稍后重试。",
839
873
  "desktop.update.downloadFailed": "下载更新失败,请重试。",
840
874
  "desktop.update.installFailed": "安装更新失败,请稍后重试。",
841
- "desktop.update.checkNetworkFailed": "检查更新失败,请稍后重试。网络连接异常,请检查网络后重试。",
842
- "desktop.update.downloadNetworkFailed": "下载更新失败,请重试。网络连接异常,请检查网络后重试。",
843
- "desktop.update.installNetworkFailed": "安装更新失败,请稍后重试。网络连接异常,请检查网络后重试。",
844
- "desktop.update.stopFailed": "未能安全停止任务,更新未安装。请稍后重试。",
845
- "desktop.update.tasksChanged": "有新任务开始,请重新确认更新。",
875
+ "desktop.update.checkNetworkFailed": "检查更新失败,请检查网络连接后重试。",
876
+ "desktop.update.downloadNetworkFailed": "下载更新失败,请检查网络连接后重试。",
877
+ "desktop.update.installNetworkFailed": "安装更新失败,请检查网络连接后重试。",
878
+ "desktop.update.stopFailed": "未能安全停止任务,更新尚未安装,请稍后重试。",
879
+ "desktop.update.tasksChanged": "有新任务开始运行,请重新确认是否停止任务并更新。",
846
880
  "desktop.update.tasksUnavailable": "无法确认任务状态,请在工作区就绪后重试更新。",
847
881
  "title": "设置",
848
882
  "close": "关闭",
@@ -850,9 +884,9 @@ window.__ModuleLoader__.load({
850
884
  "openDocument.error": "无法打开配置文件",
851
885
  "general.nav": "通用设置",
852
886
  "general.currentVersion": "当前版本:{version}",
853
- "developerTools.title": "开发者工具",
887
+ "developerTools.title": "代码工作工具",
854
888
  "developerTools.error": "保存失败,请重试",
855
- "developerTools.description": "显示用于调试和排查问题的工具与信息",
889
+ "developerTools.description": "开启后显示轨迹、本轮代码差异,新对话中的 Agent 预设切换",
856
890
  "connection.error": "连接异常,刷新重试",
857
891
  "connection.connecting": "重新连接中",
858
892
  "connection.connected": "连接成功",
@@ -862,6 +896,7 @@ window.__ModuleLoader__.load({
862
896
  /** English dictionary, checked complete against the zh key set. */
863
897
  const en = {
864
898
  "trigger": "Settings",
899
+ "shortcut.open": "Open settings",
865
900
  "desktop.update.available": "Update",
866
901
  "desktop.update.checking": "Checking for updates…",
867
902
  "desktop.update.progress": "{percent}%",
@@ -869,16 +904,16 @@ window.__ModuleLoader__.load({
869
904
  "desktop.update.installing": "Preparing to restart…",
870
905
  "desktop.update.ready": "Install and Restart",
871
906
  "desktop.update.retry": "Retry update",
872
- "desktop.update.versionDetail": "{label} — V{version}",
873
- "desktop.update.downloadDetail": "Downloading update: {percent}%\nTarget version: V{version}",
907
+ "desktop.update.versionDetail": "{label}: {version}",
908
+ "desktop.update.downloadDetail": "Downloading update: {percent}%\nTarget version: {version}",
874
909
  "desktop.update.checkFailed": "Could not check for updates. Please try again later.",
875
910
  "desktop.update.downloadFailed": "Could not download the update. Please try again.",
876
911
  "desktop.update.installFailed": "Could not install the update. Please try again later.",
877
- "desktop.update.checkNetworkFailed": "Could not check for updates. Please try again later. The connection was interrupted. Check your network and try again.",
878
- "desktop.update.downloadNetworkFailed": "Could not download the update. Please try again. The connection was interrupted. Check your network and try again.",
879
- "desktop.update.installNetworkFailed": "Could not install the update. Please try again later. The connection was interrupted. Check your network and try again.",
880
- "desktop.update.stopFailed": "Tasks could not be stopped safely. The update was not installed. Please try again later.",
881
- "desktop.update.tasksChanged": "New tasks started. Review the update confirmation again.",
912
+ "desktop.update.checkNetworkFailed": "Could not check for updates. Check your connection and try again.",
913
+ "desktop.update.downloadNetworkFailed": "Could not download the update. Check your connection and try again.",
914
+ "desktop.update.installNetworkFailed": "Could not install the update. Check your connection and try again.",
915
+ "desktop.update.stopFailed": "Could not safely stop the tasks. The update has not been installed. Please try again later.",
916
+ "desktop.update.tasksChanged": "New tasks have started. Confirm again to stop the tasks and update.",
882
917
  "desktop.update.tasksUnavailable": "Task status is unavailable. Try updating again when the workspace is ready.",
883
918
  "title": "Settings",
884
919
  "close": "Close",
@@ -886,9 +921,9 @@ window.__ModuleLoader__.load({
886
921
  "openDocument.error": "Could not open configuration file",
887
922
  "general.nav": "General",
888
923
  "general.currentVersion": "Current version: {version}",
889
- "developerTools.title": "Developer tools",
924
+ "developerTools.title": "Coding Tools",
890
925
  "developerTools.error": "Could not save. Please try again.",
891
- "developerTools.description": "Show tools and information for debugging and troubleshooting",
926
+ "developerTools.description": "Shows trajectory, code diffs, and Agent preset switching in new chats",
892
927
  "connection.error": "Disconnected",
893
928
  "connection.connecting": "Reconnecting",
894
929
  "connection.connected": "Connected",
@@ -910,7 +945,8 @@ window.__ModuleLoader__.load({
910
945
  "connection",
911
946
  "remote",
912
947
  "remote.settings",
913
- "configForms"
948
+ "configForms",
949
+ "shortcuts"
914
950
  ];
915
951
  /**
916
952
  * Register the `settings` dictionaries, the chrome content, and the General
@@ -974,6 +1010,7 @@ window.__ModuleLoader__.load({
974
1010
  connection.reconnect();
975
1011
  },
976
1012
  hooks: {
1013
+ shortcuts: ctx.shortcuts.catalog,
977
1014
  desktopUpdate: desktopUpdate.store,
978
1015
  connectionState: connection.state,
979
1016
  sections: {
@@ -1018,41 +1055,100 @@ window.__ModuleLoader__.load({
1018
1055
  }
1019
1056
  }
1020
1057
  });
1021
- ctx.slots.inject("sidebar.settings", () => ctx.slots.register({
1022
- name: "sidebar.settings",
1023
- locale: NS,
1024
- children: {
1025
- "settings.launcher": {
1026
- kind: "single",
1027
- scope: "root"
1028
- },
1029
- "settings.trigger": {
1030
- kind: "single",
1031
- scope: "root"
1032
- },
1033
- "settings.header": {
1034
- kind: "single",
1035
- scope: "root"
1036
- },
1037
- "settings.action": {
1038
- kind: "list",
1039
- scope: "root"
1040
- },
1041
- "settings.close": {
1042
- kind: "single",
1043
- scope: "root"
1044
- },
1045
- "settings.section": {
1046
- kind: "list",
1047
- scope: "root"
1058
+ ctx.slots.inject("sidebar.settings", () => {
1059
+ const shellHandle = createSettingsShellStore();
1060
+ const shellInstance = shellHandle.create();
1061
+ const shellStore = {
1062
+ ...shellHandle,
1063
+ create: () => shellInstance
1064
+ };
1065
+ const disposeCommand = ctx.shortcuts.register({
1066
+ id: "settings.open",
1067
+ label: () => t("shortcut.open"),
1068
+ aliases: ["settings", "preferences"],
1069
+ defaults: {
1070
+ "desktop:macos": {
1071
+ code: "Comma",
1072
+ modifiers: ["primary"]
1073
+ },
1074
+ "desktop:windows": {
1075
+ code: "Comma",
1076
+ modifiers: ["primary"]
1077
+ },
1078
+ "desktop:linux": {
1079
+ code: "Comma",
1080
+ modifiers: ["primary"]
1081
+ },
1082
+ "web:macos": {
1083
+ code: "Comma",
1084
+ modifiers: ["primary"]
1085
+ },
1086
+ "web:windows": {
1087
+ code: "Comma",
1088
+ modifiers: ["primary"]
1089
+ }
1048
1090
  },
1049
- "settings.onboarding": {
1050
- kind: "list",
1051
- scope: "root"
1091
+ regions: [
1092
+ "page",
1093
+ "editable",
1094
+ "terminal"
1095
+ ],
1096
+ modals: ["settings"],
1097
+ resolve: ({ modal }) => {
1098
+ if (modal !== null && modal !== "settings") return {
1099
+ status: "blocked",
1100
+ reason: "modal"
1101
+ };
1102
+ return {
1103
+ status: "handled",
1104
+ run: () => {
1105
+ if (modal === "settings") (0, _deepseek_ai_dsh_client_ui_primitives.closeTopModal)(document);
1106
+ else shellInstance.actions.open();
1107
+ }
1108
+ };
1052
1109
  }
1053
- },
1054
- inject: shellInjected
1055
- }, SettingsRoot));
1110
+ });
1111
+ const disposeSlot = ctx.slots.register({
1112
+ name: "sidebar.settings",
1113
+ locale: NS,
1114
+ store: shellStore,
1115
+ children: {
1116
+ "settings.launcher": {
1117
+ kind: "single",
1118
+ scope: "root"
1119
+ },
1120
+ "settings.trigger": {
1121
+ kind: "single",
1122
+ scope: "root"
1123
+ },
1124
+ "settings.header": {
1125
+ kind: "single",
1126
+ scope: "root"
1127
+ },
1128
+ "settings.action": {
1129
+ kind: "list",
1130
+ scope: "root"
1131
+ },
1132
+ "settings.close": {
1133
+ kind: "single",
1134
+ scope: "root"
1135
+ },
1136
+ "settings.section": {
1137
+ kind: "list",
1138
+ scope: "root"
1139
+ },
1140
+ "settings.onboarding": {
1141
+ kind: "list",
1142
+ scope: "root"
1143
+ }
1144
+ },
1145
+ inject: shellInjected
1146
+ }, SettingsRoot);
1147
+ return () => {
1148
+ disposeCommand();
1149
+ disposeSlot();
1150
+ };
1151
+ });
1056
1152
  ctx.slots.inject("settings.trigger", () => ctx.slots.register({
1057
1153
  name: "settings.trigger",
1058
1154
  locale: NS
@@ -2,6 +2,7 @@
2
2
  /** Simplified Chinese dictionary (the key-set source of truth). */
3
3
  export declare const zh: {
4
4
  trigger: string;
5
+ 'shortcut.open': string;
5
6
  'desktop.update.available': string;
6
7
  'desktop.update.checking': string;
7
8
  'desktop.update.progress': string;
@@ -40,6 +41,7 @@ export type SettingsKey = keyof typeof zh;
40
41
  /** English dictionary, checked complete against the zh key set. */
41
42
  export declare const en: {
42
43
  trigger: string;
44
+ 'shortcut.open': string;
43
45
  'desktop.update.available': string;
44
46
  'desktop.update.checking': string;
45
47
  'desktop.update.progress': string;
@@ -8,6 +8,9 @@
8
8
  */
9
9
  import type { ConnectionState } from '@deepseek-ai/dsh-client-connection/client';
10
10
  import type { HostObservable, InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
11
+ import type { PropsStore } from '@deepseek-ai/dsh-client-store';
12
+ import type { createSettingsShellStore } from './shell-store.ts';
13
+ import type { ShortcutCatalogEntry } from '@deepseek-ai/dsh-client-shortcuts/client';
11
14
  import type { DesktopUpdateView } from '../types.ts';
12
15
  /** One nav row projected from a settings.section registration's options. */
13
16
  export interface SettingsSectionRow {
@@ -31,6 +34,8 @@ export type SettingsRootInjected = {
31
34
  /** Request a fresh logical generation and physical WebSocket immediately. */
32
35
  reconnect: () => void;
33
36
  hooks: {
37
+ /** Effective command presentation, shared with the reference. */
38
+ shortcuts: HostObservable<readonly ShortcutCatalogEntry[]>;
34
39
  /** Shared Electron status for both sidebar locations. */
35
40
  desktopUpdate: HostObservable<DesktopUpdateView>;
36
41
  /** Connection-owned state for the current Host connection. */
@@ -44,8 +49,8 @@ export type SettingsRootInjected = {
44
49
  /**
45
50
  * Full component props of the settings shell root: the sidebar owner share
46
51
  * (wide/rail state) plus the declared render shares and the injected face
47
- * (hooks compartment bound to useSections). No store is registered — modal
48
- * open state and active section id are component-local viewing state.
52
+ * (hooks compartment bound to useSections). The declared store shares modal
53
+ * visibility and section selection with application commands.
49
54
  */
50
- export type SettingsRootComponentProps = PropsRuntime<'sidebar.settings'> & PropsRenderSlots<'settings.launcher' | 'settings.trigger' | 'settings.header' | 'settings.action' | 'settings.close' | 'settings.section' | 'settings.onboarding'> & InjectFace<SettingsRootInjected> & PropsLocale<'settings'>;
55
+ export type SettingsRootComponentProps = PropsRuntime<'sidebar.settings'> & PropsRenderSlots<'settings.launcher' | 'settings.trigger' | 'settings.header' | 'settings.action' | 'settings.close' | 'settings.section' | 'settings.onboarding'> & InjectFace<SettingsRootInjected> & PropsLocale<'settings'> & PropsStore<ReturnType<typeof createSettingsShellStore>>;
51
56
  //# sourceMappingURL=shell-contract.d.ts.map
@@ -0,0 +1,19 @@
1
+ /** Shared settings viewing state for its mouse and command entry points. */
2
+ import { type EngineStoreHandle } from '@deepseek-ai/dsh-client-store';
3
+ type State = {
4
+ open: boolean;
5
+ activeId: string | undefined;
6
+ };
7
+ type Actions = {
8
+ open(draft: State): void;
9
+ close(draft: State): void;
10
+ select(draft: State, id: string): void;
11
+ openSection(draft: State, id: string): void;
12
+ };
13
+ /**
14
+ * Declare the settings dialog state and its complete mutation API.
15
+ * @returns one root-scoped store handle for the settings shell.
16
+ */
17
+ export declare function createSettingsShellStore(): EngineStoreHandle<State, Actions>;
18
+ export {};
19
+ //# sourceMappingURL=shell-store.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-settings-general",
3
3
  "description": "Settings ownerless-copy and product onboarding plugin: the General section, shell trigger/header chrome content, settings dictionaries, and the versioned welcome notice",
4
- "version": "0.1.7-rc.1",
4
+ "version": "0.2.0-rc.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -35,7 +35,8 @@
35
35
  "@deepseek-ai/dsh-client-locale",
36
36
  "@deepseek-ai/dsh-client-connection",
37
37
  "@deepseek-ai/dsh-api-remotes",
38
- "@deepseek-ai/dsh-client-ui-sidebar"
38
+ "@deepseek-ai/dsh-client-ui-sidebar",
39
+ "@deepseek-ai/dsh-client-shortcuts"
39
40
  ],
40
41
  "platform": "web"
41
42
  }
@@ -49,22 +50,25 @@
49
50
  },
50
51
  "devDependencies": {
51
52
  "@types/react": "~18.3.1",
53
+ "@types/react-dom": "~18.3.0",
52
54
  "clsx": "^2.0.0",
53
55
  "react": "^18.2.0",
56
+ "react-dom": "^18.2.0",
57
+ "@deepseek-ai/dsh-api-remotes": "0.2.0-rc.1",
54
58
  "@deepseek-ai/cordis": "~4.0.4",
55
- "@deepseek-ai/dsh-api-remotes": "0.1.7-rc.1",
56
- "@deepseek-ai/dsh-client-store": "0.1.7-rc.1",
57
- "@deepseek-ai/dsh-client-connection": "0.1.7-rc.1",
58
- "@deepseek-ai/dsh-client-locale": "0.1.7-rc.1",
59
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.1",
60
- "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.1",
61
- "@deepseek-ai/dsh-client-ui-session": "0.1.7-rc.1",
62
- "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.1",
63
- "@deepseek-ai/dsh-client-ui-sidebar": "0.1.7-rc.1",
64
- "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.1",
65
- "@deepseek-ai/dsh-remote-mock": "0.1.7-rc.1",
66
- "@deepseek-ai/dsh-client-test-runtime": "0.1.7-rc.1",
67
- "@deepseek-ai/dsh-settings": "0.1.7-rc.1"
59
+ "@deepseek-ai/dsh-client-connection": "0.2.0-rc.1",
60
+ "@deepseek-ai/dsh-client-locale": "0.2.0-rc.1",
61
+ "@deepseek-ai/dsh-client-store": "0.2.0-rc.1",
62
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.1",
63
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.1",
64
+ "@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.1",
65
+ "@deepseek-ai/dsh-client-test-runtime": "0.2.0-rc.1",
66
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.2.0-rc.1",
67
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.1",
68
+ "@deepseek-ai/dsh-remote-mock": "0.2.0-rc.1",
69
+ "@deepseek-ai/dsh-settings": "0.2.0-rc.1",
70
+ "@deepseek-ai/dsh-client-shortcuts": "0.2.0-rc.1",
71
+ "@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.1"
68
72
  },
69
73
  "files": [
70
74
  "lib/index.js",