dshmarket 1.6.0 → 1.7.0

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/client/client.js CHANGED
@@ -90,6 +90,31 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
90
90
  marketUpdate: "市场有新版本,升级",
91
91
  updateAll: "全部更新",
92
92
  tabThemes: "主题",
93
+ tabBackup: "备份与恢复",
94
+ backupLocal: "本地文件",
95
+ backupDownload: "导出备份",
96
+ backupImport: "导入并预览",
97
+ backupHint: "仅包含插件清单和 profile 配置,不包含 node_modules;恢复会按清单重新安装插件。",
98
+ webdav: "WebDAV",
99
+ webdavPreset: "服务商预设",
100
+ webdavUrl: "备份文件 URL",
101
+ webdavUser: "用户名(可选)",
102
+ webdavPassword: "密码(可选)",
103
+ webdavUpload: "上传备份",
104
+ webdavRestore: "从 WebDAV 恢复",
105
+ autoBackup: "每天自动备份(打开市场时)",
106
+ webdavNote: "WebDAV 地址与用户名仅保存在当前浏览器;密码只存于服务端,每次会话需重新输入。",
107
+ localOnly: "WebDAV 地址和凭证仅保存在当前浏览器。",
108
+ credsWarning: "注意:备份包含配置与可能含密钥的文件(config.toml、.env 等)。下载件会原样导出,上传 WebDAV 前请确认目标可信。",
109
+ backupWorking: "处理中…",
110
+ backupDone: "备份已上传",
111
+ restoreDone: "恢复完成,请重启 DeepSeek Harness",
112
+ restorePartial: "恢复已继续完成,但以下插件安装失败:",
113
+ restoreConfirm: "恢复将覆盖当前 profile 配置并重新安装插件,确定继续吗?",
114
+ restorePreviewDone: "备份已导入,请在“已安装”中确认后开始恢复",
115
+ restoreMissing: "备份中有 {0} 个插件尚未安装",
116
+ restoreStart: "开始恢复",
117
+ notInstalled: "未安装",
93
118
  themeApply: "使用",
94
119
  themeActive: "使用中",
95
120
  themeEmpty: "目录里暂时还没有主题,敬请期待",
@@ -192,6 +217,31 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
192
217
  marketUpdate: "Market update available — upgrade",
193
218
  updateAll: "Update all",
194
219
  tabThemes: "Themes",
220
+ tabBackup: "Backup & Restore",
221
+ backupLocal: "Local file",
222
+ backupDownload: "Export backup",
223
+ backupImport: "Import and preview",
224
+ backupHint: "Includes the plugin list and profile configuration, never node_modules. Restore reinstalls plugins from the list.",
225
+ webdav: "WebDAV",
226
+ webdavPreset: "Provider preset",
227
+ webdavUrl: "Backup file URL",
228
+ webdavUser: "Username (optional)",
229
+ webdavPassword: "Password (optional)",
230
+ webdavUpload: "Upload backup",
231
+ webdavRestore: "Restore from WebDAV",
232
+ autoBackup: "Back up daily (when the market opens)",
233
+ webdavNote: "The WebDAV URL and username stay in this browser only; the password is kept server-side and must be re-entered each session.",
234
+ localOnly: "The WebDAV URL and credentials stay in this browser only.",
235
+ credsWarning: "Heads up: backups include profile configuration and files that may hold secrets (config.toml, .env, …). Local exports are unmodified, so only upload to a WebDAV target you trust.",
236
+ backupWorking: "Working…",
237
+ backupDone: "Backup uploaded",
238
+ restoreDone: "Restore complete — restart DeepSeek Harness",
239
+ restorePartial: "Restore continued, but these plugins failed to install:",
240
+ restoreConfirm: "Restore will overwrite this profile configuration and reinstall plugins. Continue?",
241
+ restorePreviewDone: "Backup imported. Review Installed, then start restore.",
242
+ restoreMissing: "{0} plugins from this backup are not installed",
243
+ restoreStart: "Start restore",
244
+ notInstalled: "Not installed",
195
245
  themeApply: "Use",
196
246
  themeActive: "Active",
197
247
  themeEmpty: "No more theme plugins in the catalog yet — stay tuned",
@@ -432,7 +482,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
432
482
  }
433
483
  //#endregion
434
484
  //#region \0dsh-css:/home/runner/work/dsh-market/dsh-market/src/client/Market.module.css.mjs
435
- const css = ".eGUBIq_root{min-width:0;height:100%;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;display:flex;position:relative}.eGUBIq_head{flex-direction:column;gap:12px;padding:4px 4px 12px;display:flex}.eGUBIq_title{margin:0;font-size:16px;font-weight:500;line-height:24px}.eGUBIq_sub{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:0;font-size:14px;line-height:22px}.eGUBIq_searchInline{flex-shrink:0;width:200px;margin-bottom:6px}.eGUBIq_tabs{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:flex-end;gap:2px;display:flex}.eGUBIq_tab{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;padding:7px 12px;font-size:13px}.eGUBIq_tab.eGUBIq_on{color:var(--dsw-alias-brand-primary,#4f6ef7);border-bottom-color:var(--dsw-alias-brand-primary,#4f6ef7);font-weight:600}.eGUBIq_restart{background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;align-items:center;gap:8px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_body{flex:1;padding:12px 4px 24px;overflow-x:hidden;overflow-y:auto}.eGUBIq_cats{z-index:5;background:var(--dsw-alias-bg-layer-2,#f7f8fa);margin:-12px -4px 2px;padding:12px 4px 4px;position:sticky;top:-13px}.eGUBIq_catsRow{align-items:flex-start;gap:8px;display:flex;position:relative}.eGUBIq_filterWrap{flex-shrink:0;position:relative}.eGUBIq_filterBtn{border:1px solid var(--dsw-alias-border-l1,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font:inherit;cursor:pointer;white-space:nowrap;border-radius:8px;align-items:center;gap:5px;padding:5px 11px;font-size:12px;line-height:18px;display:flex}.eGUBIq_filterBtn:hover,.eGUBIq_filterBtnOn{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_filterPanel{z-index:30;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:10px;flex-direction:column;gap:10px;width:250px;padding:8px;display:flex;position:absolute;top:calc(100% + 6px);right:0;box-shadow:0 8px 28px #00000024}.eGUBIq_filterGroup{flex-direction:column;gap:2px;display:flex}.eGUBIq_filterTitle{color:var(--dsw-alias-label-secondary,#6b7280);padding:4px 8px 2px;font-size:11px;font-weight:600}.eGUBIq_filterOption{cursor:pointer;color:var(--dsw-alias-label-primary,#1f2328);border-radius:6px;align-items:center;gap:8px;padding:5px 8px;font-size:12px;line-height:18px;display:flex}.eGUBIq_filterOption:hover{background:var(--dsw-alias-bg-layer-2,#f3f4f6)}.eGUBIq_filterOption input{margin:0}.eGUBIq_star{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_top{z-index:20;border:1px solid var(--dsw-alias-border-l1,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);width:38px;height:38px;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;border-radius:99px;font-size:16px;position:absolute;bottom:18px;right:18px;box-shadow:0 4px 14px #0000001f}.eGUBIq_top:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_tag{border:1px solid var(--dsw-alias-border-l3,#d9dde3);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:4px;flex-shrink:0;padding:1px 6px;font-size:11px;line-height:16px}.eGUBIq_okState{color:var(--dsw-alias-state-success-primary,#16a34a);white-space:nowrap;font-size:12px;font-weight:600}.eGUBIq_dangerBtn.eGUBIq_dangerBtn{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_dangerArmed.eGUBIq_dangerArmed{background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}.eGUBIq_warnBtn.eGUBIq_warnBtn{background:var(--dsw-alias-state-warn-primary,#ea580c);color:#fff}.eGUBIq_catsWrap{flex-wrap:wrap;flex:1;align-items:center;gap:6px;min-width:0;display:flex}.eGUBIq_catsCollapsed{max-height:62px;overflow:hidden}.eGUBIq_catsToggle.eGUBIq_catsToggle{height:26px;min-height:26px;color:var(--dsw-alias-label-secondary,#6b7280);padding:0 6px}.eGUBIq_cmdDetails{margin:0}.eGUBIq_cmdSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary,#6b7280);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.eGUBIq_cmdSummary::-webkit-details-marker{display:none}.eGUBIq_cmdSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.eGUBIq_cmdDetails[open]>.eGUBIq_cmdSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.eGUBIq_cmdSummary:hover{color:var(--dsw-alias-label-primary,#1f2328)}.eGUBIq_cmd{background:var(--dsw-alias-bg-layer-2,#f3f4f6);word-break:break-all;border-radius:6px;margin:8px 0 0;padding:8px 10px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:18px}.eGUBIq_warnLine{color:var(--dsw-alias-state-warn-primary,#b45309);margin:0;font-size:12px;font-weight:600;line-height:18px}.eGUBIq_modalNote{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:12px 0 0;font-size:12px;line-height:18px}.eGUBIq_grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.eGUBIq_sect{color:var(--dsw-alias-label-secondary,#6b7280);margin:14px 2px 8px;font-size:12px;font-weight:600}.eGUBIq_sect:first-child{margin-top:2px}.eGUBIq_swatches{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;gap:0;height:34px;display:flex;overflow:hidden}.eGUBIq_themesGrid{margin-bottom:12px}.eGUBIq_swatches i{flex:1}.eGUBIq_card{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.eGUBIq_row1{align-items:center;gap:10px;min-width:0;display:flex}.eGUBIq_av{color:#fff;object-fit:cover;background:var(--dsw-alias-bg-layer-2,#f3f4f6);border-radius:8px;flex-shrink:0;place-items:center;width:32px;height:32px;font-size:14px;font-weight:700;display:grid}.eGUBIq_nm{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:22px;overflow:hidden}.eGUBIq_owner{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_desc{color:var(--dsw-alias-label-tertiary,#8b93a1);min-height:36px;margin:0;font-size:12px;line-height:18px}.eGUBIq_foot{align-items:center;gap:8px;margin-top:auto;display:flex}.eGUBIq_grow{flex:1}.eGUBIq_titleRow{align-items:center;gap:10px;display:flex}.eGUBIq_descTight{min-height:0}.eGUBIq_src{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px;text-decoration:none}.eGUBIq_src:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_dot{vertical-align:2px;margin-left:5px}.eGUBIq_act{flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px;font-size:11px;display:flex}.eGUBIq_actLive{color:var(--dsw-alias-state-success-primary,#16a34a);align-items:center;gap:4px;font-weight:600;display:inline-flex}.eGUBIq_actWarn{color:var(--dsw-alias-state-warn-primary,#b45309);align-items:center;gap:4px;font-weight:600;display:inline-flex}.eGUBIq_actBroken{color:var(--dsw-alias-state-error-primary,#dc2626);align-items:center;gap:4px;font-weight:600;display:inline-flex}.eGUBIq_actWhy{margin:0}.eGUBIq_actWhy summary{cursor:pointer;color:var(--dsw-alias-label-secondary,#6b7280);font-size:11px}.eGUBIq_loading{color:var(--dsw-alias-label-secondary,#9ca3af);flex-direction:column;align-items:center;gap:12px;padding:48px;font-size:13px;display:flex}.eGUBIq_spin{border:3px solid var(--dsw-alias-border-l1,#e5e7eb);border-top-color:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;width:22px;height:22px;animation:.8s linear infinite eGUBIq_sp}@keyframes eGUBIq_sp{to{transform:rotate(360deg)}}.eGUBIq_progress{background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:8px;flex-wrap:wrap;align-items:center;gap:9px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_bar{background:var(--dsw-alias-border-l1,#e5e7eb);border-radius:99px;width:100%;height:4px;overflow:hidden}.eGUBIq_barFill{background:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;height:100%;transition:width .6s}.eGUBIq_barWave{width:30%;animation:1.2s ease-in-out infinite eGUBIq_dshmSlide}@keyframes eGUBIq_dshmSlide{0%{margin-left:-30%}to{margin-left:100%}}.eGUBIq_irow .eGUBIq_progress{margin-top:8px}.eGUBIq_progress .eGUBIq_spin{border-width:2px;flex-shrink:0;width:14px;height:14px}.eGUBIq_progress code{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,monospace;font-size:11px;overflow:hidden}.eGUBIq_empty{color:var(--dsw-alias-label-secondary,#9ca3af);text-align:center;padding:32px;font-size:13px}.eGUBIq_err{color:var(--dsw-alias-state-error-primary,#dc2626);white-space:pre-wrap;word-break:break-all;margin:8px 0;font-size:12px}.eGUBIq_irow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;align-items:center;gap:10px;margin-bottom:8px;padding:12px 14px;display:flex}.eGUBIq_irow>.eGUBIq_src,.eGUBIq_irow>.eGUBIq_owner,.eGUBIq_dangerBtn.eGUBIq_dangerBtn,.eGUBIq_warnBtn.eGUBIq_warnBtn,.eGUBIq_dangerArmed.eGUBIq_dangerArmed{white-space:nowrap;flex-shrink:0}.eGUBIq_spec{color:var(--dsw-alias-label-secondary,#9ca3af);font-family:ui-monospace,Menlo,monospace;font-size:11px}.eGUBIq_staleAction{margin-top:8px}.eGUBIq_pct{color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0;font-size:11px;font-weight:600}.eGUBIq_cancelBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;white-space:nowrap;border-radius:6px;flex-shrink:0;padding:2px 10px;font-size:11px;line-height:16px}.eGUBIq_cancelBtn:hover{color:var(--dsw-alias-state-error-primary,#dc2626);border-color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_pager{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin:16px 0 4px;display:flex}.eGUBIq_pagerPages{flex-wrap:wrap;flex:1;justify-content:center;align-items:center;gap:6px;min-width:0;display:flex}.eGUBIq_pagerSize{flex-shrink:0;align-items:center;gap:4px;display:flex}.eGUBIq_sizeLabel{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px}.eGUBIq_sizeBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;border-radius:6px;padding:3px 8px;font-size:12px;line-height:18px}.eGUBIq_sizeBtn:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_sizeOn{background:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;font-weight:600}.eGUBIq_pageBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;border-radius:6px;min-width:28px;padding:4px 10px;font-size:12px;line-height:18px}.eGUBIq_pageBtn:hover:not(:disabled){color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_pageBtn:disabled{opacity:.45;cursor:default}.eGUBIq_pageOn{background:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;font-weight:600}.eGUBIq_pageEllipsis{color:var(--dsw-alias-label-secondary,#9ca3af);padding:0 2px;font-size:12px}.eGUBIq_pageInfo{color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap;font-size:12px}";
485
+ const css = ".eGUBIq_root{min-width:0;height:100%;color:var(--dsw-alias-label-primary,#1f2328);flex-direction:column;display:flex;position:relative}.eGUBIq_head{flex-direction:column;gap:12px;padding:4px 4px 12px;display:flex}.eGUBIq_title{margin:0;font-size:16px;font-weight:500;line-height:24px}.eGUBIq_sub{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:0;font-size:14px;line-height:22px}.eGUBIq_searchInline{flex-shrink:0;width:200px;margin-bottom:6px}.eGUBIq_tabs{border-bottom:1px solid var(--dsw-alias-border-l2,#e5e7eb);align-items:flex-end;gap:2px;display:flex}.eGUBIq_tab{font:inherit;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;white-space:nowrap;background:0 0;border:none;border-bottom:2px solid #0000;padding:7px 12px;font-size:13px}.eGUBIq_tab.eGUBIq_on{color:var(--dsw-alias-brand-primary,#4f6ef7);border-bottom-color:var(--dsw-alias-brand-primary,#4f6ef7);font-weight:600}.eGUBIq_restart{background:var(--dsw-alias-bg-layer-2,#fdf3e3);border:1px solid var(--dsw-alias-border-l2,#f3e3c3);border-radius:8px;align-items:center;gap:8px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_body{flex:1;padding:12px 4px 24px;overflow-x:hidden;overflow-y:auto}.eGUBIq_cats{z-index:5;background:var(--dsw-alias-bg-layer-2,#f7f8fa);margin:-12px -4px 2px;padding:12px 4px 4px;position:sticky;top:-13px}.eGUBIq_catsRow{align-items:flex-start;gap:8px;display:flex;position:relative}.eGUBIq_filterWrap{flex-shrink:0;position:relative}.eGUBIq_filterBtn{border:1px solid var(--dsw-alias-border-l1,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-primary,#1f2328);font:inherit;cursor:pointer;white-space:nowrap;border-radius:8px;align-items:center;gap:5px;padding:5px 11px;font-size:12px;line-height:18px;display:flex}.eGUBIq_filterBtn:hover,.eGUBIq_filterBtnOn{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_filterPanel{z-index:30;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:10px;flex-direction:column;gap:10px;width:250px;padding:8px;display:flex;position:absolute;top:calc(100% + 6px);right:0;box-shadow:0 8px 28px #00000024}.eGUBIq_filterGroup{flex-direction:column;gap:2px;display:flex}.eGUBIq_filterTitle{color:var(--dsw-alias-label-secondary,#6b7280);padding:4px 8px 2px;font-size:11px;font-weight:600}.eGUBIq_filterOption{cursor:pointer;color:var(--dsw-alias-label-primary,#1f2328);border-radius:6px;align-items:center;gap:8px;padding:5px 8px;font-size:12px;line-height:18px;display:flex}.eGUBIq_filterOption:hover{background:var(--dsw-alias-bg-layer-2,#f3f4f6)}.eGUBIq_filterOption input{margin:0}.eGUBIq_star{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_top{z-index:20;border:1px solid var(--dsw-alias-border-l1,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);width:38px;height:38px;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;border-radius:99px;font-size:16px;position:absolute;bottom:18px;right:18px;box-shadow:0 4px 14px #0000001f}.eGUBIq_top:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_tag{border:1px solid var(--dsw-alias-border-l3,#d9dde3);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:4px;flex-shrink:0;padding:1px 6px;font-size:11px;line-height:16px}.eGUBIq_okState{color:var(--dsw-alias-state-success-primary,#16a34a);white-space:nowrap;font-size:12px;font-weight:600}.eGUBIq_dangerBtn.eGUBIq_dangerBtn{border-color:var(--dsw-alias-state-error-primary,#dc2626);color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_dangerArmed.eGUBIq_dangerArmed{background:var(--dsw-alias-state-error-primary,#dc2626);color:#fff}.eGUBIq_warnBtn.eGUBIq_warnBtn{background:var(--dsw-alias-state-warn-primary,#ea580c);color:#fff}.eGUBIq_catsWrap{flex-wrap:wrap;flex:1;align-items:center;gap:6px;min-width:0;display:flex}.eGUBIq_catsCollapsed{max-height:62px;overflow:hidden}.eGUBIq_catsToggle.eGUBIq_catsToggle{height:26px;min-height:26px;color:var(--dsw-alias-label-secondary,#6b7280);padding:0 6px}.eGUBIq_cmdDetails{margin:0}.eGUBIq_cmdSummary{cursor:pointer;width:fit-content;color:var(--dsw-alias-label-secondary,#6b7280);border-radius:6px;align-items:center;gap:6px;margin-left:-4px;padding:2px 4px;font-size:12px;font-weight:500;line-height:18px;list-style:none;display:flex}.eGUBIq_cmdSummary::-webkit-details-marker{display:none}.eGUBIq_cmdSummary:before{content:\"\";border-bottom:1.5px solid;border-right:1.5px solid;width:5px;height:5px;transition:transform .12s;transform:rotate(-45deg)translate(-1px,-1px)}.eGUBIq_cmdDetails[open]>.eGUBIq_cmdSummary:before{transform:rotate(45deg)translate(-1px,-1px)}.eGUBIq_cmdSummary:hover{color:var(--dsw-alias-label-primary,#1f2328)}.eGUBIq_cmd{background:var(--dsw-alias-bg-layer-2,#f3f4f6);word-break:break-all;border-radius:6px;margin:8px 0 0;padding:8px 10px;font-family:ui-monospace,Menlo,monospace;font-size:11px;line-height:18px}.eGUBIq_warnLine{color:var(--dsw-alias-state-warn-primary,#b45309);margin:0;font-size:12px;font-weight:600;line-height:18px}.eGUBIq_modalNote{color:var(--dsw-alias-label-tertiary,#8b93a1);margin:12px 0 0;font-size:12px;line-height:18px}.eGUBIq_grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px;display:grid}.eGUBIq_sect{color:var(--dsw-alias-label-secondary,#6b7280);margin:14px 2px 8px;font-size:12px;font-weight:600}.eGUBIq_sect:first-child{margin-top:2px}.eGUBIq_swatches{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:8px;gap:0;height:34px;display:flex;overflow:hidden}.eGUBIq_themesGrid{margin-bottom:12px}.eGUBIq_swatches i{flex:1}.eGUBIq_card{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.eGUBIq_row1{align-items:center;gap:10px;min-width:0;display:flex}.eGUBIq_av{color:#fff;object-fit:cover;background:var(--dsw-alias-bg-layer-2,#f3f4f6);border-radius:8px;flex-shrink:0;place-items:center;width:32px;height:32px;font-size:14px;font-weight:700;display:grid}.eGUBIq_nm{text-overflow:ellipsis;white-space:nowrap;font-size:14px;font-weight:500;line-height:22px;overflow:hidden}.eGUBIq_owner{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px}.eGUBIq_desc{color:var(--dsw-alias-label-tertiary,#8b93a1);min-height:36px;margin:0;font-size:12px;line-height:18px}.eGUBIq_foot{align-items:center;gap:8px;margin-top:auto;display:flex}.eGUBIq_grow{flex:1}.eGUBIq_titleRow{align-items:center;gap:10px;display:flex}.eGUBIq_descTight{min-height:0}.eGUBIq_src{color:var(--dsw-alias-label-secondary,#9ca3af);font-size:11px;text-decoration:none}.eGUBIq_src:hover{color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_dot{vertical-align:2px;margin-left:5px}.eGUBIq_act{flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px;font-size:11px;display:flex}.eGUBIq_actLive{color:var(--dsw-alias-state-success-primary,#16a34a);align-items:center;gap:4px;font-weight:600;display:inline-flex}.eGUBIq_actWarn{color:var(--dsw-alias-state-warn-primary,#b45309);align-items:center;gap:4px;font-weight:600;display:inline-flex}.eGUBIq_actBroken{color:var(--dsw-alias-state-error-primary,#dc2626);align-items:center;gap:4px;font-weight:600;display:inline-flex}.eGUBIq_actWhy{margin:0}.eGUBIq_actWhy summary{cursor:pointer;color:var(--dsw-alias-label-secondary,#6b7280);font-size:11px}.eGUBIq_loading{color:var(--dsw-alias-label-secondary,#9ca3af);flex-direction:column;align-items:center;gap:12px;padding:48px;font-size:13px;display:flex}.eGUBIq_spin{border:3px solid var(--dsw-alias-border-l1,#e5e7eb);border-top-color:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;width:22px;height:22px;animation:.8s linear infinite eGUBIq_sp}@keyframes eGUBIq_sp{to{transform:rotate(360deg)}}.eGUBIq_progress{background:var(--dsw-alias-bg-layer-2,#f3f4f6);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);color:var(--dsw-alias-label-secondary,#6b7280);border-radius:8px;flex-wrap:wrap;align-items:center;gap:9px;margin:0;padding:8px 12px;font-size:12px;display:flex}.eGUBIq_bar{background:var(--dsw-alias-border-l1,#e5e7eb);border-radius:99px;width:100%;height:4px;overflow:hidden}.eGUBIq_barFill{background:var(--dsw-alias-brand-primary,#4f6ef7);border-radius:99px;height:100%;transition:width .6s}.eGUBIq_barWave{width:30%;animation:1.2s ease-in-out infinite eGUBIq_dshmSlide}@keyframes eGUBIq_dshmSlide{0%{margin-left:-30%}to{margin-left:100%}}.eGUBIq_irow .eGUBIq_progress{margin-top:8px}.eGUBIq_progress .eGUBIq_spin{border-width:2px;flex-shrink:0;width:14px;height:14px}.eGUBIq_progress code{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,monospace;font-size:11px;overflow:hidden}.eGUBIq_empty{color:var(--dsw-alias-label-secondary,#9ca3af);text-align:center;padding:32px;font-size:13px}.eGUBIq_err{color:var(--dsw-alias-state-error-primary,#dc2626);white-space:pre-wrap;word-break:break-all;margin:8px 0;font-size:12px}.eGUBIq_irow{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;align-items:center;gap:10px;margin-bottom:8px;padding:12px 14px;display:flex}.eGUBIq_irowMissing{filter:grayscale();opacity:.5}.eGUBIq_irow>.eGUBIq_src,.eGUBIq_irow>.eGUBIq_owner,.eGUBIq_dangerBtn.eGUBIq_dangerBtn,.eGUBIq_warnBtn.eGUBIq_warnBtn,.eGUBIq_dangerArmed.eGUBIq_dangerArmed{white-space:nowrap;flex-shrink:0}.eGUBIq_spec{color:var(--dsw-alias-label-secondary,#9ca3af);font-family:ui-monospace,Menlo,monospace;font-size:11px}.eGUBIq_staleAction{margin-top:8px}.eGUBIq_pct{color:var(--dsw-alias-label-secondary,#6b7280);flex-shrink:0;font-size:11px;font-weight:600}.eGUBIq_cancelBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;white-space:nowrap;border-radius:6px;flex-shrink:0;padding:2px 10px;font-size:11px;line-height:16px}.eGUBIq_cancelBtn:hover{color:var(--dsw-alias-state-error-primary,#dc2626);border-color:var(--dsw-alias-state-error-primary,#dc2626)}.eGUBIq_pager{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;margin:16px 0 4px;display:flex}.eGUBIq_pagerPages{flex-wrap:wrap;flex:1;justify-content:center;align-items:center;gap:6px;min-width:0;display:flex}.eGUBIq_pagerSize{flex-shrink:0;align-items:center;gap:4px;display:flex}.eGUBIq_sizeLabel{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px}.eGUBIq_sizeBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;border-radius:6px;padding:3px 8px;font-size:12px;line-height:18px}.eGUBIq_sizeBtn:hover{color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_sizeOn{background:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;font-weight:600}.eGUBIq_pageBtn{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);color:var(--dsw-alias-label-secondary,#6b7280);font:inherit;cursor:pointer;border-radius:6px;min-width:28px;padding:4px 10px;font-size:12px;line-height:18px}.eGUBIq_pageBtn:hover:not(:disabled){color:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7)}.eGUBIq_pageBtn:disabled{opacity:.45;cursor:default}.eGUBIq_pageOn{background:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff;font-weight:600}.eGUBIq_pageEllipsis{color:var(--dsw-alias-label-secondary,#9ca3af);padding:0 2px;font-size:12px}.eGUBIq_pageInfo{color:var(--dsw-alias-label-secondary,#6b7280);white-space:nowrap;font-size:12px}.eGUBIq_backupGrid{grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px;display:grid}.eGUBIq_backupCard{background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;flex-direction:column;gap:10px;padding:16px;display:flex}.eGUBIq_backupCard h3{margin:0;font-size:14px}.eGUBIq_backupCard p{color:var(--dsw-alias-label-secondary,#6b7280);margin:0;font-size:12px;line-height:18px}.eGUBIq_backupActions{flex-wrap:wrap;gap:8px;display:flex}.eGUBIq_backupButton{border:1px solid var(--dsw-alias-border-l2,#e5e7eb);color:var(--dsw-alias-label-primary,#1f2328);cursor:pointer;border-radius:6px;align-items:center;padding:4px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex;position:relative}.eGUBIq_backupButton[aria-disabled=true]{opacity:.5;pointer-events:none}.eGUBIq_backupButton input{opacity:0;width:1px;height:1px;position:absolute}.eGUBIq_backupPrimary{background:var(--dsw-alias-brand-primary,#4f6ef7);border-color:var(--dsw-alias-brand-primary,#4f6ef7);color:#fff}.eGUBIq_backupInput{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);background:var(--dsw-alias-bg-layer-1,#fff);width:100%;color:var(--dsw-alias-label-primary,#1f2328);font:inherit;border-radius:8px;padding:7px 9px;font-size:12px}.eGUBIq_backupCheck{cursor:pointer;align-items:center;gap:6px;font-size:12px;display:flex}.eGUBIq_backupWarn{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-primary,#b45309)!important}.eGUBIq_backupMessage{color:var(--dsw-alias-label-secondary,#6b7280);grid-column:1/-1;font-size:12px}";
436
486
  const tagId = "dshmarket/Market.module.css";
437
487
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
438
488
  const tag = document.createElement("style");
@@ -442,86 +492,96 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
442
492
  document.head.appendChild(tag);
443
493
  }
444
494
  var Market_module_css_default = {
445
- "swatches": "eGUBIq_swatches",
446
- "body": "eGUBIq_body",
447
- "okState": "eGUBIq_okState",
448
- "empty": "eGUBIq_empty",
449
- "desc": "eGUBIq_desc",
450
- "modalNote": "eGUBIq_modalNote",
451
- "dangerArmed": "eGUBIq_dangerArmed",
452
- "sizeOn": "eGUBIq_sizeOn",
453
- "filterTitle": "eGUBIq_filterTitle",
454
- "warnLine": "eGUBIq_warnLine",
455
495
  "actLive": "eGUBIq_actLive",
456
- "cmdSummary": "eGUBIq_cmdSummary",
457
- "foot": "eGUBIq_foot",
458
- "filterBtnOn": "eGUBIq_filterBtnOn",
459
- "grow": "eGUBIq_grow",
460
- "actBroken": "eGUBIq_actBroken",
461
- "act": "eGUBIq_act",
462
- "pagerSize": "eGUBIq_pagerSize",
463
- "pageOn": "eGUBIq_pageOn",
464
- "barWave": "eGUBIq_barWave",
465
- "tag": "eGUBIq_tag",
466
- "head": "eGUBIq_head",
467
496
  "searchInline": "eGUBIq_searchInline",
468
- "tab": "eGUBIq_tab",
469
- "filterBtn": "eGUBIq_filterBtn",
470
- "catsWrap": "eGUBIq_catsWrap",
471
- "catsToggle": "eGUBIq_catsToggle",
472
- "title": "eGUBIq_title",
473
- "themesGrid": "eGUBIq_themesGrid",
474
- "nm": "eGUBIq_nm",
475
- "pager": "eGUBIq_pager",
476
- "irow": "eGUBIq_irow",
477
- "pageBtn": "eGUBIq_pageBtn",
478
- "staleAction": "eGUBIq_staleAction",
497
+ "backupCard": "eGUBIq_backupCard",
479
498
  "pageInfo": "eGUBIq_pageInfo",
499
+ "title": "eGUBIq_title",
500
+ "backupButton": "eGUBIq_backupButton",
501
+ "descTight": "eGUBIq_descTight",
480
502
  "err": "eGUBIq_err",
481
- "catsCollapsed": "eGUBIq_catsCollapsed",
482
- "actWarn": "eGUBIq_actWarn",
483
- "actWhy": "eGUBIq_actWhy",
503
+ "sp": "eGUBIq_sp",
504
+ "catsRow": "eGUBIq_catsRow",
505
+ "warnLine": "eGUBIq_warnLine",
484
506
  "src": "eGUBIq_src",
485
- "card": "eGUBIq_card",
486
- "star": "eGUBIq_star",
487
- "dangerBtn": "eGUBIq_dangerBtn",
488
- "pct": "eGUBIq_pct",
507
+ "actWhy": "eGUBIq_actWhy",
489
508
  "filterGroup": "eGUBIq_filterGroup",
490
- "pageEllipsis": "eGUBIq_pageEllipsis",
491
- "grid": "eGUBIq_grid",
492
- "dshmSlide": "eGUBIq_dshmSlide",
493
- "av": "eGUBIq_av",
494
- "sizeBtn": "eGUBIq_sizeBtn",
495
- "root": "eGUBIq_root",
496
- "filterWrap": "eGUBIq_filterWrap",
497
- "pagerPages": "eGUBIq_pagerPages",
498
- "row1": "eGUBIq_row1",
509
+ "actBroken": "eGUBIq_actBroken",
510
+ "empty": "eGUBIq_empty",
511
+ "sizeOn": "eGUBIq_sizeOn",
512
+ "dangerBtn": "eGUBIq_dangerBtn",
513
+ "backupMessage": "eGUBIq_backupMessage",
499
514
  "owner": "eGUBIq_owner",
500
- "warnBtn": "eGUBIq_warnBtn",
501
515
  "tabs": "eGUBIq_tabs",
502
- "titleRow": "eGUBIq_titleRow",
503
- "barFill": "eGUBIq_barFill",
504
- "spec": "eGUBIq_spec",
505
- "cmdDetails": "eGUBIq_cmdDetails",
506
- "top": "eGUBIq_top",
507
- "sub": "eGUBIq_sub",
508
- "restart": "eGUBIq_restart",
509
- "descTight": "eGUBIq_descTight",
510
- "progress": "eGUBIq_progress",
511
- "cmd": "eGUBIq_cmd",
512
- "bar": "eGUBIq_bar",
513
516
  "cancelBtn": "eGUBIq_cancelBtn",
514
- "sect": "eGUBIq_sect",
515
- "spin": "eGUBIq_spin",
516
- "filterPanel": "eGUBIq_filterPanel",
517
+ "pager": "eGUBIq_pager",
518
+ "card": "eGUBIq_card",
519
+ "filterWrap": "eGUBIq_filterWrap",
520
+ "row1": "eGUBIq_row1",
517
521
  "dot": "eGUBIq_dot",
518
- "catsRow": "eGUBIq_catsRow",
519
- "sp": "eGUBIq_sp",
522
+ "foot": "eGUBIq_foot",
523
+ "filterPanel": "eGUBIq_filterPanel",
524
+ "backupPrimary": "eGUBIq_backupPrimary",
525
+ "cats": "eGUBIq_cats",
526
+ "sub": "eGUBIq_sub",
527
+ "spec": "eGUBIq_spec",
528
+ "restart": "eGUBIq_restart",
529
+ "catsToggle": "eGUBIq_catsToggle",
530
+ "titleRow": "eGUBIq_titleRow",
531
+ "sizeBtn": "eGUBIq_sizeBtn",
532
+ "barFill": "eGUBIq_barFill",
533
+ "okState": "eGUBIq_okState",
534
+ "av": "eGUBIq_av",
535
+ "desc": "eGUBIq_desc",
520
536
  "loading": "eGUBIq_loading",
537
+ "pct": "eGUBIq_pct",
538
+ "bar": "eGUBIq_bar",
539
+ "filterBtn": "eGUBIq_filterBtn",
540
+ "grow": "eGUBIq_grow",
521
541
  "on": "eGUBIq_on",
522
- "cats": "eGUBIq_cats",
542
+ "themesGrid": "eGUBIq_themesGrid",
543
+ "dshmSlide": "eGUBIq_dshmSlide",
544
+ "irowMissing": "eGUBIq_irowMissing",
523
545
  "filterOption": "eGUBIq_filterOption",
524
- "sizeLabel": "eGUBIq_sizeLabel"
546
+ "filterTitle": "eGUBIq_filterTitle",
547
+ "cmdSummary": "eGUBIq_cmdSummary",
548
+ "tag": "eGUBIq_tag",
549
+ "cmd": "eGUBIq_cmd",
550
+ "head": "eGUBIq_head",
551
+ "root": "eGUBIq_root",
552
+ "sect": "eGUBIq_sect",
553
+ "progress": "eGUBIq_progress",
554
+ "pagerSize": "eGUBIq_pagerSize",
555
+ "sizeLabel": "eGUBIq_sizeLabel",
556
+ "backupGrid": "eGUBIq_backupGrid",
557
+ "body": "eGUBIq_body",
558
+ "pageBtn": "eGUBIq_pageBtn",
559
+ "pageOn": "eGUBIq_pageOn",
560
+ "pageEllipsis": "eGUBIq_pageEllipsis",
561
+ "backupInput": "eGUBIq_backupInput",
562
+ "backupActions": "eGUBIq_backupActions",
563
+ "backupCheck": "eGUBIq_backupCheck",
564
+ "irow": "eGUBIq_irow",
565
+ "cmdDetails": "eGUBIq_cmdDetails",
566
+ "modalNote": "eGUBIq_modalNote",
567
+ "backupWarn": "eGUBIq_backupWarn",
568
+ "nm": "eGUBIq_nm",
569
+ "grid": "eGUBIq_grid",
570
+ "actWarn": "eGUBIq_actWarn",
571
+ "swatches": "eGUBIq_swatches",
572
+ "barWave": "eGUBIq_barWave",
573
+ "dangerArmed": "eGUBIq_dangerArmed",
574
+ "filterBtnOn": "eGUBIq_filterBtnOn",
575
+ "act": "eGUBIq_act",
576
+ "warnBtn": "eGUBIq_warnBtn",
577
+ "top": "eGUBIq_top",
578
+ "staleAction": "eGUBIq_staleAction",
579
+ "catsWrap": "eGUBIq_catsWrap",
580
+ "tab": "eGUBIq_tab",
581
+ "spin": "eGUBIq_spin",
582
+ "pagerPages": "eGUBIq_pagerPages",
583
+ "star": "eGUBIq_star",
584
+ "catsCollapsed": "eGUBIq_catsCollapsed"
525
585
  };
526
586
  //#endregion
527
587
  //#region src/client/MarketSection.tsx
@@ -617,6 +677,38 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
617
677
  96
618
678
  ];
619
679
  const DEFAULT_PAGE_SIZE = 24;
680
+ const WEBDAV_STORAGE_KEY = "dshm-webdav";
681
+ function savedWebdav() {
682
+ try {
683
+ const value = JSON.parse(localStorage.getItem(WEBDAV_STORAGE_KEY) ?? "{}");
684
+ return {
685
+ url: typeof value.url === "string" ? value.url : "",
686
+ username: typeof value.username === "string" ? value.username : "",
687
+ password: "",
688
+ auto: value.auto === true
689
+ };
690
+ } catch {
691
+ return {
692
+ url: "",
693
+ username: "",
694
+ password: "",
695
+ auto: false
696
+ };
697
+ }
698
+ }
699
+ function backupDependencies(value) {
700
+ if (value === null || typeof value !== "object") throw new Error("invalid backup");
701
+ const backup = value;
702
+ if (backup.format !== "dsh-profile-backup" || backup.version !== .2) throw new Error("unsupported backup format");
703
+ const files = backup.files;
704
+ if (!Array.isArray(files)) throw new Error("unsupported backup format");
705
+ const manifest = files.find((file) => file !== null && typeof file === "object" && file.path === "package.json");
706
+ if (manifest?.json === null || typeof manifest?.json !== "object" || Array.isArray(manifest.json)) throw new Error("backup package.json is invalid");
707
+ const dependencies = manifest.json.dependencies;
708
+ if (dependencies === null || typeof dependencies !== "object" || Array.isArray(dependencies)) return {};
709
+ if (!Object.values(dependencies).every((spec) => typeof spec === "string")) throw new Error("backup dependencies are invalid");
710
+ return dependencies;
711
+ }
620
712
  /** Sort field choices in the filter panel. */
621
713
  const SORT_FIELD_OPTIONS = [{
622
714
  key: "stars",
@@ -656,6 +748,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
656
748
  ];
657
749
  function MarketSection(props) {
658
750
  const t = props.t;
751
+ const initialWebdav = (0, react.useMemo)(savedWebdav, []);
659
752
  const localeSnap = (0, react.useSyncExternalStore)((cb) => props.locale.subscribe(cb), () => props.locale.getSnapshot());
660
753
  const lang = String(localeSnap.active).toLowerCase().startsWith("zh") ? "zh" : "en";
661
754
  const themeSnap = (0, react.useSyncExternalStore)(props.themeStore.subscribe, props.themeStore.getSnapshot);
@@ -666,6 +759,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
666
759
  cachedInstalled = value;
667
760
  setInstalledState(value);
668
761
  }, []);
762
+ const [installedFiles, setInstalledFiles] = (0, react.useState)([]);
669
763
  const [skins, setSkins] = (0, react.useState)([]);
670
764
  const [tab, setTab] = (0, react.useState)(() => {
671
765
  const saved = sessionStorage.getItem("dshm-tab");
@@ -720,6 +814,15 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
720
814
  const [restartEnabled, setRestartEnabled] = (0, react.useState)(false);
721
815
  const [restarting, setRestarting] = (0, react.useState)(false);
722
816
  const [showTop, setShowTop] = (0, react.useState)(false);
817
+ const [backupBusy, setBackupBusy] = (0, react.useState)(false);
818
+ const [backupMessage, setBackupMessage] = (0, react.useState)(null);
819
+ const [backupRestored, setBackupRestored] = (0, react.useState)(false);
820
+ const [pendingBackup, setPendingBackup] = (0, react.useState)(null);
821
+ const [pendingDependencies, setPendingDependencies] = (0, react.useState)({});
822
+ const [webdavUrl, setWebdavUrl] = (0, react.useState)(initialWebdav.url);
823
+ const [webdavUser, setWebdavUser] = (0, react.useState)(initialWebdav.username);
824
+ const [webdavPassword, setWebdavPassword] = (0, react.useState)(initialWebdav.password);
825
+ const [autoBackup, setAutoBackup] = (0, react.useState)(initialWebdav.auto);
723
826
  const bodyRef = (0, react.useRef)(null);
724
827
  const [sortField, setSortField] = (0, react.useState)("stars");
725
828
  const [sortDir, setSortDir] = (0, react.useState)("desc");
@@ -734,6 +837,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
734
837
  const refreshInstalled = (0, react.useCallback)((force) => {
735
838
  fetch("/dsh-market/installed", { cache: "no-store" }).then((res) => res.json()).then((body) => {
736
839
  setInstalled(body.installed || {});
840
+ setInstalledFiles(Array.isArray(body.present) ? body.present : Object.keys(body.installed || {}));
737
841
  setSkins(body.live || []);
738
842
  if (body.activation && typeof body.activation === "object") setActivations(body.activation);
739
843
  }).catch(() => {});
@@ -1144,7 +1248,100 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1144
1248
  };
1145
1249
  next();
1146
1250
  }, [updatableNames, doUpdate]);
1147
- const pendingRestart = doneUrls.length + updatedNames.length + removedCount;
1251
+ const finishRestore = (0, react.useCallback)((body) => {
1252
+ const errors = Array.isArray(body.errors) ? body.errors : [];
1253
+ if (errors.length > 0) window.alert(`${t("restorePartial")}\n\n${errors.map((item) => `${String(item.name)}: ${String(item.error)}`).join("\n")}`);
1254
+ setBackupRestored(true);
1255
+ setBackupMessage(t("restoreDone"));
1256
+ if (errors.length === 0) {
1257
+ setPendingBackup(null);
1258
+ setPendingDependencies({});
1259
+ }
1260
+ refreshInstalled(true);
1261
+ }, [refreshInstalled, t]);
1262
+ const previewBackup = (0, react.useCallback)((backup) => {
1263
+ const dependencies = backupDependencies(backup);
1264
+ setPendingBackup(backup);
1265
+ setPendingDependencies(dependencies);
1266
+ setBackupMessage(t("restorePreviewDone"));
1267
+ setTab("installed");
1268
+ }, [t]);
1269
+ const restoreBackup = (0, react.useCallback)(() => {
1270
+ if (pendingBackup === null) return Promise.resolve();
1271
+ if (!window.confirm(t("restoreConfirm"))) return Promise.resolve();
1272
+ setBackupBusy(true);
1273
+ setBackupMessage(null);
1274
+ return fetch("/dsh-market/restore", {
1275
+ method: "POST",
1276
+ headers: { "content-type": "application/json" },
1277
+ body: JSON.stringify({ backup: pendingBackup })
1278
+ }).then(async (response) => {
1279
+ const body = await response.json();
1280
+ if (!response.ok) throw new Error(String(body.error || "restore failed"));
1281
+ finishRestore(body);
1282
+ }).catch((error) => setBackupMessage(String(error))).finally(() => setBackupBusy(false));
1283
+ }, [
1284
+ finishRestore,
1285
+ pendingBackup,
1286
+ t
1287
+ ]);
1288
+ const runWebdav = (0, react.useCallback)((action) => {
1289
+ if (webdavUrl.trim() === "") return;
1290
+ setBackupBusy(true);
1291
+ setBackupMessage(null);
1292
+ fetch("/dsh-market/webdav", {
1293
+ method: "POST",
1294
+ headers: { "content-type": "application/json" },
1295
+ body: JSON.stringify({
1296
+ action,
1297
+ url: webdavUrl.trim(),
1298
+ username: webdavUser,
1299
+ password: webdavPassword
1300
+ })
1301
+ }).then(async (response) => {
1302
+ const body = await response.json();
1303
+ if (!response.ok) throw new Error(String(body.error || "WebDAV failed"));
1304
+ if (action === "restore") previewBackup(body.backup);
1305
+ if (action === "backup") {
1306
+ try {
1307
+ localStorage.setItem("dshm-webdav-last", String(Date.now()));
1308
+ } catch {}
1309
+ setBackupMessage(t("backupDone"));
1310
+ }
1311
+ }).catch((error) => setBackupMessage(String(error))).finally(() => setBackupBusy(false));
1312
+ }, [
1313
+ previewBackup,
1314
+ t,
1315
+ webdavPassword,
1316
+ webdavUrl,
1317
+ webdavUser
1318
+ ]);
1319
+ (0, react.useEffect)(() => {
1320
+ try {
1321
+ localStorage.setItem(WEBDAV_STORAGE_KEY, JSON.stringify({
1322
+ url: webdavUrl,
1323
+ username: webdavUser,
1324
+ auto: autoBackup
1325
+ }));
1326
+ } catch {}
1327
+ if (!autoBackup || webdavUrl.trim() === "") return;
1328
+ let last = 0;
1329
+ try {
1330
+ last = Number(localStorage.getItem("dshm-webdav-last")) || 0;
1331
+ } catch {}
1332
+ if (Date.now() - last >= 864e5) runWebdav("backup");
1333
+ }, [
1334
+ autoBackup,
1335
+ runWebdav,
1336
+ webdavUrl,
1337
+ webdavUser
1338
+ ]);
1339
+ const pendingRestart = doneUrls.length + updatedNames.length + removedCount + (backupRestored ? 1 : 0);
1340
+ const displayedInstalled = pendingBackup === null ? installed : {
1341
+ ...pendingDependencies,
1342
+ ...installed
1343
+ };
1344
+ const missingRestoreCount = Object.keys(pendingDependencies).filter((name) => !installedFiles.includes(name)).length;
1148
1345
  const hasUpdates = Object.keys(installed).some((name) => !updatedNames.includes(name) && updates[name] && updates[name].updateAvailable);
1149
1346
  /** Live status line: structured phase, or the human-line fallback. */
1150
1347
  const phasePart = progressPhase != null ? phaseLabel(progressPhase, t) + (progressCurrent !== null ? " · " + progressCurrent : "") + (progressDone > 0 ? " · " + t("packagesDone").replace("{0}", String(progressDone)) : "") : progressLine || t("progressHint");
@@ -1491,8 +1688,13 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1491
1688
  className: Market_module_css_default.dot
1492
1689
  })]
1493
1690
  }),
1691
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1692
+ className: tab === "backup" ? `${Market_module_css_default.tab} ${Market_module_css_default.on}` : Market_module_css_default.tab,
1693
+ onClick: () => setTab("backup"),
1694
+ children: t("tabBackup")
1695
+ }),
1494
1696
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: Market_module_css_default.grow }),
1495
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
1697
+ tab !== "backup" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
1496
1698
  className: Market_module_css_default.searchInline,
1497
1699
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }),
1498
1700
  placeholder: t("searchPh"),
@@ -1518,6 +1720,23 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1518
1720
  })
1519
1721
  ]
1520
1722
  }),
1723
+ tab === "installed" && pendingBackup !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1724
+ className: Market_module_css_default.restart,
1725
+ children: [
1726
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "↺" }),
1727
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1728
+ className: Market_module_css_default.grow,
1729
+ children: t("restoreMissing").replace("{0}", String(missingRestoreCount))
1730
+ }),
1731
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1732
+ variant: "primary",
1733
+ size: "sm",
1734
+ disabled: backupBusy,
1735
+ onClick: restoreBackup,
1736
+ children: backupBusy ? t("backupWorking") : t("restoreStart")
1737
+ })
1738
+ ]
1739
+ }),
1521
1740
  hotUrls.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1522
1741
  className: Market_module_css_default.restart,
1523
1742
  children: [
@@ -1631,7 +1850,137 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1631
1850
  className: Market_module_css_default.body,
1632
1851
  ref: bodyRef,
1633
1852
  onScroll: (e) => setShowTop(e.currentTarget.scrollTop > 400),
1634
- children: tab === "discover" ? loadError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1853
+ children: tab === "backup" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1854
+ className: Market_module_css_default.backupGrid,
1855
+ children: [
1856
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1857
+ className: Market_module_css_default.backupCard,
1858
+ children: [
1859
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("backupLocal") }),
1860
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("backupHint") }),
1861
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1862
+ className: Market_module_css_default.backupWarn,
1863
+ children: t("credsWarning")
1864
+ }),
1865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1866
+ className: Market_module_css_default.backupActions,
1867
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1868
+ className: `${Market_module_css_default.backupButton} ${Market_module_css_default.backupPrimary}`,
1869
+ href: "/dsh-market/backup",
1870
+ download: true,
1871
+ "aria-disabled": backupBusy,
1872
+ children: t("backupDownload")
1873
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1874
+ className: Market_module_css_default.backupButton,
1875
+ "aria-disabled": backupBusy,
1876
+ children: [backupBusy ? t("backupWorking") : t("backupImport"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1877
+ type: "file",
1878
+ accept: "application/json,.json",
1879
+ disabled: backupBusy,
1880
+ onChange: (event) => {
1881
+ const file = event.currentTarget.files?.[0];
1882
+ event.currentTarget.value = "";
1883
+ if (file !== void 0) file.text().then((text) => previewBackup(JSON.parse(text))).catch((error) => setBackupMessage(String(error)));
1884
+ }
1885
+ })]
1886
+ })]
1887
+ })
1888
+ ]
1889
+ }),
1890
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1891
+ className: Market_module_css_default.backupCard,
1892
+ children: [
1893
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("webdav") }),
1894
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1895
+ className: Market_module_css_default.backupInput,
1896
+ "aria-label": t("webdavPreset"),
1897
+ defaultValue: "",
1898
+ onChange: (event) => {
1899
+ const urls = {
1900
+ jianguoyun: "https://dav.jianguoyun.com/dav/dsh-profile-backup.json",
1901
+ koofr: "https://app.koofr.net/dav/Koofr/dsh-profile-backup.json",
1902
+ nextcloud: "https://nextcloud.example/remote.php/dav/files/USERNAME/dsh-profile-backup.json"
1903
+ };
1904
+ if (urls[event.target.value] !== void 0) setWebdavUrl(urls[event.target.value]);
1905
+ },
1906
+ children: [
1907
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1908
+ value: "",
1909
+ children: t("webdavPreset")
1910
+ }),
1911
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1912
+ value: "jianguoyun",
1913
+ children: "坚果云 / Nutstore"
1914
+ }),
1915
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1916
+ value: "koofr",
1917
+ children: "Koofr"
1918
+ }),
1919
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1920
+ value: "nextcloud",
1921
+ children: "Nextcloud"
1922
+ })
1923
+ ]
1924
+ }),
1925
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1926
+ className: Market_module_css_default.backupInput,
1927
+ type: "url",
1928
+ value: webdavUrl,
1929
+ placeholder: t("webdavUrl"),
1930
+ onChange: (e) => setWebdavUrl(e.target.value)
1931
+ }),
1932
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1933
+ className: Market_module_css_default.backupInput,
1934
+ autoComplete: "username",
1935
+ value: webdavUser,
1936
+ placeholder: t("webdavUser"),
1937
+ onChange: (e) => setWebdavUser(e.target.value)
1938
+ }),
1939
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1940
+ className: Market_module_css_default.backupInput,
1941
+ type: "password",
1942
+ autoComplete: "current-password",
1943
+ value: webdavPassword,
1944
+ placeholder: t("webdavPassword"),
1945
+ onChange: (e) => setWebdavPassword(e.target.value)
1946
+ }),
1947
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1948
+ className: Market_module_css_default.backupActions,
1949
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1950
+ variant: "primary",
1951
+ size: "sm",
1952
+ disabled: backupBusy || webdavUrl.trim() === "",
1953
+ onClick: () => runWebdav("backup"),
1954
+ children: backupBusy ? t("backupWorking") : t("webdavUpload")
1955
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1956
+ variant: "outline",
1957
+ size: "sm",
1958
+ disabled: backupBusy || webdavUrl.trim() === "",
1959
+ onClick: () => runWebdav("restore"),
1960
+ children: t("webdavRestore")
1961
+ })]
1962
+ }),
1963
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1964
+ className: Market_module_css_default.backupCheck,
1965
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1966
+ type: "checkbox",
1967
+ checked: autoBackup,
1968
+ onChange: (e) => setAutoBackup(e.target.checked)
1969
+ }), t("autoBackup")]
1970
+ }),
1971
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("webdavNote") }),
1972
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1973
+ className: Market_module_css_default.backupWarn,
1974
+ children: t("credsWarning")
1975
+ })
1976
+ ]
1977
+ }),
1978
+ backupMessage !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1979
+ className: Market_module_css_default.backupMessage,
1980
+ children: backupMessage
1981
+ })
1982
+ ]
1983
+ }) : tab === "discover" ? loadError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1635
1984
  className: Market_module_css_default.empty,
1636
1985
  children: t("loadFail")
1637
1986
  }) : data === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1812,10 +2161,11 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1812
2161
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1813
2162
  className: Market_module_css_default.grid,
1814
2163
  children: themePlugins$1.map(themePluginCard)
1815
- })] }) : Object.keys(installed).length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2164
+ })] }) : Object.keys(displayedInstalled).length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1816
2165
  className: Market_module_css_default.empty,
1817
2166
  children: t("installedEmpty")
1818
- }) : Object.entries(installed).map(([name, spec]) => {
2167
+ }) : Object.entries(displayedInstalled).map(([name, spec]) => {
2168
+ const missing = pendingBackup !== null && !installedFiles.includes(name);
1819
2169
  const entry = data === null ? void 0 : entryForDep(data.plugins, name, String(spec));
1820
2170
  const status = updates[name];
1821
2171
  const act = activations[name];
@@ -1825,7 +2175,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1825
2175
  const ghSpec = /^github:([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+?)(?:#|$)/.exec(specText);
1826
2176
  const repoUrl = entry !== void 0 ? entry.url : ghSpec !== null ? "https://github.com/" + ghSpec[1] : null;
1827
2177
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1828
- className: Market_module_css_default.irow,
2178
+ className: missing ? `${Market_module_css_default.irow} ${Market_module_css_default.irowMissing}` : Market_module_css_default.irow,
1829
2179
  children: [
1830
2180
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1831
2181
  style: { minWidth: 0 },
@@ -1906,7 +2256,10 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1906
2256
  rel: "noreferrer",
1907
2257
  children: t("readme")
1908
2258
  }),
1909
- updatedNames.includes(name) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2259
+ missing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2260
+ className: Market_module_css_default.owner,
2261
+ children: t("notInstalled")
2262
+ }) : updatedNames.includes(name) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1910
2263
  className: Market_module_css_default.okState,
1911
2264
  children: act?.state === "live" ? t("updatedLive") : t("updated")
1912
2265
  }) : updatingName === name ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
@@ -1929,7 +2282,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
1929
2282
  className: Market_module_css_default.owner,
1930
2283
  children: t("upToDate")
1931
2284
  }),
1932
- name !== "dsh-market" && name !== "dshmarket" && (removingName === name ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
2285
+ !missing && name !== "dsh-market" && name !== "dshmarket" && (removingName === name ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1933
2286
  variant: "outline",
1934
2287
  size: "sm",
1935
2288
  className: Market_module_css_default.dangerBtn,