@deepseek-ai/dsh-client-ui-settings-general 0.1.5-rc.2 → 0.1.6-alpha.2

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
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
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: 845d9c48dce2264d478f0ac854ef85a480828a14
6
- README.zh.md: 4a96e78a13dd8e6aefd759926f6e2cb153f0b259
5
+ README.md: 0ea39cbb56b2040004513723b6d838a1ce9e7557
6
+ README.zh.md: 9566471c68067e4ec461c28aeeb9a806a718fb90
package/README.md CHANGED
@@ -25,7 +25,9 @@ Use this package to give the dsh web client a Settings panel, connection-recover
25
25
  <a id="use-this-package"></a>
26
26
  ## Use this package
27
27
 
28
- 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. Automatic recovery shows **Reconnecting** with one to three dots advancing every 500ms. Hover or keyboard focus changes either yellow label to **Reconnect now** without changing its background; press feedback stays within the warning palette, and selecting it starts retry 1 immediately. Recovery changes the region to pale-green **Connected** for two seconds before it disappears. The icon, left-aligned text origin, height, and width remain fixed across every visible state. 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.
28
+ 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 a spinner 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.
29
+
30
+ In Desktop, the account-row update control shows availability, progress, verification, readiness, and persistent retry feedback. 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 dot on its top expand button. 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.
29
31
 
30
32
  ### The General section
31
33
 
@@ -55,7 +57,7 @@ The navigation is a projection of the `settings.section` ledger; nav labels may
55
57
 
56
58
  ### Connection recovery
57
59
 
58
- The shell is an explicit recovery consumer, so it injects Connection directly rather than adding lifecycle controls to `ctx.remote`. Its private hooks compartment binds `ctx.connection.state`, while the component receives only the selected state and an injected callback for `ctx.connection.reconnect()`. `ConnectionIndicator` owns the inline presentation and receives all visible and accessible copy from the `settings` locale namespace; the shell owns the two-second recovered-state timer.
60
+ The shell is an explicit recovery consumer, so it injects Connection directly rather than adding lifecycle controls to `ctx.remote`. Its private hooks compartment binds `ctx.connection.state`, while the component receives only the selected state and an injected callback for `ctx.connection.reconnect()`. `ConnectionIndicator` owns the inline presentation and receives all visible and accessible copy from the `settings` locale namespace; the shell owns the 800ms minimum-visible hold for the connecting state and the two-second recovered-state timer, which starts when the recovered pill becomes visible after the hold.
59
61
 
60
62
  ### Document availability
61
63
 
package/README.zh.md CHANGED
@@ -25,7 +25,9 @@ kind: "package-reference"
25
25
  <a id="use-this-package"></a>
26
26
  ## 使用本包
27
27
 
28
- 用户通过侧边栏底部的 Settings 控件进入外壳;功能插件通过本外壳所投影的 slot 账本贡献自己的页面与引导步骤。在展开侧边栏和收起轨道中,该控件都会把本地化的 Settings 文案作为其可访问名称。Settings 右侧浅黄色的**连接异常**操作表示浏览器离线暂停;自动恢复期间显示**自动重连中**,其后一至三个点每 500ms 前进一次。鼠标悬浮或键盘聚焦任一黄色状态时,只有文案变为**立即重连**,背景保持不变;按压反馈留在黄色色阶内,选中后立即从 retry 1 开始。恢复后该区域变为浅绿色的**连接成功**,驻留 2 秒再消失。所有可见状态的文字都左对齐,且图标、文字起点、高度和宽度保持固定。首次启动与未曾中断的健康连接保持静默。外壳渲染模态面板、由 `settings.section` 条目构建的导航,以及每次只挂载一个的引导步骤。
28
+ 用户通过侧边栏底部的 Settings 控件进入外壳;功能插件通过本外壳所投影的 slot 账本贡献自己的页面与引导步骤。在展开侧边栏和收起轨道中,该控件都会把本地化的 Settings 文案作为其可访问名称。Settings 右侧浅黄色的**连接异常**操作表示浏览器离线暂停;其常驻重试图形与中文文案「连接异常,刷新重试」都指明重试动作。每次恢复尝试都显示 spinner 加**重新连接中**,其后一至三个点每 500ms 前进一次,且每次尝试至少可见 800ms,短暂重试不会闪动。选中任一黄色状态都会立即发起重试;按压反馈留在黄色色阶内。恢复后该区域变为浅绿色的**连接成功**,从绿色药丸可见起驻留 2 秒再消失。药丸出现时淡入、移除时以 150ms 淡出,宽度随当前文案自适应。首次启动与未曾中断的健康连接保持静默。外壳渲染模态面板、由 `settings.section` 条目构建的导航,以及每次只挂载一个的引导步骤。
29
+
30
+ 在 Desktop 中,账户行更新控件显示可用版本、进度、验证、就绪和持久重试反馈。预加载仅传递语义化阶段、版本、进度和失败类别;组件从当前 `settings` locale 解析全部可见与无障碍文案,并在应用内语言变化后同步更新。选择可用更新即开始下载;安装需要独立的壳拥有的确认。侧栏收起时,其顶部展开按钮以圆点显示相同状态。连接反馈通常优先展示,但壳报告正在安装时,预期的后端断开不能遮盖更新状态;失败后恢复连接反馈。两个控件共用一次载体订阅;浏览器代码不能选择安装包或授权安装。[Desktop 更新](../../../apps/desktop/README.zh.md)负责发布流程。
29
31
 
30
32
  ### 「通用」分区
31
33
 
@@ -55,11 +57,11 @@ kind: "package-reference"
55
57
 
56
58
  ### 连接恢复
57
59
 
58
- 外壳是明确的恢复功能消费方,因此直接注入 Connection,而不把生命周期控制放进 `ctx.remote`。它的私有 hooks compartment 绑定 `ctx.connection.state`,组件只接收选出的状态与调用 `ctx.connection.reconnect()` 的注入回调。`ConnectionIndicator` 拥有内联展示并从 `settings` locale namespace 接收全部可见与无障碍文案;2 秒恢复状态计时器归外壳所有。
60
+ 外壳是明确的恢复功能消费方,因此直接注入 Connection,而不把生命周期控制放进 `ctx.remote`。它的私有 hooks compartment 绑定 `ctx.connection.state`,组件只接收选出的状态与调用 `ctx.connection.reconnect()` 的注入回调。`ConnectionIndicator` 拥有内联展示并从 `settings` locale namespace 接收全部可见与无障碍文案;连接中状态的 800ms 最短可见驻留与 2 秒恢复确认计时器归外壳所有;恢复计时从驻留结束、恢复药丸实际可见时开始。
59
61
 
60
62
  ### 文档可用性
61
63
 
62
- 在 loopback 页面上,Client 通过 `settings/describe` 加载提供方的 `hasDocument` 能力,且只有在 Host 确认可准备好一份由提供方持有的本地文档时才渲染配置文件操作。该操作调用无路径参数且经浏览器认证的 `settings/openSettingsDocument` Remote;Host 会再次解析提供方路径、在文档缺失时将其创建出来,并交给原生文本编辑器(macOS 上使用 `open -t`,绕过浏览器文件关联;Linux 和 Windows 上使用桌面文件关联;WSL 上经 `wslpath -w` 转换后使用 Windows 文件关联)。打开失败时该操作仍可使用,并渲染本地化错误。临时读取失败或 Host 拓扑变化后,重新打开对话框或重新连接会刷新可用性。非 loopback 页面保留 Client 策略,不提供该原生操作及其 settings 读取。
64
+ 在 loopback 页面上,Client 通过 `settings/describe` 加载提供方的 `hasDocument` 能力,且只有在 Host 确认可准备好一份由提供方持有的本地文档时才渲染**打开配置文件**操作。该操作调用无路径参数且经浏览器认证的 `settings/openSettingsDocument` Remote;Host 会再次解析提供方路径、在文档缺失时将其创建出来,并交给原生文本编辑器(macOS 上使用 `open -t`,绕过浏览器文件关联;Linux 和 Windows 上使用桌面文件关联;WSL 上经 `wslpath -w` 转换后使用 Windows 文件关联)。打开失败时该操作仍可使用,并渲染本地化错误。临时读取失败或 Host 拓扑变化后,重新打开对话框或重新连接会刷新可用性。非 loopback 页面保留 Client 策略,不提供该原生操作及其 settings 读取。
63
65
 
64
66
  ### 宿主端
65
67
 
package/lib/client.js CHANGED
@@ -25,13 +25,13 @@ window.__ModuleLoader__.load({
25
25
  }
26
26
  //#endregion
27
27
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-general/src/client/SettingsRoot.module.css.mjs
28
- const css$3 = ".VOzbGW_triggerRow{flex:none;align-items:center;gap:8px;width:calc(100% + 4px);margin:4px -2px;display:flex}.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{corner-shape:round;border-radius:50%;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;background:var(--dsw-alias-bg-layer-2);width:800px;max-width:calc(100vw - 48px);height:min(800px,100vh - 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}.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}";
29
- const tagId$3 = "@deepseek-ai/dsh-client-ui-settings-general/SettingsRoot.module.css";
30
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
28
+ const css$4 = ".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;background:var(--dsw-alias-bg-layer-2);width:800px;max-width:calc(100vw - 48px);height:min(800px,100vh - 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}.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}";
29
+ const tagId$4 = "@deepseek-ai/dsh-client-ui-settings-general/SettingsRoot.module.css";
30
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
31
31
  const tag = document.createElement("style");
32
32
  tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-settings-general";
33
- tag.dataset.pluginCss = tagId$3;
34
- tag.textContent = css$3;
33
+ tag.dataset.pluginCss = tagId$4;
34
+ tag.textContent = css$4;
35
35
  document.head.appendChild(tag);
36
36
  }
37
37
  var SettingsRoot_module_css_default = {
@@ -58,6 +58,135 @@ window.__ModuleLoader__.load({
58
58
  "triggerRow": "VOzbGW_triggerRow"
59
59
  };
60
60
  //#endregion
61
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.module.css.mjs
62
+ const css$3 = ".DOUpOa_indicator{border:1px solid color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 20%, transparent);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);font:inherit;white-space:nowrap;cursor:pointer;border-radius:6px;flex:none;align-items:center;gap:6px;margin-inline-end:4px;padding:1px 8px;font-size:12px;line-height:20px;display:inline-flex}.DOUpOa_indicator[data-error]{color:var(--dsw-alias-state-error-primary)}.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_badge[data-error],.DOUpOa_errorDot{background:var(--dsw-alias-state-error-primary)}.DOUpOa_errorDot{corner-shape:round;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
+ const tagId$3 = "@deepseek-ai/dsh-client-ui-settings-general/DesktopUpdateIndicator.module.css";
64
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
65
+ const tag = document.createElement("style");
66
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-settings-general";
67
+ tag.dataset.pluginCss = tagId$3;
68
+ tag.textContent = css$3;
69
+ document.head.appendChild(tag);
70
+ }
71
+ var DesktopUpdateIndicator_module_css_default = {
72
+ "badge": "DOUpOa_badge",
73
+ "errorDot": "DOUpOa_errorDot",
74
+ "indicator": "DOUpOa_indicator",
75
+ "spin": "DOUpOa_spin",
76
+ "spinner": "DOUpOa_spinner"
77
+ };
78
+ //#endregion
79
+ //#region lib/types/client/DesktopUpdateIndicator.js
80
+ /** Optional Electron status presentation; the native shell owns actions and Web owns visible copy. */
81
+ const BUSY_PHASES = new Set([
82
+ "checking",
83
+ "downloading",
84
+ "verifying",
85
+ "installing"
86
+ ]);
87
+ function updateCopy(state, t) {
88
+ const label = {
89
+ idle: "",
90
+ checking: t("desktop.update.checking"),
91
+ available: t("desktop.update.available"),
92
+ downloading: t("desktop.update.progress", { percent: state.percent ?? 0 }),
93
+ verifying: t("desktop.update.verifying"),
94
+ installing: t("desktop.update.installing"),
95
+ ready: t("desktop.update.ready"),
96
+ error: t("desktop.update.retry")
97
+ }[state.phase];
98
+ if (state.phase === "error") return {
99
+ label,
100
+ detail: {
101
+ check: t("desktop.update.checkFailed"),
102
+ "check-network": t("desktop.update.checkNetworkFailed"),
103
+ download: t("desktop.update.downloadFailed"),
104
+ "download-network": t("desktop.update.downloadNetworkFailed"),
105
+ install: t("desktop.update.installFailed"),
106
+ "install-network": t("desktop.update.installNetworkFailed"),
107
+ "stop-failed": t("desktop.update.stopFailed"),
108
+ "tasks-changed": t("desktop.update.tasksChanged"),
109
+ "tasks-unavailable": t("desktop.update.tasksUnavailable")
110
+ }[state.failure ?? "install"]
111
+ };
112
+ if (state.phase === "downloading" && state.version !== void 0) return {
113
+ label,
114
+ detail: t("desktop.update.downloadDetail", {
115
+ percent: state.percent ?? 0,
116
+ version: state.version
117
+ })
118
+ };
119
+ return {
120
+ label,
121
+ detail: state.version === void 0 ? label : t("desktop.update.versionDetail", {
122
+ label,
123
+ version: state.version
124
+ })
125
+ };
126
+ }
127
+ /**
128
+ * @param props - Connection priority, sidebar width, and localized bridge-failure copy.
129
+ * @returns Desktop-only status beside the account button, or nothing in browsers.
130
+ */
131
+ function DesktopUpdateIndicator({ wide, hidden, t, view, onOpen }) {
132
+ const { presentation: state, failed, opening } = view;
133
+ if (!wide || hidden || !failed && (state === void 0 || state.phase === "idle")) return null;
134
+ const retryLabel = t("desktop.update.retry");
135
+ const copy = state === void 0 ? {
136
+ label: retryLabel,
137
+ detail: retryLabel
138
+ } : updateCopy(state, t);
139
+ const label = failed ? retryLabel : copy.label;
140
+ const error = failed || state?.phase === "error";
141
+ const busy = opening || state !== void 0 && BUSY_PHASES.has(state.phase);
142
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
143
+ label: failed ? retryLabel : copy.detail,
144
+ side: "top",
145
+ children: (0, react_jsx_runtime.jsxs)("button", {
146
+ type: "button",
147
+ className: DesktopUpdateIndicator_module_css_default.indicator,
148
+ "data-error": error || void 0,
149
+ "aria-label": label,
150
+ "aria-disabled": busy,
151
+ onClick: () => {
152
+ if (!busy) onOpen();
153
+ },
154
+ children: [error ? (0, react_jsx_runtime.jsx)("span", {
155
+ className: DesktopUpdateIndicator_module_css_default.errorDot,
156
+ "aria-hidden": "true"
157
+ }) : busy ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconLoadingOutline16, {
158
+ className: DesktopUpdateIndicator_module_css_default.spinner,
159
+ size: 16
160
+ }) : (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDownloadOutline16, { size: 14 }), (0, react_jsx_runtime.jsx)("span", { children: label })]
161
+ })
162
+ });
163
+ }
164
+ /**
165
+ * @param props - Framework-bound carrier and connection state.
166
+ * @returns A non-interactive notification on the sidebar expand button.
167
+ */
168
+ function DesktopUpdateBadge({ useDesktopUpdate, useConnectionState, t }) {
169
+ const { presentation: state, failed } = useDesktopUpdate((value) => value);
170
+ const connection = useConnectionState((value) => value);
171
+ if ((connection === "disconnected" || connection === "connecting") && state?.phase !== "installing" || !failed && (state === void 0 || state.phase === "idle")) return null;
172
+ const retryLabel = t("desktop.update.retry");
173
+ const copy = state === void 0 ? {
174
+ label: retryLabel,
175
+ detail: retryLabel
176
+ } : updateCopy(state, t);
177
+ const label = failed ? retryLabel : copy.label;
178
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
179
+ label: failed ? label : copy.detail,
180
+ side: "right",
181
+ children: (0, react_jsx_runtime.jsx)("span", {
182
+ role: "img",
183
+ "aria-label": label,
184
+ className: DesktopUpdateIndicator_module_css_default.badge,
185
+ "data-error": failed || state?.phase === "error" || void 0
186
+ })
187
+ });
188
+ }
189
+ //#endregion
61
190
  //#region lib/types/client/SettingsRoot.js
62
191
  /**
63
192
  * Settings shell root: the sidebar-foot trigger row plus the centered modal
@@ -72,6 +201,8 @@ window.__ModuleLoader__.load({
72
201
  * to the step, so a mounted-but-deciding step paints nothing here.
73
202
  */
74
203
  const RECOVERY_CONFIRMATION_MS = 2e3;
204
+ /** Minimum visible time for the connecting pill; shorter attempts read as flicker. */
205
+ const CONNECTING_MIN_VISIBLE_MS = 800;
75
206
  /** Nav glyph by section id; unknown ids fall back to the settings gear. */
76
207
  function navIcon(id) {
77
208
  if (id === "models") return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconDataOutline16, {
@@ -86,6 +217,10 @@ window.__ModuleLoader__.load({
86
217
  className: SettingsRoot_module_css_default.navIcon,
87
218
  size: 16
88
219
  });
220
+ if (id === "archived-sessions") return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconArchiveOutline20, {
221
+ className: SettingsRoot_module_css_default.navIcon,
222
+ size: 16
223
+ });
89
224
  return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSettingsOutline16, {
90
225
  className: SettingsRoot_module_css_default.navIcon,
91
226
  size: 16
@@ -176,11 +311,13 @@ window.__ModuleLoader__.load({
176
311
  * @returns the settings shell element tree.
177
312
  */
178
313
  function SettingsRoot(props) {
179
- const { wide, reconnect, useConnectionState, useSections, useOnboardingSteps, useSessions, renderSlot, t } = props;
314
+ const { wide, reconnect, useConnectionState, useSections, useOnboardingSteps, useSessions, renderSlot, t, useDesktopUpdate, openDesktopUpdate } = props;
180
315
  const [open, setOpen] = (0, react.useState)(false);
181
316
  const [activeId, setActiveId] = (0, react.useState)(void 0);
182
317
  const [completedOnboarding, setCompletedOnboarding] = (0, react.useState)(() => /* @__PURE__ */ new Set());
183
318
  const [showRecovery, setShowRecovery] = (0, react.useState)(false);
319
+ const [holdConnecting, setHoldConnecting] = (0, react.useState)(false);
320
+ const connectingShownAt = (0, react.useRef)(void 0);
184
321
  const triggerButton = (0, react.useRef)(null);
185
322
  const wasOpen = (0, react.useRef)(open);
186
323
  const close = (0, react.useCallback)(() => {
@@ -196,10 +333,14 @@ window.__ModuleLoader__.load({
196
333
  setOpen(true);
197
334
  }, []);
198
335
  const rows = useSections((s) => s);
336
+ const desktopUpdate = useDesktopUpdate((state) => state);
199
337
  const connectionState = useConnectionState((state) => state);
200
338
  const previousConnectionState = (0, react.useRef)(connectionState);
201
339
  const onboardingSteps = useOnboardingSteps((s) => s);
202
- const onboardingActive = useSessions((state) => state.phase === "ready" && (state.current === void 0 || state.byId[state.current]?.blank === true));
340
+ const onboardingActive = useSessions((state) => {
341
+ const main = Object.values(state.byId).find((session) => (session.retainedBy.mainView ?? 0) > 0);
342
+ return state.phase === "ready" && (main === void 0 || main.blank);
343
+ });
203
344
  const onboardingStep = onboardingActive ? onboardingSteps.find((step) => !completedOnboarding.has(step.id)) : void 0;
204
345
  (0, react.useEffect)(() => {
205
346
  if (onboardingActive) return;
@@ -214,12 +355,34 @@ window.__ModuleLoader__.load({
214
355
  }
215
356
  if (previous !== "disconnected" && previous !== "connecting") return;
216
357
  setShowRecovery(true);
358
+ }, [connectionState]);
359
+ (0, react.useLayoutEffect)(() => {
360
+ if (!showRecovery || holdConnecting) return;
217
361
  const timeout = window.setTimeout(() => {
218
362
  setShowRecovery(false);
219
363
  }, RECOVERY_CONFIRMATION_MS);
220
364
  return () => {
221
365
  window.clearTimeout(timeout);
222
366
  };
367
+ }, [showRecovery, holdConnecting]);
368
+ (0, react.useLayoutEffect)(() => {
369
+ if (connectionState === "connecting") {
370
+ connectingShownAt.current = Date.now();
371
+ return;
372
+ }
373
+ const shownAt = connectingShownAt.current;
374
+ if (shownAt === void 0) return;
375
+ connectingShownAt.current = void 0;
376
+ const remaining = CONNECTING_MIN_VISIBLE_MS - (Date.now() - shownAt);
377
+ if (remaining <= 0) return;
378
+ setHoldConnecting(true);
379
+ const timeout = window.setTimeout(() => {
380
+ setHoldConnecting(false);
381
+ }, remaining);
382
+ return () => {
383
+ window.clearTimeout(timeout);
384
+ setHoldConnecting(false);
385
+ };
223
386
  }, [connectionState]);
224
387
  const completeOnboardingStep = (0, react.useCallback)((id) => {
225
388
  setCompletedOnboarding((previous) => {
@@ -228,33 +391,42 @@ window.__ModuleLoader__.load({
228
391
  });
229
392
  }, []);
230
393
  let connectionIndicator;
231
- if (connectionState === "disconnected") connectionIndicator = "disconnected";
232
- else if (connectionState === "connecting") connectionIndicator = "connecting";
394
+ if (connectionState === "connecting" || holdConnecting) connectionIndicator = "connecting";
395
+ else if (connectionState === "disconnected") connectionIndicator = "disconnected";
233
396
  else if (showRecovery) connectionIndicator = "recovered";
234
397
  return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
235
398
  (0, react_jsx_runtime.jsxs)("div", {
236
399
  className: clsx(SettingsRoot_module_css_default.triggerRow, !wide && SettingsRoot_module_css_default.railRow),
237
- children: [(0, react_jsx_runtime.jsx)("button", {
238
- ref: triggerButton,
239
- type: "button",
240
- className: clsx(SettingsRoot_module_css_default.trigger, !wide && SettingsRoot_module_css_default.rail),
241
- "aria-label": t("trigger"),
242
- "aria-haspopup": "dialog",
243
- "aria-expanded": open,
244
- onClick: () => {
245
- setOpen(true);
246
- },
247
- children: renderSlot("settings.trigger", { wide })
248
- }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ConnectionIndicator, {
249
- state: wide ? connectionIndicator : void 0,
250
- disconnectedLabel: t("connection.error"),
251
- reconnectLabel: t("connection.retry"),
252
- connectingLabel: t("connection.connecting"),
253
- recoveredLabel: t("connection.connected"),
254
- reconnectActionLabel: t("connection.reconnect"),
255
- restartActionLabel: t("connection.restart"),
256
- onReconnect: reconnect
257
- })]
400
+ children: [
401
+ (0, react_jsx_runtime.jsx)("button", {
402
+ ref: triggerButton,
403
+ type: "button",
404
+ className: clsx(SettingsRoot_module_css_default.trigger, !wide && SettingsRoot_module_css_default.rail),
405
+ "aria-label": t("trigger"),
406
+ "aria-haspopup": "dialog",
407
+ "aria-expanded": open,
408
+ onClick: () => {
409
+ setOpen(true);
410
+ },
411
+ children: renderSlot("settings.trigger", { wide })
412
+ }),
413
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.ConnectionIndicator, {
414
+ state: wide && desktopUpdate.presentation?.phase !== "installing" ? connectionIndicator : void 0,
415
+ disconnectedLabel: t("connection.error"),
416
+ connectingLabel: t("connection.connecting"),
417
+ recoveredLabel: t("connection.connected"),
418
+ reconnectActionLabel: t("connection.reconnect"),
419
+ restartActionLabel: t("connection.restart"),
420
+ onReconnect: reconnect
421
+ }),
422
+ (0, react_jsx_runtime.jsx)(DesktopUpdateIndicator, {
423
+ wide,
424
+ hidden: connectionIndicator !== void 0 && desktopUpdate.presentation?.phase !== "installing",
425
+ t,
426
+ view: desktopUpdate,
427
+ onOpen: openDesktopUpdate
428
+ })
429
+ ]
258
430
  }),
259
431
  open && (0, react_jsx_runtime.jsx)(SettingsPanel, {
260
432
  rows,
@@ -273,6 +445,76 @@ window.__ModuleLoader__.load({
273
445
  ] });
274
446
  }
275
447
  //#endregion
448
+ //#region lib/types/client/desktop-update-source.js
449
+ /** Client-owned observation of the optional Desktop preload. */
450
+ /** Owns one preload subscription across both sidebar locations. */
451
+ var DesktopUpdateSource = class {
452
+ bridge;
453
+ /** Framework-observed carrier status shared by both sidebar controls. */
454
+ store = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)({
455
+ failed: false,
456
+ opening: false
457
+ });
458
+ live = true;
459
+ received = false;
460
+ unsubscribe;
461
+ /** @param bridge - Optional isolated Electron API, absent in ordinary browsers. */
462
+ constructor(bridge) {
463
+ this.bridge = bridge;
464
+ this.unsubscribe = bridge?.subscribe((presentation) => {
465
+ if (!this.live) return;
466
+ this.received = true;
467
+ this.store.set({
468
+ ...this.store.getSnapshot(),
469
+ presentation,
470
+ failed: false
471
+ });
472
+ });
473
+ bridge?.status().then((presentation) => {
474
+ if (this.live && !this.received) this.store.set({
475
+ ...this.store.getSnapshot(),
476
+ presentation
477
+ });
478
+ }, () => {
479
+ if (this.live && !this.received) this.store.set({
480
+ ...this.store.getSnapshot(),
481
+ failed: true
482
+ });
483
+ });
484
+ }
485
+ /** Invoke one user action; subsequent clicks join the shell-owned operation. */
486
+ open() {
487
+ if (!this.live || this.bridge === void 0) return;
488
+ const state = this.store.getSnapshot();
489
+ if (state.opening || state.presentation !== void 0 && [
490
+ "checking",
491
+ "downloading",
492
+ "verifying",
493
+ "installing"
494
+ ].includes(state.presentation.phase)) return;
495
+ this.store.set({
496
+ ...state,
497
+ opening: true
498
+ });
499
+ this.bridge.open().catch(() => {
500
+ if (this.live) this.store.set({
501
+ ...this.store.getSnapshot(),
502
+ failed: true
503
+ });
504
+ }).finally(() => {
505
+ if (this.live) this.store.set({
506
+ ...this.store.getSnapshot(),
507
+ opening: false
508
+ });
509
+ });
510
+ }
511
+ /** Detach the carrier and ignore any pending status or action completion. */
512
+ dispose() {
513
+ this.live = false;
514
+ this.unsubscribe?.();
515
+ }
516
+ };
517
+ //#endregion
276
518
  //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-settings-general/src/client/chrome.module.css.mjs
277
519
  const css$2 = ".UQsH_q_triggerLabel{white-space:nowrap;overflow:hidden}";
278
520
  const tagId$2 = "@deepseek-ai/dsh-client-ui-settings-general/chrome.module.css";
@@ -481,32 +723,66 @@ window.__ModuleLoader__.load({
481
723
  /** Simplified Chinese dictionary (the key-set source of truth). */
482
724
  const zh = {
483
725
  "trigger": "设置",
726
+ "desktop.update.available": "新版本",
727
+ "desktop.update.checking": "正在检查更新…",
728
+ "desktop.update.progress": "{percent}%…",
729
+ "desktop.update.verifying": "正在校验更新文件…",
730
+ "desktop.update.installing": "正在准备重启…",
731
+ "desktop.update.ready": "安装并重启",
732
+ "desktop.update.retry": "重试更新",
733
+ "desktop.update.versionDetail": "{label} — V{version}",
734
+ "desktop.update.downloadDetail": "正在下载更新:{percent}%\n目标版本:V{version}",
735
+ "desktop.update.checkFailed": "检查更新失败,请稍后重试。",
736
+ "desktop.update.downloadFailed": "下载更新失败,请重试。",
737
+ "desktop.update.installFailed": "安装更新失败,请稍后重试。",
738
+ "desktop.update.checkNetworkFailed": "检查更新失败,请稍后重试。网络连接异常,请检查网络后重试。",
739
+ "desktop.update.downloadNetworkFailed": "下载更新失败,请重试。网络连接异常,请检查网络后重试。",
740
+ "desktop.update.installNetworkFailed": "安装更新失败,请稍后重试。网络连接异常,请检查网络后重试。",
741
+ "desktop.update.stopFailed": "未能安全停止任务,更新未安装。请稍后重试。",
742
+ "desktop.update.tasksChanged": "有新任务开始,请重新确认更新。",
743
+ "desktop.update.tasksUnavailable": "无法确认任务状态,请在工作区就绪后重试更新。",
484
744
  "title": "设置",
485
745
  "close": "关闭",
486
746
  "openDocument": "打开配置文件",
487
747
  "openDocument.error": "无法打开配置文件",
488
748
  "general.nav": "通用设置",
489
- "connection.error": "连接异常",
490
- "connection.retry": "立即重连",
491
- "connection.connecting": "自动重连中",
749
+ "connection.error": "连接异常,刷新重试",
750
+ "connection.connecting": "重新连接中",
492
751
  "connection.connected": "连接成功",
493
752
  "connection.reconnect": "连接异常,点击立即重连",
494
- "connection.restart": "连接中断,正在自动重试,点击立即重连"
753
+ "connection.restart": "连接中断,正在重试,点击立即重连"
495
754
  };
496
755
  /** English dictionary, checked complete against the zh key set. */
497
756
  const en = {
498
757
  "trigger": "Settings",
758
+ "desktop.update.available": "Update",
759
+ "desktop.update.checking": "Checking for updates…",
760
+ "desktop.update.progress": "{percent}%…",
761
+ "desktop.update.verifying": "Verifying update files…",
762
+ "desktop.update.installing": "Preparing to restart…",
763
+ "desktop.update.ready": "Install and Restart",
764
+ "desktop.update.retry": "Retry update",
765
+ "desktop.update.versionDetail": "{label} — V{version}",
766
+ "desktop.update.downloadDetail": "Downloading update: {percent}%\nTarget version: V{version}",
767
+ "desktop.update.checkFailed": "Could not check for updates. Please try again later.",
768
+ "desktop.update.downloadFailed": "Could not download the update. Please try again.",
769
+ "desktop.update.installFailed": "Could not install the update. Please try again later.",
770
+ "desktop.update.checkNetworkFailed": "Could not check for updates. Please try again later. The connection was interrupted. Check your network and try again.",
771
+ "desktop.update.downloadNetworkFailed": "Could not download the update. Please try again. The connection was interrupted. Check your network and try again.",
772
+ "desktop.update.installNetworkFailed": "Could not install the update. Please try again later. The connection was interrupted. Check your network and try again.",
773
+ "desktop.update.stopFailed": "Tasks could not be stopped safely. The update was not installed. Please try again later.",
774
+ "desktop.update.tasksChanged": "New tasks started. Review the update confirmation again.",
775
+ "desktop.update.tasksUnavailable": "Task status is unavailable. Try updating again when the workspace is ready.",
499
776
  "title": "Settings",
500
777
  "close": "Close",
501
778
  "openDocument": "Open configuration file",
502
779
  "openDocument.error": "Could not open configuration file",
503
780
  "general.nav": "General",
504
781
  "connection.error": "Disconnected",
505
- "connection.retry": "Reconnect now",
506
782
  "connection.connecting": "Reconnecting",
507
783
  "connection.connected": "Connected",
508
784
  "connection.reconnect": "Disconnected, reconnect now",
509
- "connection.restart": "Reconnecting automatically, reconnect now"
785
+ "connection.restart": "Reconnecting, reconnect now"
510
786
  };
511
787
  //#endregion
512
788
  //#region lib/types/client/index.js
@@ -536,6 +812,19 @@ window.__ModuleLoader__.load({
536
812
  en
537
813
  }), "ui-settings-general: dictionaries");
538
814
  const connection = ctx.get("connection");
815
+ const carrier = globalThis.dshDesktop;
816
+ const desktopUpdate = new DesktopUpdateSource(carrier?.protocolVersion === 1 ? carrier.updates : void 0);
817
+ ctx.effect(() => () => {
818
+ desktopUpdate.dispose();
819
+ }, "ui-settings-general: desktop update carrier");
820
+ ctx.slots.inject("sidebar.toggle.badge", () => ctx.slots.register({
821
+ name: "sidebar.toggle.badge",
822
+ locale: NS,
823
+ inject: () => ({ hooks: {
824
+ desktopUpdate: desktopUpdate.store,
825
+ connectionState: connection.state
826
+ } })
827
+ }, DesktopUpdateBadge));
539
828
  const t = ctx.locale.bind(NS);
540
829
  const documentController = ctx.remote.$host.isLoopback ? new SettingsDocumentStore(ctx, ctx.settingsScope.describe()) : void 0;
541
830
  const documentInjected = documentController === void 0 ? void 0 : () => ({
@@ -551,10 +840,14 @@ window.__ModuleLoader__.load({
551
840
  let onboardingVersion = -1;
552
841
  let onboardingSteps = [];
553
842
  const shellInjected = () => ({
843
+ openDesktopUpdate: () => {
844
+ desktopUpdate.open();
845
+ },
554
846
  reconnect: () => {
555
847
  connection.reconnect();
556
848
  },
557
849
  hooks: {
850
+ desktopUpdate: desktopUpdate.store,
558
851
  connectionState: connection.state,
559
852
  sections: {
560
853
  getSnapshot: () => {
@@ -0,0 +1,23 @@
1
+ import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import type { DesktopUpdateView } from './desktop-update-bridge.ts';
3
+ import type { SettingsRootInjected } from './shell-contract.ts';
4
+ type SettingsTranslate = PropsLocale<'settings'>['t'];
5
+ /**
6
+ * @param props - Connection priority, sidebar width, and localized bridge-failure copy.
7
+ * @returns Desktop-only status beside the account button, or nothing in browsers.
8
+ */
9
+ export declare function DesktopUpdateIndicator({ wide, hidden, t, view, onOpen }: {
10
+ wide: boolean;
11
+ hidden: boolean;
12
+ t: SettingsTranslate;
13
+ view: DesktopUpdateView;
14
+ onOpen: () => void;
15
+ }): import("react").JSX.Element | null;
16
+ type BadgeProps = PropsRuntime<'sidebar.toggle.badge'> & PropsLocale<'settings'> & Pick<InjectFace<SettingsRootInjected>, 'useDesktopUpdate' | 'useConnectionState'>;
17
+ /**
18
+ * @param props - Framework-bound carrier and connection state.
19
+ * @returns A non-interactive notification on the sidebar expand button.
20
+ */
21
+ export declare function DesktopUpdateBadge({ useDesktopUpdate, useConnectionState, t }: BadgeProps): import("react").JSX.Element | null;
22
+ export {};
23
+ //# sourceMappingURL=DesktopUpdateIndicator.d.ts.map
@@ -0,0 +1,23 @@
1
+ /** Optional Electron preload declarations consumed by the settings row. */
2
+ /** Classified failure copy selected by the Web locale without exposing raw updater diagnostics. */
3
+ export type DesktopUpdateFailureKind = 'check' | 'check-network' | 'download' | 'download-network' | 'install' | 'install-network' | 'stop-failed' | 'tasks-changed' | 'tasks-unavailable';
4
+ /** Public semantic preload fields consumed by the settings row. */
5
+ export interface DesktopUpdatePresentation {
6
+ readonly phase: 'idle' | 'checking' | 'available' | 'downloading' | 'verifying' | 'installing' | 'ready' | 'error';
7
+ readonly version?: string;
8
+ readonly percent?: number;
9
+ readonly failure?: DesktopUpdateFailureKind;
10
+ }
11
+ /** Optional carrier API; it cannot select artifacts or authorize installation. */
12
+ export interface DesktopUpdateBridge {
13
+ status(): Promise<DesktopUpdatePresentation>;
14
+ open(): Promise<void>;
15
+ subscribe(listener: (state: DesktopUpdatePresentation) => void): () => void;
16
+ }
17
+ /** Shared carrier status for the account row and collapsed sidebar badge. */
18
+ export interface DesktopUpdateView {
19
+ readonly presentation?: DesktopUpdatePresentation;
20
+ readonly failed: boolean;
21
+ readonly opening: boolean;
22
+ }
23
+ //# sourceMappingURL=desktop-update-bridge.d.ts.map
@@ -0,0 +1,17 @@
1
+ import type { DesktopUpdateBridge, DesktopUpdateView } from './desktop-update-bridge.ts';
2
+ /** Owns one preload subscription across both sidebar locations. */
3
+ export declare class DesktopUpdateSource {
4
+ private readonly bridge;
5
+ /** Framework-observed carrier status shared by both sidebar controls. */
6
+ readonly store: import("@deepseek-ai/dsh-client-store").SnapshotStore<DesktopUpdateView>;
7
+ private live;
8
+ private received;
9
+ private readonly unsubscribe;
10
+ /** @param bridge - Optional isolated Electron API, absent in ordinary browsers. */
11
+ constructor(bridge: DesktopUpdateBridge | undefined);
12
+ /** Invoke one user action; subsequent clicks join the shell-owned operation. */
13
+ open(): void;
14
+ /** Detach the carrier and ignore any pending status or action completion. */
15
+ dispose(): void;
16
+ }
17
+ //# sourceMappingURL=desktop-update-source.d.ts.map
@@ -2,13 +2,30 @@
2
2
  /** Simplified Chinese dictionary (the key-set source of truth). */
3
3
  export declare const zh: {
4
4
  trigger: string;
5
+ 'desktop.update.available': string;
6
+ 'desktop.update.checking': string;
7
+ 'desktop.update.progress': string;
8
+ 'desktop.update.verifying': string;
9
+ 'desktop.update.installing': string;
10
+ 'desktop.update.ready': string;
11
+ 'desktop.update.retry': string;
12
+ 'desktop.update.versionDetail': string;
13
+ 'desktop.update.downloadDetail': string;
14
+ 'desktop.update.checkFailed': string;
15
+ 'desktop.update.downloadFailed': string;
16
+ 'desktop.update.installFailed': string;
17
+ 'desktop.update.checkNetworkFailed': string;
18
+ 'desktop.update.downloadNetworkFailed': string;
19
+ 'desktop.update.installNetworkFailed': string;
20
+ 'desktop.update.stopFailed': string;
21
+ 'desktop.update.tasksChanged': string;
22
+ 'desktop.update.tasksUnavailable': string;
5
23
  title: string;
6
24
  close: string;
7
25
  openDocument: string;
8
26
  'openDocument.error': string;
9
27
  'general.nav': string;
10
28
  'connection.error': string;
11
- 'connection.retry': string;
12
29
  'connection.connecting': string;
13
30
  'connection.connected': string;
14
31
  'connection.reconnect': string;
@@ -19,13 +36,30 @@ export type SettingsKey = keyof typeof zh;
19
36
  /** English dictionary, checked complete against the zh key set. */
20
37
  export declare const en: {
21
38
  trigger: string;
39
+ 'desktop.update.available': string;
40
+ 'desktop.update.checking': string;
41
+ 'desktop.update.progress': string;
42
+ 'desktop.update.verifying': string;
43
+ 'desktop.update.installing': string;
44
+ 'desktop.update.ready': string;
45
+ 'desktop.update.retry': string;
46
+ 'desktop.update.versionDetail': string;
47
+ 'desktop.update.downloadDetail': string;
48
+ 'desktop.update.checkFailed': string;
49
+ 'desktop.update.downloadFailed': string;
50
+ 'desktop.update.installFailed': string;
51
+ 'desktop.update.checkNetworkFailed': string;
52
+ 'desktop.update.downloadNetworkFailed': string;
53
+ 'desktop.update.installNetworkFailed': string;
54
+ 'desktop.update.stopFailed': string;
55
+ 'desktop.update.tasksChanged': string;
56
+ 'desktop.update.tasksUnavailable': string;
22
57
  title: string;
23
58
  close: string;
24
59
  openDocument: string;
25
60
  'openDocument.error': string;
26
61
  'general.nav': string;
27
62
  'connection.error': string;
28
- 'connection.retry': string;
29
63
  'connection.connecting': string;
30
64
  'connection.connected': string;
31
65
  'connection.reconnect': string;
@@ -8,6 +8,7 @@
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 { DesktopUpdateView } from './desktop-update-bridge.ts';
11
12
  /** One nav row projected from a settings.section registration's options. */
12
13
  export interface SettingsSectionRow {
13
14
  id: string;
@@ -25,9 +26,13 @@ export interface SettingsOnboardingStep {
25
26
  * sources, while the reconnect command remains a plain callback.
26
27
  */
27
28
  export type SettingsRootInjected = {
29
+ /** Request the current shell-owned update action. */
30
+ openDesktopUpdate: () => void;
28
31
  /** Request a fresh logical generation and physical WebSocket immediately. */
29
32
  reconnect: () => void;
30
33
  hooks: {
34
+ /** Shared Electron status for both sidebar locations. */
35
+ desktopUpdate: HostObservable<DesktopUpdateView>;
31
36
  /** Connection-owned state for the current Host connection. */
32
37
  connectionState: HostObservable<ConnectionState | undefined>;
33
38
  /** settings.section ledger projected into ordered nav rows. */
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.5-rc.2",
4
+ "version": "0.1.6-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -46,21 +46,22 @@
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/react": "~18.3.1",
49
- "react": "^18.2.0",
50
49
  "clsx": "^2.0.0",
51
- "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
52
- "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.2",
53
- "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
54
- "@deepseek-ai/dsh-client-store": "^0.1.5-rc.2",
55
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.5-rc.2",
56
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
57
- "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.5-rc.2",
58
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
50
+ "react": "^18.2.0",
51
+ "@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.2",
52
+ "@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.2",
59
53
  "@deepseek-ai/cordis": "^4.0.2",
60
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2",
61
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
62
- "@deepseek-ai/dsh-client-ui-session": "^0.1.5-rc.2",
63
- "@deepseek-ai/dsh-settings": "^0.1.5-rc.2"
54
+ "@deepseek-ai/dsh-client-connection": "^0.1.6-alpha.2",
55
+ "@deepseek-ai/dsh-client-store": "^0.1.6-alpha.2",
56
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.2",
57
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.2",
58
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.6-alpha.2",
59
+ "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.6-alpha.2",
60
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.2",
61
+ "@deepseek-ai/dsh-settings": "^0.1.6-alpha.2",
62
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.2",
63
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.6-alpha.2",
64
+ "@deepseek-ai/dsh-remote-mock": "^0.1.6-alpha.2"
64
65
  },
65
66
  "files": [
66
67
  "lib/index.js",