diff --git a/packages/client/ui-settings-general/README.i18n.yaml b/packages/client/ui-settings-general/README.i18n.yaml
index c5b1574510..67988f04dd 100644
--- a/packages/client/ui-settings-general/README.i18n.yaml
+++ b/packages/client/ui-settings-general/README.i18n.yaml
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/ui-settings-general/README.md
-README.md: e06ecf5f36234f634cbff708f592fbe83c9f175e
-README.zh.md: 6eb54b49a7a7f1a55a6f9389c57f736533715185
+README.md: a500722e765e66e55efa83c9fc12c50756ef37d9
+README.zh.md: 28f3a39260c4ccaaf844d0d457fc85e39214bb7c
diff --git a/packages/client/ui-settings-general/README.md b/packages/client/ui-settings-general/README.md
index e06ecf5f36..a500722e76 100644
--- a/packages/client/ui-settings-general/README.md
+++ b/packages/client/ui-settings-general/README.md
@@ -29,7 +29,7 @@ Users reach the shell through the sidebar's bottom Settings control; feature plu
When the section navigation exceeds the panel's available height, the list scrolls independently of the settings content and keeps the Settings title fixed.
-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.
+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 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.
### The General section
diff --git a/packages/client/ui-settings-general/README.zh.md b/packages/client/ui-settings-general/README.zh.md
index 6eb54b49a7..28f3a39260 100644
--- a/packages/client/ui-settings-general/README.zh.md
+++ b/packages/client/ui-settings-general/README.zh.md
@@ -29,7 +29,7 @@ kind: "package-reference"
当分区导航超出面板可用高度时,列表独立于设置内容滚动,并保持「设置」标题固定。
-在 Desktop 中,账户行更新控件显示可用版本、进度、验证、就绪和持久重试反馈。预加载仅传递语义化阶段、版本、进度和失败类别;组件从当前 `settings` locale 解析全部可见与无障碍文案,并在应用内语言变化后同步更新。选择可用更新即开始下载;安装需要独立的壳拥有的确认。侧栏收起时,其顶部展开按钮以圆点显示相同状态。连接反馈通常优先展示,但壳报告正在安装时,预期的后端断开不能遮盖更新状态;失败后恢复连接反馈。两个控件共用一次载体订阅;浏览器代码不能选择安装包或授权安装。[Desktop 更新](../../../apps/desktop/README.zh.md)负责发布流程。
+在 Desktop 中,账户行更新控件显示可用版本、进度、验证、就绪和持久重试反馈。它与连接指示器共用 28px 高度、13px 圆角、14px 图标占位、4px 图文间距,以及 12px 中等字重文字和 18px 行高;更新状态的边框、填充和文字颜色独立定义。重试文字及其圆点与其他更新文案使用相同的品牌蓝色;数字下载进度不带省略号。预加载仅传递语义化阶段、版本、进度和失败类别;组件从当前 `settings` locale 解析全部可见与无障碍文案,并在应用内语言变化后同步更新。选择可用更新即开始下载;安装需要独立的壳拥有的确认。侧栏收起时,其顶部展开按钮以圆点显示相同状态。连接反馈通常优先展示,但壳报告正在安装时,预期的后端断开不能遮盖更新状态;失败后恢复连接反馈。两个控件共用一次载体订阅;浏览器代码不能选择安装包或授权安装。[Desktop 更新](../../../apps/desktop/README.zh.md)负责发布流程。
### 「通用」分区
diff --git a/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.module.css b/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.module.css
index a51a817ca4..e50a1a8a82 100644
--- a/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.module.css
+++ b/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.module.css
@@ -1,28 +1,30 @@
-/* The entry's seat is the Settings foot row, which bleeds 2px past the sidebar
- content box for the Settings hover surface. The New Session bar and panel
- rows instead keep a 2px inset inside that box, so this entry gives back both
- 2px offsets and ends on their trailing edge rather than flush against the
- sidebar edge. */
.indicator {
- display: inline-flex;
+ display: inline-grid;
+ grid-template-columns: 14px max-content;
align-items: center;
- gap: 6px;
+ column-gap: 4px;
flex: none;
- margin-inline-end: 4px;
- padding: 1px 8px;
+ height: 28px;
+ padding: 0 8px;
+ box-sizing: border-box;
border: 1px solid color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 20%, transparent);
- border-radius: 6px;
+ border-radius: 13px;
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;
+ font-family: inherit;
font-size: 12px;
- line-height: 20px;
+ font-weight: 500;
+ line-height: 18px;
+ text-align: left;
white-space: nowrap;
cursor: pointer;
}
-.indicator[data-error] {
- color: var(--dsw-alias-state-error-primary);
+.icon {
+ display: grid;
+ place-items: center;
+ width: 14px;
+ height: 14px;
}
.badge {
@@ -36,8 +38,8 @@
background: var(--dsw-alias-brand-primary-new-colorprimary-new-color);
}
-.badge[data-error], .errorDot { background: var(--dsw-alias-state-error-primary); }
-.errorDot { width: 6px; height: 6px; border-radius: 50%; corner-shape: round; }
+.badge[data-error] { background: var(--dsw-alias-state-error-primary); }
+.errorDot { background: currentColor; width: 6px; height: 6px; border-radius: 50%; corner-shape: round; }
.spinner { animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }
diff --git a/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.tsx b/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.tsx
index 4ee64a0f34..064b12196c 100644
--- a/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.tsx
+++ b/packages/client/ui-settings-general/src/client/DesktopUpdateIndicator.tsx
@@ -46,6 +46,7 @@ function updateCopy(state: DesktopUpdatePresentation, t: SettingsTranslate): { l
}
/**
+ * Render update status with connection-indicator geometry and brand-blue labels, including retries.
* @param props - Connection priority, sidebar width, and localized bridge-failure copy.
* @returns Desktop-only status beside the account button, or nothing in browsers.
*/
@@ -64,10 +65,12 @@ export function DesktopUpdateIndicator({ wide, hidden, t, view, onOpen }: {
const error = failed || state?.phase === 'error'
const busy = opening || (state !== undefined && BUSY_PHASES.has(state.phase))
return
-
diff --git a/packages/client/ui-settings-general/src/client/locales.ts b/packages/client/ui-settings-general/src/client/locales.ts
index 9608a28152..8640aa2cec 100644
--- a/packages/client/ui-settings-general/src/client/locales.ts
+++ b/packages/client/ui-settings-general/src/client/locales.ts
@@ -5,7 +5,7 @@ export const zh = {
'trigger': '设置',
'desktop.update.available': '新版本',
'desktop.update.checking': '正在检查更新…',
- 'desktop.update.progress': '{percent}%…',
+ 'desktop.update.progress': '{percent}%',
'desktop.update.verifying': '正在校验更新文件…',
'desktop.update.installing': '正在准备重启…',
'desktop.update.ready': '安装并重启',
@@ -45,7 +45,7 @@ export const en = {
'trigger': 'Settings',
'desktop.update.available': 'Update',
'desktop.update.checking': 'Checking for updates…',
- 'desktop.update.progress': '{percent}%…',
+ 'desktop.update.progress': '{percent}%',
'desktop.update.verifying': 'Verifying update files…',
'desktop.update.installing': 'Preparing to restart…',
'desktop.update.ready': 'Install and Restart',
diff --git a/packages/client/ui-settings-general/tests/desktop-update.client.spec.tsx b/packages/client/ui-settings-general/tests/desktop-update.client.spec.tsx
index 316204370d..704dd28760 100644
--- a/packages/client/ui-settings-general/tests/desktop-update.client.spec.tsx
+++ b/packages/client/ui-settings-general/tests/desktop-update.client.spec.tsx
@@ -71,11 +71,10 @@ it('keeps the newest event, hides for connection priority, and invokes only the
expect(screen.queryByRole('button')).toBeNull()
await f.emit({ phase: 'downloading', version: available.version, percent: 58 })
f.view.rerender()
- fireEvent.click(screen.getByRole('button', { name: '58%…' }))
+ fireEvent.click(screen.getByRole('button', { name: '58%' }))
expect(f.open).toHaveBeenCalledOnce()
await f.emit({ phase: 'error', version: available.version, failure: 'download' })
const retry = screen.getByRole('button', { name: '重试更新' })
- expect(retry.getAttribute('data-error')).toBe('true')
fireEvent.focus(retry)
expect((await screen.findByRole('tooltip')).textContent).toBe('下载更新失败,请重试。')
f.view.rerender()
@@ -90,7 +89,6 @@ it('keeps bridge failures actionable and ignores status completion after unmount
f.open.mockRejectedValueOnce(new Error('IPC unavailable'))
fireEvent.click(screen.getByRole('button', { name: '重试更新' }))
await act(async () => {})
- expect(screen.getByRole('button').getAttribute('data-error')).toBe('true')
f.view.unmount()
const late = fixture()
late.view.unmount()
@@ -143,11 +141,11 @@ it('shows fallback progress and error details when the shell omits optional fiel
const f = fixture()
try {
await f.emit({ phase: 'downloading' })
- const progress = screen.getByRole('button', { name: '0%…' })
+ const progress = screen.getByRole('button', { name: '0%' })
fireEvent.focus(progress)
- expect((await screen.findByRole('tooltip')).textContent).toBe('0%…')
+ expect((await screen.findByRole('tooltip')).textContent).toBe('0%')
await f.emit({ phase: 'downloading', version: '1.0.1' })
- fireEvent.focus(screen.getByRole('button', { name: '0%…' }))
+ fireEvent.focus(screen.getByRole('button', { name: '0%' }))
expect((await screen.findByRole('tooltip')).textContent).toContain('1.0.1')
await f.emit({ phase: 'error' })
fireEvent.focus(screen.getByRole('button', { name: '重试更新' }))