dsh-session-cloud 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/lib/client.js +6 -1
- package/lib/client.js.map +2 -2
- package/lib/dsh-types.d.ts +21 -0
- package/lib/index.js +16 -0
- package/lib/sync/restore.d.ts +18 -1
- package/lib/sync/restore.js +68 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.7
|
|
4
|
+
|
|
5
|
+
### 修复
|
|
6
|
+
|
|
7
|
+
- **恢复成功后侧边栏出现工作区但会话列表为空,需刷新页面才显示**
|
|
8
|
+
根因:恢复只写了会话文件、更新了 workspace 成员关系,但 web 会话列表
|
|
9
|
+
(`useSessions` 的 `list.byId`)靠 Host 的 `api-session/added` 事件增量
|
|
10
|
+
更新;插件没有发这个事件,所以新会话要等刷新页面触发全量 `list()` 才
|
|
11
|
+
出现(工作区行与会员行是两个独立通道)。
|
|
12
|
+
修复:恢复落位成功后 emit `api-session/added`(携带 sessionId / 有效
|
|
13
|
+
cwd / running / blank 等列表所需字段),侧边栏立即出现新会话,无需刷新。
|
|
14
|
+
|
|
15
|
+
- **跨机恢复把会话误判「本地已存在」、无法重新落位**
|
|
16
|
+
根因:`existsLocal` 只看「本地持久化里有没有这个 session id」。旧版
|
|
17
|
+
相对路径 bug 把会话写到了错误 cwd,文件在盘上却没挂到工作区,于是被
|
|
18
|
+
判「本地已存在」且恢复弹窗禁用勾选,会话卡死无法归位。
|
|
19
|
+
修复:
|
|
20
|
+
- `existsLocal` 收紧为「本地有文件 **且** 已挂载到工作区」(以
|
|
21
|
+
`workspaceRegistry.list()` 的 `sessionIds` 并集判定);无工作区注册表
|
|
22
|
+
的环境退回旧语义,不回归 headless 场景;
|
|
23
|
+
- 重新恢复落位后清理同 id 残留在其它 cwd 的旧副本,避免同 id 出现两份
|
|
24
|
+
(一份在工作区、一份在未分组)。
|
|
25
|
+
|
|
26
|
+
## 0.1.6
|
|
27
|
+
|
|
28
|
+
### 修复
|
|
29
|
+
|
|
30
|
+
- **恢复弹窗手动路径输入框宽度随输入漂移**
|
|
31
|
+
根因:`flex: 1 1 0%` 在 dsh 渲染环境里对 `<input>` 的宽度分配不稳定,
|
|
32
|
+
输入内容后宽度会变长(或缩窄)。
|
|
33
|
+
修复:改为显式 `flex: 1 1 auto; width: 100%; max-width: 100%;
|
|
34
|
+
box-sizing: border-box`,不依赖 flex 对 input 的宽度分配。
|
|
35
|
+
|
|
36
|
+
- **跨机恢复支持相对路径与 `~` 展开(落位路径规范化)**
|
|
37
|
+
根因:恢复时用户改选的落位路径只接受绝对路径,输入 `my-app` 这类
|
|
38
|
+
相对路径时按进程工作目录解析导致「目标目录不存在」失败。
|
|
39
|
+
修复:新增 `normalizeTargetCwd`——`~` / `~/…` 展开到本机 home,相对路径
|
|
40
|
+
按本机 home 解析,绝对路径原样;两台机器 home 不同也能各自落到
|
|
41
|
+
home 下的同名目录,并学习来源→本机的路径映射。
|
|
42
|
+
|
|
3
43
|
## 0.1.5
|
|
4
44
|
|
|
5
45
|
### 修复
|
package/lib/client.js
CHANGED
|
@@ -1811,8 +1811,13 @@ var CARD_CSS = `
|
|
|
1811
1811
|
cursor: pointer;
|
|
1812
1812
|
}
|
|
1813
1813
|
.dsh-cloud-sync-restore__manual {
|
|
1814
|
-
flex
|
|
1814
|
+
/* \u663E\u5F0F\u6491\u6EE1\u5355\u5143\u683C + \u6536\u7F29\u4E0A\u9650\uFF1A\u4E0D\u4F9D\u8D56 flex \u5BF9 input \u7684\u5BBD\u5EA6\u5206\u914D
|
|
1815
|
+
\uFF08flex: 1 1 0% \u5728\u6E32\u67D3\u73AF\u5883\u91CC\u4F1A\u968F\u8F93\u5165\u5185\u5BB9\u6F02\u79FB\u2014\u2014\u53D8\u957F\u6216\u7F29\u7A84\uFF09 */
|
|
1816
|
+
flex: 1 1 auto;
|
|
1817
|
+
width: 100%;
|
|
1815
1818
|
min-width: 0;
|
|
1819
|
+
max-width: 100%;
|
|
1820
|
+
box-sizing: border-box;
|
|
1816
1821
|
height: 26px;
|
|
1817
1822
|
padding: 0 8px;
|
|
1818
1823
|
border: 1px solid var(--dsw-alias-border-l2);
|
package/lib/client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client/index.ts", "../src/client/CloudSyncCard.tsx", "../src/client/pure.ts", "../src/client/CloudSyncOverlay.tsx", "../src/client/RestoreDialog.tsx", "../src/client/cloud-sync-card-controller.ts", "../src/client/locales.ts", "../src/client/styles.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * dsh-cloud-sync \u63D2\u4EF6 Client \u534A\u5165\u53E3\uFF08\u6D4F\u89C8\u5668\u4FA7\uFF09\u3002\n * named exports `inject` / `apply`\uFF0C\u65E0 default export\uFF08dsh \u51FD\u6570\u63D2\u4EF6\u7EA6\u5B9A\uFF09\u3002\n * \u4EA7\u7269\u7ECF scripts/build-client.mjs \u6253\u5305\u4E3A __ModuleLoader__ \u7684 lazy-CJS factory\u3002\n */\n\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\n// \u4EE5\u4E0B\u5747\u4E3A\u7C7B\u578B import\uFF08\u6784\u5EFA\u65F6\u88AB\u64E6\u9664\uFF0C\u4E0D\u89E6\u53D1 bundle-purity gate\uFF09\uFF1A\nimport type {} from '@deepseek-ai/dsh-client-locale/client' // ctx.locale \u5408\u5E76\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client' // ctx.settingsScope \u5408\u5E76\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client' // ctx.slots \u5408\u5E76\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client' // settings.plugin.item \u69FD\u7684 SlotMap \u5408\u5E76\nimport { CloudSyncCard } from './CloudSyncCard.tsx'\nimport { CloudSyncOverlay } from './CloudSyncOverlay.tsx'\nimport { CLOUD_SYNC_NS, CLOUD_SYNC_STATUS_NS, CloudSyncCardController, type DirectoryPickerLike } from './cloud-sync-card-controller.ts'\nimport { en, zh } from './locales.ts'\nimport { installCardStyles } from './styles.ts'\n\n/** \u672C\u63D2\u4EF6\u7684\u5B57\u5178\u547D\u540D\u7A7A\u95F4\u3002 */\nconst LOCALE_NS = 'settings.cloudSync'\n\n/** \u9700\u8981\u7684 cordis \u670D\u52A1\uFF08fiber inject\uFF09\u3002 */\nexport const inject = ['slots', 'locale', 'settingsScope']\n\n/**\n * \u6302\u8F7D\u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361\uFF1A\u6CE8\u518C\u5B57\u5178\u3001\u521B\u5EFA controller\u3001\u5411 settings.plugin.item \u69FD\u6CE8\u518C\u5361\u7247\u3002\n * @param ctx - \u6D4F\u89C8\u5668\u63D2\u4EF6\u4E0A\u4E0B\u6587\u3002\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(LOCALE_NS, { zh, en }), 'cloud-sync: dictionaries')\n // \u53CC namespace\uFF1A\u914D\u7F6E\uFF08'cloud-sync'\uFF0CHost \u4E0D\u5199\uFF09\u4E0E\u72B6\u6001/\u89E6\u53D1\uFF08'cloud-sync-status'\uFF0CHost \u9AD8\u9891\u56DE\u5199\uFF09\n // \u76EE\u5F55\u9009\u62E9\u5668\u662F\u53EF\u9009 Remote \u80FD\u529B\uFF08'remote' \u670D\u52A1\u7F3A\u5931\u65F6\u6062\u590D\u5BF9\u8BDD\u6846\u964D\u7EA7\u4E3A\u624B\u52A8\u8F93\u5165\uFF09\n const directoryPicker = (ctx.get('remote') as { directoryPicker?: DirectoryPickerLike } | undefined)?.directoryPicker\n const controller = new CloudSyncCardController(\n ctx.settingsScope.bind({ namespace: CLOUD_SYNC_NS }),\n ctx.settingsScope.bind({ namespace: CLOUD_SYNC_STATUS_NS }),\n directoryPicker,\n )\n ctx.effect(() => () => { controller.dispose() }, 'cloud-sync: card controller')\n installCardStyles()\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n key: CLOUD_SYNC_NS,\n locale: LOCALE_NS,\n inject: () => controller.inject(),\n }, CloudSyncCard))\n // M4\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846\u4E0E\u542F\u52A8\u901A\u77E5\u6302\u5728\u6839\u4F5C\u7528\u57DF\u6D6E\u5C42\u69FD\uFF08\u8BBE\u7F6E\u9875\u5916\u4E5F\u80FD\u6253\u5F00\u5BF9\u8BDD\u6846\uFF09\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'cloud-sync-overlay',\n order: 100,\n locale: LOCALE_NS,\n inject: () => controller.inject(),\n }, CloudSyncOverlay))\n}\n", "/**\n * \u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361\u7EC4\u4EF6\uFF08M3 \u56DB\u533A\uFF1A\u670D\u52A1\u5668\u8FDE\u63A5 / \u72B6\u6001\u884C / \u540C\u6B65\u65B9\u5F0F / \u64CD\u4F5C\uFF09\u3002\n * \u300C\u4ECE\u4E91\u7AEF\u6062\u590D\u4F1A\u8BDD\u300D\u6309\u94AE\u4E0E\u300C\u8DEF\u5F84\u6620\u5C04\u300D\u533A\u5C5E M4\uFF0C\u672C\u5361\u7247\u4E0D\u5B9E\u73B0\u3002\n * \u5361\u7247 chrome \u81EA\u7ED8\uFF08ui-settings-plugins \u7684 PluginCard/CardForm/fields \u662F\u8BE5\u5305\u5185\u90E8\u5B9E\u73B0\uFF0C\n * \u8DE8\u5305\u503C import \u4F1A\u89E6\u53D1 client bundle-purity gate\uFF09\uFF0C\u6837\u5F0F\u89C1 styles.ts\u3002\n * \u5168\u90E8\u53EF\u89C1\u6587\u6848\u7ECF props.t \u67E5\u5B57\u5178\uFF08locales.ts\uFF09\uFF0C\u672C\u6587\u4EF6\u4E0D\u51FA\u73B0\u81EA\u7136\u8BED\u8A00\u5B57\u9762\u91CF\u3002\n */\n\nimport { useState } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\n// \u7C7B\u578B import\uFF08\u88AB\u64E6\u9664\uFF09\uFF1A\u62C9\u5165 settings.plugin.item \u69FD\u7684 SlotMap \u5408\u5E76\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport type { CloudSyncCardFace } from './cloud-sync-card-controller.ts'\nimport { errorKeyOf, type RelativeTimeSpec } from './pure.ts'\n\n/** \u6E32\u67D3\u5668\u7EC4\u88C5\u540E\u7684\u7EC4\u4EF6 props\uFF1A\u8FD0\u884C\u65F6\u4EFD + \u5B57\u5178 t \u5EA7\u4F4D + controller \u6CE8\u5165\u4EFD\u3002 */\nexport type CloudSyncCardProps =\n PropsRuntime<'settings.plugin.item'>\n & PropsLocale<'settings.cloudSync'>\n & InjectFace<CloudSyncCardFace>\n\n/** \u663E\u793A/\u9690\u85CF\u5BC6\u7801\u7684\u56FE\u6807\u5B57\u5F62\uFF1A\u7B26\u53F7\u800C\u975E\u81EA\u7136\u8BED\u8A00\uFF0C\u4E0D\u8FDB\u5B57\u5178\u3002 */\nconst EYE_GLYPH = '\uD83D\uDC41'\n/** \u5220\u9664\u6620\u5C04\u7684\u56FE\u6807\u5B57\u5F62\uFF1A\u540C\u4E0A\uFF0C\u7B26\u53F7\u4E0D\u8FDB\u5B57\u5178\u3002 */\nconst TRASH_GLYPH = '\uD83D\uDDD1'\n\n/** secret \u5DF2\u914D\u7F6E\u65F6\u7684\u5360\u4F4D\u7B26\uFF0801 \u00A74\uFF1A\u5199\u5165\u540E\u663E\u793A\u5360\u4F4D\u7B26\uFF0C\u503C\u672C\u8EAB\u6C38\u4E0D\u56DE\u663E\uFF09\u3002 */\nconst SECRET_MASK = '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022'\n\n/**\n * \u544A\u8BC9\u6D4F\u89C8\u5668\u4E0E\u5BC6\u7801\u7BA1\u7406\u5668\u300C\u8FD9\u4E0D\u662F\u767B\u5F55\u51ED\u636E\u300D\uFF1AChrome \u5BF9 autocomplete=\"off\"\n * \u7684 password \u5B57\u6BB5\u7167\u6837\u5F39\u4FDD\u5B58/\u66F4\u65B0\u63D0\u793A\uFF0Cnew-password \u624D\u662F\u5B83\u8BA4\u7684\u4FE1\u53F7\uFF1B\n * data-* \u5206\u522B\u538B\u4F4F LastPass / 1Password / Bitwarden\u3002\n */\nconst NOT_A_CREDENTIAL = {\n autoComplete: 'new-password',\n 'data-lpignore': 'true',\n 'data-1p-ignore': 'true',\n 'data-bwignore': 'true',\n 'data-form-type': 'other',\n} as const\n\n/** \u628A\u76F8\u5BF9\u65F6\u95F4\u89C4\u683C\u7FFB\u8BD1\u6210\u6587\u6848\uFF08\u6062\u590D\u5BF9\u8BDD\u6846\u590D\u7528\uFF09\u3002 */\nexport function relativeText(t: TranslateNS<'settings.cloudSync'>, spec: RelativeTimeSpec): string {\n return spec.key === 'time.justNow' ? t('time.justNow') : t(spec.key, { count: spec.count })\n}\n\n/**\n * \u6E32\u67D3\u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361\u3002\n * @param props - \u5B57\u5178 t\u3001\u5361\u7247\u5FEB\u7167\uFF08useCloudSyncCard\uFF09\u4E0E\u8868\u5355\u52A8\u4F5C\u3002\n * @returns \u5361\u7247\uFF1Bnamespace \u4E0D\u53EF\u7528\u65F6\u4E0D\u6E32\u67D3\uFF08Host \u672A serve \u5373 UI \u65E0\u75D5\u8FF9\uFF09\u3002\n */\nexport function CloudSyncCard(props: CloudSyncCardProps) {\n const { t } = props\n const state = props.useCloudSyncCard(snapshot => snapshot)\n // \u663E\u793A/\u9690\u85CF\u5BC6\u7801\u662F\u7EAF\u9605\u8BFB\u59FF\u6001\uFF0C\u7EC4\u4EF6\u672C\u5730\u72B6\u6001\u5373\u53EF\n const [showToken, setShowToken] = useState(false)\n const [showPassphrase, setShowPassphrase] = useState(false)\n if (!state.available) return null\n const disabled = !state.writable\n const line = state.statusLine\n return (\n <li className=\"dsh-cloud-sync-card\">\n <div className=\"dsh-cloud-sync-card__header\">\n <span className=\"dsh-cloud-sync-card__name\">{t('title')}</span>\n <span className=\"dsh-cloud-sync-card__description\">{t('description')}</span>\n </div>\n <div className=\"dsh-cloud-sync-card__body\">\n <section className=\"dsh-cloud-sync-card__section\">\n <h3 className=\"dsh-cloud-sync-card__section-title\">{t('section.connection')}</h3>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-server-url\">\n {t('field.serverUrl')}\n </label>\n </div>\n <input\n id=\"dsh-cloud-sync-server-url\"\n className=\"dsh-cloud-sync-card__input\"\n type=\"text\"\n value={state.serverUrlDraft}\n placeholder={t('field.serverUrl.placeholder')}\n disabled={disabled}\n {...state.serverUrlInvalid ? { 'aria-invalid': true } : {}}\n onChange={(event) => { props.editServerUrl(event.target.value) }}\n onBlur={() => { props.commitServerUrl() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitServerUrl()\n }}\n />\n {state.serverUrlInvalid\n ? <p className=\"dsh-cloud-sync-card__error\">{t('invalid.serverUrl')}</p>\n : null}\n </div>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-username\">\n {t('field.username')}\n </label>\n </div>\n <input\n id=\"dsh-cloud-sync-username\"\n className=\"dsh-cloud-sync-card__input\"\n type=\"text\"\n autoComplete=\"off\"\n value={state.usernameDraft}\n placeholder={state.usernameDraft === '' ? t('field.username.hint') : undefined}\n disabled={disabled}\n onChange={(event) => { props.editUsername(event.target.value) }}\n onBlur={() => { props.commitUsername() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitUsername()\n }}\n />\n </div>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-token\">\n {t('field.token')}\n </label>\n <span\n className={state.tokenConfigured\n ? 'dsh-cloud-sync-card__badge'\n : 'dsh-cloud-sync-card__badge dsh-cloud-sync-card__badge--muted'}\n >\n {t(state.tokenConfigured ? 'field.configured' : 'field.unconfigured')}\n </span>\n </div>\n <div className=\"dsh-cloud-sync-card__input-row\">\n <input\n id=\"dsh-cloud-sync-token\"\n className=\"dsh-cloud-sync-card__input\"\n type={showToken ? 'text' : 'password'}\n {...NOT_A_CREDENTIAL}\n placeholder={state.tokenConfigured ? SECRET_MASK : undefined}\n value={state.tokenDraft}\n disabled={disabled}\n onChange={(event) => { props.editToken(event.target.value) }}\n onBlur={() => { props.commitToken() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitToken()\n }}\n />\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__icon-button\"\n aria-label={t(showToken ? 'field.secret.hide' : 'field.secret.show')}\n disabled={disabled}\n onClick={() => { setShowToken(!showToken) }}\n >\n {EYE_GLYPH}\n </button>\n </div>\n </div>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-passphrase\">\n {t('field.passphrase')}\n </label>\n <span\n className={state.passphraseConfigured\n ? 'dsh-cloud-sync-card__badge'\n : 'dsh-cloud-sync-card__badge dsh-cloud-sync-card__badge--muted'}\n >\n {t(state.passphraseConfigured ? 'field.configured' : 'field.unconfigured')}\n </span>\n </div>\n <div className=\"dsh-cloud-sync-card__input-row\">\n <input\n id=\"dsh-cloud-sync-passphrase\"\n className=\"dsh-cloud-sync-card__input\"\n type={showPassphrase ? 'text' : 'password'}\n {...NOT_A_CREDENTIAL}\n placeholder={state.passphraseConfigured ? SECRET_MASK : undefined}\n value={state.passphraseDraft}\n disabled={disabled}\n onChange={(event) => { props.editPassphrase(event.target.value) }}\n onBlur={() => { props.commitPassphrase() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitPassphrase()\n }}\n />\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__icon-button\"\n aria-label={t(showPassphrase ? 'field.secret.hide' : 'field.secret.show')}\n disabled={disabled}\n onClick={() => { setShowPassphrase(!showPassphrase) }}\n >\n {EYE_GLYPH}\n </button>\n </div>\n </div>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-device-name\">\n {t('field.deviceName')}\n </label>\n </div>\n <input\n id=\"dsh-cloud-sync-device-name\"\n className=\"dsh-cloud-sync-card__input\"\n type=\"text\"\n value={state.deviceNameDraft}\n disabled={disabled}\n {...state.deviceNameInvalid ? { 'aria-invalid': true } : {}}\n onChange={(event) => { props.editDeviceName(event.target.value) }}\n onBlur={() => { props.commitDeviceName() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitDeviceName()\n }}\n />\n <p className={state.deviceNameInvalid\n ? 'dsh-cloud-sync-card__error'\n : 'dsh-cloud-sync-card__hint'}\n >\n {t(state.deviceNameInvalid ? 'invalid.deviceName' : 'field.deviceName.hint')}\n </p>\n </div>\n <div className=\"dsh-cloud-sync-card__row-end\">\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button\"\n disabled={disabled || state.testPending || !state.canTest}\n onClick={() => { props.testConnection() }}\n >\n {t(state.testPending ? 'action.testing' : 'action.testConnection')}\n </button>\n </div>\n </section>\n <section className=\"dsh-cloud-sync-card__section\">\n {line.kind === 'unconfigured'\n ? (\n <p className=\"dsh-cloud-sync-card__status dsh-cloud-sync-card__status--muted\">\n <span className=\"dsh-cloud-sync-card__dot\" />\n {t('status.unconfigured')}\n </p>\n )\n : null}\n {line.kind === 'failed'\n ? (\n <p\n className=\"dsh-cloud-sync-card__status dsh-cloud-sync-card__status--error\"\n title={line.detail === '' ? undefined : line.detail}\n >\n <span className=\"dsh-cloud-sync-card__dot\" data-state=\"error\" />\n {t('status.disconnected', { reason: t(errorKeyOf(line.error)) })}\n </p>\n )\n : null}\n {line.kind === 'connected'\n ? (\n <p\n className=\"dsh-cloud-sync-card__status\"\n title={line.warn\n ? (line.error === 'deviceConflict'\n ? t('error.deviceConflict')\n : line.detail === '' ? undefined : line.detail)\n : undefined}\n >\n <span className=\"dsh-cloud-sync-card__dot\" data-state={line.warn ? 'warning' : 'done'} />\n {line.warn ? <span className=\"dsh-cloud-sync-card__warn\">{t('status.warnMark')}</span> : null}\n {t('status.connected', { count: line.count })}\n {line.lastSync !== null\n ? ` \u00B7 ${t('status.lastSync', { time: relativeText(t, line.lastSync) })}`\n : null}\n </p>\n )\n : null}\n </section>\n <section className=\"dsh-cloud-sync-card__section\">\n <h3 className=\"dsh-cloud-sync-card__section-title\">{t('section.sync')}</h3>\n <div className=\"dsh-cloud-sync-card__switch-row\">\n <span className=\"dsh-cloud-sync-card__switch-label\" id=\"dsh-cloud-sync-auto-upload-label\">\n {t('field.autoUpload')}\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={state.autoUpload}\n aria-labelledby=\"dsh-cloud-sync-auto-upload-label\"\n className=\"dsh-cloud-sync-card__switch\"\n data-on={state.autoUpload}\n disabled={disabled}\n onClick={() => { props.toggleAutoUpload() }}\n >\n <span className=\"dsh-cloud-sync-card__switch-knob\" />\n </button>\n <span className=\"dsh-cloud-sync-card__hint\">{t('field.autoUpload.hint')}</span>\n </div>\n <div className=\"dsh-cloud-sync-card__switch-row\">\n <span className=\"dsh-cloud-sync-card__switch-label\" id=\"dsh-cloud-sync-check-on-startup-label\">\n {t('field.checkOnStartup')}\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={state.checkOnStartup}\n aria-labelledby=\"dsh-cloud-sync-check-on-startup-label\"\n className=\"dsh-cloud-sync-card__switch\"\n data-on={state.checkOnStartup}\n disabled={disabled}\n onClick={() => { props.toggleCheckOnStartup() }}\n >\n <span className=\"dsh-cloud-sync-card__switch-knob\" />\n </button>\n <span className=\"dsh-cloud-sync-card__hint\">{t('field.checkOnStartup.hint')}</span>\n </div>\n </section>\n <section className=\"dsh-cloud-sync-card__section\">\n <h3 className=\"dsh-cloud-sync-card__section-title\">{t('section.actions')}</h3>\n {state.syncResult !== null\n ? (\n <p className=\"dsh-cloud-sync-card__sync-result\" role=\"status\">\n {t('sync.result', { ok: state.syncResult.ok, failed: state.syncResult.failed })}\n {state.syncResult.failed > 0 && state.syncResult.error !== ''\n ? ` \u2014 ${state.syncResult.error}`\n : null}\n </p>\n )\n : null}\n <div className=\"dsh-cloud-sync-card__row-end\">\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button\"\n disabled={disabled || state.catalogPending}\n onClick={() => { props.openRestore() }}\n >\n {t('action.restore')}\n </button>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button dsh-cloud-sync-card__button--primary\"\n disabled={disabled || state.syncPending || !state.canSync}\n onClick={() => { props.syncAll() }}\n >\n {t(state.syncPending ? 'action.syncing' : 'action.syncAll')}\n </button>\n </div>\n </section>\n <section className=\"dsh-cloud-sync-card__section\">\n <h3 className=\"dsh-cloud-sync-card__section-title\">{t('section.mappings')}</h3>\n {state.mappings.length === 0\n ? <p className=\"dsh-cloud-sync-card__hint\">{t('mapping.empty')}</p>\n : (\n <ul className=\"dsh-cloud-sync-card__mappings\">\n {state.mappings.map((pair) => (\n <li key={pair.from} className=\"dsh-cloud-sync-card__mapping\">\n <span className=\"dsh-cloud-sync-card__mapping-paths\" title={`${pair.from} \u2192 ${pair.to}`}>\n {pair.from}\n <span className=\"dsh-cloud-sync-card__mapping-arrow\">\u2192</span>\n {pair.to}\n </span>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__icon-button\"\n aria-label={t('mapping.delete')}\n disabled={disabled}\n onClick={() => { props.deleteMapping(pair.from) }}\n >\n {TRASH_GLYPH}\n </button>\n </li>\n ))}\n </ul>\n )}\n </section>\n </div>\n </li>\n )\n}\n", "/**\n * \u8BBE\u7F6E\u5361\u7EAF\u51FD\u6570\u5C42\uFF1A\u5B57\u6BB5\u6821\u9A8C\u3001\u76F8\u5BF9\u65F6\u95F4\u89C4\u683C\u3001\u72B6\u6001\u884C\u6295\u5F71\u3002\n * \u53EA\u4F9D\u8D56\u5165\u53C2\uFF0C\u4E0D\u89E6\u78B0 React / DOM / settings scope\uFF0C\u4F9B node:test \u76F4\u63A5\u5355\u6D4B\n * \uFF08client \u5355\u6D4B\u53EA\u6D4B\u7EAF\u51FD\u6570\uFF0C\u4E0D import tsx \u7EC4\u4EF6\uFF09\u3002\n */\n\n/** \u670D\u52A1\u5668\u5730\u5740\u6821\u9A8C\uFF1A\u7A7A\u4E32\u89C6\u4E3A\u300C\u672A\u586B\u300D\uFF08\u4E0D\u7B97\u975E\u6CD5\uFF09\uFF0C\u975E\u7A7A\u5FC5\u987B\u662F\u5408\u6CD5\u7684 http(s) URL\u3002 */\nexport function isValidServerUrl(text: string): boolean {\n if (text === '') return true\n let url: URL\n try {\n url = new URL(text)\n } catch {\n return false\n }\n return url.protocol === 'http:' || url.protocol === 'https:'\n}\n\n/** \u8BBE\u5907\u540D\u6821\u9A8C\uFF1A\u7A7A\u4E32 = \u56DE\u9000\u672C\u673A\u4E3B\u673A\u540D\uFF1B\u5426\u5219\u4EC5\u9650 [a-zA-Z0-9-_]\uFF0C\u957F\u5EA6 \u2264 32\u3002 */\nexport function isValidDeviceName(text: string): boolean {\n return text === '' || /^[a-zA-Z0-9-_]{1,32}$/.test(text)\n}\n\n/** \u76F8\u5BF9\u65F6\u95F4\u6587\u6848\u89C4\u683C\uFF1A\u6E32\u67D3\u5C42\u636E key\uFF08+ count\uFF09\u67E5\u5B57\u5178\uFF0C\u672C\u5C42\u4E0D\u51FA\u73B0\u4EFB\u4F55\u6587\u6848\u3002 */\nexport type RelativeTimeSpec =\n | { key: 'time.justNow' }\n | { key: 'time.minutesAgo' | 'time.hoursAgo' | 'time.daysAgo'; count: number }\n\nconst MINUTE_MS = 60_000\nconst HOUR_MS = 3_600_000\nconst DAY_MS = 86_400_000\n\n/** \u628A epoch \u6BEB\u79D2\u65F6\u95F4\u6233\u6295\u5F71\u4E3A\u76F8\u5BF9\u65F6\u95F4\u89C4\u683C\uFF1Bat <= 0\uFF08\u4ECE\u672A\u540C\u6B65\uFF09\u8FD4\u56DE null\uFF08\u4E0D\u663E\u793A\u8BE5\u9879\uFF09\u3002 */\nexport function relativeTime(at: number, now: number): RelativeTimeSpec | null {\n if (at <= 0) return null\n const diff = Math.max(0, now - at)\n if (diff < MINUTE_MS) return { key: 'time.justNow' }\n if (diff < HOUR_MS) return { key: 'time.minutesAgo', count: Math.max(1, Math.round(diff / MINUTE_MS)) }\n if (diff < DAY_MS) return { key: 'time.hoursAgo', count: Math.max(1, Math.round(diff / HOUR_MS)) }\n return { key: 'time.daysAgo', count: Math.max(1, Math.round(diff / DAY_MS)) }\n}\n\n/** Host \u5199\u5165\u7684\u72B6\u6001\u673A\u76F8\u4F4D\uFF08docs/02 \u00A79 \u4E0B\u534A\u8868\uFF09\u3002 */\nexport type StatusPhase = 'unconfigured' | 'connected' | 'failed'\n\n/** Host \u5199\u5165\u7684\u9519\u8BEF\u7801\uFF1B'' \u8868\u793A\u65E0\u9519\u8BEF\u3002 */\nexport type StatusError =\n '' | 'unreachable' | 'tokenInvalid' | 'userMismatch' | 'passphraseMismatch' | 'deviceConflict' | 'syncFailed'\n\n/** \u72B6\u6001\u884C\u6295\u5F71\u5165\u53C2\u3002 */\nexport interface StatusLineInput {\n phase: StatusPhase\n error: StatusError\n errorDetail: string\n cloudCount: number\n lastSyncAt: number\n now: number\n}\n\n/** \u72B6\u6001\u884C\u6295\u5F71\u7ED3\u679C\uFF1A\u6E32\u67D3\u5C42\u6309 kind \u5206\u652F\u53D6\u6587\u6848\uFF0C\u9519\u8BEF\u7801\u7ECF errorKeyOf \u6620\u5C04\u5B57\u5178 key\u3002 */\nexport type StatusLine =\n | { kind: 'unconfigured' }\n | { kind: 'failed'; error: StatusError; detail: string }\n | {\n kind: 'connected'\n count: number\n lastSync: RelativeTimeSpec | null\n /** \u8FDE\u7EED\u63A8\u9001\u5931\u8D25 / \u8BBE\u5907\u540D\u51B2\u7A81\u65F6\u524D\u7F00\u544A\u8B66\u6807\u8BB0\uFF0801 \u00A75.2\u3001\u00A76\uFF09\u3002 */\n warn: boolean\n error: StatusError\n detail: string\n }\n\n/** \u628A Host \u72B6\u6001\u5B57\u6BB5\u6295\u5F71\u4E3A\u4E00\u884C\u5F0F\u8FD0\u884C\u6982\u51B5\u3002 */\nexport function projectStatusLine(input: StatusLineInput): StatusLine {\n if (input.phase === 'unconfigured') return { kind: 'unconfigured' }\n if (input.phase === 'failed') return { kind: 'failed', error: input.error, detail: input.errorDetail }\n return {\n kind: 'connected',\n count: input.cloudCount,\n lastSync: relativeTime(input.lastSyncAt, input.now),\n warn: input.error === 'syncFailed' || input.error === 'deviceConflict',\n error: input.error,\n detail: input.errorDetail,\n }\n}\n\n/** \u9519\u8BEF\u7801 \u2192 \u6587\u6848\u5B57\u5178 key\uFF1B\u7A7A\u7801\u4E0E syncFailed\uFF08failed \u76F8\u4F4D\u4E0B\u7684\u515C\u5E95\uFF09\u843D\u5230 error.unknown\u3002 */\nexport function errorKeyOf(\n error: StatusError,\n): 'error.unreachable' | 'error.tokenInvalid' | 'error.userMismatch' | 'error.passphraseMismatch' | 'error.deviceConflict' | 'error.unknown' {\n switch (error) {\n case 'unreachable': return 'error.unreachable'\n case 'tokenInvalid': return 'error.tokenInvalid'\n case 'userMismatch': return 'error.userMismatch'\n case 'passphraseMismatch': return 'error.passphraseMismatch'\n case 'deviceConflict': return 'error.deviceConflict'\n default: return 'error.unknown'\n }\n}\n\n/**\n * \u6D4B\u8BD5\u8FDE\u63A5 / \u7ACB\u5373\u5168\u90E8\u540C\u6B65\u7684\u53EF\u7528\u6027\uFF0801 \u00A76\uFF09\uFF1A\u5730\u5740\u5DF2\u586B\u4E14\u5408\u6CD5\uFF0C\u4E14\u4EE4\u724C\u53EF\u7528\n * \uFF08\u5DF2\u914D\u7F6E\u6216\u672C\u6B21\u65B0\u586B\u4E86\u8349\u7A3F\uFF09\u3002\n */\nexport function canRunServerAction(serverUrlDraft: string, tokenAvailable: boolean): boolean {\n return serverUrlDraft !== '' && isValidServerUrl(serverUrlDraft) && tokenAvailable\n}\n\n/** \u89E6\u53D1\u5668\u5199\u7684\u91CD\u8BD5\u8282\u62CD\uFF1A1s \u5185\u6CA1\u770B\u5230\u56DE\u663E\u5C31\u91CD\u53D1\u3002 */\nexport const TRIGGER_RETRY_MS = 1_000\n/** \u89E6\u53D1\u5668\u5199\u7684\u603B\u8D85\u65F6\uFF1A20s \u4ECD\u65E0\u56DE\u663E\u5219\u9000\u51FA\u8FDB\u884C\u6001\u3002 */\nexport const TRIGGER_TIMEOUT_MS = 20_000\n\n/**\n * \u89E6\u53D1\u5668\u5199\u7684\u4E0B\u4E00\u6B65\u52A8\u4F5C\u3002\n *\n * \u80CC\u666F\uFF1ASettingsScope.set \u7528\u300C\u5BA2\u6237\u7AEF\u5DF2\u77E5\u6700\u65B0 revision\u300D\u505A\u4E50\u89C2\u9501 fence\uFF0C\u51B2\u7A81\u65F6\n * Host \u8FD4\u56DE {ok:false}\uFF0Cclient recover() \u91CD\u8F7D\u540E\u5199\u5165\u88AB\u9759\u9ED8\u4E22\u5F03\u3001Promise \u6B63\u5E38\n * resolve\u3002Host \u9AD8\u9891\u56DE\u5199 'cloud-sync-status' namespace\uFF0C\u89E6\u53D1\u5668\u5199\uFF08\n * testRequestedAt/syncAllRequestedAt\uFF09\u53EF\u80FD\u649E fence \u88AB\u5403\u6389\uFF0C\u56E0\u6B64\u5BF9\u89E6\u53D1\u5668\u5199\u505A\n * \u300C\u91CD\u8BD5\u5230\u56DE\u663E\u300D\u81EA\u6108\uFF1A\u56DE\u663E\uFF08statusCheckedAt/statusSyncAllAt >= issuedAt\uFF09\u672C\u6765\n * \u5C31\u662F\u5B8C\u6210\u4FE1\u53F7\u3002\n *\n * @param echoAt - \u56DE\u663E\u5B57\u6BB5\u5F53\u524D\u503C\uFF08\u65E0\u5219 0\uFF09\u3002\n * @param issuedAt - \u672C\u6B21\u89E6\u53D1\u53D1\u51FA\u7684\u65F6\u95F4\u6233\uFF08\u91CD\u8BD5\u4FDD\u6301\u540C\u4E00\u503C\uFF0C\u8FDB\u884C\u6001\u5224\u5B9A\u4E0D\u6296\u52A8\uFF09\u3002\n * @param lastSentAt - \u4E0A\u4E00\u6B21\u5B9E\u9645\u5199\u51FA\u7684\u65F6\u523B\u3002\n * @param now - \u5F53\u524D\u65F6\u523B\u3002\n * @returns done\uFF08\u56DE\u663E\u5230\u8FBE\uFF09/ timeout\uFF08\u603B\u8D85\u65F6\uFF09/ resend\uFF08\u8DDD\u4E0A\u6B21\u5199\u5DF2\u6EE1\u4E00\u4E2A\u8282\u62CD\uFF09/ wait\u3002\n */\nexport type TriggerAction = 'done' | 'timeout' | 'resend' | 'wait'\nexport function triggerAction(echoAt: number, issuedAt: number, lastSentAt: number, now: number): TriggerAction {\n // \u56DE\u663E\u4F18\u5148\u4E8E\u8D85\u65F6\uFF1A\u8FDF\u5230\u4F46\u5B8C\u6210\u7684\u56DE\u663E\u4E0D\u80FD\u628A\u4E00\u6B21\u6210\u529F\u5224\u6210\u5931\u8D25\n if (echoAt >= issuedAt) return 'done'\n if (now - issuedAt >= TRIGGER_TIMEOUT_MS) return 'timeout'\n if (now - lastSentAt >= TRIGGER_RETRY_MS) return 'resend'\n return 'wait'\n}\n\n// ---- M4\uFF1A\u6062\u590D\u6D41\u7A0B\uFF08\u901A\u9053 JSON \u8F7D\u8377\u7684 Client \u4FA7\u62FC\u5199\uFF0C\u4E0E Host sync/restore.ts \u5206\u522B\u7EF4\u62A4\uFF09 ----\n\n/** \u6062\u590D\u76EE\u5F55\u7684\u4E00\u884C\uFF08Host \u5DF2\u8BA1\u7B97\u597D existsLocal / \u7248\u672C / \u8DEF\u5F84\u89E3\u6790\uFF0CClient \u53EA\u5448\u73B0\u4E0E\u6539\u9009\uFF09\u3002 */\nexport interface CatalogEntry {\n sessionId: string\n device: string\n title: string\n updatedAt: number\n eventCount: number\n cwd: string\n formatVersion: number\n existsLocal: boolean\n versionIncompatible: boolean\n resolvedCwd: string | null\n resolution: 'mapping' | 'suggested' | 'none'\n}\n\n/** restoreCatalogJson \u7684\u8F7D\u8377\uFF08at \u56DE\u663E restoreListRequestedAt\uFF09\u3002 */\nexport interface RestoreCatalog {\n at: number\n selfDevice: string\n sessions: CatalogEntry[]\n error: string\n}\n\n/** restoreResultJson \u7684\u8F7D\u8377\uFF08at \u56DE\u663E\u8BF7\u6C42\u7684 at\uFF09\u3002 */\nexport interface RestoreResultPayload {\n at: number\n ok: number\n failed: number\n firstError: string\n}\n\n/** startupNoticeJson \u7684\u8F7D\u8377\uFF08\u542F\u52A8\u68C0\u67E5\u53D1\u73B0\u4ED6\u673A\u589E\u91CF\uFF09\u3002 */\nexport interface StartupNoticePayload {\n at: number\n count: number\n}\n\n/** pathMappingsJson \u7684\u5143\u7D20\uFF08\u8BBE\u7F6E\u5361\u300C\u8DEF\u5F84\u6620\u5C04\u300D\u533A\uFF09\u3002 */\nexport interface MappingPair {\n from: string\n to: string\n}\n\n/** \u9632\u5FA1\u6027\u89E3\u6790\uFF1A\u901A\u9053\u5B57\u6BB5\u53EF\u80FD\u88AB\u65E7\u7248\u672C/\u624B\u5DE5\u7F16\u8F91\u6C61\u67D3\uFF0C\u4EFB\u4F55\u4E0D\u7B26\u90FD\u6309\u65E0\u6570\u636E\u5904\u7406\u3002 */\nexport function parseCatalog(raw: string): RestoreCatalog | null {\n if (raw === '') return null\n try {\n const parsed = JSON.parse(raw) as RestoreCatalog\n if (typeof parsed.at !== 'number' || !Array.isArray(parsed.sessions) || typeof parsed.selfDevice !== 'string') return null\n return parsed\n } catch {\n return null\n }\n}\n\nexport function parseRestoreResult(raw: string): RestoreResultPayload | null {\n if (raw === '') return null\n try {\n const parsed = JSON.parse(raw) as RestoreResultPayload\n if (typeof parsed.at !== 'number' || typeof parsed.ok !== 'number' || typeof parsed.failed !== 'number') return null\n return parsed\n } catch {\n return null\n }\n}\n\nexport function parseMappings(raw: string): MappingPair[] {\n if (raw === '') return []\n try {\n const parsed = JSON.parse(raw) as MappingPair[]\n if (!Array.isArray(parsed)) return []\n return parsed.filter((pair) => typeof pair?.from === 'string' && typeof pair?.to === 'string')\n } catch {\n return []\n }\n}\n\nexport function parseStartupNotice(raw: string): StartupNoticePayload | null {\n if (raw === '') return null\n try {\n const parsed = JSON.parse(raw) as StartupNoticePayload\n if (typeof parsed.at !== 'number' || typeof parsed.count !== 'number' || parsed.at <= 0) return null\n return parsed\n } catch {\n return null\n }\n}\n\n/** \u6062\u590D\u8BF7\u6C42\uFF08Client\u2192Host\uFF09\uFF1A\u6BCF\u884C targetCwd \u4E3A null \u8868\u793A\u539F\u6837\u843D\u4F4D\uFF08\u6062\u590D\u540E\u672A\u5206\u7EC4\uFF09\u3002 */\nexport function encodeRestoreRequest(\n at: number,\n items: { sessionId: string; device: string; targetCwd: string | null }[],\n): string {\n return JSON.stringify({ at, items })\n}\n\n/** \u8DEF\u5F84\u6620\u5C04\u5220\u9664\u8BF7\u6C42\uFF08Client\u2192Host\uFF09\u3002 */\nexport function encodeMappingDelete(at: number, from: string): string {\n return JSON.stringify({ at, from })\n}\n\n/** \u884C\u4E3B\u952E\uFF1A\u8BBE\u5907 + \u4F1A\u8BDD id\uFF08\u540C id \u8DE8\u8BBE\u5907\u662F\u4E24\u884C\uFF09\u3002 */\nexport function rowKeyOf(entry: Pick<CatalogEntry, 'device' | 'sessionId'>): string {\n return `${entry.device}/${entry.sessionId}`\n}\n\n/** \u884C\u53EF\u6062\u590D\u6027\uFF1A\u672C\u5730\u5DF2\u5B58\u5728 / \u7248\u672C\u4E0D\u517C\u5BB9\u7684\u884C\u7981\u7528\uFF0801 \u00A75.4\uFF09\u3002 */\nexport function isRowRestorable(entry: Pick<CatalogEntry, 'existsLocal' | 'versionIncompatible'>): boolean {\n return !entry.existsLocal && !entry.versionIncompatible\n}\n\n/** \u9ED8\u8BA4\u52FE\u9009\uFF1A\u5F53\u524D\u8BBE\u5907\u4E4B\u5916\u3001\u4E14\u53EF\u6062\u590D\u7684\u4F1A\u8BDD\uFF0801 \u00A75.4\uFF09\u3002 */\nexport function defaultSelectedKeys(catalog: RestoreCatalog): string[] {\n return catalog.sessions\n .filter((entry) => entry.device !== catalog.selfDevice && isRowRestorable(entry))\n .map(rowKeyOf)\n}\n\n/** \u6765\u6E90\u8BBE\u5907\u7B5B\u9009\u9009\u9879\uFF08\u76EE\u5F55\u5185\u51FA\u73B0\u8FC7\u7684\u8BBE\u5907\uFF0C\u6309\u5B57\u5178\u5E8F\u7A33\u5B9A\uFF09\u3002 */\nexport function deviceOptions(catalog: RestoreCatalog): string[] {\n return [...new Set(catalog.sessions.map((entry) => entry.device))].sort()\n}\n\n/** \u6309\u6765\u6E90\u8BBE\u5907\u7B5B\u9009\uFF1B\u7A7A filter \u8868\u793A\u5168\u90E8\u3002 */\nexport function filterEntries(entries: CatalogEntry[], filter: string): CatalogEntry[] {\n if (filter === '') return entries\n return entries.filter((entry) => entry.device === filter)\n}\n\n/** \u76EE\u5F55\u62C9\u53D6/\u6062\u590D\u6267\u884C\u5931\u8D25\u7684\u9519\u8BEF\u7801 \u2192 \u6587\u6848\u5B57\u5178 key\uFF08\u590D\u7528\u72B6\u6001\u884C\u9519\u8BEF\u6587\u6848\uFF0C\u52A0 unconfigured\uFF09\u3002 */\nexport function catalogErrorKeyOf(\n error: string,\n): 'status.unconfigured' | 'error.unreachable' | 'error.tokenInvalid' | 'error.userMismatch' {\n if (error === 'tokenInvalid') return 'error.tokenInvalid'\n if (error === 'userMismatch') return 'error.userMismatch'\n if (error === 'unconfigured') return 'status.unconfigured'\n return 'error.unreachable'\n}\n", "/**\n * shell.overlay \u6D6E\u5C42\uFF08M4\uFF09\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846 + \u542F\u52A8\u68C0\u67E5\u901A\u77E5 + \u6062\u590D\u7ED3\u679C\u63D0\u793A\u3002\n * \u6302\u5728\u6839\u4F5C\u7528\u57DF\u5217\u8868\u69FD\uFF0C\u8BBE\u7F6E\u9875\u5916\u540C\u6837\u53EF\u7528\uFF08\u542F\u52A8 toast \u7684\u300C\u70B9\u51FB\u67E5\u770B\u300D\u8981\u80FD\u5728\n * \u4EFB\u610F\u9875\u9762\u6253\u5F00\u6062\u590D\u5BF9\u8BDD\u6846\uFF09\u3002\u5BF9\u8BDD\u6846\u7528 ui-primitives \u7684 Modal\uFF1B\u542F\u52A8\u901A\u77E5\u9700\u8981\n * \u70B9\u51FB\u52A8\u4F5C\u800C Toast \u7EC4\u4EF6\u4E0D\u652F\u6301\u70B9\u51FB\uFF0C\u6309 01 \u00A75.5\uFF08\u53F3\u4E0B\u89D2\u8F7B\u63D0\u793A\uFF09\u81EA\u7ED8\u3002\n */\n\nimport { Toast } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { CloudSyncCardFace } from './cloud-sync-card-controller.ts'\nimport { RestoreDialog } from './RestoreDialog.tsx'\n\n// ui-layout \u58F0\u660E\u7684 'shell.overlay' \u69FD\uFF08list/root\uFF0C\u65E0 owner props\uFF09\u3002\u672C\u5305\u4E0D\u4F9D\u8D56\n// ui-layout \u7684\u7C7B\u578B\u5305\uFF0C\u6309 renderer \u7684 SlotMap \u5408\u5E76\u60EF\u4F8B\u5C31\u5730\u58F0\u660E\u540C\u5F62\u6761\u76EE\u3002\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface SlotMap {\n 'shell.overlay': { kind: 'list'; scope: 'root' }\n }\n}\n\nexport type CloudSyncOverlayProps =\n PropsRuntime<'shell.overlay'>\n & PropsLocale<'settings.cloudSync'>\n & InjectFace<CloudSyncCardFace>\n\n/**\n * \u4E91\u7AEF\u540C\u6B65\u6D6E\u5C42\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846\uFF08restoreOpen \u65F6\uFF09\u3001\u542F\u52A8\u901A\u77E5\uFF08\u672A\u8BFB\u65F6\uFF09\u3001\u6062\u590D\u7ED3\u679C toast\u3002\n * Host \u672A serve \u8BBE\u7F6E namespace\uFF08state.available=false\uFF09\u65F6\u6574\u4F53\u4E0D\u6E32\u67D3\uFF0C\u5378\u8F7D\u65E0\u6B8B\u7559\u3002\n */\nexport function CloudSyncOverlay(props: CloudSyncOverlayProps) {\n const { t } = props\n const state = props.useCloudSyncCard((snapshot) => snapshot)\n if (!state.available) return null\n return (\n <>\n {state.restoreOpen\n ? <RestoreDialog t={t} state={state} actions={props} />\n : null}\n {state.notice !== null\n ? (\n <div className=\"dsh-cloud-sync-notice\" role=\"alert\">\n <span className=\"dsh-cloud-sync-notice__text\">{t('toast.newCloudSessions')}</span>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-notice__action\"\n onClick={() => { props.viewNotice() }}\n >\n {t('toast.view')}\n </button>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-notice__dismiss\"\n aria-label={t('toast.dismiss')}\n onClick={() => { props.dismissNotice() }}\n >\n {'\u00D7'}\n </button>\n </div>\n )\n : null}\n {state.restoreOutcome !== null\n ? (\n <Toast\n text={t('restore.result', { ok: state.restoreOutcome.ok, failed: state.restoreOutcome.failed })}\n onDone={() => { props.clearRestoreOutcome() }}\n />\n )\n : null}\n </>\n )\n}\n", "/**\n * \u4ECE\u4E91\u7AEF\u6062\u590D\u4F1A\u8BDD\u5BF9\u8BDD\u6846\uFF0801 \u00A75.4\uFF09\uFF1A\u76EE\u5F55\u5217\u8868\u3001\u6765\u6E90\u8BBE\u5907\u7B5B\u9009\u3001\u52FE\u9009\u3001\n * \u843D\u4F4D\u8DEF\u5F84\u5448\u73B0\u4E0E\u6539\u9009\uFF08\u76EE\u5F55\u9009\u62E9\u5668\u4F18\u5148\uFF0C\u80FD\u529B\u7F3A\u5931\u964D\u7EA7\u624B\u52A8\u8F93\u5165\uFF09\u3002\n * \u7EAF\u5C55\u793A\u7EC4\u4EF6\uFF1A\u72B6\u6001\u4E0E\u52A8\u4F5C\u5168\u90E8\u7ECF props \u8FDB\u5165\uFF0C\u6587\u6848\u5168\u90E8\u7ECF t \u67E5\u5B57\u5178\u3002\n */\n\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport { Modal } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { CloudSyncCardFace, CloudSyncCardState } from './cloud-sync-card-controller.ts'\nimport {\n catalogErrorKeyOf,\n deviceOptions,\n filterEntries,\n isRowRestorable,\n relativeTime,\n rowKeyOf,\n type CatalogEntry,\n} from './pure.ts'\nimport { relativeText } from './CloudSyncCard.tsx'\n\ntype T = TranslateNS<'settings.cloudSync'>\n\n/** \u5BF9\u8BDD\u6846 props\uFF1A\u5B57\u5178 t + \u5361\u7247\u5FEB\u7167 + controller \u52A8\u4F5C\uFF08\u7531 overlay \u7EC4\u4EF6\u7EC4\u88C5\uFF09\u3002 */\nexport interface RestoreDialogProps {\n t: T\n state: CloudSyncCardState\n actions: Pick<\n CloudSyncCardFace,\n 'closeRestore' | 'setDeviceFilter' | 'toggleRow' | 'pickTarget' | 'overrideTarget' | 'restoreSelected'\n >\n}\n\n/** \u884C\u76EE\u6807\u5355\u5143\u683C\uFF1A\u843D\u4F4D\u8DEF\u5F84\u7684\u5448\u73B0\u4E0E\u6539\u9009\u63A7\u4EF6\u3002 */\nfunction TargetCell(props: { t: T; entry: CatalogEntry; rowKey: string; state: CloudSyncCardState; actions: RestoreDialogProps['actions'] }) {\n const { t, entry, rowKey, state, actions } = props\n if (entry.existsLocal) {\n return <span className=\"dsh-cloud-sync-restore__badge dsh-cloud-sync-restore__badge--muted\">{t('restore.existsLocal')}</span>\n }\n if (entry.versionIncompatible) {\n return (\n <span\n className=\"dsh-cloud-sync-restore__badge dsh-cloud-sync-restore__badge--muted\"\n title={`format v${String(entry.formatVersion)}`}\n >\n {t('restore.versionIncompatible')}\n </span>\n )\n }\n const override = state.overrides[rowKey]\n const target = override ?? entry.resolvedCwd\n return (\n <span className=\"dsh-cloud-sync-restore__target\">\n {target !== null && target !== undefined && target !== ''\n ? (\n <span className=\"dsh-cloud-sync-restore__path\" title={target}>\n {target}\n {override === undefined && entry.resolution === 'suggested'\n ? <span className=\"dsh-cloud-sync-restore__badge\">{t('restore.pathSuggested')}</span>\n : null}\n {override === undefined && entry.resolution === 'mapping'\n ? <span className=\"dsh-cloud-sync-restore__badge\">{t('restore.pathMapped')}</span>\n : null}\n </span>\n )\n : <span className=\"dsh-cloud-sync-restore__unmatched\">{t('restore.unmatchedPath')}</span>}\n {state.pickerAvailable\n ? (\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-restore__pick\"\n onClick={() => { actions.pickTarget(rowKey) }}\n >\n {t('restore.pickDirectory')}\n </button>\n )\n : (\n <input\n className=\"dsh-cloud-sync-restore__manual\"\n type=\"text\"\n value={override ?? ''}\n placeholder={t('restore.manualPath.placeholder')}\n onChange={(event) => { actions.overrideTarget(rowKey, event.target.value) }}\n />\n )}\n </span>\n )\n}\n\n/** \u6062\u590D\u5BF9\u8BDD\u6846\u4E3B\u4F53\u3002 */\nexport function RestoreDialog({ t, state, actions }: RestoreDialogProps) {\n const catalog = state.catalog\n const entries = catalog === null ? [] : filterEntries(catalog.sessions, state.deviceFilter)\n const selectedCount = state.selectedKeys.length\n return (\n <Modal\n open\n onClose={() => { actions.closeRestore() }}\n title={t('restore.title')}\n closeLabel={t('restore.close')}\n className=\"dsh-cloud-sync-restore\"\n contentClassName=\"dsh-cloud-sync-restore__content\"\n footer={(\n <>\n <span className=\"dsh-cloud-sync-restore__selected\">{t('restore.selected', { count: selectedCount })}</span>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button\"\n onClick={() => { actions.closeRestore() }}\n >\n {t('restore.cancel')}\n </button>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button dsh-cloud-sync-card__button--primary\"\n disabled={selectedCount === 0 || state.restorePending}\n onClick={() => { actions.restoreSelected() }}\n >\n {t(state.restorePending ? 'restore.restoring' : 'restore.action')}\n </button>\n </>\n )}\n >\n {catalog !== null && catalog.error !== ''\n ? <p className=\"dsh-cloud-sync-card__error\">{t(catalogErrorKeyOf(catalog.error))}</p>\n : null}\n {catalog === null && state.catalogPending\n ? <p className=\"dsh-cloud-sync-card__hint\">{t('restore.loading')}</p>\n : null}\n {catalog !== null && catalog.error === '' && entries.length === 0 && !state.catalogPending\n ? <p className=\"dsh-cloud-sync-card__hint\">{t('restore.empty')}</p>\n : null}\n {catalog !== null && catalog.sessions.length > 0\n ? (\n <>\n <div className=\"dsh-cloud-sync-restore__filter\">\n <select\n className=\"dsh-cloud-sync-restore__select\"\n value={state.deviceFilter}\n onChange={(event) => { actions.setDeviceFilter(event.target.value) }}\n >\n <option value=\"\">{t('restore.filter.all')}</option>\n {deviceOptions(catalog).map((device) => (\n <option key={device} value={device}>{device}</option>\n ))}\n </select>\n </div>\n <div className=\"dsh-cloud-sync-restore__head\" role=\"row\">\n <span />\n <span>{t('restore.column.title')}</span>\n <span>{t('restore.column.updated')}</span>\n <span>{t('restore.column.events')}</span>\n <span>{t('restore.column.target')}</span>\n </div>\n <ul className=\"dsh-cloud-sync-restore__list\">\n {entries.map((entry) => {\n const rowKey = rowKeyOf(entry)\n const restorable = isRowRestorable(entry)\n const updated = relativeTime(entry.updatedAt, Date.now())\n return (\n <li key={rowKey} className=\"dsh-cloud-sync-restore__row\" data-disabled={!restorable}>\n <input\n type=\"checkbox\"\n checked={state.selectedKeys.includes(rowKey)}\n disabled={!restorable || state.restorePending}\n onChange={() => { actions.toggleRow(rowKey) }}\n />\n <span className=\"dsh-cloud-sync-restore__title\" title={entry.title}>\n {entry.title === '' ? entry.sessionId : entry.title}\n <span className=\"dsh-cloud-sync-restore__device\">{entry.device}</span>\n </span>\n <span className=\"dsh-cloud-sync-restore__updated\">\n {updated === null ? '' : relativeText(t, updated)}\n </span>\n <span className=\"dsh-cloud-sync-restore__events\">{entry.eventCount}</span>\n <TargetCell t={t} entry={entry} rowKey={rowKey} state={state} actions={actions} />\n </li>\n )\n })}\n </ul>\n </>\n )\n : null}\n </Modal>\n )\n}\n", "/**\n * \u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361 controller\uFF1A\u6865\u63A5\u4E24\u4E2A settings namespace \u4E0E\u5361\u7247\u5FEB\u7167\u3002\n *\n * \u53CC namespace \u62C6\u5206\uFF08Host \u534A\u5951\u7EA6\uFF09\uFF1A\n * - 'cloud-sync'\uFF1A9 \u4E2A\u7528\u6237\u914D\u7F6E\u5B57\u6BB5\uFF0CHost \u6C38\u4E0D\u5199 \u2192 \u914D\u7F6E\u5199\u96F6 revision \u51B2\u7A81\uFF0C\n * scope.set/unset \u7684\u4E50\u89C2\u9501 fence \u6C38\u8FDC\u65B0\u9C9C\uFF0C\u65E0\u9700\u91CD\u8BD5\u3002\n * - 'cloud-sync-status'\uFF1A14 \u4E2A\u901A\u9053\u5B57\u6BB5\uFF08\u72B6\u6001\u56DE\u8BFB + \u89E6\u53D1\u5668\uFF09\uFF0CHost \u9891\u7E41\u56DE\u5199\u3002\n * \u89E6\u53D1\u5668\u5199\uFF08testRequestedAt/syncAllRequestedAt\uFF09\u53EF\u80FD\u649E fence \u88AB\u9759\u9ED8\u4E22\u5F03\n * \uFF08\u51B2\u7A81\u65F6 client recover() \u91CD\u8F7D\u3001Promise \u6B63\u5E38 resolve\uFF09\uFF0C\u56E0\u6B64\u89E6\u53D1\u5668\u5199\u505A\n * \u300C\u91CD\u8BD5\u5230\u56DE\u663E\u300D\u81EA\u6108\uFF0C\u89C1 pure.ts \u7684 triggerAction\u3002\n *\n * secret \u5B57\u6BB5\uFF08token/passphrase\uFF09\u6C38\u4E0D\u4E0B\u884C\uFF0C\u5361\u7247\u53EA\u51ED Host \u7EF4\u62A4\u7684\n * tokenConfigured/passphraseConfigured \u5E03\u5C14\u663E\u793A\u300C\u5DF2\u914D\u7F6E/\u672A\u914D\u7F6E\u300D\uFF0C\u8F93\u5165\u6846\u6C38\u8FDC\u7A7A\u503C\u8D77\u624B\u3002\n */\n\nimport { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport {\n canRunServerAction,\n defaultSelectedKeys,\n encodeMappingDelete,\n encodeRestoreRequest,\n isRowRestorable,\n isValidDeviceName,\n isValidServerUrl,\n parseCatalog,\n parseMappings,\n parseRestoreResult,\n parseStartupNotice,\n projectStatusLine,\n rowKeyOf,\n triggerAction,\n TRIGGER_RETRY_MS,\n type MappingPair,\n type RestoreCatalog,\n type RestoreResultPayload,\n type StartupNoticePayload,\n type StatusError,\n type StatusLine,\n type StatusPhase,\n} from './pure.ts'\n\n/** \u7528\u6237\u914D\u7F6E namespace\uFF08\u4E0E Host \u534A\u7EA6\u5B9A\u4E00\u81F4\uFF0C\u4E24\u5904\u5206\u522B\u62FC\u5199\uFF0C\u4E0D\u8DE8\u534A import\uFF09\u3002 */\nexport const CLOUD_SYNC_NS = 'cloud-sync'\n/** \u72B6\u6001/\u89E6\u53D1\u901A\u9053 namespace\uFF08Host \u9AD8\u9891\u56DE\u5199\uFF09\u3002 */\nexport const CLOUD_SYNC_STATUS_NS = 'cloud-sync-status'\n\n/** 'cloud-sync' namespace \u7684\u7528\u6237\u914D\u7F6E\u5B57\u6BB5\uFF08Host \u6C38\u4E0D\u5199\uFF09\u3002 */\nexport interface CloudSyncConfig {\n serverUrl?: string\n username?: string\n deviceName?: string\n autoUpload?: boolean\n checkOnStartup?: boolean\n uploadDebounceMs?: number\n pollIntervalMs?: number\n requestTimeoutMs?: number\n}\n\n/** 'cloud-sync-status' namespace \u7684\u901A\u9053\u5B57\u6BB5\uFF1A\u72B6\u6001\u56DE\u8BFB + \u89E6\u53D1\u5668\u3002 */\nexport interface CloudSyncStatus {\n statusPhase?: StatusPhase\n statusCloudCount?: number\n statusLastSyncAt?: number\n statusCheckedAt?: number\n statusError?: StatusError\n statusErrorDetail?: string\n tokenConfigured?: boolean\n passphraseConfigured?: boolean\n statusSyncAllAt?: number\n statusSyncAllOk?: number\n statusSyncAllFailed?: number\n statusSyncAllError?: string\n testRequestedAt?: number\n syncAllRequestedAt?: number\n /** M4\uFF1AHost \u56DE\u5199\u7684\u6062\u590D\u76EE\u5F55 / \u7ED3\u679C / \u6620\u5C04\u5217\u8868 / \u542F\u52A8\u901A\u77E5\uFF08JSON \u5B57\u7B26\u4E32\uFF09\u3002 */\n restoreCatalogJson?: string\n restoreResultJson?: string\n pathMappingsJson?: string\n startupNoticeJson?: string\n /** M4\uFF1AClient \u5199\u5165\u7684\u89E6\u53D1\u5668\u3002 */\n restoreListRequestedAt?: number\n restoreRequestJson?: string\n mappingDeleteJson?: string\n}\n\n/** \u76EE\u5F55\u9009\u62E9\u5668\u80FD\u529B\uFF08ctx.remote.directoryPicker \u7684\u6700\u5C0F\u5207\u9762\uFF1B\u80FD\u529B\u7F3A\u5931\u65F6\u964D\u7EA7\u624B\u52A8\u8F93\u5165\uFF09\u3002 */\nexport interface DirectoryPickerLike {\n pick(): Promise<{ ok: true; value: string | null } | { ok: false; error: { message: string } }>\n}\n\n/** \u4E00\u6B21\u300C\u7ACB\u5373\u5168\u90E8\u540C\u6B65\u300D\u7684\u7ED3\u679C\u6458\u8981\uFF08\u77ED\u6682\u663E\u793A\u540E\u81EA\u52A8\u6D88\u5931\uFF09\u3002 */\nexport interface SyncAllResult {\n ok: number\n failed: number\n /** \u9996\u4E2A\u5931\u8D25\u6458\u8981\uFF08Host \u539F\u6587\uFF0C\u6570\u636E\u800C\u975E\u6587\u6848\uFF09\u3002 */\n error: string\n}\n\n/** \u5361\u7247\u6E32\u67D3\u6240\u9700\u7684\u5168\u90E8\u72B6\u6001\u3002 */\nexport interface CloudSyncCardState {\n /** \u914D\u7F6E namespace \u4E0D\u53EF\u7528\uFF08Host \u672A serve\uFF09\u65F6\u5361\u7247\u6574\u4F53\u4E0D\u6E32\u67D3\u3002 */\n available: boolean\n /** Host \u6587\u6863\u662F\u5426\u53EF\u5199\uFF08memory \u6A21\u5F0F\u53EA\u8BFB\uFF0C\u5168\u90E8\u63A7\u4EF6\u7981\u7528\uFF09\u3002 */\n writable: boolean\n serverUrlDraft: string\n /** \u8349\u7A3F\u975E\u7A7A\u4E14\u975E\u5408\u6CD5 http(s) URL\uFF1A\u5C31\u5730\u62A5\u9519\u5E76\u7981\u7528\u6D4B\u8BD5\u8FDE\u63A5\u3002 */\n serverUrlInvalid: boolean\n usernameDraft: string\n deviceNameDraft: string\n deviceNameInvalid: boolean\n /** \u6C38\u8FDC\u7A7A\u503C\u8D77\u624B\uFF1B\u63D0\u4EA4\u540E\u5373\u6E05\u7A7A\u3002 */\n tokenDraft: string\n passphraseDraft: string\n tokenConfigured: boolean\n passphraseConfigured: boolean\n autoUpload: boolean\n checkOnStartup: boolean\n /** \u4E0D\u542B\u8FDB\u884C\u6001\u7684\u53EF\u7528\u6027\uFF1B\u6309\u94AE disabled \u8FD8\u9700\u53E0\u52A0 testPending/syncPending\u3002 */\n canTest: boolean\n canSync: boolean\n testPending: boolean\n syncPending: boolean\n statusLine: StatusLine\n syncResult: SyncAllResult | null\n // ---- M4\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846 / \u8DEF\u5F84\u6620\u5C04 / \u542F\u52A8\u901A\u77E5 ----\n /** \u6062\u590D\u5BF9\u8BDD\u6846\u5F00\u5173\uFF08\u5BF9\u8BDD\u6846\u6302\u5728 shell.overlay\uFF0C\u8BBE\u7F6E\u9875\u5916\u4E5F\u80FD\u5F00\uFF09\u3002 */\n restoreOpen: boolean\n /** \u6700\u8FD1\u4E00\u6B21\u76EE\u5F55\u62C9\u53D6\u7ED3\u679C\uFF1Bnull = \u4ECE\u672A\u62C9\u53D6\u3002 */\n catalog: RestoreCatalog | null\n catalogPending: boolean\n restorePending: boolean\n /** \u6062\u590D\u5B8C\u6210\u7684\u7ED3\u679C\u6458\u8981\uFF08overlay toast \u5C55\u793A\uFF0C\u77ED\u6682\u505C\u7559\uFF09\u3002 */\n restoreOutcome: RestoreResultPayload | null\n deviceFilter: string\n /** \u52FE\u9009\u7684\u884C\u4E3B\u952E\uFF08device/sessionId\uFF09\u3002 */\n selectedKeys: string[]\n /** \u7528\u6237\u6539\u9009\u7684\u843D\u4F4D\u8DEF\u5F84\uFF08\u884C\u4E3B\u952E \u2192 \u672C\u673A\u76EE\u5F55\uFF09\u3002 */\n overrides: Record<string, string>\n /** \u5DF2\u5B66\u4E60\u8DEF\u5F84\u6620\u5C04\uFF08\u8BBE\u7F6E\u5361\u53EA\u8BFB\u5217\u8868\uFF09\u3002 */\n mappings: MappingPair[]\n /** \u542F\u52A8\u68C0\u67E5\u901A\u77E5\uFF08null = \u65E0/\u5DF2\u5FFD\u7565\uFF09\u3002 */\n notice: StartupNoticePayload | null\n /** \u76EE\u5F55\u9009\u62E9\u5668\u80FD\u529B\u53EF\u7528\u6027\uFF08\u4E0D\u53EF\u7528\u65F6\u884C\u5185\u964D\u7EA7\u4E3A\u624B\u52A8\u8F93\u5165\uFF09\u3002 */\n pickerAvailable: boolean\n}\n\n/** \u6CE8\u518C\u4FA7 face\uFF1Ahooks \u7531\u6E32\u67D3\u5668\u7ED1\u5B9A\u4E3A\u7EC4\u4EF6 props \u4E0A\u7684 useCloudSyncCard\u3002 */\nexport interface CloudSyncCardFace {\n hooks: {\n /** \u5361\u7247\u5FEB\u7167\uFF0C\u6E32\u67D3\u5668\u7ED1\u5B9A\u4E3A useCloudSyncCard(selector)\u3002 */\n cloudSyncCard: SnapshotStore<CloudSyncCardState>\n }\n editServerUrl(text: string): void\n commitServerUrl(): void\n editUsername(text: string): void\n commitUsername(): void\n editToken(text: string): void\n commitToken(): void\n editPassphrase(text: string): void\n commitPassphrase(): void\n editDeviceName(text: string): void\n commitDeviceName(): void\n toggleAutoUpload(): void\n toggleCheckOnStartup(): void\n testConnection(): void\n syncAll(): void\n // ---- M4\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846 / \u8DEF\u5F84\u6620\u5C04 / \u542F\u52A8\u901A\u77E5 ----\n openRestore(): void\n closeRestore(): void\n setDeviceFilter(filter: string): void\n toggleRow(key: string): void\n /** \u76EE\u5F55\u9009\u62E9\u5668\u6539\u9009\uFF08\u80FD\u529B\u4E0D\u53EF\u7528\u65F6 UI \u4E0D\u6E32\u67D3\u8BE5\u6309\u94AE\uFF0C\u8D70 overrideTarget \u624B\u52A8\u8F93\u5165\uFF09\u3002 */\n pickTarget(key: string): void\n /** \u624B\u52A8\u8F93\u5165\u6539\u9009\uFF08\u53D7\u63A7\u8F93\u5165\u9010\u952E\u66F4\u65B0\uFF1B\u7A7A\u4E32 = \u6E05\u9664\u6539\u9009\uFF09\u3002 */\n overrideTarget(key: string, text: string): void\n restoreSelected(): void\n deleteMapping(from: string): void\n dismissNotice(): void\n /** toast \u7684\u300C\u70B9\u51FB\u67E5\u770B\u300D\uFF1A\u5173\u6389\u901A\u77E5\u5E76\u6253\u5F00\u6062\u590D\u5BF9\u8BDD\u6846\u3002 */\n viewNotice(): void\n clearRestoreOutcome(): void\n}\n\n/** \u540C\u6B65\u7ED3\u679C\u6458\u8981\u7684\u505C\u7559\u65F6\u957F\u3002 */\nconst SYNC_RESULT_VISIBLE_MS = 8_000\n/** \u76F8\u5BF9\u65F6\u95F4\uFF08\"N \u5206\u949F\u524D\"\uFF09\u7684\u9759\u9ED8\u5237\u65B0\u8282\u62CD\u3002 */\nconst CLOCK_TICK_MS = 30_000\n/** \u542F\u52A8\u901A\u77E5\u5DF2\u8BFB\u6807\u8BB0\u7684 sessionStorage key\u3002 */\nconst NOTICE_SEEN_KEY = 'dsh-cloud-sync:notice-seen-at'\n\n/** \u4E00\u6B21\u8FDB\u884C\u4E2D\u7684\u89E6\u53D1\u5668\u5199\u3002 */\ninterface PendingOp {\n /** \u672C\u6B21\u89E6\u53D1\u53D1\u51FA\u7684\u65F6\u95F4\u6233\uFF1B\u91CD\u8BD5\u4FDD\u6301\u540C\u4E00\u503C\uFF0C\u56DE\u663E >= \u5B83\u5373\u5B8C\u6210\u3002 */\n issuedAt: number\n /** \u4E0A\u4E00\u6B21\u5B9E\u9645\u5199\u51FA\u7684\u65F6\u523B\uFF08\u8DDD\u5B83\u6EE1 TRIGGER_RETRY_MS \u4E14\u65E0\u56DE\u663E\u5219\u91CD\u53D1\uFF09\u3002 */\n lastSentAt: number\n}\n\n/** \u6865\u63A5\u4E24\u4E2A SettingsScope \u4E0E\u5361\u7247\u5FEB\u7167\u3002 */\nexport class CloudSyncCardController {\n private readonly store: SnapshotStore<CloudSyncCardState>\n /** \u672C\u5730\u8349\u7A3F + \u5404\u8349\u7A3F\u5BF9\u5E94\u7684 scope \u5DF2\u540C\u6B65\u503C\uFF08\u7528\u4E8E\u8BC6\u522B\u5916\u90E8\u53D8\u66F4\u8BE5\u5426\u8986\u76D6\u8349\u7A3F\uFF09\u3002 */\n private serverUrlDraft = ''\n private serverUrlSynced = ''\n private usernameDraft = ''\n private usernameSynced = ''\n private deviceNameDraft = ''\n private deviceNameSynced = ''\n private tokenDraft = ''\n private passphraseDraft = ''\n /** \u672C\u6B21\u4F1A\u8BDD\u5185 secret \u63D0\u4EA4\u8FC7\u7684\u4E50\u89C2\u6807\u8BB0\uFF08Host \u7684 tokenConfigured \u56DE\u5199\u524D\u7684\u5373\u65F6\u53CD\u9988\uFF09 */\n private tokenCommitted = false\n private passphraseCommitted = false\n private now = Date.now()\n private pendingTest: PendingOp | null = null\n private pendingSync: PendingOp | null = null\n private syncResult: SyncAllResult | null = null\n private syncResultTimer: ReturnType<typeof setTimeout> | null = null\n /** \u89E6\u53D1\u5668\u8FDB\u884C\u671F\u95F4\u7684 1s \u9A71\u52A8\u8282\u62CD\uFF08\u65E0 pending \u65F6\u505C\u6389\uFF0C\u4E0D\u767D\u8F6C\uFF09\u3002 */\n private triggerWatch: ReturnType<typeof setInterval> | null = null\n private readonly disposers: (() => void)[] = []\n // ---- M4 \u5185\u90E8\u72B6\u6001 ----\n private restoreOpen = false\n private catalog: RestoreCatalog | null = null\n private pendingRestoreList: PendingOp | null = null\n private pendingRestore: PendingOp | null = null\n private restoreOutcome: RestoreResultPayload | null = null\n private deviceFilter = ''\n private readonly selectedKeys = new Set<string>()\n private readonly overrides = new Map<string, string>()\n /** \u6620\u5C04\u5220\u9664\u7684\u8FDB\u884C\u4E2D\u8BF7\u6C42\uFF08\u56DE\u663E = pathMappingsJson \u91CC\u4E0D\u518D\u6709\u8BE5 from\uFF09\u3002 */\n private pendingMappingDelete: (PendingOp & { from: string }) | null = null\n /** \u542F\u52A8\u901A\u77E5\u7684\u5DF2\u8BFB\u6807\u8BB0\uFF08\u540C at \u4E0D\u91CD\u590D\u5F39\uFF1BsessionStorage \u8BB0\u5FC6\uFF0C\u8DE8\u9875\u9762\u5237\u65B0\u4E0D\u91CD\u73B0\uFF09\u3002 */\n private noticeSeenAt = 0\n\n /**\n * @param configScope - \u7ED1\u5B9A 'cloud-sync' \u7684 scope\uFF08\u914D\u7F6E\u8BFB\u5199\uFF09\u3002\n * @param statusScope - \u7ED1\u5B9A 'cloud-sync-status' \u7684 scope\uFF08\u72B6\u6001\u8BFB + \u89E6\u53D1\u5668\u5199\uFF09\u3002\n * @param directoryPicker - \u76EE\u5F55\u9009\u62E9\u5668\u80FD\u529B\uFF08\u53EF\u9009\uFF1B\u7F3A\u5931\u65F6\u6062\u590D\u5BF9\u8BDD\u6846\u964D\u7EA7\u4E3A\u624B\u52A8\u8F93\u5165\uFF09\u3002\n */\n constructor(\n private readonly configScope: SettingsScope<CloudSyncConfig>,\n private readonly statusScope: SettingsScope<CloudSyncStatus>,\n private readonly directoryPicker?: DirectoryPickerLike,\n ) {\n this.store = createSnapshotStore(this.projection())\n this.disposers.push(this.configScope.subscribe(() => { this.onConfigChange() }))\n this.disposers.push(this.statusScope.subscribe(() => { this.onStatusChange() }))\n const clock = setInterval(() => {\n this.now = Date.now()\n this.reproject()\n }, CLOCK_TICK_MS)\n this.disposers.push(() => { clearInterval(clock) })\n try {\n this.noticeSeenAt = Number(globalThis.sessionStorage?.getItem(NOTICE_SEEN_KEY) ?? 0) || 0\n } catch {\n // sessionStorage \u4E0D\u53EF\u7528\uFF08\u9690\u79C1\u6A21\u5F0F\u7B49\uFF09\uFF1A\u9000\u5316\u4E3A\u6BCF\u6B21\u9875\u9762\u52A0\u8F7D\u6700\u591A\u5F39\u4E00\u6B21\n }\n this.onConfigChange()\n this.onStatusChange()\n }\n\n /** \u91CA\u653E\u8BA2\u9605\u4E0E\u8BA1\u65F6\u5668\uFF08\u63D2\u4EF6\u5378\u8F7D\u65F6\u7531 ctx.effect \u7684\u6E05\u7406\u51FD\u6570\u8C03\u7528\uFF09\u3002 */\n dispose(): void {\n for (const dispose of this.disposers.splice(0)) dispose()\n if (this.triggerWatch !== null) clearInterval(this.triggerWatch)\n if (this.syncResultTimer !== null) clearTimeout(this.syncResultTimer)\n }\n\n /** \u6784\u5EFA\u6CE8\u518C\u4FA7 face\u3002 */\n inject(): CloudSyncCardFace {\n return {\n hooks: { cloudSyncCard: this.store },\n editServerUrl: (text) => {\n this.serverUrlDraft = text\n this.reproject()\n },\n commitServerUrl: () => { this.commitServerUrl() },\n editUsername: (text) => {\n this.usernameDraft = text\n this.reproject()\n },\n commitUsername: () => { this.commitUsername() },\n editToken: (text) => {\n this.tokenDraft = text\n this.reproject()\n },\n commitToken: () => { this.commitSecret('token') },\n editPassphrase: (text) => {\n this.passphraseDraft = text\n this.reproject()\n },\n commitPassphrase: () => { this.commitSecret('passphrase') },\n editDeviceName: (text) => {\n this.deviceNameDraft = text\n this.reproject()\n },\n commitDeviceName: () => { this.commitDeviceName() },\n toggleAutoUpload: () => { this.toggleBoolean('autoUpload') },\n toggleCheckOnStartup: () => { this.toggleBoolean('checkOnStartup') },\n testConnection: () => { this.startTrigger('test') },\n syncAll: () => { this.startTrigger('sync') },\n openRestore: () => { this.openRestore() },\n closeRestore: () => {\n this.restoreOpen = false\n this.reproject()\n },\n setDeviceFilter: (filter) => {\n this.deviceFilter = filter\n this.reproject()\n },\n toggleRow: (key) => {\n if (this.selectedKeys.has(key)) this.selectedKeys.delete(key)\n else this.selectedKeys.add(key)\n this.reproject()\n },\n pickTarget: (key) => { void this.pickTarget(key) },\n overrideTarget: (key, text) => {\n const trimmed = text.trim()\n if (trimmed === '') this.overrides.delete(key)\n else this.overrides.set(key, trimmed)\n this.reproject()\n },\n restoreSelected: () => { this.startRestore() },\n deleteMapping: (from) => { this.startMappingDelete(from) },\n dismissNotice: () => { this.markNoticeSeen() },\n viewNotice: () => {\n this.markNoticeSeen()\n this.openRestore()\n },\n clearRestoreOutcome: () => {\n this.restoreOutcome = null\n this.reproject()\n },\n }\n }\n\n /** \u914D\u7F6E scope \u53D8\u66F4\uFF1A\u540C\u6B65\u672A\u4FEE\u6539\u7684\u8349\u7A3F\uFF08\u6B63\u5728\u7F16\u8F91\u7684\u8349\u7A3F\u4E0D\u88AB\u5916\u90E8\u53D8\u66F4\u8986\u76D6\uFF09\u3002 */\n private onConfigChange(): void {\n const value = this.configScope.getSnapshot().value\n const serverUrl = value?.serverUrl ?? ''\n if (this.serverUrlDraft === this.serverUrlSynced) this.serverUrlDraft = serverUrl\n this.serverUrlSynced = serverUrl\n const username = value?.username ?? ''\n if (this.usernameDraft === this.usernameSynced) this.usernameDraft = username\n this.usernameSynced = username\n const deviceName = value?.deviceName ?? ''\n if (this.deviceNameDraft === this.deviceNameSynced) this.deviceNameDraft = deviceName\n this.deviceNameSynced = deviceName\n this.reproject()\n }\n\n /** \u72B6\u6001 scope \u53D8\u66F4\uFF1A\u9A71\u52A8\u89E6\u53D1\u5668\u7684\u5B8C\u6210/\u91CD\u8BD5\u5224\u5B9A\uFF0C\u518D\u91CD\u6295\u5F71\u3002 */\n private onStatusChange(): void {\n // \u76EE\u5F55\u515C\u5E95\uFF1A\u91C7\u7528\u901A\u9053\u91CC\u6B8B\u7559\u7684\u4E0A\u6B21\u7ED3\u679C\uFF08\u6253\u5F00\u5BF9\u8BDD\u6846\u5373\u89E6\u53D1\u65B0\u62C9\u53D6\u8986\u76D6\u5B83\uFF09\uFF0C\n // \u8BA9 restoreItems \u4E0E\u6E32\u67D3\u5171\u7528\u540C\u4E00\u4EFD\u6570\u636E\n if (this.catalog === null) {\n this.catalog = parseCatalog(this.statusScope.getSnapshot().value?.restoreCatalogJson ?? '')\n }\n this.driveTriggers()\n this.reproject()\n }\n\n /** \u53D1\u8D77\u4E00\u6B21\u89E6\u53D1\u5668\u5199\uFF08\u6D4B\u8BD5\u8FDE\u63A5 / \u7ACB\u5373\u5168\u90E8\u540C\u6B65\uFF09\uFF0C\u8FDB\u884C\u6001\u7531 driveTriggers \u7EF4\u62A4\u3002 */\n private startTrigger(kind: 'test' | 'sync'): void {\n if (kind === 'test' ? this.pendingTest !== null : this.pendingSync !== null) return\n const issuedAt = Date.now()\n const op: PendingOp = { issuedAt, lastSentAt: 0 }\n if (kind === 'test') this.pendingTest = op\n else this.pendingSync = op\n // \u7ACB\u5373\u9996\u53D1\uFF1B\u540E\u7EED\u91CD\u8BD5\u7531 triggerWatch \u8282\u62CD\u9A71\u52A8\n this.sendTrigger(op, kind === 'test' ? 'testRequestedAt' : 'syncAllRequestedAt')\n this.ensureTriggerWatch()\n this.reproject()\n }\n\n /** \u5199\u4E00\u6B21\u89E6\u53D1\u5B57\u6BB5\uFF1B\u5931\u8D25\u4E0D\u91CD\u629B\u2014\u2014\u91CD\u8BD5\u8282\u62CD\u4F1A\u8865\u53D1\uFF0C\u603B\u8D85\u65F6\u515C\u5E95\u9000\u51FA\u3002 */\n private sendTrigger(op: PendingOp, field: 'testRequestedAt' | 'syncAllRequestedAt'): void {\n op.lastSentAt = Date.now()\n void this.statusScope.set(field, op.issuedAt).catch(() => {})\n }\n\n /** \u89E6\u53D1\u5668\u9A71\u52A8\uFF1A\u5BF9\u6BCF\u4E2A pending \u5224\u5B9A done/timeout/resend/wait \u5E76\u6267\u884C\u3002 */\n private driveTriggers(): void {\n if (this.pendingTest === null && this.pendingSync === null\n && this.pendingRestoreList === null && this.pendingRestore === null && this.pendingMappingDelete === null) {\n this.maybeStopTriggerWatch()\n return\n }\n const now = Date.now()\n this.now = now\n const value = this.statusScope.getSnapshot().value\n if (this.pendingTest !== null) {\n const action = triggerAction(value?.statusCheckedAt ?? 0, this.pendingTest.issuedAt, this.pendingTest.lastSentAt, now)\n if (action === 'done' || action === 'timeout') this.pendingTest = null\n else if (action === 'resend') this.sendTrigger(this.pendingTest, 'testRequestedAt')\n }\n if (this.pendingSync !== null) {\n const op = this.pendingSync\n const action = triggerAction(value?.statusSyncAllAt ?? 0, op.issuedAt, op.lastSentAt, now)\n if (action === 'done') {\n this.pendingSync = null\n this.showSyncResult({\n ok: value?.statusSyncAllOk ?? 0,\n failed: value?.statusSyncAllFailed ?? 0,\n error: value?.statusSyncAllError ?? '',\n })\n } else if (action === 'timeout') {\n this.pendingSync = null\n } else if (action === 'resend') {\n this.sendTrigger(op, 'syncAllRequestedAt')\n }\n }\n if (this.pendingRestoreList !== null) {\n const op = this.pendingRestoreList\n const catalog = parseCatalog(value?.restoreCatalogJson ?? '')\n const action = triggerAction(catalog?.at ?? 0, op.issuedAt, op.lastSentAt, now)\n if (action === 'done') {\n this.pendingRestoreList = null\n this.catalog = catalog\n // \u76EE\u5F55\u5237\u65B0\u540E\u6309\u5F53\u524D\u8BBE\u5907\u91CD\u4F30\u9ED8\u8BA4\u52FE\u9009\uFF08\u4FDD\u7559\u7528\u6237\u5DF2\u52FE\u7684\u884C\uFF09\n if (catalog !== null) {\n for (const key of defaultSelectedKeys(catalog)) this.selectedKeys.add(key)\n // \u76EE\u5F55\u91CC\u6D88\u5931\u7684\u884C\uFF08\u4ED6\u673A\u5220\u9664\u7B49\uFF09\u4ECE\u52FE\u9009\u4E0E\u6539\u9009\u4E2D\u6E05\u6389\n const live = new Set(catalog.sessions.map(rowKeyOf))\n for (const key of [...this.selectedKeys]) if (!live.has(key)) this.selectedKeys.delete(key)\n for (const key of [...this.overrides.keys()]) if (!live.has(key)) this.overrides.delete(key)\n }\n } else if (action === 'timeout') {\n this.pendingRestoreList = null\n } else if (action === 'resend') {\n op.lastSentAt = now\n void this.statusScope.set('restoreListRequestedAt', op.issuedAt).catch(() => {})\n }\n }\n if (this.pendingRestore !== null) {\n const op = this.pendingRestore\n const result = parseRestoreResult(value?.restoreResultJson ?? '')\n const action = triggerAction(result?.at ?? 0, op.issuedAt, op.lastSentAt, now)\n if (action === 'done' || action === 'timeout') {\n this.pendingRestore = null\n if (action === 'done' && result !== null) {\n this.restoreOutcome = result\n // \u6062\u590D\u5B8C\u6210 \u2192 \u5BF9\u8BDD\u6846\u5173\u95ED\uFF0801 \u00A75.4\uFF09\uFF0C\u52FE\u9009\u4E0E\u6539\u9009\u968F\u672C\u8F6E\u7ED3\u675F\u6E05\u7A7A\n this.restoreOpen = false\n this.selectedKeys.clear()\n this.overrides.clear()\n }\n } else if (action === 'resend') {\n op.lastSentAt = now\n void this.statusScope.set('restoreRequestJson', encodeRestoreRequest(op.issuedAt, this.restoreItems())).catch(() => {})\n }\n }\n if (this.pendingMappingDelete !== null) {\n const op = this.pendingMappingDelete\n const gone = !parseMappings(value?.pathMappingsJson ?? '[]').some((pair) => pair.from === op.from)\n // \u56DE\u663E\u8BED\u4E49\u4E0D\u662F\u65F6\u95F4\u6233\u800C\u662F\u300C\u8BE5 from \u5DF2\u4ECE\u6620\u5C04\u5217\u8868\u6D88\u5931\u300D\uFF1BechoAt \u5408\u6210\u4EE5\u590D\u7528 triggerAction\n const action = gone ? 'done' : triggerAction(0, op.issuedAt, op.lastSentAt, now)\n if (action === 'done' || action === 'timeout') this.pendingMappingDelete = null\n else if (action === 'resend') {\n op.lastSentAt = now\n void this.statusScope.set('mappingDeleteJson', encodeMappingDelete(op.issuedAt, op.from)).catch(() => {})\n }\n }\n this.maybeStopTriggerWatch()\n this.reproject()\n }\n\n /** pending \u51FA\u73B0\u65F6\u8D77 1s \u9A71\u52A8\u8282\u62CD\uFF08\u5199\u88AB\u9759\u9ED8\u4E22\u5F03\u65F6\u6CA1\u6709\u4EFB\u4F55\u4E8B\u4EF6\u53EF\u7B49\uFF0C\u5FC5\u987B\u4E3B\u52A8\u8F6E\u8BE2\uFF09\u3002 */\n private ensureTriggerWatch(): void {\n if (this.triggerWatch !== null) return\n this.triggerWatch = setInterval(() => { this.driveTriggers() }, TRIGGER_RETRY_MS)\n }\n\n private maybeStopTriggerWatch(): void {\n if (this.pendingTest !== null || this.pendingSync !== null\n || this.pendingRestoreList !== null || this.pendingRestore !== null || this.pendingMappingDelete !== null) return\n if (this.triggerWatch === null) return\n clearInterval(this.triggerWatch)\n this.triggerWatch = null\n }\n\n // ---- M4 \u52A8\u4F5C ----\n\n /** \u6253\u5F00\u6062\u590D\u5BF9\u8BDD\u6846\u5E76\u53D1\u8D77\u76EE\u5F55\u62C9\u53D6\uFF08\u6BCF\u6B21\u6253\u5F00\u90FD\u91CD\u65B0\u62C9\uFF0C\u4FDD\u8BC1 existsLocal \u7B49\u6807\u6CE8\u65B0\u9C9C\uFF09\u3002 */\n private openRestore(): void {\n this.restoreOpen = true\n if (this.pendingRestoreList === null) {\n const op: PendingOp = { issuedAt: Date.now(), lastSentAt: 0 }\n this.pendingRestoreList = op\n op.lastSentAt = op.issuedAt\n void this.statusScope.set('restoreListRequestedAt', op.issuedAt).catch(() => {})\n this.ensureTriggerWatch()\n }\n this.reproject()\n }\n\n /** \u76EE\u5F55\u9009\u62E9\u5668\u6539\u9009\u4E00\u884C\u843D\u4F4D\u8DEF\u5F84\u3002 */\n private async pickTarget(key: string): Promise<void> {\n if (!this.directoryPicker) return\n try {\n const result = await this.directoryPicker.pick()\n if (result.ok && result.value !== null) {\n this.overrides.set(key, result.value)\n this.reproject()\n }\n } catch (error) {\n console.warn('cloud-sync: directory pick failed:', error)\n }\n }\n\n /** \u7EC4\u88C5\u6062\u590D\u8BF7\u6C42\u6761\u76EE\uFF1A\u6539\u9009\u4F18\u5148\uFF0C\u5176\u6B21 Host \u89E3\u6790\u7ED3\u679C\uFF0C\u90FD\u6CA1\u6709\u5219 null\uFF08\u539F\u6837\u843D\u4F4D\uFF09\u3002 */\n private restoreItems(): { sessionId: string; device: string; targetCwd: string | null }[] {\n const catalog = this.catalog\n if (catalog === null) return []\n const items: { sessionId: string; device: string; targetCwd: string | null }[] = []\n for (const entry of catalog.sessions) {\n const key = rowKeyOf(entry)\n if (!this.selectedKeys.has(key) || !isRowRestorable(entry)) continue\n items.push({\n sessionId: entry.sessionId,\n device: entry.device,\n targetCwd: this.overrides.get(key) ?? entry.resolvedCwd,\n })\n }\n return items\n }\n\n /** \u53D1\u8D77\u6062\u590D\u6267\u884C\u89E6\u53D1\u5668\uFF08\u91CD\u8BD5\u4FDD\u6301\u540C\u4E00 at \u4E0E\u6761\u76EE\u96C6\uFF09\u3002 */\n private startRestore(): void {\n if (this.pendingRestore !== null) return\n const items = this.restoreItems()\n if (items.length === 0) return\n const op: PendingOp = { issuedAt: Date.now(), lastSentAt: 0 }\n this.pendingRestore = op\n op.lastSentAt = op.issuedAt\n void this.statusScope.set('restoreRequestJson', encodeRestoreRequest(op.issuedAt, items)).catch(() => {})\n this.ensureTriggerWatch()\n this.reproject()\n }\n\n /** \u5220\u9664\u4E00\u6761\u5DF2\u5B66\u4E60\u6620\u5C04\uFF08\u4E50\u89C2\u79FB\u9664 + \u89E6\u53D1\u5668\u91CD\u8BD5\u5230 Host \u56DE\u663E\u6D88\u5931\uFF09\u3002 */\n private startMappingDelete(from: string): void {\n if (this.pendingMappingDelete !== null) return\n const op: PendingOp & { from: string } = { issuedAt: Date.now(), lastSentAt: 0, from }\n this.pendingMappingDelete = op\n op.lastSentAt = op.issuedAt\n void this.statusScope.set('mappingDeleteJson', encodeMappingDelete(op.issuedAt, from)).catch(() => {})\n this.ensureTriggerWatch()\n this.reproject()\n }\n\n /** \u542F\u52A8\u901A\u77E5\u5DF2\u8BFB\uFF1A\u672C\u6B21 at \u8BB0\u5165 sessionStorage\uFF0C\u540C at \u4E0D\u518D\u5F39\uFF0801 \u00A75.5 \u4E0D\u91CD\u590D\u5F39\u51FA\uFF09\u3002 */\n private markNoticeSeen(): void {\n const notice = parseStartupNotice(this.statusScope.getSnapshot().value?.startupNoticeJson ?? '')\n if (notice !== null) {\n this.noticeSeenAt = notice.at\n try {\n globalThis.sessionStorage?.setItem(NOTICE_SEEN_KEY, String(notice.at))\n } catch {\n // sessionStorage \u4E0D\u53EF\u7528\u65F6\u4EC5\u672C\u6B21\u9875\u9762\u751F\u547D\u5468\u671F\u5185\u6709\u6548\n }\n }\n this.reproject()\n }\n\n /** \u5C55\u793A\u540C\u6B65\u7ED3\u679C\u6458\u8981\uFF0C\u77ED\u6682\u505C\u7559\u540E\u81EA\u52A8\u6D88\u5931\u3002 */\n private showSyncResult(result: SyncAllResult): void {\n this.syncResult = result\n if (this.syncResultTimer !== null) clearTimeout(this.syncResultTimer)\n this.syncResultTimer = setTimeout(() => {\n this.syncResult = null\n this.syncResultTimer = null\n this.reproject()\n }, SYNC_RESULT_VISIBLE_MS)\n }\n\n /** \u670D\u52A1\u5668\u5730\u5740\u63D0\u4EA4\uFF08blur/Enter\uFF09\uFF1A\u975E\u6CD5\u503C\u4E0D\u843D\u76D8\uFF0C\u5C31\u5730\u62A5\u9519\u4EA4\u7ED9\u6295\u5F71\u3002 */\n private commitServerUrl(): void {\n const text = this.serverUrlDraft.trim()\n this.serverUrlDraft = text\n if (text !== '' && !isValidServerUrl(text)) {\n this.reproject()\n return\n }\n if (text === this.serverUrlSynced) return\n this.serverUrlSynced = text\n void this.configScope.set('serverUrl', text).catch(() => {})\n }\n\n /** \u7528\u6237\u540D\u63D0\u4EA4\uFF08blur/Enter\uFF09\uFF1Atrim \u540E\u843D\u76D8\uFF0C\u4E0D\u505A\u683C\u5F0F\u6821\u9A8C\u2014\u2014\u5408\u6CD5\u6027\u7531\u670D\u52A1\u7AEF\n * \u914D\u5BF9\u6821\u9A8C\u88C1\u51B3\uFF08403 \u2192 userMismatch\uFF09\uFF0C\u63D2\u4EF6\u65E0\u4ECE\u611F\u77E5\u670D\u52A1\u7AEF\u662F\u5355\u7528\u6237\u8FD8\u662F\u591A\u7528\u6237\u3002 */\n private commitUsername(): void {\n const text = this.usernameDraft.trim()\n this.usernameDraft = text\n if (text === this.usernameSynced) return\n this.usernameSynced = text\n void this.configScope.set('username', text).catch(() => {})\n }\n\n /** \u8BBE\u5907\u540D\u63D0\u4EA4\uFF1A\u5408\u6CD5\u6216\u7A7A\uFF08= \u56DE\u9000\u4E3B\u673A\u540D\uFF09\u624D\u843D\u76D8\u3002 */\n private commitDeviceName(): void {\n const text = this.deviceNameDraft.trim()\n this.deviceNameDraft = text\n if (!isValidDeviceName(text)) {\n this.reproject()\n return\n }\n if (text === this.deviceNameSynced) return\n this.deviceNameSynced = text\n void this.configScope.set('deviceName', text).catch(() => {})\n }\n\n /** secret \u63D0\u4EA4\uFF1A\u7A7A\u8349\u7A3F\u4E0D\u52A8\u65E2\u6709\u914D\u7F6E\uFF1B\u63D0\u4EA4\u540E\u7ACB\u523B\u6E05\u7A7A\u672C\u5730\u8349\u7A3F\u3002\n * \u4E50\u89C2\u7F6E\u5DF2\u914D\u7F6E\u6807\u8BB0\uFF08Host \u7EA0\u504F\u56DE\u5199\u524D\u7528\u6237\u5373\u53EF\u770B\u5230\u53CD\u9988\uFF09\uFF0C\u5426\u5219\u8F93\u5165\u6846\u6E05\u7A7A\n * \u53C8\u6CA1\u6709\u5373\u65F6\u53CD\u9988\uFF0C\u8BFB\u8D77\u6765\u50CF\"\u503C\u88AB\u541E\u4E86\"\u3002 */\n private commitSecret(field: 'token' | 'passphrase'): void {\n const text = field === 'token' ? this.tokenDraft : this.passphraseDraft\n if (text === '') return\n if (field === 'token') {\n this.tokenDraft = ''\n this.tokenCommitted = true\n } else {\n this.passphraseDraft = ''\n this.passphraseCommitted = true\n }\n this.reproject()\n void this.configScope.set(field, text).catch(() => {})\n }\n\n /** \u5E03\u5C14\u5F00\u5173\uFF1A\u70B9\u51FB\u5373\u5199\uFF08\u914D\u7F6E\u5373\u65F6\u4FDD\u5B58\uFF0C\u65E0\"\u786E\u5B9A\"\u6309\u94AE\uFF09\u3002 */\n private toggleBoolean(field: 'autoUpload' | 'checkOnStartup'): void {\n const current = this.configScope.getSnapshot().value?.[field] ?? true\n void this.configScope.set(field, !current).catch(() => {})\n }\n\n private reproject(): void {\n this.store.set(this.projection())\n }\n\n private projection(): CloudSyncCardState {\n const config = this.configScope.getSnapshot()\n const status = this.statusScope.getSnapshot().value\n // \u4EE4\u724C\u300C\u53EF\u7528\u300D= Host \u5DF2\u914D\u7F6E\uFF0C\u6216\u672C\u6B21\u65B0\u586B\u4E86\u8349\u7A3F\uFF08\u5C1A\u672A\u5199\u56DE\u4E5F\u53EF\u53D1\u8D77\u6D4B\u8BD5\uFF09\n const tokenAvailable = status?.tokenConfigured === true || this.tokenDraft !== ''\n return {\n available: config.status !== 'unavailable',\n writable: config.writable,\n serverUrlDraft: this.serverUrlDraft,\n serverUrlInvalid: this.serverUrlDraft !== '' && !isValidServerUrl(this.serverUrlDraft),\n usernameDraft: this.usernameDraft,\n deviceNameDraft: this.deviceNameDraft,\n deviceNameInvalid: !isValidDeviceName(this.deviceNameDraft),\n tokenDraft: this.tokenDraft,\n passphraseDraft: this.passphraseDraft,\n tokenConfigured: status?.tokenConfigured === true || this.tokenCommitted,\n passphraseConfigured: status?.passphraseConfigured === true || this.passphraseCommitted,\n autoUpload: config.value?.autoUpload ?? true,\n checkOnStartup: config.value?.checkOnStartup ?? true,\n canTest: canRunServerAction(this.serverUrlDraft, tokenAvailable),\n canSync: canRunServerAction(this.serverUrlDraft, tokenAvailable),\n testPending: this.pendingTest !== null,\n syncPending: this.pendingSync !== null,\n statusLine: projectStatusLine({\n phase: status?.statusPhase ?? 'unconfigured',\n error: status?.statusError ?? '',\n errorDetail: status?.statusErrorDetail ?? '',\n cloudCount: status?.statusCloudCount ?? 0,\n lastSyncAt: status?.statusLastSyncAt ?? 0,\n now: this.now,\n }),\n syncResult: this.syncResult,\n restoreOpen: this.restoreOpen,\n catalog: this.catalog,\n catalogPending: this.pendingRestoreList !== null,\n restorePending: this.pendingRestore !== null,\n restoreOutcome: this.restoreOutcome,\n deviceFilter: this.deviceFilter,\n selectedKeys: [...this.selectedKeys],\n overrides: Object.fromEntries(this.overrides),\n mappings: parseMappings(status?.pathMappingsJson ?? '[]'),\n notice: this.projectNotice(status?.startupNoticeJson ?? ''),\n pickerAvailable: this.directoryPicker !== undefined,\n }\n }\n\n /** \u542F\u52A8\u901A\u77E5\u6295\u5F71\uFF1A\u672A\u8BFB\uFF08at \u665A\u4E8E\u5DF2\u8BFB\u6807\u8BB0\uFF09\u624D\u5F39\u51FA\u3002 */\n private projectNotice(raw: string): StartupNoticePayload | null {\n const notice = parseStartupNotice(raw)\n if (notice === null || notice.at <= this.noticeSeenAt) return null\n return notice\n }\n}\n", "/**\n * \u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361\u6587\u6848\u5B57\u5178\uFF08locale-owned\uFF09\u3002\n * \u94C1\u5F8B\uFF1A\u5361\u7247\u5168\u90E8\u53EF\u89C1\u6587\u6848\u53EA\u80FD\u51FA\u73B0\u5728\u672C\u6587\u4EF6 \u2014\u2014 dsh \u7684 verify-client-ui-i18n\n * \u7EA6\u5B9A\u7981\u6B62 JSX \u6587\u672C\u4E0E label/placeholder/title/aria-* \u5C5E\u6027\u51FA\u73B0\u81EA\u7136\u8BED\u8A00\u5B57\u9762\u91CF\u3002\n * zh/en \u5FC5\u987B\u540C key\uFF1B`{name}` \u4E3A\u6A21\u677F\u53C2\u6570\u4F4D\u3002\n */\n\n/** \u4E2D\u6587\u5B57\u5178\uFF08key \u96C6\u5408\u662F CloudSyncLocaleKey \u7684\u552F\u4E00\u4E8B\u5B9E\u6765\u6E90\uFF09\u3002 */\nexport const zh = {\n title: '\u4E91\u7AEF\u540C\u6B65',\n description: '\u628A\u4F1A\u8BDD\u7AEF\u5230\u7AEF\u52A0\u5BC6\u540C\u6B65\u5230\u81EA\u5EFA\u670D\u52A1\u5668',\n 'section.connection': '\u670D\u52A1\u5668\u8FDE\u63A5',\n 'section.sync': '\u540C\u6B65\u65B9\u5F0F',\n 'section.actions': '\u64CD\u4F5C',\n 'field.serverUrl': '\u670D\u52A1\u5668\u5730\u5740',\n 'field.serverUrl.placeholder': 'http://host:8787',\n 'field.username': '\u7528\u6237\u540D',\n 'field.username.hint': '\u591A\u7528\u6237\u670D\u52A1\u7AEF\u586B\u7BA1\u7406\u5458\u5206\u914D\u7684\u7528\u6237\u540D\uFF1B\u5355\u7528\u6237\u670D\u52A1\u7AEF\u7559\u7A7A',\n 'field.token': '\u8BBF\u95EE\u4EE4\u724C',\n 'field.passphrase': '\u52A0\u5BC6\u53E3\u4EE4',\n 'field.deviceName': '\u8BBE\u5907\u540D',\n 'field.deviceName.hint': '\u7559\u7A7A\u4F7F\u7528\u672C\u673A\u4E3B\u673A\u540D',\n 'field.autoUpload': '\u81EA\u52A8\u4E0A\u4F20',\n 'field.autoUpload.hint': '\u4F1A\u8BDD\u6709\u65B0\u5185\u5BB9\u65F6\u540E\u53F0\u63A8\u9001',\n 'field.checkOnStartup': '\u542F\u52A8\u65F6\u68C0\u67E5\u4E91\u7AEF',\n 'field.checkOnStartup.hint': '\u53D1\u73B0\u5176\u4ED6\u8BBE\u5907\u7684\u4F1A\u8BDD\u65F6\u63D0\u793A',\n 'field.configured': '\u5DF2\u914D\u7F6E',\n 'field.unconfigured': '\u672A\u914D\u7F6E',\n 'field.secret.show': '\u663E\u793A',\n 'field.secret.hide': '\u9690\u85CF',\n 'invalid.serverUrl': '\u8BF7\u8F93\u5165\u5408\u6CD5\u7684 http:// \u6216 https:// \u5730\u5740',\n 'invalid.deviceName': '\u4EC5\u9650\u5B57\u6BCD\u3001\u6570\u5B57\u3001- \u548C _\uFF0C\u4E0D\u8D85\u8FC7 32 \u4E2A\u5B57\u7B26',\n 'action.testConnection': '\u6D4B\u8BD5\u8FDE\u63A5',\n 'action.testing': '\u6D4B\u8BD5\u4E2D\u2026',\n 'action.syncAll': '\u7ACB\u5373\u5168\u90E8\u540C\u6B65',\n 'action.syncing': '\u540C\u6B65\u4E2D\u2026',\n 'status.unconfigured': '\u672A\u914D\u7F6E',\n 'status.connected': '\u5DF2\u8FDE\u63A5 \u00B7 \u4E91\u7AEF {count} \u4E2A\u4F1A\u8BDD',\n 'status.lastSync': '\u4E0A\u6B21\u540C\u6B65 {time}',\n 'status.disconnected': '\u8FDE\u63A5\u5931\u8D25\uFF1A{reason}',\n 'status.warnMark': '\u26A0',\n 'error.unreachable': '\u670D\u52A1\u5668\u5730\u5740\u4E0D\u53EF\u8FBE',\n 'error.tokenInvalid': '\u8BBF\u95EE\u4EE4\u724C\u65E0\u6548',\n 'error.userMismatch': '\u7528\u6237\u540D\u4E0E\u8BBF\u95EE\u4EE4\u724C\u4E0D\u5339\u914D',\n 'error.passphraseMismatch': '\u52A0\u5BC6\u53E3\u4EE4\u4E0E\u4E91\u7AEF\u6570\u636E\u4E0D\u5339\u914D',\n 'error.deviceConflict': '\u8BBE\u5907\u540D\u4E0E\u4E91\u7AEF\u5DF2\u6709\u6570\u636E\u51B2\u7A81\uFF0C\u8BF7\u66F4\u6362\u8BBE\u5907\u540D',\n 'error.unknown': '\u540C\u6B65\u5931\u8D25',\n 'time.justNow': '\u521A\u521A',\n 'time.minutesAgo': '{count} \u5206\u949F\u524D',\n 'time.hoursAgo': '{count} \u5C0F\u65F6\u524D',\n 'time.daysAgo': '{count} \u5929\u524D',\n 'sync.result': '\u6210\u529F {ok} \u00B7 \u5931\u8D25 {failed}',\n 'section.mappings': '\u8DEF\u5F84\u6620\u5C04',\n 'mapping.empty': '\u6062\u590D\u4F1A\u8BDD\u65F6\u81EA\u52A8\u5B66\u4E60\uFF0C\u53EF\u5220\u9664\u540E\u91CD\u65B0\u5EFA\u7ACB',\n 'mapping.delete': '\u5220\u9664',\n 'action.restore': '\u4ECE\u4E91\u7AEF\u6062\u590D\u4F1A\u8BDD\u2026',\n 'restore.title': '\u4ECE\u4E91\u7AEF\u6062\u590D\u4F1A\u8BDD',\n 'restore.close': '\u5173\u95ED',\n 'restore.cancel': '\u53D6\u6D88',\n 'restore.loading': '\u6B63\u5728\u52A0\u8F7D\u4E91\u7AEF\u4F1A\u8BDD\u2026',\n 'restore.empty': '\u4E91\u7AEF\u6CA1\u6709\u4F1A\u8BDD',\n 'restore.filter.all': '\u5168\u90E8\u8BBE\u5907',\n 'restore.column.title': '\u6807\u9898',\n 'restore.column.updated': '\u6700\u540E\u66F4\u65B0',\n 'restore.column.events': '\u4E8B\u4EF6\u6570',\n 'restore.column.target': '\u843D\u4F4D\u8DEF\u5F84',\n 'restore.pathSuggested': '\u5EFA\u8BAE',\n 'restore.pathMapped': '\u5DF2\u6620\u5C04',\n 'restore.unmatchedPath': '\u6062\u590D\u540E\u672A\u5206\u7EC4',\n 'restore.pickDirectory': '\u9009\u62E9\u76EE\u5F55',\n 'restore.manualPath.placeholder': '\u8F93\u5165\u672C\u673A\u76EE\u5F55\u8DEF\u5F84',\n 'restore.existsLocal': '\u672C\u5730\u5DF2\u5B58\u5728',\n 'restore.versionIncompatible': '\u7248\u672C\u4E0D\u517C\u5BB9',\n 'restore.action': '\u6062\u590D\u9009\u4E2D\u4F1A\u8BDD',\n 'restore.restoring': '\u6062\u590D\u4E2D\u2026',\n 'restore.selected': '\u5DF2\u9009 {count} \u4E2A',\n 'restore.result': '\u6062\u590D\u5B8C\u6210\uFF1A\u6210\u529F {ok} \u00B7 \u5931\u8D25 {failed}',\n 'toast.newCloudSessions': '\u4E91\u7AEF\u6709\u6765\u81EA\u5176\u4ED6\u8BBE\u5907\u7684\u4F1A\u8BDD\uFF0C\u70B9\u51FB\u67E5\u770B',\n 'toast.view': '\u67E5\u770B',\n 'toast.dismiss': '\u5173\u95ED',\n} as const\n\n/** \u5B57\u5178 key \u8054\u5408\u7C7B\u578B\uFF08zh \u7684 key \u96C6\u5408\u5373\u4E8B\u5B9E\u6765\u6E90\uFF0Cen \u7ECF Record \u7EA6\u675F\u4FDD\u6301\u540C key\uFF09\u3002 */\nexport type CloudSyncLocaleKey = keyof typeof zh\n\n/** \u82F1\u6587\u5B57\u5178\u3002 */\nexport const en: Record<CloudSyncLocaleKey, string> = {\n title: 'Cloud Sync',\n description: 'End-to-end encrypted sync of sessions to your own server',\n 'section.connection': 'Server connection',\n 'section.sync': 'Sync',\n 'section.actions': 'Actions',\n 'field.serverUrl': 'Server address',\n 'field.serverUrl.placeholder': 'http://host:8787',\n 'field.username': 'Username',\n 'field.username.hint': 'Only for multi-user servers (assigned by the admin); leave empty otherwise',\n 'field.token': 'Access token',\n 'field.passphrase': 'Encryption passphrase',\n 'field.deviceName': 'Device name',\n 'field.deviceName.hint': 'Leave blank to use the hostname',\n 'field.autoUpload': 'Automatic upload',\n 'field.autoUpload.hint': 'Push in the background when sessions change',\n 'field.checkOnStartup': 'Check cloud on startup',\n 'field.checkOnStartup.hint': 'Prompt when other devices have sessions',\n 'field.configured': 'Configured',\n 'field.unconfigured': 'Not configured',\n 'field.secret.show': 'Show',\n 'field.secret.hide': 'Hide',\n 'invalid.serverUrl': 'Enter a valid http:// or https:// URL',\n 'invalid.deviceName': 'Letters, digits, - and _ only, up to 32 characters',\n 'action.testConnection': 'Test connection',\n 'action.testing': 'Testing\u2026',\n 'action.syncAll': 'Sync all now',\n 'action.syncing': 'Syncing\u2026',\n 'status.unconfigured': 'Not configured',\n 'status.connected': 'Connected \u00B7 {count} sessions in cloud',\n 'status.lastSync': 'Last synced {time}',\n 'status.disconnected': 'Connection failed: {reason}',\n 'status.warnMark': '\u26A0',\n 'error.unreachable': 'Server unreachable',\n 'error.tokenInvalid': 'Invalid access token',\n 'error.userMismatch': 'Username does not match the access token',\n 'error.passphraseMismatch': 'Passphrase does not match cloud data',\n 'error.deviceConflict': 'Device name conflicts with cloud data; rename this device',\n 'error.unknown': 'Sync failed',\n 'time.justNow': 'just now',\n 'time.minutesAgo': '{count} min ago',\n 'time.hoursAgo': '{count} h ago',\n 'time.daysAgo': '{count} d ago',\n 'sync.result': 'Succeeded {ok} \u00B7 failed {failed}',\n 'section.mappings': 'Path mappings',\n 'mapping.empty': 'Learned automatically when restoring sessions; delete to re-establish',\n 'mapping.delete': 'Delete',\n 'action.restore': 'Restore from cloud\u2026',\n 'restore.title': 'Restore sessions from cloud',\n 'restore.close': 'Close',\n 'restore.cancel': 'Cancel',\n 'restore.loading': 'Loading cloud sessions\u2026',\n 'restore.empty': 'No sessions in cloud',\n 'restore.filter.all': 'All devices',\n 'restore.column.title': 'Title',\n 'restore.column.updated': 'Last updated',\n 'restore.column.events': 'Events',\n 'restore.column.target': 'Target path',\n 'restore.pathSuggested': 'Suggested',\n 'restore.pathMapped': 'Mapped',\n 'restore.unmatchedPath': 'Ungrouped after restore',\n 'restore.pickDirectory': 'Choose directory',\n 'restore.manualPath.placeholder': 'Enter a local directory path',\n 'restore.existsLocal': 'Exists locally',\n 'restore.versionIncompatible': 'Version incompatible',\n 'restore.action': 'Restore selected',\n 'restore.restoring': 'Restoring\u2026',\n 'restore.selected': '{count} selected',\n 'restore.result': 'Restore finished: {ok} succeeded \u00B7 {failed} failed',\n 'toast.newCloudSessions': 'Sessions from other devices found in cloud \u2014 click to view',\n 'toast.view': 'View',\n 'toast.dismiss': 'Dismiss',\n}\n\n// \u5411\u69FD\u7CFB\u7EDF\u58F0\u660E\u672C\u63D2\u4EF6\u7684\u5B57\u5178\u547D\u540D\u7A7A\u95F4\uFF1A\u6CE8\u518C\u5904\u58F0\u660E locale: 'settings.cloudSync' \u540E\uFF0C\n// \u6846\u67B6\u4F1A\u628A\u7C7B\u578B\u5316\u4E3A\u672C\u5B57\u5178\u7684 t \u5EA7\u4F4D\u653E\u5230\u7EC4\u4EF6 props \u4E0A\uFF08\u540C SlotMap \u7684\u5408\u5E76\u60EF\u4F8B\uFF09\u3002\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n 'settings.cloudSync': CloudSyncLocaleKey\n }\n}\n", "/**\n * \u5361\u7247\u6837\u5F0F\u4E0E\u6CE8\u5165\u5668\uFF1A\u4E0D\u7528 CSS modules\uFF08\u5916\u90E8\u5305\u6CA1\u6709 tsdown \u7684\u865A\u62DF\u6A21\u5757\u7BA1\u7EBF\uFF09\uFF0C\n * \u6539\u4E3A\u300C\u6837\u5F0F\u6587\u672C + \u5E26 data-plugin \u6807\u8BB0\u7684 <style> \u6CE8\u5165\u5668\u300D\uFF0C\u601D\u8DEF\u4EFF\n * deepseek-harness packages/client/tsdown.client.ts \u7684 styleInjectionModule \u7B80\u5316\u7248\u3002\n * \u989C\u8272\u5168\u90E8\u8D70 dsw token\uFF08--dsw-alias-*\uFF09\uFF0C\u7C7B\u540D\u7EDF\u4E00 dsh-cloud-sync-card \u524D\u7F00\u9632\u649E\u540D\u3002\n */\n\n/** \u6837\u5F0F\u6587\u672C\u3002 */\nexport const CARD_CSS = `\n.dsh-cloud-sync-card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n}\n.dsh-cloud-sync-card__header {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 14px 16px;\n}\n.dsh-cloud-sync-card__name {\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__description {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding: 4px 0 12px;\n}\n.dsh-cloud-sync-card__section {\n padding: 12px 0 0;\n}\n.dsh-cloud-sync-card__section + .dsh-cloud-sync-card__section {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin-top: 12px;\n}\n.dsh-cloud-sync-card__section-title {\n margin: 0 0 4px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 0;\n}\n.dsh-cloud-sync-card__field-head {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.dsh-cloud-sync-card__label {\n flex: 1;\n min-width: 0;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__badge {\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n line-height: 17px;\n white-space: nowrap;\n font-weight: 500;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-card__badge--muted {\n background: none;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__input {\n display: block;\n width: 100%;\n box-sizing: border-box;\n height: 34px;\n padding: 0 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n.dsh-cloud-sync-card__input:focus-visible {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n.dsh-cloud-sync-card__input:disabled {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n.dsh-cloud-sync-card__input[aria-invalid='true'] {\n border-color: var(--dsw-alias-label-error);\n}\n/* secret \u884C\uFF1A\u5916\u6846\u662F\u89C6\u89C9\u63A7\u4EF6\uFF08\u7126\u70B9\u73AF\u843D\u5728\u884C\u4E0A\uFF09\uFF0C\u8F93\u5165\u6846\u88F8\u5D4C\u3001\uD83D\uDC41 \u6536\u8FDB\u6846\u5185\u3002\n \u6CE8\u610F\u8F93\u5165\u6846\u81EA\u8EAB\u4E0D\u80FD\u518D\u5E26 flex: 1 \u2014\u2014 \u5728\u7EB5\u5411 flex \u5BB9\u5668\u91CC flex-basis: 0%\n \u4F1A\u628A\u9AD8\u5EA6\u538B\u6CA1\uFF08M3 \u9A8C\u6536\u5B9E\u6D4B text \u8F93\u5165\u584C\u5230 15px\uFF09\uFF0C\u53EA\u80FD\u653E\u5728\u6A2A\u5411\u884C\u91CC\u7528\u3002 */\n.dsh-cloud-sync-card__input-row {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 34px;\n box-sizing: border-box;\n padding: 0 6px 0 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-3);\n}\n.dsh-cloud-sync-card__input-row:focus-within {\n border-color: var(--dsw-alias-brand-primary);\n}\n.dsh-cloud-sync-card__input-row .dsh-cloud-sync-card__input {\n flex: 1;\n min-width: 0;\n height: auto;\n padding: 0;\n border: none;\n background: transparent;\n}\n.dsh-cloud-sync-card__input-row .dsh-cloud-sync-card__input:focus-visible {\n border: none;\n}\n.dsh-cloud-sync-card__hint {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__error {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n.dsh-cloud-sync-card__icon-button {\n flex: none;\n appearance: none;\n border: none;\n background: none;\n padding: 4px;\n font: inherit;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n border-radius: 6px;\n}\n.dsh-cloud-sync-card__icon-button:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__icon-button:disabled {\n cursor: default;\n opacity: 0.4;\n}\n.dsh-cloud-sync-card__row-end {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 4px 0;\n}\n.dsh-cloud-sync-card__button {\n appearance: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 5px 14px;\n background: none;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n.dsh-cloud-sync-card__button:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-cloud-sync-card__button:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.dsh-cloud-sync-card__button:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n.dsh-cloud-sync-card__button--primary {\n background: var(--dsw-alias-label-primary);\n border-color: transparent;\n color: var(--dsw-alias-bg-layer-3);\n}\n.dsh-cloud-sync-card__button--primary:hover:not(:disabled) {\n color: var(--dsw-alias-bg-layer-3);\n border-color: transparent;\n}\n.dsh-cloud-sync-card__status {\n display: flex;\n align-items: baseline;\n gap: 6px;\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-card__status--muted {\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__status--error {\n color: var(--dsw-alias-label-error);\n}\n.dsh-cloud-sync-card__dot {\n position: relative;\n top: -1px;\n align-self: center;\n flex: none;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__dot[data-state='done'] {\n background: var(--dsw-alias-state-success-primary);\n}\n.dsh-cloud-sync-card__dot[data-state='warning'] {\n background: var(--dsw-alias-state-warn-primary);\n}\n.dsh-cloud-sync-card__dot[data-state='error'] {\n background: var(--dsw-alias-state-error-primary);\n}\n.dsh-cloud-sync-card__warn {\n color: var(--dsw-alias-state-warn-primary);\n}\n.dsh-cloud-sync-card__switch-row {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 0;\n}\n.dsh-cloud-sync-card__switch-label {\n width: 120px;\n flex: none;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__switch {\n position: relative;\n flex: none;\n width: 36px;\n height: 20px;\n border: none;\n border-radius: 999px;\n padding: 0;\n background: var(--dsw-alias-bg-module-platform);\n cursor: pointer;\n transition: background .16s;\n}\n.dsh-cloud-sync-card__switch[data-on='true'] {\n background: var(--dsw-alias-brand-primary);\n}\n.dsh-cloud-sync-card__switch:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.dsh-cloud-sync-card__switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n.dsh-cloud-sync-card__switch-knob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--dsw-alias-bg-layer-3);\n transition: transform .16s;\n}\n.dsh-cloud-sync-card__switch[data-on='true'] .dsh-cloud-sync-card__switch-knob {\n transform: translateX(16px);\n}\n.dsh-cloud-sync-card__sync-result {\n margin: 0 0 4px;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n/* ---- M4\uFF1A\u8DEF\u5F84\u6620\u5C04\u533A ---- */\n.dsh-cloud-sync-card__mappings {\n list-style: none;\n margin: 4px 0 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.dsh-cloud-sync-card__mapping {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n}\n.dsh-cloud-sync-card__mapping-paths {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-card__mapping-arrow {\n margin: 0 6px;\n color: var(--dsw-alias-label-dimmed);\n}\n/* ---- M4\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846 ---- */\n.dsh-cloud-sync-restore {\n width: 720px;\n max-width: calc(100vw - 64px);\n}\n.dsh-cloud-sync-restore__content {\n max-height: 60vh;\n overflow-y: auto;\n}\n.dsh-cloud-sync-restore__filter {\n display: flex;\n justify-content: flex-end;\n margin-bottom: 8px;\n}\n.dsh-cloud-sync-restore__select {\n appearance: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 4px 10px;\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-restore__head,\n.dsh-cloud-sync-restore__row {\n display: grid;\n grid-template-columns: 24px minmax(0, 2fr) 88px 56px minmax(0, 2.2fr);\n align-items: center;\n gap: 10px;\n}\n.dsh-cloud-sync-restore__head {\n padding: 4px 0;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-cloud-sync-restore__list {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n.dsh-cloud-sync-restore__row {\n padding: 8px 0;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-cloud-sync-restore__row[data-disabled='true'] {\n opacity: 0.55;\n}\n.dsh-cloud-sync-restore__title {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-restore__device {\n margin-left: 6px;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n.dsh-cloud-sync-restore__updated,\n.dsh-cloud-sync-restore__events {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-restore__target {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n.dsh-cloud-sync-restore__path {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-restore__badge {\n flex: none;\n margin-left: 6px;\n border-radius: 999px;\n padding: 0 6px;\n font-size: 10px;\n line-height: 16px;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-restore__badge--muted {\n background: none;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-restore__unmatched {\n flex: none;\n font-size: 12px;\n color: var(--dsw-alias-state-warn-primary);\n}\n.dsh-cloud-sync-restore__pick {\n flex: none;\n appearance: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 2px 8px;\n background: none;\n font: inherit;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n.dsh-cloud-sync-restore__manual {\n flex: 1;\n min-width: 0;\n height: 26px;\n padding: 0 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-restore__selected {\n flex: 1;\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n}\n/* ---- M4\uFF1A\u542F\u52A8\u68C0\u67E5\u901A\u77E5\uFF0801 \u00A75.5 \u53F3\u4E0B\u89D2\u8F7B\u63D0\u793A\uFF1Bshell.overlay \u5C42\u9ED8\u8BA4 click-through\uFF0C\u9700\u81EA\u884C\u6062\u590D\u6307\u9488\u4E8B\u4EF6\uFF09 ---- */\n.dsh-cloud-sync-notice {\n position: fixed;\n right: 20px;\n bottom: 20px;\n z-index: 60;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-3);\n box-shadow: 0 8px 24px rgb(0 0 0 / 18%);\n pointer-events: auto;\n}\n.dsh-cloud-sync-notice__text {\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-notice__action {\n appearance: none;\n border: none;\n background: none;\n padding: 0;\n font: inherit;\n font-size: 13px;\n color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n.dsh-cloud-sync-notice__dismiss {\n appearance: none;\n border: none;\n background: none;\n padding: 2px 4px;\n font: inherit;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n`\n\n/** \u6837\u5F0F\u6807\u7B7E\u7684\u5E42\u7B49\u6807\u8BB0\uFF08\u91CD\u590D\u6302\u8F7D/\u91CD\u653E\u65F6\u53EA\u6CE8\u5165\u4E00\u6B21\uFF09\u3002 */\nconst STYLE_TAG_ID = 'dsh-cloud-sync/card'\n\n/** \u628A\u5361\u7247\u6837\u5F0F\u6CE8\u5165 document.head\uFF1B\u975E\u6D4F\u89C8\u5668\u73AF\u5883\uFF08node \u5355\u6D4B\uFF09\u76F4\u63A5\u8DF3\u8FC7\u3002 */\nexport function installCardStyles(): void {\n if (typeof document === 'undefined') return\n if (document.querySelector(`style[data-plugin-css=\"${STYLE_TAG_ID}\"]`) !== null) return\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-cloud-sync'\n tag.dataset.pluginCss = STYLE_TAG_ID\n tag.textContent = CARD_CSS\n document.head.appendChild(tag)\n}\n"],
|
|
5
|
-
"mappings": "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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgfxB,IAAM,eAAe;AAGd,SAAS,oBAA0B;AACxC,MAAI,OAAO,aAAa,YAAa;AACrC,MAAI,SAAS,cAAc,0BAA0B,YAAY,IAAI,MAAM,KAAM;AACjF,QAAM,MAAM,SAAS,cAAc,OAAO;AAC1C,MAAI,QAAQ,SAAS;AACrB,MAAI,QAAQ,YAAY;AACxB,MAAI,cAAc;AAClB,WAAS,KAAK,YAAY,GAAG;AAC/B;;;APhfA,IAAM,YAAY;AAGX,IAAM,SAAS,CAAC,SAAS,UAAU,eAAe;AAMlD,SAAS,MAAM,KAA0B;AAC9C,MAAI,OAAO,MAAM,IAAI,OAAO,SAAS,WAAW,EAAE,IAAI,GAAG,CAAC,GAAG,0BAA0B;AAGvF,QAAM,kBAAmB,IAAI,IAAI,QAAQ,GAA6D;AACtG,QAAM,aAAa,IAAI;AAAA,IACrB,IAAI,cAAc,KAAK,EAAE,WAAW,cAAc,CAAC;AAAA,IACnD,IAAI,cAAc,KAAK,EAAE,WAAW,qBAAqB,CAAC;AAAA,IAC1D;AAAA,EACF;AACA,MAAI,OAAO,MAAM,MAAM;AAAE,eAAW,QAAQ;AAAA,EAAE,GAAG,6BAA6B;AAC9E,oBAAkB;AAClB,MAAI,MAAM,OAAO,wBAAwB,MAAM,IAAI,MAAM,SAAS;AAAA,IAChE,MAAM;AAAA,IACN,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ,MAAM,WAAW,OAAO;AAAA,EAClC,GAAG,aAAa,CAAC;AAEjB,MAAI,MAAM,OAAO,iBAAiB,MAAM,IAAI,MAAM,SAAS;AAAA,IACzD,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ,MAAM,WAAW,OAAO;AAAA,EAClC,GAAG,gBAAgB,CAAC;AACtB;",
|
|
4
|
+
"sourcesContent": ["/**\n * dsh-cloud-sync \u63D2\u4EF6 Client \u534A\u5165\u53E3\uFF08\u6D4F\u89C8\u5668\u4FA7\uFF09\u3002\n * named exports `inject` / `apply`\uFF0C\u65E0 default export\uFF08dsh \u51FD\u6570\u63D2\u4EF6\u7EA6\u5B9A\uFF09\u3002\n * \u4EA7\u7269\u7ECF scripts/build-client.mjs \u6253\u5305\u4E3A __ModuleLoader__ \u7684 lazy-CJS factory\u3002\n */\n\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\n// \u4EE5\u4E0B\u5747\u4E3A\u7C7B\u578B import\uFF08\u6784\u5EFA\u65F6\u88AB\u64E6\u9664\uFF0C\u4E0D\u89E6\u53D1 bundle-purity gate\uFF09\uFF1A\nimport type {} from '@deepseek-ai/dsh-client-locale/client' // ctx.locale \u5408\u5E76\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client' // ctx.settingsScope \u5408\u5E76\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client' // ctx.slots \u5408\u5E76\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client' // settings.plugin.item \u69FD\u7684 SlotMap \u5408\u5E76\nimport { CloudSyncCard } from './CloudSyncCard.tsx'\nimport { CloudSyncOverlay } from './CloudSyncOverlay.tsx'\nimport { CLOUD_SYNC_NS, CLOUD_SYNC_STATUS_NS, CloudSyncCardController, type DirectoryPickerLike } from './cloud-sync-card-controller.ts'\nimport { en, zh } from './locales.ts'\nimport { installCardStyles } from './styles.ts'\n\n/** \u672C\u63D2\u4EF6\u7684\u5B57\u5178\u547D\u540D\u7A7A\u95F4\u3002 */\nconst LOCALE_NS = 'settings.cloudSync'\n\n/** \u9700\u8981\u7684 cordis \u670D\u52A1\uFF08fiber inject\uFF09\u3002 */\nexport const inject = ['slots', 'locale', 'settingsScope']\n\n/**\n * \u6302\u8F7D\u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361\uFF1A\u6CE8\u518C\u5B57\u5178\u3001\u521B\u5EFA controller\u3001\u5411 settings.plugin.item \u69FD\u6CE8\u518C\u5361\u7247\u3002\n * @param ctx - \u6D4F\u89C8\u5668\u63D2\u4EF6\u4E0A\u4E0B\u6587\u3002\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(LOCALE_NS, { zh, en }), 'cloud-sync: dictionaries')\n // \u53CC namespace\uFF1A\u914D\u7F6E\uFF08'cloud-sync'\uFF0CHost \u4E0D\u5199\uFF09\u4E0E\u72B6\u6001/\u89E6\u53D1\uFF08'cloud-sync-status'\uFF0CHost \u9AD8\u9891\u56DE\u5199\uFF09\n // \u76EE\u5F55\u9009\u62E9\u5668\u662F\u53EF\u9009 Remote \u80FD\u529B\uFF08'remote' \u670D\u52A1\u7F3A\u5931\u65F6\u6062\u590D\u5BF9\u8BDD\u6846\u964D\u7EA7\u4E3A\u624B\u52A8\u8F93\u5165\uFF09\n const directoryPicker = (ctx.get('remote') as { directoryPicker?: DirectoryPickerLike } | undefined)?.directoryPicker\n const controller = new CloudSyncCardController(\n ctx.settingsScope.bind({ namespace: CLOUD_SYNC_NS }),\n ctx.settingsScope.bind({ namespace: CLOUD_SYNC_STATUS_NS }),\n directoryPicker,\n )\n ctx.effect(() => () => { controller.dispose() }, 'cloud-sync: card controller')\n installCardStyles()\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n key: CLOUD_SYNC_NS,\n locale: LOCALE_NS,\n inject: () => controller.inject(),\n }, CloudSyncCard))\n // M4\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846\u4E0E\u542F\u52A8\u901A\u77E5\u6302\u5728\u6839\u4F5C\u7528\u57DF\u6D6E\u5C42\u69FD\uFF08\u8BBE\u7F6E\u9875\u5916\u4E5F\u80FD\u6253\u5F00\u5BF9\u8BDD\u6846\uFF09\n ctx.slots.inject('shell.overlay', () => ctx.slots.register({\n name: 'shell.overlay',\n id: 'cloud-sync-overlay',\n order: 100,\n locale: LOCALE_NS,\n inject: () => controller.inject(),\n }, CloudSyncOverlay))\n}\n", "/**\n * \u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361\u7EC4\u4EF6\uFF08M3 \u56DB\u533A\uFF1A\u670D\u52A1\u5668\u8FDE\u63A5 / \u72B6\u6001\u884C / \u540C\u6B65\u65B9\u5F0F / \u64CD\u4F5C\uFF09\u3002\n * \u300C\u4ECE\u4E91\u7AEF\u6062\u590D\u4F1A\u8BDD\u300D\u6309\u94AE\u4E0E\u300C\u8DEF\u5F84\u6620\u5C04\u300D\u533A\u5C5E M4\uFF0C\u672C\u5361\u7247\u4E0D\u5B9E\u73B0\u3002\n * \u5361\u7247 chrome \u81EA\u7ED8\uFF08ui-settings-plugins \u7684 PluginCard/CardForm/fields \u662F\u8BE5\u5305\u5185\u90E8\u5B9E\u73B0\uFF0C\n * \u8DE8\u5305\u503C import \u4F1A\u89E6\u53D1 client bundle-purity gate\uFF09\uFF0C\u6837\u5F0F\u89C1 styles.ts\u3002\n * \u5168\u90E8\u53EF\u89C1\u6587\u6848\u7ECF props.t \u67E5\u5B57\u5178\uFF08locales.ts\uFF09\uFF0C\u672C\u6587\u4EF6\u4E0D\u51FA\u73B0\u81EA\u7136\u8BED\u8A00\u5B57\u9762\u91CF\u3002\n */\n\nimport { useState } from 'react'\nimport type { InjectFace, PropsLocale, PropsRuntime, TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\n// \u7C7B\u578B import\uFF08\u88AB\u64E6\u9664\uFF09\uFF1A\u62C9\u5165 settings.plugin.item \u69FD\u7684 SlotMap \u5408\u5E76\nimport type {} from '@deepseek-ai/dsh-client-ui-settings-plugins/client'\nimport type { CloudSyncCardFace } from './cloud-sync-card-controller.ts'\nimport { errorKeyOf, type RelativeTimeSpec } from './pure.ts'\n\n/** \u6E32\u67D3\u5668\u7EC4\u88C5\u540E\u7684\u7EC4\u4EF6 props\uFF1A\u8FD0\u884C\u65F6\u4EFD + \u5B57\u5178 t \u5EA7\u4F4D + controller \u6CE8\u5165\u4EFD\u3002 */\nexport type CloudSyncCardProps =\n PropsRuntime<'settings.plugin.item'>\n & PropsLocale<'settings.cloudSync'>\n & InjectFace<CloudSyncCardFace>\n\n/** \u663E\u793A/\u9690\u85CF\u5BC6\u7801\u7684\u56FE\u6807\u5B57\u5F62\uFF1A\u7B26\u53F7\u800C\u975E\u81EA\u7136\u8BED\u8A00\uFF0C\u4E0D\u8FDB\u5B57\u5178\u3002 */\nconst EYE_GLYPH = '\uD83D\uDC41'\n/** \u5220\u9664\u6620\u5C04\u7684\u56FE\u6807\u5B57\u5F62\uFF1A\u540C\u4E0A\uFF0C\u7B26\u53F7\u4E0D\u8FDB\u5B57\u5178\u3002 */\nconst TRASH_GLYPH = '\uD83D\uDDD1'\n\n/** secret \u5DF2\u914D\u7F6E\u65F6\u7684\u5360\u4F4D\u7B26\uFF0801 \u00A74\uFF1A\u5199\u5165\u540E\u663E\u793A\u5360\u4F4D\u7B26\uFF0C\u503C\u672C\u8EAB\u6C38\u4E0D\u56DE\u663E\uFF09\u3002 */\nconst SECRET_MASK = '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022'\n\n/**\n * \u544A\u8BC9\u6D4F\u89C8\u5668\u4E0E\u5BC6\u7801\u7BA1\u7406\u5668\u300C\u8FD9\u4E0D\u662F\u767B\u5F55\u51ED\u636E\u300D\uFF1AChrome \u5BF9 autocomplete=\"off\"\n * \u7684 password \u5B57\u6BB5\u7167\u6837\u5F39\u4FDD\u5B58/\u66F4\u65B0\u63D0\u793A\uFF0Cnew-password \u624D\u662F\u5B83\u8BA4\u7684\u4FE1\u53F7\uFF1B\n * data-* \u5206\u522B\u538B\u4F4F LastPass / 1Password / Bitwarden\u3002\n */\nconst NOT_A_CREDENTIAL = {\n autoComplete: 'new-password',\n 'data-lpignore': 'true',\n 'data-1p-ignore': 'true',\n 'data-bwignore': 'true',\n 'data-form-type': 'other',\n} as const\n\n/** \u628A\u76F8\u5BF9\u65F6\u95F4\u89C4\u683C\u7FFB\u8BD1\u6210\u6587\u6848\uFF08\u6062\u590D\u5BF9\u8BDD\u6846\u590D\u7528\uFF09\u3002 */\nexport function relativeText(t: TranslateNS<'settings.cloudSync'>, spec: RelativeTimeSpec): string {\n return spec.key === 'time.justNow' ? t('time.justNow') : t(spec.key, { count: spec.count })\n}\n\n/**\n * \u6E32\u67D3\u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361\u3002\n * @param props - \u5B57\u5178 t\u3001\u5361\u7247\u5FEB\u7167\uFF08useCloudSyncCard\uFF09\u4E0E\u8868\u5355\u52A8\u4F5C\u3002\n * @returns \u5361\u7247\uFF1Bnamespace \u4E0D\u53EF\u7528\u65F6\u4E0D\u6E32\u67D3\uFF08Host \u672A serve \u5373 UI \u65E0\u75D5\u8FF9\uFF09\u3002\n */\nexport function CloudSyncCard(props: CloudSyncCardProps) {\n const { t } = props\n const state = props.useCloudSyncCard(snapshot => snapshot)\n // \u663E\u793A/\u9690\u85CF\u5BC6\u7801\u662F\u7EAF\u9605\u8BFB\u59FF\u6001\uFF0C\u7EC4\u4EF6\u672C\u5730\u72B6\u6001\u5373\u53EF\n const [showToken, setShowToken] = useState(false)\n const [showPassphrase, setShowPassphrase] = useState(false)\n if (!state.available) return null\n const disabled = !state.writable\n const line = state.statusLine\n return (\n <li className=\"dsh-cloud-sync-card\">\n <div className=\"dsh-cloud-sync-card__header\">\n <span className=\"dsh-cloud-sync-card__name\">{t('title')}</span>\n <span className=\"dsh-cloud-sync-card__description\">{t('description')}</span>\n </div>\n <div className=\"dsh-cloud-sync-card__body\">\n <section className=\"dsh-cloud-sync-card__section\">\n <h3 className=\"dsh-cloud-sync-card__section-title\">{t('section.connection')}</h3>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-server-url\">\n {t('field.serverUrl')}\n </label>\n </div>\n <input\n id=\"dsh-cloud-sync-server-url\"\n className=\"dsh-cloud-sync-card__input\"\n type=\"text\"\n value={state.serverUrlDraft}\n placeholder={t('field.serverUrl.placeholder')}\n disabled={disabled}\n {...state.serverUrlInvalid ? { 'aria-invalid': true } : {}}\n onChange={(event) => { props.editServerUrl(event.target.value) }}\n onBlur={() => { props.commitServerUrl() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitServerUrl()\n }}\n />\n {state.serverUrlInvalid\n ? <p className=\"dsh-cloud-sync-card__error\">{t('invalid.serverUrl')}</p>\n : null}\n </div>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-username\">\n {t('field.username')}\n </label>\n </div>\n <input\n id=\"dsh-cloud-sync-username\"\n className=\"dsh-cloud-sync-card__input\"\n type=\"text\"\n autoComplete=\"off\"\n value={state.usernameDraft}\n placeholder={state.usernameDraft === '' ? t('field.username.hint') : undefined}\n disabled={disabled}\n onChange={(event) => { props.editUsername(event.target.value) }}\n onBlur={() => { props.commitUsername() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitUsername()\n }}\n />\n </div>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-token\">\n {t('field.token')}\n </label>\n <span\n className={state.tokenConfigured\n ? 'dsh-cloud-sync-card__badge'\n : 'dsh-cloud-sync-card__badge dsh-cloud-sync-card__badge--muted'}\n >\n {t(state.tokenConfigured ? 'field.configured' : 'field.unconfigured')}\n </span>\n </div>\n <div className=\"dsh-cloud-sync-card__input-row\">\n <input\n id=\"dsh-cloud-sync-token\"\n className=\"dsh-cloud-sync-card__input\"\n type={showToken ? 'text' : 'password'}\n {...NOT_A_CREDENTIAL}\n placeholder={state.tokenConfigured ? SECRET_MASK : undefined}\n value={state.tokenDraft}\n disabled={disabled}\n onChange={(event) => { props.editToken(event.target.value) }}\n onBlur={() => { props.commitToken() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitToken()\n }}\n />\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__icon-button\"\n aria-label={t(showToken ? 'field.secret.hide' : 'field.secret.show')}\n disabled={disabled}\n onClick={() => { setShowToken(!showToken) }}\n >\n {EYE_GLYPH}\n </button>\n </div>\n </div>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-passphrase\">\n {t('field.passphrase')}\n </label>\n <span\n className={state.passphraseConfigured\n ? 'dsh-cloud-sync-card__badge'\n : 'dsh-cloud-sync-card__badge dsh-cloud-sync-card__badge--muted'}\n >\n {t(state.passphraseConfigured ? 'field.configured' : 'field.unconfigured')}\n </span>\n </div>\n <div className=\"dsh-cloud-sync-card__input-row\">\n <input\n id=\"dsh-cloud-sync-passphrase\"\n className=\"dsh-cloud-sync-card__input\"\n type={showPassphrase ? 'text' : 'password'}\n {...NOT_A_CREDENTIAL}\n placeholder={state.passphraseConfigured ? SECRET_MASK : undefined}\n value={state.passphraseDraft}\n disabled={disabled}\n onChange={(event) => { props.editPassphrase(event.target.value) }}\n onBlur={() => { props.commitPassphrase() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitPassphrase()\n }}\n />\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__icon-button\"\n aria-label={t(showPassphrase ? 'field.secret.hide' : 'field.secret.show')}\n disabled={disabled}\n onClick={() => { setShowPassphrase(!showPassphrase) }}\n >\n {EYE_GLYPH}\n </button>\n </div>\n </div>\n <div className=\"dsh-cloud-sync-card__field\">\n <div className=\"dsh-cloud-sync-card__field-head\">\n <label className=\"dsh-cloud-sync-card__label\" htmlFor=\"dsh-cloud-sync-device-name\">\n {t('field.deviceName')}\n </label>\n </div>\n <input\n id=\"dsh-cloud-sync-device-name\"\n className=\"dsh-cloud-sync-card__input\"\n type=\"text\"\n value={state.deviceNameDraft}\n disabled={disabled}\n {...state.deviceNameInvalid ? { 'aria-invalid': true } : {}}\n onChange={(event) => { props.editDeviceName(event.target.value) }}\n onBlur={() => { props.commitDeviceName() }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') props.commitDeviceName()\n }}\n />\n <p className={state.deviceNameInvalid\n ? 'dsh-cloud-sync-card__error'\n : 'dsh-cloud-sync-card__hint'}\n >\n {t(state.deviceNameInvalid ? 'invalid.deviceName' : 'field.deviceName.hint')}\n </p>\n </div>\n <div className=\"dsh-cloud-sync-card__row-end\">\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button\"\n disabled={disabled || state.testPending || !state.canTest}\n onClick={() => { props.testConnection() }}\n >\n {t(state.testPending ? 'action.testing' : 'action.testConnection')}\n </button>\n </div>\n </section>\n <section className=\"dsh-cloud-sync-card__section\">\n {line.kind === 'unconfigured'\n ? (\n <p className=\"dsh-cloud-sync-card__status dsh-cloud-sync-card__status--muted\">\n <span className=\"dsh-cloud-sync-card__dot\" />\n {t('status.unconfigured')}\n </p>\n )\n : null}\n {line.kind === 'failed'\n ? (\n <p\n className=\"dsh-cloud-sync-card__status dsh-cloud-sync-card__status--error\"\n title={line.detail === '' ? undefined : line.detail}\n >\n <span className=\"dsh-cloud-sync-card__dot\" data-state=\"error\" />\n {t('status.disconnected', { reason: t(errorKeyOf(line.error)) })}\n </p>\n )\n : null}\n {line.kind === 'connected'\n ? (\n <p\n className=\"dsh-cloud-sync-card__status\"\n title={line.warn\n ? (line.error === 'deviceConflict'\n ? t('error.deviceConflict')\n : line.detail === '' ? undefined : line.detail)\n : undefined}\n >\n <span className=\"dsh-cloud-sync-card__dot\" data-state={line.warn ? 'warning' : 'done'} />\n {line.warn ? <span className=\"dsh-cloud-sync-card__warn\">{t('status.warnMark')}</span> : null}\n {t('status.connected', { count: line.count })}\n {line.lastSync !== null\n ? ` \u00B7 ${t('status.lastSync', { time: relativeText(t, line.lastSync) })}`\n : null}\n </p>\n )\n : null}\n </section>\n <section className=\"dsh-cloud-sync-card__section\">\n <h3 className=\"dsh-cloud-sync-card__section-title\">{t('section.sync')}</h3>\n <div className=\"dsh-cloud-sync-card__switch-row\">\n <span className=\"dsh-cloud-sync-card__switch-label\" id=\"dsh-cloud-sync-auto-upload-label\">\n {t('field.autoUpload')}\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={state.autoUpload}\n aria-labelledby=\"dsh-cloud-sync-auto-upload-label\"\n className=\"dsh-cloud-sync-card__switch\"\n data-on={state.autoUpload}\n disabled={disabled}\n onClick={() => { props.toggleAutoUpload() }}\n >\n <span className=\"dsh-cloud-sync-card__switch-knob\" />\n </button>\n <span className=\"dsh-cloud-sync-card__hint\">{t('field.autoUpload.hint')}</span>\n </div>\n <div className=\"dsh-cloud-sync-card__switch-row\">\n <span className=\"dsh-cloud-sync-card__switch-label\" id=\"dsh-cloud-sync-check-on-startup-label\">\n {t('field.checkOnStartup')}\n </span>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={state.checkOnStartup}\n aria-labelledby=\"dsh-cloud-sync-check-on-startup-label\"\n className=\"dsh-cloud-sync-card__switch\"\n data-on={state.checkOnStartup}\n disabled={disabled}\n onClick={() => { props.toggleCheckOnStartup() }}\n >\n <span className=\"dsh-cloud-sync-card__switch-knob\" />\n </button>\n <span className=\"dsh-cloud-sync-card__hint\">{t('field.checkOnStartup.hint')}</span>\n </div>\n </section>\n <section className=\"dsh-cloud-sync-card__section\">\n <h3 className=\"dsh-cloud-sync-card__section-title\">{t('section.actions')}</h3>\n {state.syncResult !== null\n ? (\n <p className=\"dsh-cloud-sync-card__sync-result\" role=\"status\">\n {t('sync.result', { ok: state.syncResult.ok, failed: state.syncResult.failed })}\n {state.syncResult.failed > 0 && state.syncResult.error !== ''\n ? ` \u2014 ${state.syncResult.error}`\n : null}\n </p>\n )\n : null}\n <div className=\"dsh-cloud-sync-card__row-end\">\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button\"\n disabled={disabled || state.catalogPending}\n onClick={() => { props.openRestore() }}\n >\n {t('action.restore')}\n </button>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button dsh-cloud-sync-card__button--primary\"\n disabled={disabled || state.syncPending || !state.canSync}\n onClick={() => { props.syncAll() }}\n >\n {t(state.syncPending ? 'action.syncing' : 'action.syncAll')}\n </button>\n </div>\n </section>\n <section className=\"dsh-cloud-sync-card__section\">\n <h3 className=\"dsh-cloud-sync-card__section-title\">{t('section.mappings')}</h3>\n {state.mappings.length === 0\n ? <p className=\"dsh-cloud-sync-card__hint\">{t('mapping.empty')}</p>\n : (\n <ul className=\"dsh-cloud-sync-card__mappings\">\n {state.mappings.map((pair) => (\n <li key={pair.from} className=\"dsh-cloud-sync-card__mapping\">\n <span className=\"dsh-cloud-sync-card__mapping-paths\" title={`${pair.from} \u2192 ${pair.to}`}>\n {pair.from}\n <span className=\"dsh-cloud-sync-card__mapping-arrow\">\u2192</span>\n {pair.to}\n </span>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__icon-button\"\n aria-label={t('mapping.delete')}\n disabled={disabled}\n onClick={() => { props.deleteMapping(pair.from) }}\n >\n {TRASH_GLYPH}\n </button>\n </li>\n ))}\n </ul>\n )}\n </section>\n </div>\n </li>\n )\n}\n", "/**\n * \u8BBE\u7F6E\u5361\u7EAF\u51FD\u6570\u5C42\uFF1A\u5B57\u6BB5\u6821\u9A8C\u3001\u76F8\u5BF9\u65F6\u95F4\u89C4\u683C\u3001\u72B6\u6001\u884C\u6295\u5F71\u3002\n * \u53EA\u4F9D\u8D56\u5165\u53C2\uFF0C\u4E0D\u89E6\u78B0 React / DOM / settings scope\uFF0C\u4F9B node:test \u76F4\u63A5\u5355\u6D4B\n * \uFF08client \u5355\u6D4B\u53EA\u6D4B\u7EAF\u51FD\u6570\uFF0C\u4E0D import tsx \u7EC4\u4EF6\uFF09\u3002\n */\n\n/** \u670D\u52A1\u5668\u5730\u5740\u6821\u9A8C\uFF1A\u7A7A\u4E32\u89C6\u4E3A\u300C\u672A\u586B\u300D\uFF08\u4E0D\u7B97\u975E\u6CD5\uFF09\uFF0C\u975E\u7A7A\u5FC5\u987B\u662F\u5408\u6CD5\u7684 http(s) URL\u3002 */\nexport function isValidServerUrl(text: string): boolean {\n if (text === '') return true\n let url: URL\n try {\n url = new URL(text)\n } catch {\n return false\n }\n return url.protocol === 'http:' || url.protocol === 'https:'\n}\n\n/** \u8BBE\u5907\u540D\u6821\u9A8C\uFF1A\u7A7A\u4E32 = \u56DE\u9000\u672C\u673A\u4E3B\u673A\u540D\uFF1B\u5426\u5219\u4EC5\u9650 [a-zA-Z0-9-_]\uFF0C\u957F\u5EA6 \u2264 32\u3002 */\nexport function isValidDeviceName(text: string): boolean {\n return text === '' || /^[a-zA-Z0-9-_]{1,32}$/.test(text)\n}\n\n/** \u76F8\u5BF9\u65F6\u95F4\u6587\u6848\u89C4\u683C\uFF1A\u6E32\u67D3\u5C42\u636E key\uFF08+ count\uFF09\u67E5\u5B57\u5178\uFF0C\u672C\u5C42\u4E0D\u51FA\u73B0\u4EFB\u4F55\u6587\u6848\u3002 */\nexport type RelativeTimeSpec =\n | { key: 'time.justNow' }\n | { key: 'time.minutesAgo' | 'time.hoursAgo' | 'time.daysAgo'; count: number }\n\nconst MINUTE_MS = 60_000\nconst HOUR_MS = 3_600_000\nconst DAY_MS = 86_400_000\n\n/** \u628A epoch \u6BEB\u79D2\u65F6\u95F4\u6233\u6295\u5F71\u4E3A\u76F8\u5BF9\u65F6\u95F4\u89C4\u683C\uFF1Bat <= 0\uFF08\u4ECE\u672A\u540C\u6B65\uFF09\u8FD4\u56DE null\uFF08\u4E0D\u663E\u793A\u8BE5\u9879\uFF09\u3002 */\nexport function relativeTime(at: number, now: number): RelativeTimeSpec | null {\n if (at <= 0) return null\n const diff = Math.max(0, now - at)\n if (diff < MINUTE_MS) return { key: 'time.justNow' }\n if (diff < HOUR_MS) return { key: 'time.minutesAgo', count: Math.max(1, Math.round(diff / MINUTE_MS)) }\n if (diff < DAY_MS) return { key: 'time.hoursAgo', count: Math.max(1, Math.round(diff / HOUR_MS)) }\n return { key: 'time.daysAgo', count: Math.max(1, Math.round(diff / DAY_MS)) }\n}\n\n/** Host \u5199\u5165\u7684\u72B6\u6001\u673A\u76F8\u4F4D\uFF08docs/02 \u00A79 \u4E0B\u534A\u8868\uFF09\u3002 */\nexport type StatusPhase = 'unconfigured' | 'connected' | 'failed'\n\n/** Host \u5199\u5165\u7684\u9519\u8BEF\u7801\uFF1B'' \u8868\u793A\u65E0\u9519\u8BEF\u3002 */\nexport type StatusError =\n '' | 'unreachable' | 'tokenInvalid' | 'userMismatch' | 'passphraseMismatch' | 'deviceConflict' | 'syncFailed'\n\n/** \u72B6\u6001\u884C\u6295\u5F71\u5165\u53C2\u3002 */\nexport interface StatusLineInput {\n phase: StatusPhase\n error: StatusError\n errorDetail: string\n cloudCount: number\n lastSyncAt: number\n now: number\n}\n\n/** \u72B6\u6001\u884C\u6295\u5F71\u7ED3\u679C\uFF1A\u6E32\u67D3\u5C42\u6309 kind \u5206\u652F\u53D6\u6587\u6848\uFF0C\u9519\u8BEF\u7801\u7ECF errorKeyOf \u6620\u5C04\u5B57\u5178 key\u3002 */\nexport type StatusLine =\n | { kind: 'unconfigured' }\n | { kind: 'failed'; error: StatusError; detail: string }\n | {\n kind: 'connected'\n count: number\n lastSync: RelativeTimeSpec | null\n /** \u8FDE\u7EED\u63A8\u9001\u5931\u8D25 / \u8BBE\u5907\u540D\u51B2\u7A81\u65F6\u524D\u7F00\u544A\u8B66\u6807\u8BB0\uFF0801 \u00A75.2\u3001\u00A76\uFF09\u3002 */\n warn: boolean\n error: StatusError\n detail: string\n }\n\n/** \u628A Host \u72B6\u6001\u5B57\u6BB5\u6295\u5F71\u4E3A\u4E00\u884C\u5F0F\u8FD0\u884C\u6982\u51B5\u3002 */\nexport function projectStatusLine(input: StatusLineInput): StatusLine {\n if (input.phase === 'unconfigured') return { kind: 'unconfigured' }\n if (input.phase === 'failed') return { kind: 'failed', error: input.error, detail: input.errorDetail }\n return {\n kind: 'connected',\n count: input.cloudCount,\n lastSync: relativeTime(input.lastSyncAt, input.now),\n warn: input.error === 'syncFailed' || input.error === 'deviceConflict',\n error: input.error,\n detail: input.errorDetail,\n }\n}\n\n/** \u9519\u8BEF\u7801 \u2192 \u6587\u6848\u5B57\u5178 key\uFF1B\u7A7A\u7801\u4E0E syncFailed\uFF08failed \u76F8\u4F4D\u4E0B\u7684\u515C\u5E95\uFF09\u843D\u5230 error.unknown\u3002 */\nexport function errorKeyOf(\n error: StatusError,\n): 'error.unreachable' | 'error.tokenInvalid' | 'error.userMismatch' | 'error.passphraseMismatch' | 'error.deviceConflict' | 'error.unknown' {\n switch (error) {\n case 'unreachable': return 'error.unreachable'\n case 'tokenInvalid': return 'error.tokenInvalid'\n case 'userMismatch': return 'error.userMismatch'\n case 'passphraseMismatch': return 'error.passphraseMismatch'\n case 'deviceConflict': return 'error.deviceConflict'\n default: return 'error.unknown'\n }\n}\n\n/**\n * \u6D4B\u8BD5\u8FDE\u63A5 / \u7ACB\u5373\u5168\u90E8\u540C\u6B65\u7684\u53EF\u7528\u6027\uFF0801 \u00A76\uFF09\uFF1A\u5730\u5740\u5DF2\u586B\u4E14\u5408\u6CD5\uFF0C\u4E14\u4EE4\u724C\u53EF\u7528\n * \uFF08\u5DF2\u914D\u7F6E\u6216\u672C\u6B21\u65B0\u586B\u4E86\u8349\u7A3F\uFF09\u3002\n */\nexport function canRunServerAction(serverUrlDraft: string, tokenAvailable: boolean): boolean {\n return serverUrlDraft !== '' && isValidServerUrl(serverUrlDraft) && tokenAvailable\n}\n\n/** \u89E6\u53D1\u5668\u5199\u7684\u91CD\u8BD5\u8282\u62CD\uFF1A1s \u5185\u6CA1\u770B\u5230\u56DE\u663E\u5C31\u91CD\u53D1\u3002 */\nexport const TRIGGER_RETRY_MS = 1_000\n/** \u89E6\u53D1\u5668\u5199\u7684\u603B\u8D85\u65F6\uFF1A20s \u4ECD\u65E0\u56DE\u663E\u5219\u9000\u51FA\u8FDB\u884C\u6001\u3002 */\nexport const TRIGGER_TIMEOUT_MS = 20_000\n\n/**\n * \u89E6\u53D1\u5668\u5199\u7684\u4E0B\u4E00\u6B65\u52A8\u4F5C\u3002\n *\n * \u80CC\u666F\uFF1ASettingsScope.set \u7528\u300C\u5BA2\u6237\u7AEF\u5DF2\u77E5\u6700\u65B0 revision\u300D\u505A\u4E50\u89C2\u9501 fence\uFF0C\u51B2\u7A81\u65F6\n * Host \u8FD4\u56DE {ok:false}\uFF0Cclient recover() \u91CD\u8F7D\u540E\u5199\u5165\u88AB\u9759\u9ED8\u4E22\u5F03\u3001Promise \u6B63\u5E38\n * resolve\u3002Host \u9AD8\u9891\u56DE\u5199 'cloud-sync-status' namespace\uFF0C\u89E6\u53D1\u5668\u5199\uFF08\n * testRequestedAt/syncAllRequestedAt\uFF09\u53EF\u80FD\u649E fence \u88AB\u5403\u6389\uFF0C\u56E0\u6B64\u5BF9\u89E6\u53D1\u5668\u5199\u505A\n * \u300C\u91CD\u8BD5\u5230\u56DE\u663E\u300D\u81EA\u6108\uFF1A\u56DE\u663E\uFF08statusCheckedAt/statusSyncAllAt >= issuedAt\uFF09\u672C\u6765\n * \u5C31\u662F\u5B8C\u6210\u4FE1\u53F7\u3002\n *\n * @param echoAt - \u56DE\u663E\u5B57\u6BB5\u5F53\u524D\u503C\uFF08\u65E0\u5219 0\uFF09\u3002\n * @param issuedAt - \u672C\u6B21\u89E6\u53D1\u53D1\u51FA\u7684\u65F6\u95F4\u6233\uFF08\u91CD\u8BD5\u4FDD\u6301\u540C\u4E00\u503C\uFF0C\u8FDB\u884C\u6001\u5224\u5B9A\u4E0D\u6296\u52A8\uFF09\u3002\n * @param lastSentAt - \u4E0A\u4E00\u6B21\u5B9E\u9645\u5199\u51FA\u7684\u65F6\u523B\u3002\n * @param now - \u5F53\u524D\u65F6\u523B\u3002\n * @returns done\uFF08\u56DE\u663E\u5230\u8FBE\uFF09/ timeout\uFF08\u603B\u8D85\u65F6\uFF09/ resend\uFF08\u8DDD\u4E0A\u6B21\u5199\u5DF2\u6EE1\u4E00\u4E2A\u8282\u62CD\uFF09/ wait\u3002\n */\nexport type TriggerAction = 'done' | 'timeout' | 'resend' | 'wait'\nexport function triggerAction(echoAt: number, issuedAt: number, lastSentAt: number, now: number): TriggerAction {\n // \u56DE\u663E\u4F18\u5148\u4E8E\u8D85\u65F6\uFF1A\u8FDF\u5230\u4F46\u5B8C\u6210\u7684\u56DE\u663E\u4E0D\u80FD\u628A\u4E00\u6B21\u6210\u529F\u5224\u6210\u5931\u8D25\n if (echoAt >= issuedAt) return 'done'\n if (now - issuedAt >= TRIGGER_TIMEOUT_MS) return 'timeout'\n if (now - lastSentAt >= TRIGGER_RETRY_MS) return 'resend'\n return 'wait'\n}\n\n// ---- M4\uFF1A\u6062\u590D\u6D41\u7A0B\uFF08\u901A\u9053 JSON \u8F7D\u8377\u7684 Client \u4FA7\u62FC\u5199\uFF0C\u4E0E Host sync/restore.ts \u5206\u522B\u7EF4\u62A4\uFF09 ----\n\n/** \u6062\u590D\u76EE\u5F55\u7684\u4E00\u884C\uFF08Host \u5DF2\u8BA1\u7B97\u597D existsLocal / \u7248\u672C / \u8DEF\u5F84\u89E3\u6790\uFF0CClient \u53EA\u5448\u73B0\u4E0E\u6539\u9009\uFF09\u3002 */\nexport interface CatalogEntry {\n sessionId: string\n device: string\n title: string\n updatedAt: number\n eventCount: number\n cwd: string\n formatVersion: number\n existsLocal: boolean\n versionIncompatible: boolean\n resolvedCwd: string | null\n resolution: 'mapping' | 'suggested' | 'none'\n}\n\n/** restoreCatalogJson \u7684\u8F7D\u8377\uFF08at \u56DE\u663E restoreListRequestedAt\uFF09\u3002 */\nexport interface RestoreCatalog {\n at: number\n selfDevice: string\n sessions: CatalogEntry[]\n error: string\n}\n\n/** restoreResultJson \u7684\u8F7D\u8377\uFF08at \u56DE\u663E\u8BF7\u6C42\u7684 at\uFF09\u3002 */\nexport interface RestoreResultPayload {\n at: number\n ok: number\n failed: number\n firstError: string\n}\n\n/** startupNoticeJson \u7684\u8F7D\u8377\uFF08\u542F\u52A8\u68C0\u67E5\u53D1\u73B0\u4ED6\u673A\u589E\u91CF\uFF09\u3002 */\nexport interface StartupNoticePayload {\n at: number\n count: number\n}\n\n/** pathMappingsJson \u7684\u5143\u7D20\uFF08\u8BBE\u7F6E\u5361\u300C\u8DEF\u5F84\u6620\u5C04\u300D\u533A\uFF09\u3002 */\nexport interface MappingPair {\n from: string\n to: string\n}\n\n/** \u9632\u5FA1\u6027\u89E3\u6790\uFF1A\u901A\u9053\u5B57\u6BB5\u53EF\u80FD\u88AB\u65E7\u7248\u672C/\u624B\u5DE5\u7F16\u8F91\u6C61\u67D3\uFF0C\u4EFB\u4F55\u4E0D\u7B26\u90FD\u6309\u65E0\u6570\u636E\u5904\u7406\u3002 */\nexport function parseCatalog(raw: string): RestoreCatalog | null {\n if (raw === '') return null\n try {\n const parsed = JSON.parse(raw) as RestoreCatalog\n if (typeof parsed.at !== 'number' || !Array.isArray(parsed.sessions) || typeof parsed.selfDevice !== 'string') return null\n return parsed\n } catch {\n return null\n }\n}\n\nexport function parseRestoreResult(raw: string): RestoreResultPayload | null {\n if (raw === '') return null\n try {\n const parsed = JSON.parse(raw) as RestoreResultPayload\n if (typeof parsed.at !== 'number' || typeof parsed.ok !== 'number' || typeof parsed.failed !== 'number') return null\n return parsed\n } catch {\n return null\n }\n}\n\nexport function parseMappings(raw: string): MappingPair[] {\n if (raw === '') return []\n try {\n const parsed = JSON.parse(raw) as MappingPair[]\n if (!Array.isArray(parsed)) return []\n return parsed.filter((pair) => typeof pair?.from === 'string' && typeof pair?.to === 'string')\n } catch {\n return []\n }\n}\n\nexport function parseStartupNotice(raw: string): StartupNoticePayload | null {\n if (raw === '') return null\n try {\n const parsed = JSON.parse(raw) as StartupNoticePayload\n if (typeof parsed.at !== 'number' || typeof parsed.count !== 'number' || parsed.at <= 0) return null\n return parsed\n } catch {\n return null\n }\n}\n\n/** \u6062\u590D\u8BF7\u6C42\uFF08Client\u2192Host\uFF09\uFF1A\u6BCF\u884C targetCwd \u4E3A null \u8868\u793A\u539F\u6837\u843D\u4F4D\uFF08\u6062\u590D\u540E\u672A\u5206\u7EC4\uFF09\u3002 */\nexport function encodeRestoreRequest(\n at: number,\n items: { sessionId: string; device: string; targetCwd: string | null }[],\n): string {\n return JSON.stringify({ at, items })\n}\n\n/** \u8DEF\u5F84\u6620\u5C04\u5220\u9664\u8BF7\u6C42\uFF08Client\u2192Host\uFF09\u3002 */\nexport function encodeMappingDelete(at: number, from: string): string {\n return JSON.stringify({ at, from })\n}\n\n/** \u884C\u4E3B\u952E\uFF1A\u8BBE\u5907 + \u4F1A\u8BDD id\uFF08\u540C id \u8DE8\u8BBE\u5907\u662F\u4E24\u884C\uFF09\u3002 */\nexport function rowKeyOf(entry: Pick<CatalogEntry, 'device' | 'sessionId'>): string {\n return `${entry.device}/${entry.sessionId}`\n}\n\n/** \u884C\u53EF\u6062\u590D\u6027\uFF1A\u672C\u5730\u5DF2\u5B58\u5728 / \u7248\u672C\u4E0D\u517C\u5BB9\u7684\u884C\u7981\u7528\uFF0801 \u00A75.4\uFF09\u3002 */\nexport function isRowRestorable(entry: Pick<CatalogEntry, 'existsLocal' | 'versionIncompatible'>): boolean {\n return !entry.existsLocal && !entry.versionIncompatible\n}\n\n/** \u9ED8\u8BA4\u52FE\u9009\uFF1A\u5F53\u524D\u8BBE\u5907\u4E4B\u5916\u3001\u4E14\u53EF\u6062\u590D\u7684\u4F1A\u8BDD\uFF0801 \u00A75.4\uFF09\u3002 */\nexport function defaultSelectedKeys(catalog: RestoreCatalog): string[] {\n return catalog.sessions\n .filter((entry) => entry.device !== catalog.selfDevice && isRowRestorable(entry))\n .map(rowKeyOf)\n}\n\n/** \u6765\u6E90\u8BBE\u5907\u7B5B\u9009\u9009\u9879\uFF08\u76EE\u5F55\u5185\u51FA\u73B0\u8FC7\u7684\u8BBE\u5907\uFF0C\u6309\u5B57\u5178\u5E8F\u7A33\u5B9A\uFF09\u3002 */\nexport function deviceOptions(catalog: RestoreCatalog): string[] {\n return [...new Set(catalog.sessions.map((entry) => entry.device))].sort()\n}\n\n/** \u6309\u6765\u6E90\u8BBE\u5907\u7B5B\u9009\uFF1B\u7A7A filter \u8868\u793A\u5168\u90E8\u3002 */\nexport function filterEntries(entries: CatalogEntry[], filter: string): CatalogEntry[] {\n if (filter === '') return entries\n return entries.filter((entry) => entry.device === filter)\n}\n\n/** \u76EE\u5F55\u62C9\u53D6/\u6062\u590D\u6267\u884C\u5931\u8D25\u7684\u9519\u8BEF\u7801 \u2192 \u6587\u6848\u5B57\u5178 key\uFF08\u590D\u7528\u72B6\u6001\u884C\u9519\u8BEF\u6587\u6848\uFF0C\u52A0 unconfigured\uFF09\u3002 */\nexport function catalogErrorKeyOf(\n error: string,\n): 'status.unconfigured' | 'error.unreachable' | 'error.tokenInvalid' | 'error.userMismatch' {\n if (error === 'tokenInvalid') return 'error.tokenInvalid'\n if (error === 'userMismatch') return 'error.userMismatch'\n if (error === 'unconfigured') return 'status.unconfigured'\n return 'error.unreachable'\n}\n", "/**\n * shell.overlay \u6D6E\u5C42\uFF08M4\uFF09\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846 + \u542F\u52A8\u68C0\u67E5\u901A\u77E5 + \u6062\u590D\u7ED3\u679C\u63D0\u793A\u3002\n * \u6302\u5728\u6839\u4F5C\u7528\u57DF\u5217\u8868\u69FD\uFF0C\u8BBE\u7F6E\u9875\u5916\u540C\u6837\u53EF\u7528\uFF08\u542F\u52A8 toast \u7684\u300C\u70B9\u51FB\u67E5\u770B\u300D\u8981\u80FD\u5728\n * \u4EFB\u610F\u9875\u9762\u6253\u5F00\u6062\u590D\u5BF9\u8BDD\u6846\uFF09\u3002\u5BF9\u8BDD\u6846\u7528 ui-primitives \u7684 Modal\uFF1B\u542F\u52A8\u901A\u77E5\u9700\u8981\n * \u70B9\u51FB\u52A8\u4F5C\u800C Toast \u7EC4\u4EF6\u4E0D\u652F\u6301\u70B9\u51FB\uFF0C\u6309 01 \u00A75.5\uFF08\u53F3\u4E0B\u89D2\u8F7B\u63D0\u793A\uFF09\u81EA\u7ED8\u3002\n */\n\nimport { Toast } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\nimport type { CloudSyncCardFace } from './cloud-sync-card-controller.ts'\nimport { RestoreDialog } from './RestoreDialog.tsx'\n\n// ui-layout \u58F0\u660E\u7684 'shell.overlay' \u69FD\uFF08list/root\uFF0C\u65E0 owner props\uFF09\u3002\u672C\u5305\u4E0D\u4F9D\u8D56\n// ui-layout \u7684\u7C7B\u578B\u5305\uFF0C\u6309 renderer \u7684 SlotMap \u5408\u5E76\u60EF\u4F8B\u5C31\u5730\u58F0\u660E\u540C\u5F62\u6761\u76EE\u3002\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface SlotMap {\n 'shell.overlay': { kind: 'list'; scope: 'root' }\n }\n}\n\nexport type CloudSyncOverlayProps =\n PropsRuntime<'shell.overlay'>\n & PropsLocale<'settings.cloudSync'>\n & InjectFace<CloudSyncCardFace>\n\n/**\n * \u4E91\u7AEF\u540C\u6B65\u6D6E\u5C42\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846\uFF08restoreOpen \u65F6\uFF09\u3001\u542F\u52A8\u901A\u77E5\uFF08\u672A\u8BFB\u65F6\uFF09\u3001\u6062\u590D\u7ED3\u679C toast\u3002\n * Host \u672A serve \u8BBE\u7F6E namespace\uFF08state.available=false\uFF09\u65F6\u6574\u4F53\u4E0D\u6E32\u67D3\uFF0C\u5378\u8F7D\u65E0\u6B8B\u7559\u3002\n */\nexport function CloudSyncOverlay(props: CloudSyncOverlayProps) {\n const { t } = props\n const state = props.useCloudSyncCard((snapshot) => snapshot)\n if (!state.available) return null\n return (\n <>\n {state.restoreOpen\n ? <RestoreDialog t={t} state={state} actions={props} />\n : null}\n {state.notice !== null\n ? (\n <div className=\"dsh-cloud-sync-notice\" role=\"alert\">\n <span className=\"dsh-cloud-sync-notice__text\">{t('toast.newCloudSessions')}</span>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-notice__action\"\n onClick={() => { props.viewNotice() }}\n >\n {t('toast.view')}\n </button>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-notice__dismiss\"\n aria-label={t('toast.dismiss')}\n onClick={() => { props.dismissNotice() }}\n >\n {'\u00D7'}\n </button>\n </div>\n )\n : null}\n {state.restoreOutcome !== null\n ? (\n <Toast\n text={t('restore.result', { ok: state.restoreOutcome.ok, failed: state.restoreOutcome.failed })}\n onDone={() => { props.clearRestoreOutcome() }}\n />\n )\n : null}\n </>\n )\n}\n", "/**\n * \u4ECE\u4E91\u7AEF\u6062\u590D\u4F1A\u8BDD\u5BF9\u8BDD\u6846\uFF0801 \u00A75.4\uFF09\uFF1A\u76EE\u5F55\u5217\u8868\u3001\u6765\u6E90\u8BBE\u5907\u7B5B\u9009\u3001\u52FE\u9009\u3001\n * \u843D\u4F4D\u8DEF\u5F84\u5448\u73B0\u4E0E\u6539\u9009\uFF08\u76EE\u5F55\u9009\u62E9\u5668\u4F18\u5148\uFF0C\u80FD\u529B\u7F3A\u5931\u964D\u7EA7\u624B\u52A8\u8F93\u5165\uFF09\u3002\n * \u7EAF\u5C55\u793A\u7EC4\u4EF6\uFF1A\u72B6\u6001\u4E0E\u52A8\u4F5C\u5168\u90E8\u7ECF props \u8FDB\u5165\uFF0C\u6587\u6848\u5168\u90E8\u7ECF t \u67E5\u5B57\u5178\u3002\n */\n\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'\nimport { Modal } from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { CloudSyncCardFace, CloudSyncCardState } from './cloud-sync-card-controller.ts'\nimport {\n catalogErrorKeyOf,\n deviceOptions,\n filterEntries,\n isRowRestorable,\n relativeTime,\n rowKeyOf,\n type CatalogEntry,\n} from './pure.ts'\nimport { relativeText } from './CloudSyncCard.tsx'\n\ntype T = TranslateNS<'settings.cloudSync'>\n\n/** \u5BF9\u8BDD\u6846 props\uFF1A\u5B57\u5178 t + \u5361\u7247\u5FEB\u7167 + controller \u52A8\u4F5C\uFF08\u7531 overlay \u7EC4\u4EF6\u7EC4\u88C5\uFF09\u3002 */\nexport interface RestoreDialogProps {\n t: T\n state: CloudSyncCardState\n actions: Pick<\n CloudSyncCardFace,\n 'closeRestore' | 'setDeviceFilter' | 'toggleRow' | 'pickTarget' | 'overrideTarget' | 'restoreSelected'\n >\n}\n\n/** \u884C\u76EE\u6807\u5355\u5143\u683C\uFF1A\u843D\u4F4D\u8DEF\u5F84\u7684\u5448\u73B0\u4E0E\u6539\u9009\u63A7\u4EF6\u3002 */\nfunction TargetCell(props: { t: T; entry: CatalogEntry; rowKey: string; state: CloudSyncCardState; actions: RestoreDialogProps['actions'] }) {\n const { t, entry, rowKey, state, actions } = props\n if (entry.existsLocal) {\n return <span className=\"dsh-cloud-sync-restore__badge dsh-cloud-sync-restore__badge--muted\">{t('restore.existsLocal')}</span>\n }\n if (entry.versionIncompatible) {\n return (\n <span\n className=\"dsh-cloud-sync-restore__badge dsh-cloud-sync-restore__badge--muted\"\n title={`format v${String(entry.formatVersion)}`}\n >\n {t('restore.versionIncompatible')}\n </span>\n )\n }\n const override = state.overrides[rowKey]\n const target = override ?? entry.resolvedCwd\n return (\n <span className=\"dsh-cloud-sync-restore__target\">\n {target !== null && target !== undefined && target !== ''\n ? (\n <span className=\"dsh-cloud-sync-restore__path\" title={target}>\n {target}\n {override === undefined && entry.resolution === 'suggested'\n ? <span className=\"dsh-cloud-sync-restore__badge\">{t('restore.pathSuggested')}</span>\n : null}\n {override === undefined && entry.resolution === 'mapping'\n ? <span className=\"dsh-cloud-sync-restore__badge\">{t('restore.pathMapped')}</span>\n : null}\n </span>\n )\n : <span className=\"dsh-cloud-sync-restore__unmatched\">{t('restore.unmatchedPath')}</span>}\n {state.pickerAvailable\n ? (\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-restore__pick\"\n onClick={() => { actions.pickTarget(rowKey) }}\n >\n {t('restore.pickDirectory')}\n </button>\n )\n : (\n <input\n className=\"dsh-cloud-sync-restore__manual\"\n type=\"text\"\n value={override ?? ''}\n placeholder={t('restore.manualPath.placeholder')}\n onChange={(event) => { actions.overrideTarget(rowKey, event.target.value) }}\n />\n )}\n </span>\n )\n}\n\n/** \u6062\u590D\u5BF9\u8BDD\u6846\u4E3B\u4F53\u3002 */\nexport function RestoreDialog({ t, state, actions }: RestoreDialogProps) {\n const catalog = state.catalog\n const entries = catalog === null ? [] : filterEntries(catalog.sessions, state.deviceFilter)\n const selectedCount = state.selectedKeys.length\n return (\n <Modal\n open\n onClose={() => { actions.closeRestore() }}\n title={t('restore.title')}\n closeLabel={t('restore.close')}\n className=\"dsh-cloud-sync-restore\"\n contentClassName=\"dsh-cloud-sync-restore__content\"\n footer={(\n <>\n <span className=\"dsh-cloud-sync-restore__selected\">{t('restore.selected', { count: selectedCount })}</span>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button\"\n onClick={() => { actions.closeRestore() }}\n >\n {t('restore.cancel')}\n </button>\n <button\n type=\"button\"\n className=\"dsh-cloud-sync-card__button dsh-cloud-sync-card__button--primary\"\n disabled={selectedCount === 0 || state.restorePending}\n onClick={() => { actions.restoreSelected() }}\n >\n {t(state.restorePending ? 'restore.restoring' : 'restore.action')}\n </button>\n </>\n )}\n >\n {catalog !== null && catalog.error !== ''\n ? <p className=\"dsh-cloud-sync-card__error\">{t(catalogErrorKeyOf(catalog.error))}</p>\n : null}\n {catalog === null && state.catalogPending\n ? <p className=\"dsh-cloud-sync-card__hint\">{t('restore.loading')}</p>\n : null}\n {catalog !== null && catalog.error === '' && entries.length === 0 && !state.catalogPending\n ? <p className=\"dsh-cloud-sync-card__hint\">{t('restore.empty')}</p>\n : null}\n {catalog !== null && catalog.sessions.length > 0\n ? (\n <>\n <div className=\"dsh-cloud-sync-restore__filter\">\n <select\n className=\"dsh-cloud-sync-restore__select\"\n value={state.deviceFilter}\n onChange={(event) => { actions.setDeviceFilter(event.target.value) }}\n >\n <option value=\"\">{t('restore.filter.all')}</option>\n {deviceOptions(catalog).map((device) => (\n <option key={device} value={device}>{device}</option>\n ))}\n </select>\n </div>\n <div className=\"dsh-cloud-sync-restore__head\" role=\"row\">\n <span />\n <span>{t('restore.column.title')}</span>\n <span>{t('restore.column.updated')}</span>\n <span>{t('restore.column.events')}</span>\n <span>{t('restore.column.target')}</span>\n </div>\n <ul className=\"dsh-cloud-sync-restore__list\">\n {entries.map((entry) => {\n const rowKey = rowKeyOf(entry)\n const restorable = isRowRestorable(entry)\n const updated = relativeTime(entry.updatedAt, Date.now())\n return (\n <li key={rowKey} className=\"dsh-cloud-sync-restore__row\" data-disabled={!restorable}>\n <input\n type=\"checkbox\"\n checked={state.selectedKeys.includes(rowKey)}\n disabled={!restorable || state.restorePending}\n onChange={() => { actions.toggleRow(rowKey) }}\n />\n <span className=\"dsh-cloud-sync-restore__title\" title={entry.title}>\n {entry.title === '' ? entry.sessionId : entry.title}\n <span className=\"dsh-cloud-sync-restore__device\">{entry.device}</span>\n </span>\n <span className=\"dsh-cloud-sync-restore__updated\">\n {updated === null ? '' : relativeText(t, updated)}\n </span>\n <span className=\"dsh-cloud-sync-restore__events\">{entry.eventCount}</span>\n <TargetCell t={t} entry={entry} rowKey={rowKey} state={state} actions={actions} />\n </li>\n )\n })}\n </ul>\n </>\n )\n : null}\n </Modal>\n )\n}\n", "/**\n * \u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361 controller\uFF1A\u6865\u63A5\u4E24\u4E2A settings namespace \u4E0E\u5361\u7247\u5FEB\u7167\u3002\n *\n * \u53CC namespace \u62C6\u5206\uFF08Host \u534A\u5951\u7EA6\uFF09\uFF1A\n * - 'cloud-sync'\uFF1A9 \u4E2A\u7528\u6237\u914D\u7F6E\u5B57\u6BB5\uFF0CHost \u6C38\u4E0D\u5199 \u2192 \u914D\u7F6E\u5199\u96F6 revision \u51B2\u7A81\uFF0C\n * scope.set/unset \u7684\u4E50\u89C2\u9501 fence \u6C38\u8FDC\u65B0\u9C9C\uFF0C\u65E0\u9700\u91CD\u8BD5\u3002\n * - 'cloud-sync-status'\uFF1A14 \u4E2A\u901A\u9053\u5B57\u6BB5\uFF08\u72B6\u6001\u56DE\u8BFB + \u89E6\u53D1\u5668\uFF09\uFF0CHost \u9891\u7E41\u56DE\u5199\u3002\n * \u89E6\u53D1\u5668\u5199\uFF08testRequestedAt/syncAllRequestedAt\uFF09\u53EF\u80FD\u649E fence \u88AB\u9759\u9ED8\u4E22\u5F03\n * \uFF08\u51B2\u7A81\u65F6 client recover() \u91CD\u8F7D\u3001Promise \u6B63\u5E38 resolve\uFF09\uFF0C\u56E0\u6B64\u89E6\u53D1\u5668\u5199\u505A\n * \u300C\u91CD\u8BD5\u5230\u56DE\u663E\u300D\u81EA\u6108\uFF0C\u89C1 pure.ts \u7684 triggerAction\u3002\n *\n * secret \u5B57\u6BB5\uFF08token/passphrase\uFF09\u6C38\u4E0D\u4E0B\u884C\uFF0C\u5361\u7247\u53EA\u51ED Host \u7EF4\u62A4\u7684\n * tokenConfigured/passphraseConfigured \u5E03\u5C14\u663E\u793A\u300C\u5DF2\u914D\u7F6E/\u672A\u914D\u7F6E\u300D\uFF0C\u8F93\u5165\u6846\u6C38\u8FDC\u7A7A\u503C\u8D77\u624B\u3002\n */\n\nimport { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport {\n canRunServerAction,\n defaultSelectedKeys,\n encodeMappingDelete,\n encodeRestoreRequest,\n isRowRestorable,\n isValidDeviceName,\n isValidServerUrl,\n parseCatalog,\n parseMappings,\n parseRestoreResult,\n parseStartupNotice,\n projectStatusLine,\n rowKeyOf,\n triggerAction,\n TRIGGER_RETRY_MS,\n type MappingPair,\n type RestoreCatalog,\n type RestoreResultPayload,\n type StartupNoticePayload,\n type StatusError,\n type StatusLine,\n type StatusPhase,\n} from './pure.ts'\n\n/** \u7528\u6237\u914D\u7F6E namespace\uFF08\u4E0E Host \u534A\u7EA6\u5B9A\u4E00\u81F4\uFF0C\u4E24\u5904\u5206\u522B\u62FC\u5199\uFF0C\u4E0D\u8DE8\u534A import\uFF09\u3002 */\nexport const CLOUD_SYNC_NS = 'cloud-sync'\n/** \u72B6\u6001/\u89E6\u53D1\u901A\u9053 namespace\uFF08Host \u9AD8\u9891\u56DE\u5199\uFF09\u3002 */\nexport const CLOUD_SYNC_STATUS_NS = 'cloud-sync-status'\n\n/** 'cloud-sync' namespace \u7684\u7528\u6237\u914D\u7F6E\u5B57\u6BB5\uFF08Host \u6C38\u4E0D\u5199\uFF09\u3002 */\nexport interface CloudSyncConfig {\n serverUrl?: string\n username?: string\n deviceName?: string\n autoUpload?: boolean\n checkOnStartup?: boolean\n uploadDebounceMs?: number\n pollIntervalMs?: number\n requestTimeoutMs?: number\n}\n\n/** 'cloud-sync-status' namespace \u7684\u901A\u9053\u5B57\u6BB5\uFF1A\u72B6\u6001\u56DE\u8BFB + \u89E6\u53D1\u5668\u3002 */\nexport interface CloudSyncStatus {\n statusPhase?: StatusPhase\n statusCloudCount?: number\n statusLastSyncAt?: number\n statusCheckedAt?: number\n statusError?: StatusError\n statusErrorDetail?: string\n tokenConfigured?: boolean\n passphraseConfigured?: boolean\n statusSyncAllAt?: number\n statusSyncAllOk?: number\n statusSyncAllFailed?: number\n statusSyncAllError?: string\n testRequestedAt?: number\n syncAllRequestedAt?: number\n /** M4\uFF1AHost \u56DE\u5199\u7684\u6062\u590D\u76EE\u5F55 / \u7ED3\u679C / \u6620\u5C04\u5217\u8868 / \u542F\u52A8\u901A\u77E5\uFF08JSON \u5B57\u7B26\u4E32\uFF09\u3002 */\n restoreCatalogJson?: string\n restoreResultJson?: string\n pathMappingsJson?: string\n startupNoticeJson?: string\n /** M4\uFF1AClient \u5199\u5165\u7684\u89E6\u53D1\u5668\u3002 */\n restoreListRequestedAt?: number\n restoreRequestJson?: string\n mappingDeleteJson?: string\n}\n\n/** \u76EE\u5F55\u9009\u62E9\u5668\u80FD\u529B\uFF08ctx.remote.directoryPicker \u7684\u6700\u5C0F\u5207\u9762\uFF1B\u80FD\u529B\u7F3A\u5931\u65F6\u964D\u7EA7\u624B\u52A8\u8F93\u5165\uFF09\u3002 */\nexport interface DirectoryPickerLike {\n pick(): Promise<{ ok: true; value: string | null } | { ok: false; error: { message: string } }>\n}\n\n/** \u4E00\u6B21\u300C\u7ACB\u5373\u5168\u90E8\u540C\u6B65\u300D\u7684\u7ED3\u679C\u6458\u8981\uFF08\u77ED\u6682\u663E\u793A\u540E\u81EA\u52A8\u6D88\u5931\uFF09\u3002 */\nexport interface SyncAllResult {\n ok: number\n failed: number\n /** \u9996\u4E2A\u5931\u8D25\u6458\u8981\uFF08Host \u539F\u6587\uFF0C\u6570\u636E\u800C\u975E\u6587\u6848\uFF09\u3002 */\n error: string\n}\n\n/** \u5361\u7247\u6E32\u67D3\u6240\u9700\u7684\u5168\u90E8\u72B6\u6001\u3002 */\nexport interface CloudSyncCardState {\n /** \u914D\u7F6E namespace \u4E0D\u53EF\u7528\uFF08Host \u672A serve\uFF09\u65F6\u5361\u7247\u6574\u4F53\u4E0D\u6E32\u67D3\u3002 */\n available: boolean\n /** Host \u6587\u6863\u662F\u5426\u53EF\u5199\uFF08memory \u6A21\u5F0F\u53EA\u8BFB\uFF0C\u5168\u90E8\u63A7\u4EF6\u7981\u7528\uFF09\u3002 */\n writable: boolean\n serverUrlDraft: string\n /** \u8349\u7A3F\u975E\u7A7A\u4E14\u975E\u5408\u6CD5 http(s) URL\uFF1A\u5C31\u5730\u62A5\u9519\u5E76\u7981\u7528\u6D4B\u8BD5\u8FDE\u63A5\u3002 */\n serverUrlInvalid: boolean\n usernameDraft: string\n deviceNameDraft: string\n deviceNameInvalid: boolean\n /** \u6C38\u8FDC\u7A7A\u503C\u8D77\u624B\uFF1B\u63D0\u4EA4\u540E\u5373\u6E05\u7A7A\u3002 */\n tokenDraft: string\n passphraseDraft: string\n tokenConfigured: boolean\n passphraseConfigured: boolean\n autoUpload: boolean\n checkOnStartup: boolean\n /** \u4E0D\u542B\u8FDB\u884C\u6001\u7684\u53EF\u7528\u6027\uFF1B\u6309\u94AE disabled \u8FD8\u9700\u53E0\u52A0 testPending/syncPending\u3002 */\n canTest: boolean\n canSync: boolean\n testPending: boolean\n syncPending: boolean\n statusLine: StatusLine\n syncResult: SyncAllResult | null\n // ---- M4\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846 / \u8DEF\u5F84\u6620\u5C04 / \u542F\u52A8\u901A\u77E5 ----\n /** \u6062\u590D\u5BF9\u8BDD\u6846\u5F00\u5173\uFF08\u5BF9\u8BDD\u6846\u6302\u5728 shell.overlay\uFF0C\u8BBE\u7F6E\u9875\u5916\u4E5F\u80FD\u5F00\uFF09\u3002 */\n restoreOpen: boolean\n /** \u6700\u8FD1\u4E00\u6B21\u76EE\u5F55\u62C9\u53D6\u7ED3\u679C\uFF1Bnull = \u4ECE\u672A\u62C9\u53D6\u3002 */\n catalog: RestoreCatalog | null\n catalogPending: boolean\n restorePending: boolean\n /** \u6062\u590D\u5B8C\u6210\u7684\u7ED3\u679C\u6458\u8981\uFF08overlay toast \u5C55\u793A\uFF0C\u77ED\u6682\u505C\u7559\uFF09\u3002 */\n restoreOutcome: RestoreResultPayload | null\n deviceFilter: string\n /** \u52FE\u9009\u7684\u884C\u4E3B\u952E\uFF08device/sessionId\uFF09\u3002 */\n selectedKeys: string[]\n /** \u7528\u6237\u6539\u9009\u7684\u843D\u4F4D\u8DEF\u5F84\uFF08\u884C\u4E3B\u952E \u2192 \u672C\u673A\u76EE\u5F55\uFF09\u3002 */\n overrides: Record<string, string>\n /** \u5DF2\u5B66\u4E60\u8DEF\u5F84\u6620\u5C04\uFF08\u8BBE\u7F6E\u5361\u53EA\u8BFB\u5217\u8868\uFF09\u3002 */\n mappings: MappingPair[]\n /** \u542F\u52A8\u68C0\u67E5\u901A\u77E5\uFF08null = \u65E0/\u5DF2\u5FFD\u7565\uFF09\u3002 */\n notice: StartupNoticePayload | null\n /** \u76EE\u5F55\u9009\u62E9\u5668\u80FD\u529B\u53EF\u7528\u6027\uFF08\u4E0D\u53EF\u7528\u65F6\u884C\u5185\u964D\u7EA7\u4E3A\u624B\u52A8\u8F93\u5165\uFF09\u3002 */\n pickerAvailable: boolean\n}\n\n/** \u6CE8\u518C\u4FA7 face\uFF1Ahooks \u7531\u6E32\u67D3\u5668\u7ED1\u5B9A\u4E3A\u7EC4\u4EF6 props \u4E0A\u7684 useCloudSyncCard\u3002 */\nexport interface CloudSyncCardFace {\n hooks: {\n /** \u5361\u7247\u5FEB\u7167\uFF0C\u6E32\u67D3\u5668\u7ED1\u5B9A\u4E3A useCloudSyncCard(selector)\u3002 */\n cloudSyncCard: SnapshotStore<CloudSyncCardState>\n }\n editServerUrl(text: string): void\n commitServerUrl(): void\n editUsername(text: string): void\n commitUsername(): void\n editToken(text: string): void\n commitToken(): void\n editPassphrase(text: string): void\n commitPassphrase(): void\n editDeviceName(text: string): void\n commitDeviceName(): void\n toggleAutoUpload(): void\n toggleCheckOnStartup(): void\n testConnection(): void\n syncAll(): void\n // ---- M4\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846 / \u8DEF\u5F84\u6620\u5C04 / \u542F\u52A8\u901A\u77E5 ----\n openRestore(): void\n closeRestore(): void\n setDeviceFilter(filter: string): void\n toggleRow(key: string): void\n /** \u76EE\u5F55\u9009\u62E9\u5668\u6539\u9009\uFF08\u80FD\u529B\u4E0D\u53EF\u7528\u65F6 UI \u4E0D\u6E32\u67D3\u8BE5\u6309\u94AE\uFF0C\u8D70 overrideTarget \u624B\u52A8\u8F93\u5165\uFF09\u3002 */\n pickTarget(key: string): void\n /** \u624B\u52A8\u8F93\u5165\u6539\u9009\uFF08\u53D7\u63A7\u8F93\u5165\u9010\u952E\u66F4\u65B0\uFF1B\u7A7A\u4E32 = \u6E05\u9664\u6539\u9009\uFF09\u3002 */\n overrideTarget(key: string, text: string): void\n restoreSelected(): void\n deleteMapping(from: string): void\n dismissNotice(): void\n /** toast \u7684\u300C\u70B9\u51FB\u67E5\u770B\u300D\uFF1A\u5173\u6389\u901A\u77E5\u5E76\u6253\u5F00\u6062\u590D\u5BF9\u8BDD\u6846\u3002 */\n viewNotice(): void\n clearRestoreOutcome(): void\n}\n\n/** \u540C\u6B65\u7ED3\u679C\u6458\u8981\u7684\u505C\u7559\u65F6\u957F\u3002 */\nconst SYNC_RESULT_VISIBLE_MS = 8_000\n/** \u76F8\u5BF9\u65F6\u95F4\uFF08\"N \u5206\u949F\u524D\"\uFF09\u7684\u9759\u9ED8\u5237\u65B0\u8282\u62CD\u3002 */\nconst CLOCK_TICK_MS = 30_000\n/** \u542F\u52A8\u901A\u77E5\u5DF2\u8BFB\u6807\u8BB0\u7684 sessionStorage key\u3002 */\nconst NOTICE_SEEN_KEY = 'dsh-cloud-sync:notice-seen-at'\n\n/** \u4E00\u6B21\u8FDB\u884C\u4E2D\u7684\u89E6\u53D1\u5668\u5199\u3002 */\ninterface PendingOp {\n /** \u672C\u6B21\u89E6\u53D1\u53D1\u51FA\u7684\u65F6\u95F4\u6233\uFF1B\u91CD\u8BD5\u4FDD\u6301\u540C\u4E00\u503C\uFF0C\u56DE\u663E >= \u5B83\u5373\u5B8C\u6210\u3002 */\n issuedAt: number\n /** \u4E0A\u4E00\u6B21\u5B9E\u9645\u5199\u51FA\u7684\u65F6\u523B\uFF08\u8DDD\u5B83\u6EE1 TRIGGER_RETRY_MS \u4E14\u65E0\u56DE\u663E\u5219\u91CD\u53D1\uFF09\u3002 */\n lastSentAt: number\n}\n\n/** \u6865\u63A5\u4E24\u4E2A SettingsScope \u4E0E\u5361\u7247\u5FEB\u7167\u3002 */\nexport class CloudSyncCardController {\n private readonly store: SnapshotStore<CloudSyncCardState>\n /** \u672C\u5730\u8349\u7A3F + \u5404\u8349\u7A3F\u5BF9\u5E94\u7684 scope \u5DF2\u540C\u6B65\u503C\uFF08\u7528\u4E8E\u8BC6\u522B\u5916\u90E8\u53D8\u66F4\u8BE5\u5426\u8986\u76D6\u8349\u7A3F\uFF09\u3002 */\n private serverUrlDraft = ''\n private serverUrlSynced = ''\n private usernameDraft = ''\n private usernameSynced = ''\n private deviceNameDraft = ''\n private deviceNameSynced = ''\n private tokenDraft = ''\n private passphraseDraft = ''\n /** \u672C\u6B21\u4F1A\u8BDD\u5185 secret \u63D0\u4EA4\u8FC7\u7684\u4E50\u89C2\u6807\u8BB0\uFF08Host \u7684 tokenConfigured \u56DE\u5199\u524D\u7684\u5373\u65F6\u53CD\u9988\uFF09 */\n private tokenCommitted = false\n private passphraseCommitted = false\n private now = Date.now()\n private pendingTest: PendingOp | null = null\n private pendingSync: PendingOp | null = null\n private syncResult: SyncAllResult | null = null\n private syncResultTimer: ReturnType<typeof setTimeout> | null = null\n /** \u89E6\u53D1\u5668\u8FDB\u884C\u671F\u95F4\u7684 1s \u9A71\u52A8\u8282\u62CD\uFF08\u65E0 pending \u65F6\u505C\u6389\uFF0C\u4E0D\u767D\u8F6C\uFF09\u3002 */\n private triggerWatch: ReturnType<typeof setInterval> | null = null\n private readonly disposers: (() => void)[] = []\n // ---- M4 \u5185\u90E8\u72B6\u6001 ----\n private restoreOpen = false\n private catalog: RestoreCatalog | null = null\n private pendingRestoreList: PendingOp | null = null\n private pendingRestore: PendingOp | null = null\n private restoreOutcome: RestoreResultPayload | null = null\n private deviceFilter = ''\n private readonly selectedKeys = new Set<string>()\n private readonly overrides = new Map<string, string>()\n /** \u6620\u5C04\u5220\u9664\u7684\u8FDB\u884C\u4E2D\u8BF7\u6C42\uFF08\u56DE\u663E = pathMappingsJson \u91CC\u4E0D\u518D\u6709\u8BE5 from\uFF09\u3002 */\n private pendingMappingDelete: (PendingOp & { from: string }) | null = null\n /** \u542F\u52A8\u901A\u77E5\u7684\u5DF2\u8BFB\u6807\u8BB0\uFF08\u540C at \u4E0D\u91CD\u590D\u5F39\uFF1BsessionStorage \u8BB0\u5FC6\uFF0C\u8DE8\u9875\u9762\u5237\u65B0\u4E0D\u91CD\u73B0\uFF09\u3002 */\n private noticeSeenAt = 0\n\n /**\n * @param configScope - \u7ED1\u5B9A 'cloud-sync' \u7684 scope\uFF08\u914D\u7F6E\u8BFB\u5199\uFF09\u3002\n * @param statusScope - \u7ED1\u5B9A 'cloud-sync-status' \u7684 scope\uFF08\u72B6\u6001\u8BFB + \u89E6\u53D1\u5668\u5199\uFF09\u3002\n * @param directoryPicker - \u76EE\u5F55\u9009\u62E9\u5668\u80FD\u529B\uFF08\u53EF\u9009\uFF1B\u7F3A\u5931\u65F6\u6062\u590D\u5BF9\u8BDD\u6846\u964D\u7EA7\u4E3A\u624B\u52A8\u8F93\u5165\uFF09\u3002\n */\n constructor(\n private readonly configScope: SettingsScope<CloudSyncConfig>,\n private readonly statusScope: SettingsScope<CloudSyncStatus>,\n private readonly directoryPicker?: DirectoryPickerLike,\n ) {\n this.store = createSnapshotStore(this.projection())\n this.disposers.push(this.configScope.subscribe(() => { this.onConfigChange() }))\n this.disposers.push(this.statusScope.subscribe(() => { this.onStatusChange() }))\n const clock = setInterval(() => {\n this.now = Date.now()\n this.reproject()\n }, CLOCK_TICK_MS)\n this.disposers.push(() => { clearInterval(clock) })\n try {\n this.noticeSeenAt = Number(globalThis.sessionStorage?.getItem(NOTICE_SEEN_KEY) ?? 0) || 0\n } catch {\n // sessionStorage \u4E0D\u53EF\u7528\uFF08\u9690\u79C1\u6A21\u5F0F\u7B49\uFF09\uFF1A\u9000\u5316\u4E3A\u6BCF\u6B21\u9875\u9762\u52A0\u8F7D\u6700\u591A\u5F39\u4E00\u6B21\n }\n this.onConfigChange()\n this.onStatusChange()\n }\n\n /** \u91CA\u653E\u8BA2\u9605\u4E0E\u8BA1\u65F6\u5668\uFF08\u63D2\u4EF6\u5378\u8F7D\u65F6\u7531 ctx.effect \u7684\u6E05\u7406\u51FD\u6570\u8C03\u7528\uFF09\u3002 */\n dispose(): void {\n for (const dispose of this.disposers.splice(0)) dispose()\n if (this.triggerWatch !== null) clearInterval(this.triggerWatch)\n if (this.syncResultTimer !== null) clearTimeout(this.syncResultTimer)\n }\n\n /** \u6784\u5EFA\u6CE8\u518C\u4FA7 face\u3002 */\n inject(): CloudSyncCardFace {\n return {\n hooks: { cloudSyncCard: this.store },\n editServerUrl: (text) => {\n this.serverUrlDraft = text\n this.reproject()\n },\n commitServerUrl: () => { this.commitServerUrl() },\n editUsername: (text) => {\n this.usernameDraft = text\n this.reproject()\n },\n commitUsername: () => { this.commitUsername() },\n editToken: (text) => {\n this.tokenDraft = text\n this.reproject()\n },\n commitToken: () => { this.commitSecret('token') },\n editPassphrase: (text) => {\n this.passphraseDraft = text\n this.reproject()\n },\n commitPassphrase: () => { this.commitSecret('passphrase') },\n editDeviceName: (text) => {\n this.deviceNameDraft = text\n this.reproject()\n },\n commitDeviceName: () => { this.commitDeviceName() },\n toggleAutoUpload: () => { this.toggleBoolean('autoUpload') },\n toggleCheckOnStartup: () => { this.toggleBoolean('checkOnStartup') },\n testConnection: () => { this.startTrigger('test') },\n syncAll: () => { this.startTrigger('sync') },\n openRestore: () => { this.openRestore() },\n closeRestore: () => {\n this.restoreOpen = false\n this.reproject()\n },\n setDeviceFilter: (filter) => {\n this.deviceFilter = filter\n this.reproject()\n },\n toggleRow: (key) => {\n if (this.selectedKeys.has(key)) this.selectedKeys.delete(key)\n else this.selectedKeys.add(key)\n this.reproject()\n },\n pickTarget: (key) => { void this.pickTarget(key) },\n overrideTarget: (key, text) => {\n const trimmed = text.trim()\n if (trimmed === '') this.overrides.delete(key)\n else this.overrides.set(key, trimmed)\n this.reproject()\n },\n restoreSelected: () => { this.startRestore() },\n deleteMapping: (from) => { this.startMappingDelete(from) },\n dismissNotice: () => { this.markNoticeSeen() },\n viewNotice: () => {\n this.markNoticeSeen()\n this.openRestore()\n },\n clearRestoreOutcome: () => {\n this.restoreOutcome = null\n this.reproject()\n },\n }\n }\n\n /** \u914D\u7F6E scope \u53D8\u66F4\uFF1A\u540C\u6B65\u672A\u4FEE\u6539\u7684\u8349\u7A3F\uFF08\u6B63\u5728\u7F16\u8F91\u7684\u8349\u7A3F\u4E0D\u88AB\u5916\u90E8\u53D8\u66F4\u8986\u76D6\uFF09\u3002 */\n private onConfigChange(): void {\n const value = this.configScope.getSnapshot().value\n const serverUrl = value?.serverUrl ?? ''\n if (this.serverUrlDraft === this.serverUrlSynced) this.serverUrlDraft = serverUrl\n this.serverUrlSynced = serverUrl\n const username = value?.username ?? ''\n if (this.usernameDraft === this.usernameSynced) this.usernameDraft = username\n this.usernameSynced = username\n const deviceName = value?.deviceName ?? ''\n if (this.deviceNameDraft === this.deviceNameSynced) this.deviceNameDraft = deviceName\n this.deviceNameSynced = deviceName\n this.reproject()\n }\n\n /** \u72B6\u6001 scope \u53D8\u66F4\uFF1A\u9A71\u52A8\u89E6\u53D1\u5668\u7684\u5B8C\u6210/\u91CD\u8BD5\u5224\u5B9A\uFF0C\u518D\u91CD\u6295\u5F71\u3002 */\n private onStatusChange(): void {\n // \u76EE\u5F55\u515C\u5E95\uFF1A\u91C7\u7528\u901A\u9053\u91CC\u6B8B\u7559\u7684\u4E0A\u6B21\u7ED3\u679C\uFF08\u6253\u5F00\u5BF9\u8BDD\u6846\u5373\u89E6\u53D1\u65B0\u62C9\u53D6\u8986\u76D6\u5B83\uFF09\uFF0C\n // \u8BA9 restoreItems \u4E0E\u6E32\u67D3\u5171\u7528\u540C\u4E00\u4EFD\u6570\u636E\n if (this.catalog === null) {\n this.catalog = parseCatalog(this.statusScope.getSnapshot().value?.restoreCatalogJson ?? '')\n }\n this.driveTriggers()\n this.reproject()\n }\n\n /** \u53D1\u8D77\u4E00\u6B21\u89E6\u53D1\u5668\u5199\uFF08\u6D4B\u8BD5\u8FDE\u63A5 / \u7ACB\u5373\u5168\u90E8\u540C\u6B65\uFF09\uFF0C\u8FDB\u884C\u6001\u7531 driveTriggers \u7EF4\u62A4\u3002 */\n private startTrigger(kind: 'test' | 'sync'): void {\n if (kind === 'test' ? this.pendingTest !== null : this.pendingSync !== null) return\n const issuedAt = Date.now()\n const op: PendingOp = { issuedAt, lastSentAt: 0 }\n if (kind === 'test') this.pendingTest = op\n else this.pendingSync = op\n // \u7ACB\u5373\u9996\u53D1\uFF1B\u540E\u7EED\u91CD\u8BD5\u7531 triggerWatch \u8282\u62CD\u9A71\u52A8\n this.sendTrigger(op, kind === 'test' ? 'testRequestedAt' : 'syncAllRequestedAt')\n this.ensureTriggerWatch()\n this.reproject()\n }\n\n /** \u5199\u4E00\u6B21\u89E6\u53D1\u5B57\u6BB5\uFF1B\u5931\u8D25\u4E0D\u91CD\u629B\u2014\u2014\u91CD\u8BD5\u8282\u62CD\u4F1A\u8865\u53D1\uFF0C\u603B\u8D85\u65F6\u515C\u5E95\u9000\u51FA\u3002 */\n private sendTrigger(op: PendingOp, field: 'testRequestedAt' | 'syncAllRequestedAt'): void {\n op.lastSentAt = Date.now()\n void this.statusScope.set(field, op.issuedAt).catch(() => {})\n }\n\n /** \u89E6\u53D1\u5668\u9A71\u52A8\uFF1A\u5BF9\u6BCF\u4E2A pending \u5224\u5B9A done/timeout/resend/wait \u5E76\u6267\u884C\u3002 */\n private driveTriggers(): void {\n if (this.pendingTest === null && this.pendingSync === null\n && this.pendingRestoreList === null && this.pendingRestore === null && this.pendingMappingDelete === null) {\n this.maybeStopTriggerWatch()\n return\n }\n const now = Date.now()\n this.now = now\n const value = this.statusScope.getSnapshot().value\n if (this.pendingTest !== null) {\n const action = triggerAction(value?.statusCheckedAt ?? 0, this.pendingTest.issuedAt, this.pendingTest.lastSentAt, now)\n if (action === 'done' || action === 'timeout') this.pendingTest = null\n else if (action === 'resend') this.sendTrigger(this.pendingTest, 'testRequestedAt')\n }\n if (this.pendingSync !== null) {\n const op = this.pendingSync\n const action = triggerAction(value?.statusSyncAllAt ?? 0, op.issuedAt, op.lastSentAt, now)\n if (action === 'done') {\n this.pendingSync = null\n this.showSyncResult({\n ok: value?.statusSyncAllOk ?? 0,\n failed: value?.statusSyncAllFailed ?? 0,\n error: value?.statusSyncAllError ?? '',\n })\n } else if (action === 'timeout') {\n this.pendingSync = null\n } else if (action === 'resend') {\n this.sendTrigger(op, 'syncAllRequestedAt')\n }\n }\n if (this.pendingRestoreList !== null) {\n const op = this.pendingRestoreList\n const catalog = parseCatalog(value?.restoreCatalogJson ?? '')\n const action = triggerAction(catalog?.at ?? 0, op.issuedAt, op.lastSentAt, now)\n if (action === 'done') {\n this.pendingRestoreList = null\n this.catalog = catalog\n // \u76EE\u5F55\u5237\u65B0\u540E\u6309\u5F53\u524D\u8BBE\u5907\u91CD\u4F30\u9ED8\u8BA4\u52FE\u9009\uFF08\u4FDD\u7559\u7528\u6237\u5DF2\u52FE\u7684\u884C\uFF09\n if (catalog !== null) {\n for (const key of defaultSelectedKeys(catalog)) this.selectedKeys.add(key)\n // \u76EE\u5F55\u91CC\u6D88\u5931\u7684\u884C\uFF08\u4ED6\u673A\u5220\u9664\u7B49\uFF09\u4ECE\u52FE\u9009\u4E0E\u6539\u9009\u4E2D\u6E05\u6389\n const live = new Set(catalog.sessions.map(rowKeyOf))\n for (const key of [...this.selectedKeys]) if (!live.has(key)) this.selectedKeys.delete(key)\n for (const key of [...this.overrides.keys()]) if (!live.has(key)) this.overrides.delete(key)\n }\n } else if (action === 'timeout') {\n this.pendingRestoreList = null\n } else if (action === 'resend') {\n op.lastSentAt = now\n void this.statusScope.set('restoreListRequestedAt', op.issuedAt).catch(() => {})\n }\n }\n if (this.pendingRestore !== null) {\n const op = this.pendingRestore\n const result = parseRestoreResult(value?.restoreResultJson ?? '')\n const action = triggerAction(result?.at ?? 0, op.issuedAt, op.lastSentAt, now)\n if (action === 'done' || action === 'timeout') {\n this.pendingRestore = null\n if (action === 'done' && result !== null) {\n this.restoreOutcome = result\n // \u6062\u590D\u5B8C\u6210 \u2192 \u5BF9\u8BDD\u6846\u5173\u95ED\uFF0801 \u00A75.4\uFF09\uFF0C\u52FE\u9009\u4E0E\u6539\u9009\u968F\u672C\u8F6E\u7ED3\u675F\u6E05\u7A7A\n this.restoreOpen = false\n this.selectedKeys.clear()\n this.overrides.clear()\n }\n } else if (action === 'resend') {\n op.lastSentAt = now\n void this.statusScope.set('restoreRequestJson', encodeRestoreRequest(op.issuedAt, this.restoreItems())).catch(() => {})\n }\n }\n if (this.pendingMappingDelete !== null) {\n const op = this.pendingMappingDelete\n const gone = !parseMappings(value?.pathMappingsJson ?? '[]').some((pair) => pair.from === op.from)\n // \u56DE\u663E\u8BED\u4E49\u4E0D\u662F\u65F6\u95F4\u6233\u800C\u662F\u300C\u8BE5 from \u5DF2\u4ECE\u6620\u5C04\u5217\u8868\u6D88\u5931\u300D\uFF1BechoAt \u5408\u6210\u4EE5\u590D\u7528 triggerAction\n const action = gone ? 'done' : triggerAction(0, op.issuedAt, op.lastSentAt, now)\n if (action === 'done' || action === 'timeout') this.pendingMappingDelete = null\n else if (action === 'resend') {\n op.lastSentAt = now\n void this.statusScope.set('mappingDeleteJson', encodeMappingDelete(op.issuedAt, op.from)).catch(() => {})\n }\n }\n this.maybeStopTriggerWatch()\n this.reproject()\n }\n\n /** pending \u51FA\u73B0\u65F6\u8D77 1s \u9A71\u52A8\u8282\u62CD\uFF08\u5199\u88AB\u9759\u9ED8\u4E22\u5F03\u65F6\u6CA1\u6709\u4EFB\u4F55\u4E8B\u4EF6\u53EF\u7B49\uFF0C\u5FC5\u987B\u4E3B\u52A8\u8F6E\u8BE2\uFF09\u3002 */\n private ensureTriggerWatch(): void {\n if (this.triggerWatch !== null) return\n this.triggerWatch = setInterval(() => { this.driveTriggers() }, TRIGGER_RETRY_MS)\n }\n\n private maybeStopTriggerWatch(): void {\n if (this.pendingTest !== null || this.pendingSync !== null\n || this.pendingRestoreList !== null || this.pendingRestore !== null || this.pendingMappingDelete !== null) return\n if (this.triggerWatch === null) return\n clearInterval(this.triggerWatch)\n this.triggerWatch = null\n }\n\n // ---- M4 \u52A8\u4F5C ----\n\n /** \u6253\u5F00\u6062\u590D\u5BF9\u8BDD\u6846\u5E76\u53D1\u8D77\u76EE\u5F55\u62C9\u53D6\uFF08\u6BCF\u6B21\u6253\u5F00\u90FD\u91CD\u65B0\u62C9\uFF0C\u4FDD\u8BC1 existsLocal \u7B49\u6807\u6CE8\u65B0\u9C9C\uFF09\u3002 */\n private openRestore(): void {\n this.restoreOpen = true\n if (this.pendingRestoreList === null) {\n const op: PendingOp = { issuedAt: Date.now(), lastSentAt: 0 }\n this.pendingRestoreList = op\n op.lastSentAt = op.issuedAt\n void this.statusScope.set('restoreListRequestedAt', op.issuedAt).catch(() => {})\n this.ensureTriggerWatch()\n }\n this.reproject()\n }\n\n /** \u76EE\u5F55\u9009\u62E9\u5668\u6539\u9009\u4E00\u884C\u843D\u4F4D\u8DEF\u5F84\u3002 */\n private async pickTarget(key: string): Promise<void> {\n if (!this.directoryPicker) return\n try {\n const result = await this.directoryPicker.pick()\n if (result.ok && result.value !== null) {\n this.overrides.set(key, result.value)\n this.reproject()\n }\n } catch (error) {\n console.warn('cloud-sync: directory pick failed:', error)\n }\n }\n\n /** \u7EC4\u88C5\u6062\u590D\u8BF7\u6C42\u6761\u76EE\uFF1A\u6539\u9009\u4F18\u5148\uFF0C\u5176\u6B21 Host \u89E3\u6790\u7ED3\u679C\uFF0C\u90FD\u6CA1\u6709\u5219 null\uFF08\u539F\u6837\u843D\u4F4D\uFF09\u3002 */\n private restoreItems(): { sessionId: string; device: string; targetCwd: string | null }[] {\n const catalog = this.catalog\n if (catalog === null) return []\n const items: { sessionId: string; device: string; targetCwd: string | null }[] = []\n for (const entry of catalog.sessions) {\n const key = rowKeyOf(entry)\n if (!this.selectedKeys.has(key) || !isRowRestorable(entry)) continue\n items.push({\n sessionId: entry.sessionId,\n device: entry.device,\n targetCwd: this.overrides.get(key) ?? entry.resolvedCwd,\n })\n }\n return items\n }\n\n /** \u53D1\u8D77\u6062\u590D\u6267\u884C\u89E6\u53D1\u5668\uFF08\u91CD\u8BD5\u4FDD\u6301\u540C\u4E00 at \u4E0E\u6761\u76EE\u96C6\uFF09\u3002 */\n private startRestore(): void {\n if (this.pendingRestore !== null) return\n const items = this.restoreItems()\n if (items.length === 0) return\n const op: PendingOp = { issuedAt: Date.now(), lastSentAt: 0 }\n this.pendingRestore = op\n op.lastSentAt = op.issuedAt\n void this.statusScope.set('restoreRequestJson', encodeRestoreRequest(op.issuedAt, items)).catch(() => {})\n this.ensureTriggerWatch()\n this.reproject()\n }\n\n /** \u5220\u9664\u4E00\u6761\u5DF2\u5B66\u4E60\u6620\u5C04\uFF08\u4E50\u89C2\u79FB\u9664 + \u89E6\u53D1\u5668\u91CD\u8BD5\u5230 Host \u56DE\u663E\u6D88\u5931\uFF09\u3002 */\n private startMappingDelete(from: string): void {\n if (this.pendingMappingDelete !== null) return\n const op: PendingOp & { from: string } = { issuedAt: Date.now(), lastSentAt: 0, from }\n this.pendingMappingDelete = op\n op.lastSentAt = op.issuedAt\n void this.statusScope.set('mappingDeleteJson', encodeMappingDelete(op.issuedAt, from)).catch(() => {})\n this.ensureTriggerWatch()\n this.reproject()\n }\n\n /** \u542F\u52A8\u901A\u77E5\u5DF2\u8BFB\uFF1A\u672C\u6B21 at \u8BB0\u5165 sessionStorage\uFF0C\u540C at \u4E0D\u518D\u5F39\uFF0801 \u00A75.5 \u4E0D\u91CD\u590D\u5F39\u51FA\uFF09\u3002 */\n private markNoticeSeen(): void {\n const notice = parseStartupNotice(this.statusScope.getSnapshot().value?.startupNoticeJson ?? '')\n if (notice !== null) {\n this.noticeSeenAt = notice.at\n try {\n globalThis.sessionStorage?.setItem(NOTICE_SEEN_KEY, String(notice.at))\n } catch {\n // sessionStorage \u4E0D\u53EF\u7528\u65F6\u4EC5\u672C\u6B21\u9875\u9762\u751F\u547D\u5468\u671F\u5185\u6709\u6548\n }\n }\n this.reproject()\n }\n\n /** \u5C55\u793A\u540C\u6B65\u7ED3\u679C\u6458\u8981\uFF0C\u77ED\u6682\u505C\u7559\u540E\u81EA\u52A8\u6D88\u5931\u3002 */\n private showSyncResult(result: SyncAllResult): void {\n this.syncResult = result\n if (this.syncResultTimer !== null) clearTimeout(this.syncResultTimer)\n this.syncResultTimer = setTimeout(() => {\n this.syncResult = null\n this.syncResultTimer = null\n this.reproject()\n }, SYNC_RESULT_VISIBLE_MS)\n }\n\n /** \u670D\u52A1\u5668\u5730\u5740\u63D0\u4EA4\uFF08blur/Enter\uFF09\uFF1A\u975E\u6CD5\u503C\u4E0D\u843D\u76D8\uFF0C\u5C31\u5730\u62A5\u9519\u4EA4\u7ED9\u6295\u5F71\u3002 */\n private commitServerUrl(): void {\n const text = this.serverUrlDraft.trim()\n this.serverUrlDraft = text\n if (text !== '' && !isValidServerUrl(text)) {\n this.reproject()\n return\n }\n if (text === this.serverUrlSynced) return\n this.serverUrlSynced = text\n void this.configScope.set('serverUrl', text).catch(() => {})\n }\n\n /** \u7528\u6237\u540D\u63D0\u4EA4\uFF08blur/Enter\uFF09\uFF1Atrim \u540E\u843D\u76D8\uFF0C\u4E0D\u505A\u683C\u5F0F\u6821\u9A8C\u2014\u2014\u5408\u6CD5\u6027\u7531\u670D\u52A1\u7AEF\n * \u914D\u5BF9\u6821\u9A8C\u88C1\u51B3\uFF08403 \u2192 userMismatch\uFF09\uFF0C\u63D2\u4EF6\u65E0\u4ECE\u611F\u77E5\u670D\u52A1\u7AEF\u662F\u5355\u7528\u6237\u8FD8\u662F\u591A\u7528\u6237\u3002 */\n private commitUsername(): void {\n const text = this.usernameDraft.trim()\n this.usernameDraft = text\n if (text === this.usernameSynced) return\n this.usernameSynced = text\n void this.configScope.set('username', text).catch(() => {})\n }\n\n /** \u8BBE\u5907\u540D\u63D0\u4EA4\uFF1A\u5408\u6CD5\u6216\u7A7A\uFF08= \u56DE\u9000\u4E3B\u673A\u540D\uFF09\u624D\u843D\u76D8\u3002 */\n private commitDeviceName(): void {\n const text = this.deviceNameDraft.trim()\n this.deviceNameDraft = text\n if (!isValidDeviceName(text)) {\n this.reproject()\n return\n }\n if (text === this.deviceNameSynced) return\n this.deviceNameSynced = text\n void this.configScope.set('deviceName', text).catch(() => {})\n }\n\n /** secret \u63D0\u4EA4\uFF1A\u7A7A\u8349\u7A3F\u4E0D\u52A8\u65E2\u6709\u914D\u7F6E\uFF1B\u63D0\u4EA4\u540E\u7ACB\u523B\u6E05\u7A7A\u672C\u5730\u8349\u7A3F\u3002\n * \u4E50\u89C2\u7F6E\u5DF2\u914D\u7F6E\u6807\u8BB0\uFF08Host \u7EA0\u504F\u56DE\u5199\u524D\u7528\u6237\u5373\u53EF\u770B\u5230\u53CD\u9988\uFF09\uFF0C\u5426\u5219\u8F93\u5165\u6846\u6E05\u7A7A\n * \u53C8\u6CA1\u6709\u5373\u65F6\u53CD\u9988\uFF0C\u8BFB\u8D77\u6765\u50CF\"\u503C\u88AB\u541E\u4E86\"\u3002 */\n private commitSecret(field: 'token' | 'passphrase'): void {\n const text = field === 'token' ? this.tokenDraft : this.passphraseDraft\n if (text === '') return\n if (field === 'token') {\n this.tokenDraft = ''\n this.tokenCommitted = true\n } else {\n this.passphraseDraft = ''\n this.passphraseCommitted = true\n }\n this.reproject()\n void this.configScope.set(field, text).catch(() => {})\n }\n\n /** \u5E03\u5C14\u5F00\u5173\uFF1A\u70B9\u51FB\u5373\u5199\uFF08\u914D\u7F6E\u5373\u65F6\u4FDD\u5B58\uFF0C\u65E0\"\u786E\u5B9A\"\u6309\u94AE\uFF09\u3002 */\n private toggleBoolean(field: 'autoUpload' | 'checkOnStartup'): void {\n const current = this.configScope.getSnapshot().value?.[field] ?? true\n void this.configScope.set(field, !current).catch(() => {})\n }\n\n private reproject(): void {\n this.store.set(this.projection())\n }\n\n private projection(): CloudSyncCardState {\n const config = this.configScope.getSnapshot()\n const status = this.statusScope.getSnapshot().value\n // \u4EE4\u724C\u300C\u53EF\u7528\u300D= Host \u5DF2\u914D\u7F6E\uFF0C\u6216\u672C\u6B21\u65B0\u586B\u4E86\u8349\u7A3F\uFF08\u5C1A\u672A\u5199\u56DE\u4E5F\u53EF\u53D1\u8D77\u6D4B\u8BD5\uFF09\n const tokenAvailable = status?.tokenConfigured === true || this.tokenDraft !== ''\n return {\n available: config.status !== 'unavailable',\n writable: config.writable,\n serverUrlDraft: this.serverUrlDraft,\n serverUrlInvalid: this.serverUrlDraft !== '' && !isValidServerUrl(this.serverUrlDraft),\n usernameDraft: this.usernameDraft,\n deviceNameDraft: this.deviceNameDraft,\n deviceNameInvalid: !isValidDeviceName(this.deviceNameDraft),\n tokenDraft: this.tokenDraft,\n passphraseDraft: this.passphraseDraft,\n tokenConfigured: status?.tokenConfigured === true || this.tokenCommitted,\n passphraseConfigured: status?.passphraseConfigured === true || this.passphraseCommitted,\n autoUpload: config.value?.autoUpload ?? true,\n checkOnStartup: config.value?.checkOnStartup ?? true,\n canTest: canRunServerAction(this.serverUrlDraft, tokenAvailable),\n canSync: canRunServerAction(this.serverUrlDraft, tokenAvailable),\n testPending: this.pendingTest !== null,\n syncPending: this.pendingSync !== null,\n statusLine: projectStatusLine({\n phase: status?.statusPhase ?? 'unconfigured',\n error: status?.statusError ?? '',\n errorDetail: status?.statusErrorDetail ?? '',\n cloudCount: status?.statusCloudCount ?? 0,\n lastSyncAt: status?.statusLastSyncAt ?? 0,\n now: this.now,\n }),\n syncResult: this.syncResult,\n restoreOpen: this.restoreOpen,\n catalog: this.catalog,\n catalogPending: this.pendingRestoreList !== null,\n restorePending: this.pendingRestore !== null,\n restoreOutcome: this.restoreOutcome,\n deviceFilter: this.deviceFilter,\n selectedKeys: [...this.selectedKeys],\n overrides: Object.fromEntries(this.overrides),\n mappings: parseMappings(status?.pathMappingsJson ?? '[]'),\n notice: this.projectNotice(status?.startupNoticeJson ?? ''),\n pickerAvailable: this.directoryPicker !== undefined,\n }\n }\n\n /** \u542F\u52A8\u901A\u77E5\u6295\u5F71\uFF1A\u672A\u8BFB\uFF08at \u665A\u4E8E\u5DF2\u8BFB\u6807\u8BB0\uFF09\u624D\u5F39\u51FA\u3002 */\n private projectNotice(raw: string): StartupNoticePayload | null {\n const notice = parseStartupNotice(raw)\n if (notice === null || notice.at <= this.noticeSeenAt) return null\n return notice\n }\n}\n", "/**\n * \u4E91\u7AEF\u540C\u6B65\u8BBE\u7F6E\u5361\u6587\u6848\u5B57\u5178\uFF08locale-owned\uFF09\u3002\n * \u94C1\u5F8B\uFF1A\u5361\u7247\u5168\u90E8\u53EF\u89C1\u6587\u6848\u53EA\u80FD\u51FA\u73B0\u5728\u672C\u6587\u4EF6 \u2014\u2014 dsh \u7684 verify-client-ui-i18n\n * \u7EA6\u5B9A\u7981\u6B62 JSX \u6587\u672C\u4E0E label/placeholder/title/aria-* \u5C5E\u6027\u51FA\u73B0\u81EA\u7136\u8BED\u8A00\u5B57\u9762\u91CF\u3002\n * zh/en \u5FC5\u987B\u540C key\uFF1B`{name}` \u4E3A\u6A21\u677F\u53C2\u6570\u4F4D\u3002\n */\n\n/** \u4E2D\u6587\u5B57\u5178\uFF08key \u96C6\u5408\u662F CloudSyncLocaleKey \u7684\u552F\u4E00\u4E8B\u5B9E\u6765\u6E90\uFF09\u3002 */\nexport const zh = {\n title: '\u4E91\u7AEF\u540C\u6B65',\n description: '\u628A\u4F1A\u8BDD\u7AEF\u5230\u7AEF\u52A0\u5BC6\u540C\u6B65\u5230\u81EA\u5EFA\u670D\u52A1\u5668',\n 'section.connection': '\u670D\u52A1\u5668\u8FDE\u63A5',\n 'section.sync': '\u540C\u6B65\u65B9\u5F0F',\n 'section.actions': '\u64CD\u4F5C',\n 'field.serverUrl': '\u670D\u52A1\u5668\u5730\u5740',\n 'field.serverUrl.placeholder': 'http://host:8787',\n 'field.username': '\u7528\u6237\u540D',\n 'field.username.hint': '\u591A\u7528\u6237\u670D\u52A1\u7AEF\u586B\u7BA1\u7406\u5458\u5206\u914D\u7684\u7528\u6237\u540D\uFF1B\u5355\u7528\u6237\u670D\u52A1\u7AEF\u7559\u7A7A',\n 'field.token': '\u8BBF\u95EE\u4EE4\u724C',\n 'field.passphrase': '\u52A0\u5BC6\u53E3\u4EE4',\n 'field.deviceName': '\u8BBE\u5907\u540D',\n 'field.deviceName.hint': '\u7559\u7A7A\u4F7F\u7528\u672C\u673A\u4E3B\u673A\u540D',\n 'field.autoUpload': '\u81EA\u52A8\u4E0A\u4F20',\n 'field.autoUpload.hint': '\u4F1A\u8BDD\u6709\u65B0\u5185\u5BB9\u65F6\u540E\u53F0\u63A8\u9001',\n 'field.checkOnStartup': '\u542F\u52A8\u65F6\u68C0\u67E5\u4E91\u7AEF',\n 'field.checkOnStartup.hint': '\u53D1\u73B0\u5176\u4ED6\u8BBE\u5907\u7684\u4F1A\u8BDD\u65F6\u63D0\u793A',\n 'field.configured': '\u5DF2\u914D\u7F6E',\n 'field.unconfigured': '\u672A\u914D\u7F6E',\n 'field.secret.show': '\u663E\u793A',\n 'field.secret.hide': '\u9690\u85CF',\n 'invalid.serverUrl': '\u8BF7\u8F93\u5165\u5408\u6CD5\u7684 http:// \u6216 https:// \u5730\u5740',\n 'invalid.deviceName': '\u4EC5\u9650\u5B57\u6BCD\u3001\u6570\u5B57\u3001- \u548C _\uFF0C\u4E0D\u8D85\u8FC7 32 \u4E2A\u5B57\u7B26',\n 'action.testConnection': '\u6D4B\u8BD5\u8FDE\u63A5',\n 'action.testing': '\u6D4B\u8BD5\u4E2D\u2026',\n 'action.syncAll': '\u7ACB\u5373\u5168\u90E8\u540C\u6B65',\n 'action.syncing': '\u540C\u6B65\u4E2D\u2026',\n 'status.unconfigured': '\u672A\u914D\u7F6E',\n 'status.connected': '\u5DF2\u8FDE\u63A5 \u00B7 \u4E91\u7AEF {count} \u4E2A\u4F1A\u8BDD',\n 'status.lastSync': '\u4E0A\u6B21\u540C\u6B65 {time}',\n 'status.disconnected': '\u8FDE\u63A5\u5931\u8D25\uFF1A{reason}',\n 'status.warnMark': '\u26A0',\n 'error.unreachable': '\u670D\u52A1\u5668\u5730\u5740\u4E0D\u53EF\u8FBE',\n 'error.tokenInvalid': '\u8BBF\u95EE\u4EE4\u724C\u65E0\u6548',\n 'error.userMismatch': '\u7528\u6237\u540D\u4E0E\u8BBF\u95EE\u4EE4\u724C\u4E0D\u5339\u914D',\n 'error.passphraseMismatch': '\u52A0\u5BC6\u53E3\u4EE4\u4E0E\u4E91\u7AEF\u6570\u636E\u4E0D\u5339\u914D',\n 'error.deviceConflict': '\u8BBE\u5907\u540D\u4E0E\u4E91\u7AEF\u5DF2\u6709\u6570\u636E\u51B2\u7A81\uFF0C\u8BF7\u66F4\u6362\u8BBE\u5907\u540D',\n 'error.unknown': '\u540C\u6B65\u5931\u8D25',\n 'time.justNow': '\u521A\u521A',\n 'time.minutesAgo': '{count} \u5206\u949F\u524D',\n 'time.hoursAgo': '{count} \u5C0F\u65F6\u524D',\n 'time.daysAgo': '{count} \u5929\u524D',\n 'sync.result': '\u6210\u529F {ok} \u00B7 \u5931\u8D25 {failed}',\n 'section.mappings': '\u8DEF\u5F84\u6620\u5C04',\n 'mapping.empty': '\u6062\u590D\u4F1A\u8BDD\u65F6\u81EA\u52A8\u5B66\u4E60\uFF0C\u53EF\u5220\u9664\u540E\u91CD\u65B0\u5EFA\u7ACB',\n 'mapping.delete': '\u5220\u9664',\n 'action.restore': '\u4ECE\u4E91\u7AEF\u6062\u590D\u4F1A\u8BDD\u2026',\n 'restore.title': '\u4ECE\u4E91\u7AEF\u6062\u590D\u4F1A\u8BDD',\n 'restore.close': '\u5173\u95ED',\n 'restore.cancel': '\u53D6\u6D88',\n 'restore.loading': '\u6B63\u5728\u52A0\u8F7D\u4E91\u7AEF\u4F1A\u8BDD\u2026',\n 'restore.empty': '\u4E91\u7AEF\u6CA1\u6709\u4F1A\u8BDD',\n 'restore.filter.all': '\u5168\u90E8\u8BBE\u5907',\n 'restore.column.title': '\u6807\u9898',\n 'restore.column.updated': '\u6700\u540E\u66F4\u65B0',\n 'restore.column.events': '\u4E8B\u4EF6\u6570',\n 'restore.column.target': '\u843D\u4F4D\u8DEF\u5F84',\n 'restore.pathSuggested': '\u5EFA\u8BAE',\n 'restore.pathMapped': '\u5DF2\u6620\u5C04',\n 'restore.unmatchedPath': '\u6062\u590D\u540E\u672A\u5206\u7EC4',\n 'restore.pickDirectory': '\u9009\u62E9\u76EE\u5F55',\n 'restore.manualPath.placeholder': '\u8F93\u5165\u672C\u673A\u76EE\u5F55\u8DEF\u5F84',\n 'restore.existsLocal': '\u672C\u5730\u5DF2\u5B58\u5728',\n 'restore.versionIncompatible': '\u7248\u672C\u4E0D\u517C\u5BB9',\n 'restore.action': '\u6062\u590D\u9009\u4E2D\u4F1A\u8BDD',\n 'restore.restoring': '\u6062\u590D\u4E2D\u2026',\n 'restore.selected': '\u5DF2\u9009 {count} \u4E2A',\n 'restore.result': '\u6062\u590D\u5B8C\u6210\uFF1A\u6210\u529F {ok} \u00B7 \u5931\u8D25 {failed}',\n 'toast.newCloudSessions': '\u4E91\u7AEF\u6709\u6765\u81EA\u5176\u4ED6\u8BBE\u5907\u7684\u4F1A\u8BDD\uFF0C\u70B9\u51FB\u67E5\u770B',\n 'toast.view': '\u67E5\u770B',\n 'toast.dismiss': '\u5173\u95ED',\n} as const\n\n/** \u5B57\u5178 key \u8054\u5408\u7C7B\u578B\uFF08zh \u7684 key \u96C6\u5408\u5373\u4E8B\u5B9E\u6765\u6E90\uFF0Cen \u7ECF Record \u7EA6\u675F\u4FDD\u6301\u540C key\uFF09\u3002 */\nexport type CloudSyncLocaleKey = keyof typeof zh\n\n/** \u82F1\u6587\u5B57\u5178\u3002 */\nexport const en: Record<CloudSyncLocaleKey, string> = {\n title: 'Cloud Sync',\n description: 'End-to-end encrypted sync of sessions to your own server',\n 'section.connection': 'Server connection',\n 'section.sync': 'Sync',\n 'section.actions': 'Actions',\n 'field.serverUrl': 'Server address',\n 'field.serverUrl.placeholder': 'http://host:8787',\n 'field.username': 'Username',\n 'field.username.hint': 'Only for multi-user servers (assigned by the admin); leave empty otherwise',\n 'field.token': 'Access token',\n 'field.passphrase': 'Encryption passphrase',\n 'field.deviceName': 'Device name',\n 'field.deviceName.hint': 'Leave blank to use the hostname',\n 'field.autoUpload': 'Automatic upload',\n 'field.autoUpload.hint': 'Push in the background when sessions change',\n 'field.checkOnStartup': 'Check cloud on startup',\n 'field.checkOnStartup.hint': 'Prompt when other devices have sessions',\n 'field.configured': 'Configured',\n 'field.unconfigured': 'Not configured',\n 'field.secret.show': 'Show',\n 'field.secret.hide': 'Hide',\n 'invalid.serverUrl': 'Enter a valid http:// or https:// URL',\n 'invalid.deviceName': 'Letters, digits, - and _ only, up to 32 characters',\n 'action.testConnection': 'Test connection',\n 'action.testing': 'Testing\u2026',\n 'action.syncAll': 'Sync all now',\n 'action.syncing': 'Syncing\u2026',\n 'status.unconfigured': 'Not configured',\n 'status.connected': 'Connected \u00B7 {count} sessions in cloud',\n 'status.lastSync': 'Last synced {time}',\n 'status.disconnected': 'Connection failed: {reason}',\n 'status.warnMark': '\u26A0',\n 'error.unreachable': 'Server unreachable',\n 'error.tokenInvalid': 'Invalid access token',\n 'error.userMismatch': 'Username does not match the access token',\n 'error.passphraseMismatch': 'Passphrase does not match cloud data',\n 'error.deviceConflict': 'Device name conflicts with cloud data; rename this device',\n 'error.unknown': 'Sync failed',\n 'time.justNow': 'just now',\n 'time.minutesAgo': '{count} min ago',\n 'time.hoursAgo': '{count} h ago',\n 'time.daysAgo': '{count} d ago',\n 'sync.result': 'Succeeded {ok} \u00B7 failed {failed}',\n 'section.mappings': 'Path mappings',\n 'mapping.empty': 'Learned automatically when restoring sessions; delete to re-establish',\n 'mapping.delete': 'Delete',\n 'action.restore': 'Restore from cloud\u2026',\n 'restore.title': 'Restore sessions from cloud',\n 'restore.close': 'Close',\n 'restore.cancel': 'Cancel',\n 'restore.loading': 'Loading cloud sessions\u2026',\n 'restore.empty': 'No sessions in cloud',\n 'restore.filter.all': 'All devices',\n 'restore.column.title': 'Title',\n 'restore.column.updated': 'Last updated',\n 'restore.column.events': 'Events',\n 'restore.column.target': 'Target path',\n 'restore.pathSuggested': 'Suggested',\n 'restore.pathMapped': 'Mapped',\n 'restore.unmatchedPath': 'Ungrouped after restore',\n 'restore.pickDirectory': 'Choose directory',\n 'restore.manualPath.placeholder': 'Enter a local directory path',\n 'restore.existsLocal': 'Exists locally',\n 'restore.versionIncompatible': 'Version incompatible',\n 'restore.action': 'Restore selected',\n 'restore.restoring': 'Restoring\u2026',\n 'restore.selected': '{count} selected',\n 'restore.result': 'Restore finished: {ok} succeeded \u00B7 {failed} failed',\n 'toast.newCloudSessions': 'Sessions from other devices found in cloud \u2014 click to view',\n 'toast.view': 'View',\n 'toast.dismiss': 'Dismiss',\n}\n\n// \u5411\u69FD\u7CFB\u7EDF\u58F0\u660E\u672C\u63D2\u4EF6\u7684\u5B57\u5178\u547D\u540D\u7A7A\u95F4\uFF1A\u6CE8\u518C\u5904\u58F0\u660E locale: 'settings.cloudSync' \u540E\uFF0C\n// \u6846\u67B6\u4F1A\u628A\u7C7B\u578B\u5316\u4E3A\u672C\u5B57\u5178\u7684 t \u5EA7\u4F4D\u653E\u5230\u7EC4\u4EF6 props \u4E0A\uFF08\u540C SlotMap \u7684\u5408\u5E76\u60EF\u4F8B\uFF09\u3002\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n 'settings.cloudSync': CloudSyncLocaleKey\n }\n}\n", "/**\n * \u5361\u7247\u6837\u5F0F\u4E0E\u6CE8\u5165\u5668\uFF1A\u4E0D\u7528 CSS modules\uFF08\u5916\u90E8\u5305\u6CA1\u6709 tsdown \u7684\u865A\u62DF\u6A21\u5757\u7BA1\u7EBF\uFF09\uFF0C\n * \u6539\u4E3A\u300C\u6837\u5F0F\u6587\u672C + \u5E26 data-plugin \u6807\u8BB0\u7684 <style> \u6CE8\u5165\u5668\u300D\uFF0C\u601D\u8DEF\u4EFF\n * deepseek-harness packages/client/tsdown.client.ts \u7684 styleInjectionModule \u7B80\u5316\u7248\u3002\n * \u989C\u8272\u5168\u90E8\u8D70 dsw token\uFF08--dsw-alias-*\uFF09\uFF0C\u7C7B\u540D\u7EDF\u4E00 dsh-cloud-sync-card \u524D\u7F00\u9632\u649E\u540D\u3002\n */\n\n/** \u6837\u5F0F\u6587\u672C\u3002 */\nexport const CARD_CSS = `\n.dsh-cloud-sync-card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n}\n.dsh-cloud-sync-card__header {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 14px 16px;\n}\n.dsh-cloud-sync-card__name {\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__description {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding: 4px 0 12px;\n}\n.dsh-cloud-sync-card__section {\n padding: 12px 0 0;\n}\n.dsh-cloud-sync-card__section + .dsh-cloud-sync-card__section {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin-top: 12px;\n}\n.dsh-cloud-sync-card__section-title {\n margin: 0 0 4px;\n font-size: 12px;\n font-weight: 500;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n padding: 8px 0;\n}\n.dsh-cloud-sync-card__field-head {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.dsh-cloud-sync-card__label {\n flex: 1;\n min-width: 0;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__badge {\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n line-height: 17px;\n white-space: nowrap;\n font-weight: 500;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-card__badge--muted {\n background: none;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__input {\n display: block;\n width: 100%;\n box-sizing: border-box;\n height: 34px;\n padding: 0 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n.dsh-cloud-sync-card__input:focus-visible {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n.dsh-cloud-sync-card__input:disabled {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n.dsh-cloud-sync-card__input[aria-invalid='true'] {\n border-color: var(--dsw-alias-label-error);\n}\n/* secret \u884C\uFF1A\u5916\u6846\u662F\u89C6\u89C9\u63A7\u4EF6\uFF08\u7126\u70B9\u73AF\u843D\u5728\u884C\u4E0A\uFF09\uFF0C\u8F93\u5165\u6846\u88F8\u5D4C\u3001\uD83D\uDC41 \u6536\u8FDB\u6846\u5185\u3002\n \u6CE8\u610F\u8F93\u5165\u6846\u81EA\u8EAB\u4E0D\u80FD\u518D\u5E26 flex: 1 \u2014\u2014 \u5728\u7EB5\u5411 flex \u5BB9\u5668\u91CC flex-basis: 0%\n \u4F1A\u628A\u9AD8\u5EA6\u538B\u6CA1\uFF08M3 \u9A8C\u6536\u5B9E\u6D4B text \u8F93\u5165\u584C\u5230 15px\uFF09\uFF0C\u53EA\u80FD\u653E\u5728\u6A2A\u5411\u884C\u91CC\u7528\u3002 */\n.dsh-cloud-sync-card__input-row {\n display: flex;\n align-items: center;\n gap: 4px;\n height: 34px;\n box-sizing: border-box;\n padding: 0 6px 0 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-3);\n}\n.dsh-cloud-sync-card__input-row:focus-within {\n border-color: var(--dsw-alias-brand-primary);\n}\n.dsh-cloud-sync-card__input-row .dsh-cloud-sync-card__input {\n flex: 1;\n min-width: 0;\n height: auto;\n padding: 0;\n border: none;\n background: transparent;\n}\n.dsh-cloud-sync-card__input-row .dsh-cloud-sync-card__input:focus-visible {\n border: none;\n}\n.dsh-cloud-sync-card__hint {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__error {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-error);\n}\n.dsh-cloud-sync-card__icon-button {\n flex: none;\n appearance: none;\n border: none;\n background: none;\n padding: 4px;\n font: inherit;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n border-radius: 6px;\n}\n.dsh-cloud-sync-card__icon-button:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__icon-button:disabled {\n cursor: default;\n opacity: 0.4;\n}\n.dsh-cloud-sync-card__row-end {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 4px 0;\n}\n.dsh-cloud-sync-card__button {\n appearance: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 5px 14px;\n background: none;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n.dsh-cloud-sync-card__button:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n.dsh-cloud-sync-card__button:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.dsh-cloud-sync-card__button:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n.dsh-cloud-sync-card__button--primary {\n background: var(--dsw-alias-label-primary);\n border-color: transparent;\n color: var(--dsw-alias-bg-layer-3);\n}\n.dsh-cloud-sync-card__button--primary:hover:not(:disabled) {\n color: var(--dsw-alias-bg-layer-3);\n border-color: transparent;\n}\n.dsh-cloud-sync-card__status {\n display: flex;\n align-items: baseline;\n gap: 6px;\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-card__status--muted {\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__status--error {\n color: var(--dsw-alias-label-error);\n}\n.dsh-cloud-sync-card__dot {\n position: relative;\n top: -1px;\n align-self: center;\n flex: none;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-card__dot[data-state='done'] {\n background: var(--dsw-alias-state-success-primary);\n}\n.dsh-cloud-sync-card__dot[data-state='warning'] {\n background: var(--dsw-alias-state-warn-primary);\n}\n.dsh-cloud-sync-card__dot[data-state='error'] {\n background: var(--dsw-alias-state-error-primary);\n}\n.dsh-cloud-sync-card__warn {\n color: var(--dsw-alias-state-warn-primary);\n}\n.dsh-cloud-sync-card__switch-row {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 0;\n}\n.dsh-cloud-sync-card__switch-label {\n width: 120px;\n flex: none;\n font-size: 13px;\n font-weight: 500;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-card__switch {\n position: relative;\n flex: none;\n width: 36px;\n height: 20px;\n border: none;\n border-radius: 999px;\n padding: 0;\n background: var(--dsw-alias-bg-module-platform);\n cursor: pointer;\n transition: background .16s;\n}\n.dsh-cloud-sync-card__switch[data-on='true'] {\n background: var(--dsw-alias-brand-primary);\n}\n.dsh-cloud-sync-card__switch:disabled {\n opacity: 0.4;\n cursor: default;\n}\n.dsh-cloud-sync-card__switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n.dsh-cloud-sync-card__switch-knob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--dsw-alias-bg-layer-3);\n transition: transform .16s;\n}\n.dsh-cloud-sync-card__switch[data-on='true'] .dsh-cloud-sync-card__switch-knob {\n transform: translateX(16px);\n}\n.dsh-cloud-sync-card__sync-result {\n margin: 0 0 4px;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n/* ---- M4\uFF1A\u8DEF\u5F84\u6620\u5C04\u533A ---- */\n.dsh-cloud-sync-card__mappings {\n list-style: none;\n margin: 4px 0 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.dsh-cloud-sync-card__mapping {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n}\n.dsh-cloud-sync-card__mapping-paths {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-card__mapping-arrow {\n margin: 0 6px;\n color: var(--dsw-alias-label-dimmed);\n}\n/* ---- M4\uFF1A\u6062\u590D\u5BF9\u8BDD\u6846 ---- */\n.dsh-cloud-sync-restore {\n width: 720px;\n max-width: calc(100vw - 64px);\n}\n.dsh-cloud-sync-restore__content {\n max-height: 60vh;\n overflow-y: auto;\n}\n.dsh-cloud-sync-restore__filter {\n display: flex;\n justify-content: flex-end;\n margin-bottom: 8px;\n}\n.dsh-cloud-sync-restore__select {\n appearance: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n padding: 4px 10px;\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-restore__head,\n.dsh-cloud-sync-restore__row {\n display: grid;\n grid-template-columns: 24px minmax(0, 2fr) 88px 56px minmax(0, 2.2fr);\n align-items: center;\n gap: 10px;\n}\n.dsh-cloud-sync-restore__head {\n padding: 4px 0;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-cloud-sync-restore__list {\n list-style: none;\n margin: 0;\n padding: 0;\n}\n.dsh-cloud-sync-restore__row {\n padding: 8px 0;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n}\n.dsh-cloud-sync-restore__row[data-disabled='true'] {\n opacity: 0.55;\n}\n.dsh-cloud-sync-restore__title {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-restore__device {\n margin-left: 6px;\n font-size: 11px;\n color: var(--dsw-alias-label-dimmed);\n}\n.dsh-cloud-sync-restore__updated,\n.dsh-cloud-sync-restore__events {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-restore__target {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n}\n.dsh-cloud-sync-restore__path {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-restore__badge {\n flex: none;\n margin-left: 6px;\n border-radius: 999px;\n padding: 0 6px;\n font-size: 10px;\n line-height: 16px;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n.dsh-cloud-sync-restore__badge--muted {\n background: none;\n color: var(--dsw-alias-label-tertiary);\n}\n.dsh-cloud-sync-restore__unmatched {\n flex: none;\n font-size: 12px;\n color: var(--dsw-alias-state-warn-primary);\n}\n.dsh-cloud-sync-restore__pick {\n flex: none;\n appearance: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n padding: 2px 8px;\n background: none;\n font: inherit;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n.dsh-cloud-sync-restore__manual {\n /* \u663E\u5F0F\u6491\u6EE1\u5355\u5143\u683C + \u6536\u7F29\u4E0A\u9650\uFF1A\u4E0D\u4F9D\u8D56 flex \u5BF9 input \u7684\u5BBD\u5EA6\u5206\u914D\n \uFF08flex: 1 1 0% \u5728\u6E32\u67D3\u73AF\u5883\u91CC\u4F1A\u968F\u8F93\u5165\u5185\u5BB9\u6F02\u79FB\u2014\u2014\u53D8\u957F\u6216\u7F29\u7A84\uFF09 */\n flex: 1 1 auto;\n width: 100%;\n min-width: 0;\n max-width: 100%;\n box-sizing: border-box;\n height: 26px;\n padding: 0 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-3);\n font: inherit;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-restore__selected {\n flex: 1;\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n}\n/* ---- M4\uFF1A\u542F\u52A8\u68C0\u67E5\u901A\u77E5\uFF0801 \u00A75.5 \u53F3\u4E0B\u89D2\u8F7B\u63D0\u793A\uFF1Bshell.overlay \u5C42\u9ED8\u8BA4 click-through\uFF0C\u9700\u81EA\u884C\u6062\u590D\u6307\u9488\u4E8B\u4EF6\uFF09 ---- */\n.dsh-cloud-sync-notice {\n position: fixed;\n right: 20px;\n bottom: 20px;\n z-index: 60;\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-3);\n box-shadow: 0 8px 24px rgb(0 0 0 / 18%);\n pointer-events: auto;\n}\n.dsh-cloud-sync-notice__text {\n font-size: 13px;\n color: var(--dsw-alias-label-primary);\n}\n.dsh-cloud-sync-notice__action {\n appearance: none;\n border: none;\n background: none;\n padding: 0;\n font: inherit;\n font-size: 13px;\n color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n.dsh-cloud-sync-notice__dismiss {\n appearance: none;\n border: none;\n background: none;\n padding: 2px 4px;\n font: inherit;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n`\n\n/** \u6837\u5F0F\u6807\u7B7E\u7684\u5E42\u7B49\u6807\u8BB0\uFF08\u91CD\u590D\u6302\u8F7D/\u91CD\u653E\u65F6\u53EA\u6CE8\u5165\u4E00\u6B21\uFF09\u3002 */\nconst STYLE_TAG_ID = 'dsh-cloud-sync/card'\n\n/** \u628A\u5361\u7247\u6837\u5F0F\u6CE8\u5165 document.head\uFF1B\u975E\u6D4F\u89C8\u5668\u73AF\u5883\uFF08node \u5355\u6D4B\uFF09\u76F4\u63A5\u8DF3\u8FC7\u3002 */\nexport function installCardStyles(): void {\n if (typeof document === 'undefined') return\n if (document.querySelector(`style[data-plugin-css=\"${STYLE_TAG_ID}\"]`) !== null) return\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-cloud-sync'\n tag.dataset.pluginCss = STYLE_TAG_ID\n tag.textContent = CARD_CSS\n document.head.appendChild(tag)\n}\n"],
|
|
5
|
+
"mappings": "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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqfxB,IAAM,eAAe;AAGd,SAAS,oBAA0B;AACxC,MAAI,OAAO,aAAa,YAAa;AACrC,MAAI,SAAS,cAAc,0BAA0B,YAAY,IAAI,MAAM,KAAM;AACjF,QAAM,MAAM,SAAS,cAAc,OAAO;AAC1C,MAAI,QAAQ,SAAS;AACrB,MAAI,QAAQ,YAAY;AACxB,MAAI,cAAc;AAClB,WAAS,KAAK,YAAY,GAAG;AAC/B;;;APrfA,IAAM,YAAY;AAGX,IAAM,SAAS,CAAC,SAAS,UAAU,eAAe;AAMlD,SAAS,MAAM,KAA0B;AAC9C,MAAI,OAAO,MAAM,IAAI,OAAO,SAAS,WAAW,EAAE,IAAI,GAAG,CAAC,GAAG,0BAA0B;AAGvF,QAAM,kBAAmB,IAAI,IAAI,QAAQ,GAA6D;AACtG,QAAM,aAAa,IAAI;AAAA,IACrB,IAAI,cAAc,KAAK,EAAE,WAAW,cAAc,CAAC;AAAA,IACnD,IAAI,cAAc,KAAK,EAAE,WAAW,qBAAqB,CAAC;AAAA,IAC1D;AAAA,EACF;AACA,MAAI,OAAO,MAAM,MAAM;AAAE,eAAW,QAAQ;AAAA,EAAE,GAAG,6BAA6B;AAC9E,oBAAkB;AAClB,MAAI,MAAM,OAAO,wBAAwB,MAAM,IAAI,MAAM,SAAS;AAAA,IAChE,MAAM;AAAA,IACN,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,QAAQ,MAAM,WAAW,OAAO;AAAA,EAClC,GAAG,aAAa,CAAC;AAEjB,MAAI,MAAM,OAAO,iBAAiB,MAAM,IAAI,MAAM,SAAS;AAAA,IACzD,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ,MAAM,WAAW,OAAO;AAAA,EAClC,GAAG,gBAAgB,CAAC;AACtB;",
|
|
6
6
|
"names": ["import_dsh_client_ui_primitives", "import_jsx_runtime", "import_jsx_runtime"]
|
|
7
7
|
}
|
package/lib/dsh-types.d.ts
CHANGED
|
@@ -33,9 +33,28 @@ export interface SessionPersistenceLike {
|
|
|
33
33
|
/** @deepseek-ai/dsh-workspace 的 Workspace(恢复挂载用到 attachSession)。 */
|
|
34
34
|
export interface WorkspaceLike {
|
|
35
35
|
readonly path: string;
|
|
36
|
+
/**
|
|
37
|
+
* 过滤后的成员账号:只有 cwd realpath 等于本工作区 path 的会话才在此
|
|
38
|
+
* (dsh 侧同步过滤缺失/无效 cwd)。恢复目录据此判定「本地已存在」。
|
|
39
|
+
*/
|
|
40
|
+
readonly sessionIds: readonly string[];
|
|
36
41
|
/** 把会话记入本工作区成员(cwd 校验:realpath 后须等于工作区 path) */
|
|
37
42
|
attachSession(sessionId: string): Promise<void>;
|
|
38
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* `api-session/added` 事件的载荷(Host 恢复成功后 emit,供 web 侧边栏的
|
|
46
|
+
* `useSessions` 列表增量插入恢复的会话;字段与 dsh 的 SessionSummary 对齐,
|
|
47
|
+
* 仅覆盖恢复链路需要的子集)。
|
|
48
|
+
*/
|
|
49
|
+
export interface SessionSummaryLike {
|
|
50
|
+
readonly sessionId: string;
|
|
51
|
+
readonly updatedAt: number;
|
|
52
|
+
readonly running: boolean;
|
|
53
|
+
readonly blank: boolean;
|
|
54
|
+
readonly parentSessionId?: string;
|
|
55
|
+
readonly origin?: 'subagent';
|
|
56
|
+
readonly cwd?: string;
|
|
57
|
+
}
|
|
39
58
|
export interface WorkspaceRegistryLike {
|
|
40
59
|
list(): WorkspaceLike[];
|
|
41
60
|
/** 按本机 canonical path 查找已有 workspace;不存在返回 undefined */
|
|
@@ -70,5 +89,7 @@ declare module '@deepseek-ai/cordis' {
|
|
|
70
89
|
'session/flush'(session: {
|
|
71
90
|
id: string;
|
|
72
91
|
}): void;
|
|
92
|
+
/** 恢复会话后回发,让 web 会话列表无需刷新页面即可出现新会话。 */
|
|
93
|
+
'api-session/added'(summary: SessionSummaryLike): void;
|
|
73
94
|
}
|
|
74
95
|
}
|
package/lib/index.js
CHANGED
|
@@ -155,6 +155,16 @@ export async function apply(ctx, config) {
|
|
|
155
155
|
function workspacePaths() {
|
|
156
156
|
return ctx.get('workspaceRegistry')?.list().map((workspace) => workspace.path) ?? [];
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* 本机已正确挂载到工作区的会话 id 并集;无 workspaceRegistry 时返回
|
|
160
|
+
* undefined(fetchCatalog 退回旧 existsLocal 语义)。
|
|
161
|
+
*/
|
|
162
|
+
function attachedSessionIds() {
|
|
163
|
+
const registry = ctx.get('workspaceRegistry');
|
|
164
|
+
if (!registry)
|
|
165
|
+
return undefined;
|
|
166
|
+
return [...new Set(registry.list().flatMap((workspace) => [...workspace.sessionIds]))];
|
|
167
|
+
}
|
|
158
168
|
/** 构造恢复链路的 deps;配置不全返回 undefined(调用方回写 unconfigured 回显)。 */
|
|
159
169
|
async function catalogDeps() {
|
|
160
170
|
const cfg = current();
|
|
@@ -168,6 +178,7 @@ export async function apply(ctx, config) {
|
|
|
168
178
|
persistence: persistencePort(),
|
|
169
179
|
state: stateStore,
|
|
170
180
|
workspacePaths: workspacePaths(),
|
|
181
|
+
attachedSessionIds: attachedSessionIds(),
|
|
171
182
|
// 恢复落位后的 workspace 挂载(web 侧边栏可见性)。复刻 dsh 自己创建会话的
|
|
172
183
|
// 路径(session-controller commands.ts):resolveByPath 复用已有工作区,
|
|
173
184
|
// 无则 create(registry 自带 realpath + isDirectory 校验),再 attachSession。
|
|
@@ -181,6 +192,11 @@ export async function apply(ctx, config) {
|
|
|
181
192
|
await workspace.attachSession(sessionId);
|
|
182
193
|
})();
|
|
183
194
|
},
|
|
195
|
+
// 恢复成功后的会话列表增量:web 侧边栏 useSessions 靠 api-session/added
|
|
196
|
+
// 事件插入新会话,否则只写文件要到刷新页面(全量 list())才可见
|
|
197
|
+
onRestored: (summary) => {
|
|
198
|
+
ctx.emit('api-session/added', summary);
|
|
199
|
+
},
|
|
184
200
|
onWarn: (message) => logger.warn(message),
|
|
185
201
|
};
|
|
186
202
|
}
|
package/lib/sync/restore.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SessionHeaderLike, SessionPersistenceLike } from '../dsh-types.js';
|
|
1
|
+
import type { SessionHeaderLike, SessionPersistenceLike, SessionSummaryLike } from '../dsh-types.js';
|
|
2
2
|
import type { SyncClient } from './http-client.js';
|
|
3
3
|
import type { StateStore } from './state.js';
|
|
4
4
|
/**
|
|
@@ -103,6 +103,12 @@ export interface CatalogDeps {
|
|
|
103
103
|
state: StateStore;
|
|
104
104
|
/** 本机工作区 canonical path 列表(workspaceRegistry 缺失时传空数组) */
|
|
105
105
|
workspacePaths: string[];
|
|
106
|
+
/**
|
|
107
|
+
* 本机已正确挂载到工作区的会话 id 并集(workspaceRegistry.list() 的
|
|
108
|
+
* sessionIds)。缺省(undefined,即无 workspaceRegistry 环境)时退回旧
|
|
109
|
+
* 语义:仅按「会话 id 在本地持久化中存在」判 existsLocal。
|
|
110
|
+
*/
|
|
111
|
+
attachedSessionIds?: readonly string[];
|
|
106
112
|
/** 单个 meta 解密失败时回调(口令不匹配的兜底呈现走状态行,不打断目录) */
|
|
107
113
|
onWarn?: (message: string) => void;
|
|
108
114
|
}
|
|
@@ -121,6 +127,11 @@ export interface RestoreDeps {
|
|
|
121
127
|
* 实现负责 resolveByPath/create/attachSession;缺省跳过(无 workspaceRegistry 环境)。
|
|
122
128
|
*/
|
|
123
129
|
attachWorkspace?: (sessionId: string, cwd: string) => Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* 单条恢复成功后回发(Host 据此 emit api-session/added,让 web 会话列表
|
|
132
|
+
* 增量出现新会话,无需刷新页面)。仅在文件落位成功后调用,挂载失败也会调用。
|
|
133
|
+
*/
|
|
134
|
+
onRestored?: (summary: SessionSummaryLike) => void;
|
|
124
135
|
onWarn?: (message: string) => void;
|
|
125
136
|
}
|
|
126
137
|
/**
|
|
@@ -129,6 +140,12 @@ export interface RestoreDeps {
|
|
|
129
140
|
* 学习实际使用的映射对。单条失败不中断其余(01 §5.4 与 syncAll 同语义)。
|
|
130
141
|
*/
|
|
131
142
|
export declare function restoreSessions(deps: RestoreDeps, items: RestoreRequest['items']): Promise<RestoreResult>;
|
|
143
|
+
/**
|
|
144
|
+
* 规范化用户改选的落位路径(§4.3):`~` 展开到本机 home,相对路径按本机
|
|
145
|
+
* home 解析(跨机恢复最常见的心智模型——「home 下的 my-app」在两台
|
|
146
|
+
* 机器 home 不同的情况下仍落到各自 home 下);绝对路径原样返回。
|
|
147
|
+
*/
|
|
148
|
+
export declare function normalizeTargetCwd(raw: string): string;
|
|
132
149
|
/**
|
|
133
150
|
* 恢复后抑制立刻回传(§4.5):把刚落位的会话以当前 revision 记入 state,
|
|
134
151
|
* 引擎下一轮 scan 不会把它当变更上传;待本机续写产生新 revision 后才作为
|
package/lib/sync/restore.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* 但 web 侧边栏按 workspace 成员关系分组,不挂载的会话只进「未分组」桶。
|
|
13
13
|
*/
|
|
14
14
|
import fsp from 'node:fs/promises';
|
|
15
|
+
import os from 'node:os';
|
|
15
16
|
import path from 'node:path';
|
|
16
17
|
import { decryptLog, decryptMeta } from '../crypto/envelope.js';
|
|
17
18
|
import { logKey } from './engine.js';
|
|
@@ -150,6 +151,11 @@ export async function fetchCatalog(deps) {
|
|
|
150
151
|
const metaKeys = objects.filter((object) => object.key.endsWith(META_SUFFIX));
|
|
151
152
|
const localHeaders = await deps.persistence.list();
|
|
152
153
|
const localIds = new Set(localHeaders.map((header) => header.id));
|
|
154
|
+
// 已挂载工作区的会话 id:existsLocal 只有同时命中「本机有文件」且「已挂载到
|
|
155
|
+
// 工作区」才算本地已存在——残留在错误 cwd(旧版相对路径 bug)的会话文件仍可恢复。
|
|
156
|
+
const attachedIds = deps.attachedSessionIds === undefined
|
|
157
|
+
? undefined
|
|
158
|
+
: new Set(deps.attachedSessionIds);
|
|
153
159
|
// 兼容基准优先取本机会话 header 的 version(即本机运行时的 SESSION_FORMAT_VERSION,
|
|
154
160
|
// 上游 bump 后随本地新会话自适应);本地一个会话都没有才落到常量兜底
|
|
155
161
|
const localFormatVersion = localHeaders.reduce((max, header) => Math.max(max, header.version), LOCAL_FORMAT_VERSION);
|
|
@@ -168,7 +174,7 @@ export async function fetchCatalog(deps) {
|
|
|
168
174
|
deps.onWarn?.(`meta 解密失败,跳过 ${object.key}:${String(error)}`);
|
|
169
175
|
continue;
|
|
170
176
|
}
|
|
171
|
-
const existsLocal = localIds.has(sessionId);
|
|
177
|
+
const existsLocal = localIds.has(sessionId) && (attachedIds === undefined || attachedIds.has(sessionId));
|
|
172
178
|
const resolution = existsLocal || meta.cwd === ''
|
|
173
179
|
? { kind: 'none', cwd: null }
|
|
174
180
|
: resolveTargetCwd(meta.cwd, mappings, deps.workspacePaths);
|
|
@@ -220,18 +226,23 @@ async function restoreOne(deps, item) {
|
|
|
220
226
|
if (header.id !== item.sessionId)
|
|
221
227
|
throw new Error(`cloud object identity mismatch: key says ${item.sessionId}, header says ${header.id}`);
|
|
222
228
|
const sourceCwd = header.cwd ?? '';
|
|
229
|
+
// 用户改选的落位路径:支持 ~ 与相对路径(相对本机 home),跨机恢复时
|
|
230
|
+
// 用户心智是「home 下的 my-app」,两台机器 home 不同也不影响落位。
|
|
231
|
+
const targetCwd = item.targetCwd !== null && item.targetCwd !== ''
|
|
232
|
+
? normalizeTargetCwd(item.targetCwd)
|
|
233
|
+
: item.targetCwd;
|
|
223
234
|
// 路径归位:目标与来源不同才改写首帧 cwd,并学习实际使用的映射对(§4.3 步骤 4)。
|
|
224
235
|
// 显式指定的目标目录必须先验证存在:写盘后失败无法回滚(同 id 重试会被拒绝覆盖)
|
|
225
236
|
let locatedHeader = header;
|
|
226
|
-
if (
|
|
237
|
+
if (targetCwd !== null && targetCwd !== sourceCwd) {
|
|
227
238
|
if (sourceCwd === '')
|
|
228
239
|
throw new Error('来源会话无 cwd,无法建立映射');
|
|
229
|
-
if (!(await isExistingDirectory(
|
|
230
|
-
throw new Error(`目标目录不存在或不是目录:${
|
|
240
|
+
if (!(await isExistingDirectory(targetCwd))) {
|
|
241
|
+
throw new Error(`目标目录不存在或不是目录:${targetCwd}`);
|
|
231
242
|
}
|
|
232
|
-
plaintext = await rewriteCwd(plaintext,
|
|
233
|
-
locatedHeader = { ...header, cwd:
|
|
234
|
-
await learnMapping(deps.state, sourceCwd,
|
|
243
|
+
plaintext = await rewriteCwd(plaintext, targetCwd);
|
|
244
|
+
locatedHeader = { ...header, cwd: targetCwd };
|
|
245
|
+
await learnMapping(deps.state, sourceCwd, targetCwd);
|
|
235
246
|
}
|
|
236
247
|
const location = deps.persistence.locate(locatedHeader);
|
|
237
248
|
if (!location)
|
|
@@ -249,11 +260,14 @@ async function restoreOne(deps, item) {
|
|
|
249
260
|
const tmp = `${location.path}.tmp-${process.pid}`;
|
|
250
261
|
await fsp.writeFile(tmp, plaintext);
|
|
251
262
|
await fsp.rename(tmp, location.path);
|
|
263
|
+
const effectiveCwd = locatedHeader.cwd ?? '';
|
|
264
|
+
// 清理同 id 的陈旧本地副本(旧版相对路径 bug 落错 cwd 的残留):新文件已就位,
|
|
265
|
+
// 只删 cwd 不同且落位路径不同的副本,避免恢复后同 id 出现两份。
|
|
266
|
+
await removeStaleLocalCopies(deps, item.sessionId, effectiveCwd, location.path);
|
|
252
267
|
// workspace 挂载(web 侧边栏按成员关系分组)。目录存在的落位 cwd 才尝试挂载:
|
|
253
268
|
// 原样落位到本机不存在的来源路径时按 §4.3 语义保持「未分组」,不算失败。
|
|
254
269
|
// 挂载失败仅告警不回滚——文件已就位,会话至少出现在「未分组」桶,重试 attach
|
|
255
270
|
// 也没有意义(同 cwd 再挂靠 registry 幂等,但失败原因通常是目录状态异常)
|
|
256
|
-
const effectiveCwd = locatedHeader.cwd ?? '';
|
|
257
271
|
if (deps.attachWorkspace && effectiveCwd !== '' && await isExistingDirectory(effectiveCwd)) {
|
|
258
272
|
try {
|
|
259
273
|
await deps.attachWorkspace(item.sessionId, effectiveCwd);
|
|
@@ -262,6 +276,17 @@ async function restoreOne(deps, item) {
|
|
|
262
276
|
deps.onWarn?.(`workspace 挂载失败 ${item.sessionId}(会话已落位,将出现在未分组):${String(error)}`);
|
|
263
277
|
}
|
|
264
278
|
}
|
|
279
|
+
// 通知 Host 回发 api-session/added:web 侧边栏的会话列表靠该事件增量更新,
|
|
280
|
+
// 只写文件不会让新会话在刷新前出现(workspace 成员关系与会话行是两个通道)。
|
|
281
|
+
deps.onRestored?.({
|
|
282
|
+
sessionId: item.sessionId,
|
|
283
|
+
updatedAt: Date.now(),
|
|
284
|
+
running: false,
|
|
285
|
+
blank: false,
|
|
286
|
+
...(effectiveCwd === '' ? {} : { cwd: effectiveCwd }),
|
|
287
|
+
...(header.parentSession === undefined ? {} : { parentSessionId: header.parentSession }),
|
|
288
|
+
...(header.origin === undefined ? {} : { origin: header.origin }),
|
|
289
|
+
});
|
|
265
290
|
return location.path;
|
|
266
291
|
}
|
|
267
292
|
async function isExistingDirectory(target) {
|
|
@@ -272,6 +297,41 @@ async function isExistingDirectory(target) {
|
|
|
272
297
|
return false;
|
|
273
298
|
}
|
|
274
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* 删除同 id、但落在其它 cwd 的陈旧本地副本(旧版相对路径 bug 落错位置后
|
|
302
|
+
* 再恢复会残留)。仅在新的正确落位文件写盘后调用:只删 cwd 与 keepCwd 不同、
|
|
303
|
+
* 且落位路径与 keepPath 不同的副本,绝不触碰刚写的文件。删除失败静默——残留
|
|
304
|
+
* 副本顶多出现在「未分组」,不影响本次恢复结果。
|
|
305
|
+
*/
|
|
306
|
+
async function removeStaleLocalCopies(deps, sessionId, keepCwd, keepPath) {
|
|
307
|
+
const locals = await deps.persistence.list();
|
|
308
|
+
for (const header of locals) {
|
|
309
|
+
if (header.id !== sessionId)
|
|
310
|
+
continue;
|
|
311
|
+
if ((header.cwd ?? '') === keepCwd)
|
|
312
|
+
continue;
|
|
313
|
+
const stale = deps.persistence.locate(header);
|
|
314
|
+
if (!stale || stale.path === keepPath)
|
|
315
|
+
continue;
|
|
316
|
+
await fsp.rm(stale.path, { force: true }).catch(() => { });
|
|
317
|
+
await fsp.rmdir(path.dirname(stale.path)).catch(() => { });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 规范化用户改选的落位路径(§4.3):`~` 展开到本机 home,相对路径按本机
|
|
322
|
+
* home 解析(跨机恢复最常见的心智模型——「home 下的 my-app」在两台
|
|
323
|
+
* 机器 home 不同的情况下仍落到各自 home 下);绝对路径原样返回。
|
|
324
|
+
*/
|
|
325
|
+
export function normalizeTargetCwd(raw) {
|
|
326
|
+
const trimmed = raw.trim();
|
|
327
|
+
if (trimmed === '' || trimmed === '~')
|
|
328
|
+
return trimmed === '' ? '' : os.homedir();
|
|
329
|
+
if (trimmed.startsWith('~/'))
|
|
330
|
+
return path.join(os.homedir(), trimmed.slice(2));
|
|
331
|
+
if (path.isAbsolute(trimmed))
|
|
332
|
+
return trimmed;
|
|
333
|
+
return path.resolve(os.homedir(), trimmed);
|
|
334
|
+
}
|
|
275
335
|
/**
|
|
276
336
|
* 恢复后抑制立刻回传(§4.5):把刚落位的会话以当前 revision 记入 state,
|
|
277
337
|
* 引擎下一轮 scan 不会把它当变更上传;待本机续写产生新 revision 后才作为
|