dsh-session-curator 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -189
- package/README.zh-CN.md +18 -144
- package/lib/client.js +440 -21
- package/lib/index.js +156 -13
- package/package.json +1 -1
- package/scripts/activation.mjs +1 -1
- package/scripts/client.mjs +151 -0
- package/scripts/smoke.mjs +226 -25
package/lib/client.js
CHANGED
|
@@ -24,6 +24,8 @@ window.__ModuleLoader__.load({
|
|
|
24
24
|
Menu,
|
|
25
25
|
MenuItemButton,
|
|
26
26
|
Modal,
|
|
27
|
+
SegmentedControl,
|
|
28
|
+
Switch,
|
|
27
29
|
IconChevronDownOutlineRegular,
|
|
28
30
|
IconChevronRightOutlineRegular,
|
|
29
31
|
IconEllipsisOutlineRegular,
|
|
@@ -123,7 +125,41 @@ window.__ModuleLoader__.load({
|
|
|
123
125
|
selectAllShort: "全选",
|
|
124
126
|
clearShort: "取消",
|
|
125
127
|
collapse: "折叠",
|
|
126
|
-
expand: "展开"
|
|
128
|
+
expand: "展开",
|
|
129
|
+
titleConfig: "配置",
|
|
130
|
+
configTitle: "基础设置",
|
|
131
|
+
configTab: "默认页签",
|
|
132
|
+
configTabHint: "打开这里时先显示哪个页签",
|
|
133
|
+
configGrouping: "按工作区分组",
|
|
134
|
+
configGroupingHint: "关掉就是平铺列表;顶部的「分组」按钮改的是同一个值",
|
|
135
|
+
configSidebarArchived: "侧边栏显示已归档会话",
|
|
136
|
+
configSidebarArchivedHint: "对应侧边栏「视图选项」里的「隐藏已归档 / 全部对话(显示已归档)」;存的是官方那份设置,改完要刷新页面才生效",
|
|
137
|
+
configSidebarRefreshHint: "已写入官方设置,但侧边栏要刷新页面才会跟着变。",
|
|
138
|
+
refreshPage: "刷新页面",
|
|
139
|
+
configMeta: "行内显示体积与文件数",
|
|
140
|
+
configMetaHint: "关掉后每行只留标题(平铺时仍会显示文件夹)",
|
|
141
|
+
configOrder: "排序",
|
|
142
|
+
configOrderHint: "原顺序就是宿主给的顺序(回收站里是最近删除在前)",
|
|
143
|
+
orderHost: "原顺序",
|
|
144
|
+
orderTitle: "标题",
|
|
145
|
+
orderBytes: "体积",
|
|
146
|
+
configAutoUpdate: "自动检查更新",
|
|
147
|
+
configAutoUpdateHint: "打开这个页面时问一次 npm registry(10 分钟内有缓存)",
|
|
148
|
+
configReset: "恢复默认",
|
|
149
|
+
configResetDone: "已恢复默认设置",
|
|
150
|
+
configSaved: "已保存",
|
|
151
|
+
aboutTitle: "关于",
|
|
152
|
+
versionLabel: "版本",
|
|
153
|
+
updateChecking: "正在检查更新…",
|
|
154
|
+
updateLatest: "已是最新版本",
|
|
155
|
+
updateAvailable: "有新版本 {latest}(当前 {current})",
|
|
156
|
+
updateFailed: "检查更新失败:{reason}",
|
|
157
|
+
updateRetry: "重新检查",
|
|
158
|
+
updateCheckNow: "检查更新",
|
|
159
|
+
updateStepsTitle: "升级步骤",
|
|
160
|
+
updateStepOne: "在终端里升级包(按你自己的 profile 名替换 desktop):",
|
|
161
|
+
updateStepTwo: "重启 DSH 让宿主半区生效;只改了客户端半区的话刷新页面就够。",
|
|
162
|
+
updateOpen: "打开 npm 页面"
|
|
127
163
|
};
|
|
128
164
|
const en = {
|
|
129
165
|
sectionLabel: "Session manager",
|
|
@@ -173,7 +209,41 @@ window.__ModuleLoader__.load({
|
|
|
173
209
|
selectAllShort: "Select all",
|
|
174
210
|
clearShort: "Clear",
|
|
175
211
|
collapse: "Collapse",
|
|
176
|
-
expand: "Expand"
|
|
212
|
+
expand: "Expand",
|
|
213
|
+
titleConfig: "Settings",
|
|
214
|
+
configTitle: "Basics",
|
|
215
|
+
configTab: "Default tab",
|
|
216
|
+
configTabHint: "Which tab the panel opens on",
|
|
217
|
+
configGrouping: "Group by workspace",
|
|
218
|
+
configGroupingHint: "Off renders a flat list; the top “Grouped” button edits the same value",
|
|
219
|
+
configSidebarArchived: "Show archived sessions in the sidebar",
|
|
220
|
+
configSidebarArchivedHint: "Mirrors “Hide archived / All conversations” in the sidebar's view options; it is the official setting, so a page refresh applies it",
|
|
221
|
+
configSidebarRefreshHint: "Written to the official setting — the sidebar only follows it after a page refresh.",
|
|
222
|
+
refreshPage: "Refresh page",
|
|
223
|
+
configMeta: "Show size and file count per row",
|
|
224
|
+
configMetaHint: "Off leaves only the title (the folder still shows in the flat list)",
|
|
225
|
+
configOrder: "Order",
|
|
226
|
+
configOrderHint: "Host order is what the host sent (the trash is most-recently-deleted first)",
|
|
227
|
+
orderHost: "Host",
|
|
228
|
+
orderTitle: "Title",
|
|
229
|
+
orderBytes: "Size",
|
|
230
|
+
configAutoUpdate: "Check for updates automatically",
|
|
231
|
+
configAutoUpdateHint: "Asks the npm registry when this page opens (cached for 10 minutes)",
|
|
232
|
+
configReset: "Reset to defaults",
|
|
233
|
+
configResetDone: "Defaults restored",
|
|
234
|
+
configSaved: "Saved",
|
|
235
|
+
aboutTitle: "About",
|
|
236
|
+
versionLabel: "Version",
|
|
237
|
+
updateChecking: "Checking for updates…",
|
|
238
|
+
updateLatest: "You are on the latest version",
|
|
239
|
+
updateAvailable: "Version {latest} is available (you have {current})",
|
|
240
|
+
updateFailed: "Update check failed: {reason}",
|
|
241
|
+
updateRetry: "Check again",
|
|
242
|
+
updateCheckNow: "Check for updates",
|
|
243
|
+
updateStepsTitle: "How to upgrade",
|
|
244
|
+
updateStepOne: "Upgrade the package in a terminal (replace desktop with your profile name):",
|
|
245
|
+
updateStepTwo: "Restart DSH so the host half reloads; a client-half change only needs a page refresh.",
|
|
246
|
+
updateOpen: "Open the npm page"
|
|
177
247
|
};
|
|
178
248
|
|
|
179
249
|
// ── 两个模块级小 store(弹窗宿主与设置面板共享) ─────────────────────
|
|
@@ -202,8 +272,100 @@ window.__ModuleLoader__.load({
|
|
|
202
272
|
const dialogStore = createStore(null);
|
|
203
273
|
/** 每次写操作成功后自增,让设置面板重新拉快照。 */
|
|
204
274
|
const mutationStore = createStore(0);
|
|
205
|
-
|
|
206
|
-
|
|
275
|
+
|
|
276
|
+
// ── 面板设置:localStorage 持久化,拿不到就只在内存里生效 ─────────────
|
|
277
|
+
const SETTINGS_KEY = "dsh-session-curator.settings";
|
|
278
|
+
/** `tab` 只允许三个数据页签:配置页不能当"默认页签"。 */
|
|
279
|
+
const DEFAULT_SETTINGS = { tab: "archived", grouping: true, showMeta: true, order: "host", autoUpdate: true };
|
|
280
|
+
|
|
281
|
+
function readSettings() {
|
|
282
|
+
try {
|
|
283
|
+
const raw = globalThis.localStorage ? globalThis.localStorage.getItem(SETTINGS_KEY) : null;
|
|
284
|
+
if (typeof raw !== "string" || raw === "") return { ...DEFAULT_SETTINGS };
|
|
285
|
+
const parsed = JSON.parse(raw);
|
|
286
|
+
const next = { ...DEFAULT_SETTINGS };
|
|
287
|
+
for (const key of Object.keys(DEFAULT_SETTINGS)) {
|
|
288
|
+
// 类型不符(手改过的 localStorage / 老版本留下的键)一律退回默认值。
|
|
289
|
+
if (typeof parsed[key] === typeof DEFAULT_SETTINGS[key]) next[key] = parsed[key];
|
|
290
|
+
}
|
|
291
|
+
if (next.tab === "config") next.tab = DEFAULT_SETTINGS.tab;
|
|
292
|
+
return next;
|
|
293
|
+
} catch {
|
|
294
|
+
return { ...DEFAULT_SETTINGS };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const settingsStore = createStore(readSettings());
|
|
299
|
+
function updateSettings(patch) {
|
|
300
|
+
const next = { ...settingsStore.get(), ...patch };
|
|
301
|
+
settingsStore.set(next);
|
|
302
|
+
try {
|
|
303
|
+
if (globalThis.localStorage) globalThis.localStorage.setItem(SETTINGS_KEY, JSON.stringify(next));
|
|
304
|
+
} catch {
|
|
305
|
+
// 隐私模式 / 配额满:设置本次会话内有效。
|
|
306
|
+
}
|
|
307
|
+
return next;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** "按工作区分组"开关;初值来自设置,工具条上改它也会写回设置。 */
|
|
311
|
+
const groupingStore = createStore(settingsStore.get().grouping);
|
|
312
|
+
|
|
313
|
+
// ── 官方侧边栏的"显示已归档" ─────────────────────────────────────────
|
|
314
|
+
/**
|
|
315
|
+
* 官方那份"视图"存储的 localStorage 键:`@deepseek-ai/dsh-client-ui-workspace` 里
|
|
316
|
+
* `defineStore({ …, persist: "dsh.workspace.view.v5" })`,整份 JSON 形如
|
|
317
|
+
* `{ groupBy, orderBy, groupExpansion, sessionOrderByAccount, archivedFilter }`。
|
|
318
|
+
* 侧边栏「视图选项」菜单里的 隐藏已归档 / 全部对话(显示已归档)/ 仅显示已归档
|
|
319
|
+
* 写的就是 `archivedFilter`(`default` / `show` / `only`)。
|
|
320
|
+
*/
|
|
321
|
+
const SIDEBAR_VIEW_KEY = "dsh.workspace.view.v5";
|
|
322
|
+
|
|
323
|
+
function readSidebarArchived() {
|
|
324
|
+
try {
|
|
325
|
+
const raw = globalThis.localStorage ? globalThis.localStorage.getItem(SIDEBAR_VIEW_KEY) : null;
|
|
326
|
+
if (typeof raw !== "string" || raw === "") return "default";
|
|
327
|
+
const parsed = JSON.parse(raw);
|
|
328
|
+
const value = parsed !== null && typeof parsed === "object" ? parsed.archivedFilter : null;
|
|
329
|
+
return value === "show" || value === "only" ? value : "default";
|
|
330
|
+
} catch {
|
|
331
|
+
return "default";
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* 只改 `archivedFilter` 一个字段,其余(分组方式 / 排序 / 折叠 / 手动顺序)原样保留。
|
|
337
|
+
*
|
|
338
|
+
* 为什么只能"写存储 + 刷新页面":`@deepseek-ai/dsh-client-store` 的 `attachPersistence()`
|
|
339
|
+
* **只在 store 创建时读一次** localStorage(之后只在自己变更时写),不监听 `storage` 事件;
|
|
340
|
+
* 而那个实例活在官方 bundle 内部(`@deepseek-ai/dsh-client-ui-workspace` 只导出
|
|
341
|
+
* `apply` / `inject`),插件既拿不到实例也拿不到它的写接口。刷新页面后官方 store 才会
|
|
342
|
+
* 带着新值重建,侧边栏跟着变。
|
|
343
|
+
*/
|
|
344
|
+
function writeSidebarArchived(show) {
|
|
345
|
+
try {
|
|
346
|
+
if (!globalThis.localStorage) return false;
|
|
347
|
+
const raw = globalThis.localStorage.getItem(SIDEBAR_VIEW_KEY);
|
|
348
|
+
let parsed = raw === null || raw === "" ? null : JSON.parse(raw);
|
|
349
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) parsed = {};
|
|
350
|
+
parsed.archivedFilter = show ? "show" : "default";
|
|
351
|
+
globalThis.localStorage.setItem(SIDEBAR_VIEW_KEY, JSON.stringify(parsed));
|
|
352
|
+
return true;
|
|
353
|
+
} catch {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/** 更新检查状态:`{ state: idle|checking|done|failed, value, error }`。 */
|
|
359
|
+
const updateStore = createStore({ state: "idle", value: null, error: null });
|
|
360
|
+
async function checkUpdate(force) {
|
|
361
|
+
updateStore.set({ state: "checking", value: updateStore.get().value, error: null });
|
|
362
|
+
try {
|
|
363
|
+
const value = await call("check-update", force === true ? { force: true } : {});
|
|
364
|
+
updateStore.set({ state: "done", value, error: null });
|
|
365
|
+
} catch (error) {
|
|
366
|
+
updateStore.set({ state: "failed", value: updateStore.get().value, error: reportError(error) });
|
|
367
|
+
}
|
|
368
|
+
}
|
|
207
369
|
|
|
208
370
|
function requestTrash(ids, title) {
|
|
209
371
|
dialogStore.set({ kind: "trash", ids, title });
|
|
@@ -462,7 +624,59 @@ window.__ModuleLoader__.load({
|
|
|
462
624
|
error: { color: "var(--dsw-alias-state-error-primary)", fontSize: "12px" },
|
|
463
625
|
footer: { color: "var(--dsw-alias-label-secondary)", fontSize: "11px", display: "flex", gap: "10px", flexWrap: "wrap" },
|
|
464
626
|
mono: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" },
|
|
465
|
-
ack: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px", color: "var(--dsw-alias-label-primary)" }
|
|
627
|
+
ack: { display: "flex", alignItems: "center", gap: "8px", fontSize: "12px", color: "var(--dsw-alias-label-primary)" },
|
|
628
|
+
/** 顶部条:页签在左,动作靠 `marginInlineStart: auto` 推到右边。 */
|
|
629
|
+
header: { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" },
|
|
630
|
+
headerActions: { display: "flex", alignItems: "center", gap: "8px", marginInlineStart: "auto" },
|
|
631
|
+
/** 配置页的一行:左边标签 + 说明,右边控件。 */
|
|
632
|
+
configRow: {
|
|
633
|
+
display: "flex",
|
|
634
|
+
alignItems: "center",
|
|
635
|
+
justifyContent: "space-between",
|
|
636
|
+
gap: "12px",
|
|
637
|
+
padding: "10px 12px",
|
|
638
|
+
borderTop: "1px solid var(--dsw-alias-border-l1)"
|
|
639
|
+
},
|
|
640
|
+
configMain: { display: "flex", flexDirection: "column", gap: "2px", minWidth: 0 },
|
|
641
|
+
configLabel: { color: "var(--dsw-alias-label-primary)", fontSize: "13px" },
|
|
642
|
+
configHint: { color: "var(--dsw-alias-label-tertiary)", fontSize: "11px" },
|
|
643
|
+
configControl: { display: "flex", alignItems: "center", gap: "8px", flex: "0 0 auto" },
|
|
644
|
+
configFoot: {
|
|
645
|
+
display: "flex",
|
|
646
|
+
alignItems: "center",
|
|
647
|
+
gap: "8px",
|
|
648
|
+
flexWrap: "wrap",
|
|
649
|
+
padding: "10px 12px",
|
|
650
|
+
borderTop: "1px solid var(--dsw-alias-border-l1)"
|
|
651
|
+
},
|
|
652
|
+
/** 版本号:浅色小字。 */
|
|
653
|
+
version: { color: "var(--dsw-alias-label-tertiary)", fontSize: "11px" },
|
|
654
|
+
/**
|
|
655
|
+
* 升级提示块。底色用带 alpha 的 `interactive-bg-active` —— 浅色主题里
|
|
656
|
+
* base / layer-1 / layer-2 / layer-3 是同一个颜色,铺上去等于没铺。
|
|
657
|
+
*/
|
|
658
|
+
updateBox: {
|
|
659
|
+
display: "flex",
|
|
660
|
+
flexDirection: "column",
|
|
661
|
+
gap: "6px",
|
|
662
|
+
padding: "10px 12px",
|
|
663
|
+
borderTop: "1px solid var(--dsw-alias-border-l1)",
|
|
664
|
+
background: "var(--dsw-alias-interactive-bg-active)"
|
|
665
|
+
},
|
|
666
|
+
updateTitle: { color: "var(--dsw-alias-label-primary)", fontSize: "12px", fontWeight: 600 },
|
|
667
|
+
updateSteps: { color: "var(--dsw-alias-label-secondary)", fontSize: "11px", display: "flex", flexDirection: "column", gap: "4px" },
|
|
668
|
+
code: {
|
|
669
|
+
display: "block",
|
|
670
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
671
|
+
fontSize: "11px",
|
|
672
|
+
color: "var(--dsw-alias-label-primary)",
|
|
673
|
+
background: "var(--dsw-alias-interactive-bg-active)",
|
|
674
|
+
borderRadius: "6px",
|
|
675
|
+
padding: "6px 8px",
|
|
676
|
+
overflowX: "auto",
|
|
677
|
+
whiteSpace: "pre"
|
|
678
|
+
},
|
|
679
|
+
link: { color: "var(--dsw-alias-label-primary)", fontSize: "11px" }
|
|
466
680
|
};
|
|
467
681
|
|
|
468
682
|
// ── 会话行 ──────────────────────────────────────────────────────────
|
|
@@ -479,8 +693,11 @@ window.__ModuleLoader__.load({
|
|
|
479
693
|
const t = props.t;
|
|
480
694
|
const meta = [];
|
|
481
695
|
if (props.showCwd) meta.push(row.cwd ? row.cwd : t("noProject"));
|
|
482
|
-
|
|
483
|
-
if (
|
|
696
|
+
// 体积 / 文件数可以在配置页关掉(默认开);文件夹那一段不受影响。
|
|
697
|
+
if (props.showMeta !== false) {
|
|
698
|
+
if (row.bytes) meta.push(formatBytes(row.bytes));
|
|
699
|
+
if (row.files) meta.push(text(t, "files", { n: row.files }));
|
|
700
|
+
}
|
|
484
701
|
const time = props.showDeletedAt ? formatTime(row.deletedAt) : formatTime(row.createdAt);
|
|
485
702
|
if (time !== "") meta.push(time);
|
|
486
703
|
|
|
@@ -573,7 +790,9 @@ window.__ModuleLoader__.load({
|
|
|
573
790
|
// ── 设置面板 ────────────────────────────────────────────────────────
|
|
574
791
|
function SessionManagerPanel(props) {
|
|
575
792
|
const t = props.t;
|
|
576
|
-
const
|
|
793
|
+
const settings = useStore(settingsStore);
|
|
794
|
+
/** 初始页签来自配置页的"默认页签"。 */
|
|
795
|
+
const [tab, setTab] = React.useState(settings.tab);
|
|
577
796
|
const [snapshot, setSnapshot] = React.useState(null);
|
|
578
797
|
const [error, setError] = React.useState(null);
|
|
579
798
|
const [loading, setLoading] = React.useState(true);
|
|
@@ -585,6 +804,11 @@ window.__ModuleLoader__.load({
|
|
|
585
804
|
const [menuFor, setMenuFor] = React.useState(null);
|
|
586
805
|
const grouping = useStore(groupingStore);
|
|
587
806
|
const revision = useStore(mutationStore);
|
|
807
|
+
const update = useStore(updateStore);
|
|
808
|
+
// 官方侧边栏的"显示已归档":初值从它自己的 localStorage 读;改完要刷新页面才生效,
|
|
809
|
+
// 所以另外记住"挂载时是什么",两者不一致时才提示刷新。
|
|
810
|
+
const [sidebarArchived, setSidebarArchived] = React.useState(readSidebarArchived);
|
|
811
|
+
const [sidebarAtMount] = React.useState(readSidebarArchived);
|
|
588
812
|
|
|
589
813
|
const applyResult = (value) => {
|
|
590
814
|
if (value && value.archived) setSnapshot(value);
|
|
@@ -606,6 +830,12 @@ window.__ModuleLoader__.load({
|
|
|
606
830
|
void refresh();
|
|
607
831
|
}, [refresh, revision]);
|
|
608
832
|
|
|
833
|
+
// 打开配置页时自动查一次更新(可以在配置页关掉;宿主那边 10 分钟内复用缓存)。
|
|
834
|
+
React.useEffect(() => {
|
|
835
|
+
if (tab !== "config" || !settings.autoUpdate || update.state !== "idle") return;
|
|
836
|
+
void checkUpdate(false);
|
|
837
|
+
}, [tab, settings.autoUpdate, update.state]);
|
|
838
|
+
|
|
609
839
|
// 换页签时清空选择,避免把上一个页签的 id 带到下一个页签的操作里。
|
|
610
840
|
React.useEffect(() => {
|
|
611
841
|
setSelected([]);
|
|
@@ -643,7 +873,17 @@ window.__ModuleLoader__.load({
|
|
|
643
873
|
const archived = snapshot ? snapshot.archived : [];
|
|
644
874
|
const pinned = snapshot ? snapshot.pinned : [];
|
|
645
875
|
const trashed = snapshot ? snapshot.trashed : [];
|
|
646
|
-
|
|
876
|
+
/** 排序是显示层的事:`host` 就是宿主给的顺序(回收站是最近删除在前)。 */
|
|
877
|
+
const sortRows = (list) => {
|
|
878
|
+
if (settings.order === "title") {
|
|
879
|
+
return [...list].sort((left, right) => String(left.title ?? "").localeCompare(String(right.title ?? "")));
|
|
880
|
+
}
|
|
881
|
+
if (settings.order === "bytes") {
|
|
882
|
+
return [...list].sort((left, right) => (right.bytes ?? 0) - (left.bytes ?? 0));
|
|
883
|
+
}
|
|
884
|
+
return list;
|
|
885
|
+
};
|
|
886
|
+
const rows = sortRows(tab === "archived" ? archived : tab === "pinned" ? pinned : tab === "trash" ? trashed : []);
|
|
647
887
|
const selectedRows = rows.filter((row) => selected.includes(row.sessionId));
|
|
648
888
|
|
|
649
889
|
// 分组只是显示层:选择、批量操作、计数仍然按整个页签的行来算。
|
|
@@ -657,7 +897,8 @@ window.__ModuleLoader__.load({
|
|
|
657
897
|
const tabs = [
|
|
658
898
|
{ id: "archived", label: t("titleArchived"), count: archived.length },
|
|
659
899
|
{ id: "pinned", label: t("titlePinned"), count: pinned.length },
|
|
660
|
-
{ id: "trash", label: t("titleTrash"), count: trashed.length }
|
|
900
|
+
{ id: "trash", label: t("titleTrash"), count: trashed.length },
|
|
901
|
+
{ id: "config", label: t("titleConfig"), count: 0 }
|
|
661
902
|
];
|
|
662
903
|
|
|
663
904
|
/**
|
|
@@ -767,6 +1008,7 @@ window.__ModuleLoader__.load({
|
|
|
767
1008
|
first,
|
|
768
1009
|
busy,
|
|
769
1010
|
showCwd: block.group === null,
|
|
1011
|
+
showMeta: settings.showMeta,
|
|
770
1012
|
showDeletedAt: tab === "trash",
|
|
771
1013
|
checked: selected.includes(row.sessionId),
|
|
772
1014
|
onToggle: () => toggle(row.sessionId),
|
|
@@ -779,12 +1021,178 @@ window.__ModuleLoader__.load({
|
|
|
779
1021
|
}
|
|
780
1022
|
}
|
|
781
1023
|
|
|
1024
|
+
const isConfig = tab === "config";
|
|
1025
|
+
|
|
1026
|
+
/** 配置页的一行:左边"标签 + 说明",右边控件。 */
|
|
1027
|
+
const configRow = (key, label, hint, control, firstRow) => h(
|
|
1028
|
+
"div",
|
|
1029
|
+
{ key, style: firstRow ? { ...S.configRow, borderTop: "none" } : S.configRow },
|
|
1030
|
+
h(
|
|
1031
|
+
"div",
|
|
1032
|
+
{ style: S.configMain },
|
|
1033
|
+
h("span", { style: S.configLabel }, label),
|
|
1034
|
+
hint === null ? null : h("span", { style: S.configHint }, hint)
|
|
1035
|
+
),
|
|
1036
|
+
h("div", { style: S.configControl }, control)
|
|
1037
|
+
);
|
|
1038
|
+
|
|
1039
|
+
/** 版本号优先用宿主报的(快照里带 `version`),其次用更新检查回来的当前版本。 */
|
|
1040
|
+
const version = typeof (snapshot && snapshot.version) === "string"
|
|
1041
|
+
? snapshot.version
|
|
1042
|
+
: (update.value && typeof update.value.current === "string" ? update.value.current : null);
|
|
1043
|
+
const outdated = update.state === "done" && update.value !== null && update.value.outdated === true;
|
|
1044
|
+
/** 状态行:检查中 / 失败 / 已是最新。有新版本时改用下面的升级块。 */
|
|
1045
|
+
const updateLine = update.state === "checking"
|
|
1046
|
+
? h("span", { style: S.configHint }, t("updateChecking"))
|
|
1047
|
+
: update.state === "failed"
|
|
1048
|
+
? h("span", { style: S.error }, text(t, "updateFailed", { reason: update.error === null ? "?" : update.error }))
|
|
1049
|
+
: update.value !== null && typeof update.value.error === "string"
|
|
1050
|
+
? h("span", { style: S.configHint }, text(t, "updateFailed", { reason: update.value.error }))
|
|
1051
|
+
: update.state === "done" && !outdated
|
|
1052
|
+
? h("span", { style: S.configHint }, t("updateLatest"))
|
|
1053
|
+
: null;
|
|
1054
|
+
|
|
1055
|
+
const configPage = h(
|
|
1056
|
+
"div",
|
|
1057
|
+
{ style: S.column },
|
|
1058
|
+
h(
|
|
1059
|
+
"div",
|
|
1060
|
+
{ style: S.card },
|
|
1061
|
+
configRow("config:tab", t("configTab"), t("configTabHint"), h(SegmentedControl, {
|
|
1062
|
+
id: "session-curator-default-tab",
|
|
1063
|
+
value: settings.tab,
|
|
1064
|
+
options: [
|
|
1065
|
+
{ value: "archived", label: t("titleArchived") },
|
|
1066
|
+
{ value: "pinned", label: t("titlePinned") },
|
|
1067
|
+
{ value: "trash", label: t("titleTrash") }
|
|
1068
|
+
],
|
|
1069
|
+
label: t("configTab"),
|
|
1070
|
+
onChange: (next) => void updateSettings({ tab: next })
|
|
1071
|
+
}), true),
|
|
1072
|
+
configRow("config:grouping", t("configGrouping"), t("configGroupingHint"), h(Switch, {
|
|
1073
|
+
checked: settings.grouping,
|
|
1074
|
+
label: t("configGrouping"),
|
|
1075
|
+
onChange: (next) => {
|
|
1076
|
+
updateSettings({ grouping: next });
|
|
1077
|
+
groupingStore.set(next);
|
|
1078
|
+
}
|
|
1079
|
+
})),
|
|
1080
|
+
configRow("config:sidebar-archived", t("configSidebarArchived"), t("configSidebarArchivedHint"), h(Switch, {
|
|
1081
|
+
checked: sidebarArchived !== "default",
|
|
1082
|
+
label: t("configSidebarArchived"),
|
|
1083
|
+
onChange: (next) => {
|
|
1084
|
+
writeSidebarArchived(next);
|
|
1085
|
+
setSidebarArchived(next ? "show" : "default");
|
|
1086
|
+
}
|
|
1087
|
+
})),
|
|
1088
|
+
// 官方 store 只在创建时读一次 localStorage,所以改完必须刷新页面。
|
|
1089
|
+
sidebarArchived === sidebarAtMount ? null : h(
|
|
1090
|
+
"div",
|
|
1091
|
+
{ key: "config:sidebar-refresh", style: S.configFoot },
|
|
1092
|
+
h("span", { style: S.configHint }, t("configSidebarRefreshHint")),
|
|
1093
|
+
h(Button, {
|
|
1094
|
+
size: "sm",
|
|
1095
|
+
variant: "outline",
|
|
1096
|
+
onClick: () => {
|
|
1097
|
+
try {
|
|
1098
|
+
globalThis.location.reload();
|
|
1099
|
+
} catch {
|
|
1100
|
+
// 没有 location(自检环境)时只保留提示
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}, t("refreshPage"))
|
|
1104
|
+
),
|
|
1105
|
+
configRow("config:meta", t("configMeta"), t("configMetaHint"), h(Switch, {
|
|
1106
|
+
checked: settings.showMeta,
|
|
1107
|
+
label: t("configMeta"),
|
|
1108
|
+
onChange: (next) => void updateSettings({ showMeta: next })
|
|
1109
|
+
})),
|
|
1110
|
+
configRow("config:order", t("configOrder"), t("configOrderHint"), h(SegmentedControl, {
|
|
1111
|
+
id: "session-curator-order",
|
|
1112
|
+
value: settings.order,
|
|
1113
|
+
options: [
|
|
1114
|
+
{ value: "host", label: t("orderHost") },
|
|
1115
|
+
{ value: "title", label: t("orderTitle") },
|
|
1116
|
+
{ value: "bytes", label: t("orderBytes") }
|
|
1117
|
+
],
|
|
1118
|
+
label: t("configOrder"),
|
|
1119
|
+
onChange: (next) => void updateSettings({ order: next })
|
|
1120
|
+
})),
|
|
1121
|
+
configRow("config:auto", t("configAutoUpdate"), t("configAutoUpdateHint"), h(Switch, {
|
|
1122
|
+
checked: settings.autoUpdate,
|
|
1123
|
+
label: t("configAutoUpdate"),
|
|
1124
|
+
onChange: (next) => void updateSettings({ autoUpdate: next })
|
|
1125
|
+
})),
|
|
1126
|
+
h(
|
|
1127
|
+
"div",
|
|
1128
|
+
{ key: "config:reset", style: { ...S.configFoot, justifyContent: "flex-end" } },
|
|
1129
|
+
h(Button, {
|
|
1130
|
+
size: "sm",
|
|
1131
|
+
variant: "outline",
|
|
1132
|
+
onClick: () => {
|
|
1133
|
+
updateSettings({ ...DEFAULT_SETTINGS });
|
|
1134
|
+
groupingStore.set(DEFAULT_SETTINGS.grouping);
|
|
1135
|
+
}
|
|
1136
|
+
}, t("configReset"))
|
|
1137
|
+
)
|
|
1138
|
+
),
|
|
1139
|
+
|
|
1140
|
+
h(
|
|
1141
|
+
"div",
|
|
1142
|
+
{ style: S.card },
|
|
1143
|
+
h(
|
|
1144
|
+
"div",
|
|
1145
|
+
{ style: { ...S.configRow, borderTop: "none" } },
|
|
1146
|
+
h(
|
|
1147
|
+
"div",
|
|
1148
|
+
{ style: S.configMain },
|
|
1149
|
+
h("span", { style: S.configLabel }, t("aboutTitle")),
|
|
1150
|
+
h("span", { style: S.configHint }, "dsh-session-curator")
|
|
1151
|
+
),
|
|
1152
|
+
h(
|
|
1153
|
+
"div",
|
|
1154
|
+
{ style: S.configControl },
|
|
1155
|
+
// 版本号:浅色小字。
|
|
1156
|
+
h("span", { style: S.version }, `${t("versionLabel")} ${version === null ? "—" : `v${version}`}`),
|
|
1157
|
+
h(Button, {
|
|
1158
|
+
size: "sm",
|
|
1159
|
+
variant: "outline",
|
|
1160
|
+
disabled: update.state === "checking",
|
|
1161
|
+
onClick: () => void checkUpdate(true)
|
|
1162
|
+
}, t("updateCheckNow"))
|
|
1163
|
+
)
|
|
1164
|
+
),
|
|
1165
|
+
updateLine === null ? null : h("div", { key: "config:update", style: S.configFoot }, updateLine),
|
|
1166
|
+
outdated
|
|
1167
|
+
? h(
|
|
1168
|
+
"div",
|
|
1169
|
+
{ key: "config:upgrade", style: S.updateBox },
|
|
1170
|
+
h("span", { style: S.updateTitle }, text(t, "updateAvailable", { latest: update.value.latest, current: version === null ? "?" : version })),
|
|
1171
|
+
h(
|
|
1172
|
+
"span",
|
|
1173
|
+
{ style: S.updateSteps },
|
|
1174
|
+
h("span", null, t("updateStepsTitle")),
|
|
1175
|
+
h("span", null, t("updateStepOne")),
|
|
1176
|
+
h("code", { style: S.code }, "dsh plugin --profile desktop add dsh-session-curator@latest"),
|
|
1177
|
+
h("span", null, t("updateStepTwo"))
|
|
1178
|
+
),
|
|
1179
|
+
h("a", {
|
|
1180
|
+
style: S.link,
|
|
1181
|
+
href: "https://www.npmjs.com/package/dsh-session-curator",
|
|
1182
|
+
target: "_blank",
|
|
1183
|
+
rel: "noreferrer"
|
|
1184
|
+
}, t("updateOpen"))
|
|
1185
|
+
)
|
|
1186
|
+
: null
|
|
1187
|
+
)
|
|
1188
|
+
);
|
|
1189
|
+
|
|
782
1190
|
return h(
|
|
783
1191
|
"div",
|
|
784
1192
|
{ style: S.column },
|
|
785
1193
|
h(
|
|
786
1194
|
"div",
|
|
787
|
-
{ style: S.
|
|
1195
|
+
{ style: S.header },
|
|
788
1196
|
h(
|
|
789
1197
|
"div",
|
|
790
1198
|
{ style: S.tabBar },
|
|
@@ -795,18 +1203,27 @@ window.__ModuleLoader__.load({
|
|
|
795
1203
|
onClick: () => setTab(entry.id)
|
|
796
1204
|
}, `${entry.label}${entry.count > 0 ? ` (${entry.count})` : ""}`))
|
|
797
1205
|
),
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
1206
|
+
// 分组与刷新推到右边;配置页上没有它们。
|
|
1207
|
+
isConfig ? null : h(
|
|
1208
|
+
"div",
|
|
1209
|
+
{ style: S.headerActions },
|
|
1210
|
+
h(Button, {
|
|
1211
|
+
size: "sm",
|
|
1212
|
+
variant: "outline",
|
|
1213
|
+
title: t("groupHint"),
|
|
1214
|
+
onClick: () => {
|
|
1215
|
+
const next = !grouping;
|
|
1216
|
+
groupingStore.set(next);
|
|
1217
|
+
updateSettings({ grouping: next });
|
|
1218
|
+
}
|
|
1219
|
+
}, grouping ? t("groupingOn") : t("groupingOff")),
|
|
1220
|
+
h(Button, { size: "sm", variant: "outline", disabled: loading || busy, onClick: () => void refresh() }, t("refresh"))
|
|
1221
|
+
)
|
|
805
1222
|
),
|
|
806
1223
|
|
|
807
1224
|
error ? h("div", { style: S.error }, error) : null,
|
|
808
1225
|
|
|
809
|
-
h(
|
|
1226
|
+
isConfig ? null : h(
|
|
810
1227
|
"div",
|
|
811
1228
|
{ style: S.card },
|
|
812
1229
|
loading && !snapshot
|
|
@@ -816,7 +1233,7 @@ window.__ModuleLoader__.load({
|
|
|
816
1233
|
: h("div", { style: S.list }, nodes)
|
|
817
1234
|
),
|
|
818
1235
|
|
|
819
|
-
h(
|
|
1236
|
+
isConfig ? null : h(
|
|
820
1237
|
"div",
|
|
821
1238
|
{ style: S.rowLine },
|
|
822
1239
|
h(Button, {
|
|
@@ -843,7 +1260,9 @@ window.__ModuleLoader__.load({
|
|
|
843
1260
|
busy ? h("span", { style: S.footer }, t("working")) : null
|
|
844
1261
|
),
|
|
845
1262
|
|
|
846
|
-
|
|
1263
|
+
isConfig ? configPage : null,
|
|
1264
|
+
|
|
1265
|
+
isConfig ? null : h(
|
|
847
1266
|
"div",
|
|
848
1267
|
{ style: S.footer },
|
|
849
1268
|
h("span", null, text(t, "total", { n: rows.length, size: formatBytes(totalBytes) })),
|