@chaoset/session-archive 0.1.2 → 0.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## [0.1.3](https://github.com/winliyou/dsh-plugins) (2026-08-18)
4
+
5
+ ### Bug Fixes
6
+
7
+ * confirm-delete button hover: red text on red background was unreadable; changed to red background with white text
8
+
9
+ ## [0.1.2](https://github.com/winliyou/dsh-plugins) (2026-08-18)
10
+
11
+ ### Bug Fixes
12
+
13
+ * keep ghost archived id after deleting archived sessions so in-memory sessions don't reappear in the sidebar
14
+
15
+ ## [0.1.1](https://github.com/winliyou/dsh-plugins) (2026-08-17)
16
+
17
+ ### Bug Fixes
18
+
19
+ * archived sessions no longer mislabeled as running; restored selectable deletion
20
+
21
+ ## [0.1.0](https://github.com/winliyou/dsh-plugins) (2026-08-17)
22
+
23
+ ### Features
24
+
25
+ * initial session archive plugin: archived session list, read-only detail, batch restore and permanent delete
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-plugins contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -53,11 +53,11 @@ dsh plugin --profile web add @chaoset/session-archive
53
53
  | `list()` | — | `{ items: ArchiveRow[] }` |
54
54
  | `detail(sessionId)` | 会话 id | `{ sessionId, header, title, messageCount, messages, live }` |
55
55
  | `delete(sessionIds[])` | id 数组 | `{ deleted, failed, removedFromArchive }` |
56
+ | `unarchive(sessionIds[])` | id 数组 | `{ restored, removedFromArchive }` |
56
57
 
57
58
  > `delete` 的 `removedFromArchive` 恒为 0:删除保留归档 ghost id(见上),
58
59
  > 侧边栏不会重新显示已删除的会话;`unarchive` 的 `removedFromArchive` 为
59
60
  > 实际从归档集合移除的 id 数。
60
- | `unarchive(sessionIds[])` | id 数组 | `{ restored, removedFromArchive }` |
61
61
 
62
62
  `ArchiveRow`:`{ sessionId, title, cwd, createdAt, updatedAt, size, live }`。
63
63
 
package/client/client.cjs CHANGED
@@ -3,15 +3,52 @@ window.__ModuleLoader__.load({
3
3
  factory: (require) => {
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
- let react = require("react");
6
+ "use strict";
7
+ var __create = Object.create;
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __getProtoOf = Object.getPrototypeOf;
12
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
7
34
 
8
- // ── 样式(注入 style 标签,复用 DSH 设计变量)────────────────────────
35
+ // client/index.tsx
36
+ var index_exports = {};
37
+ __export(index_exports, {
38
+ apply: () => apply,
39
+ inject: () => inject
40
+ });
41
+ module.exports = __toCommonJS(index_exports);
42
+ var React = __toESM(require("react"), 1);
9
43
  var css = [
10
44
  ".sa_badge{width:100%;height:49px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:0 8px 0 6px;font-family:inherit;font-size:14px;display:inline-flex;overflow:hidden}",
11
45
  ".sa_badge:hover{background:var(--dsw-alias-interactive-bg-hover)}",
12
- ".sa_badgeIcon{flex:none;font-size:15px;line-height:20px}",
46
+ ".sa_badgeIcon{flex:none;display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;color:var(--dsw-alias-label-primary)}",
47
+ ".sa_badgeIcon svg{width:18px;height:18px;display:block}",
13
48
  ".sa_badgeLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}",
14
49
  ".sa_badgeCount{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;flex:none;margin-left:auto;font-size:12px;line-height:16px}",
50
+ ".sa_badge--collapsed{width:auto;justify-content:center;padding:0}",
51
+ ".sa_badge--collapsed .sa_badgeLabel,.sa_badge--collapsed .sa_badgeCount{display:none}",
15
52
  ".sa_panel{z-index:30;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:440px;max-width:calc(100vw - 24px);max-height:62vh;box-shadow:var(--dsw-shadow-lv2);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:12px;flex-direction:column;display:flex;position:fixed;bottom:128px;left:12px;overflow:hidden}",
16
53
  ".sa_header{box-sizing:border-box;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-base);flex:none;justify-content:space-between;align-items:center;min-height:44px;padding:8px 12px;display:flex}",
17
54
  ".sa_title{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}",
@@ -33,6 +70,7 @@ window.__ModuleLoader__.load({
33
70
  ".sa_actionDanger:hover:not(:disabled){background:var(--dsw-alias-state-error-primary)}",
34
71
  ".sa_actionDanger:disabled{opacity:.4}",
35
72
  ".sa_confirm{color:var(--dsw-alias-state-error-primary);border:1px solid var(--dsw-alias-state-error-primary);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 10%,transparent)}",
73
+ ".sa_confirm:hover:not(:disabled){background:var(--dsw-alias-state-error-primary);color:#fff}",
36
74
  ".sa_body{flex:1;min-height:0;padding:4px 12px 12px;overflow-y:auto}",
37
75
  ".sa_empty{color:var(--dsw-alias-label-tertiary);margin:24px 0;text-align:center;font-size:12px;line-height:18px}",
38
76
  ".sa_error{color:var(--dsw-alias-state-error-primary);margin:8px 0;font-size:12px;line-height:18px}",
@@ -54,49 +92,48 @@ window.__ModuleLoader__.load({
54
92
  ".sa_busy{opacity:.55;pointer-events:none}"
55
93
  ].join("");
56
94
  var tagId = "@chaoset/session-archive/client.css";
57
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"" + tagId + "\"]") === null) {
58
- var tag = document.createElement("style");
95
+ if (typeof document !== "undefined" && document.querySelector('style[data-plugin-css="' + tagId + '"]') === null) {
96
+ tag = document.createElement("style");
59
97
  tag.dataset.plugin = "@chaoset/session-archive";
60
98
  tag.dataset.pluginCss = tagId;
61
99
  tag.textContent = css;
62
100
  document.head.appendChild(tag);
63
101
  }
64
-
65
- // ── 字典 ─────────────────────────────────────────────────────────────
66
- const NS = "sidebar.sessionArchive";
67
- const zh = {
68
- badge: "归档",
69
- panelTitle: "归档会话",
70
- refresh: "刷新",
71
- close: "关闭",
72
- empty: "暂无归档会话。在会话列表的更多菜单中归档会话后会出现在这里。",
73
- loadFailed: "加载归档列表失败",
74
- selectAll: "全选",
75
- selected: "已选 {n} 项",
76
- restore: "恢复所选",
77
- restoreDone: "已恢复 {n} 个会话",
78
- restoreFailed: "恢复失败",
79
- delete: "删除所选",
80
- deleteConfirm: "再次点击确认删除",
81
- deleteDone: "已删除 {n} 个会话",
82
- deleteFailed: "部分删除失败:{n} ",
83
- confirmAll: "确认删除全部 {n} 个?",
84
- noSelection: "请先勾选会话",
85
- view: "查看",
86
- collapse: "收起",
87
- live: "运行中",
88
- detailLoadFailed: "读取会话内容失败",
89
- noneTitle: "(无标题)",
90
- user: "用户",
91
- assistant: "助手",
92
- messages: "共 {n} 条消息",
93
- noMessages: "(无文本消息)",
102
+ var tag;
103
+ var NS = "sidebar.sessionArchive";
104
+ var zh = {
105
+ badge: "\u5F52\u6863",
106
+ panelTitle: "\u5F52\u6863\u4F1A\u8BDD",
107
+ refresh: "\u5237\u65B0",
108
+ close: "\u5173\u95ED",
109
+ empty: "\u6682\u65E0\u5F52\u6863\u4F1A\u8BDD\u3002\u5728\u4F1A\u8BDD\u5217\u8868\u7684\u66F4\u591A\u83DC\u5355\u4E2D\u5F52\u6863\u4F1A\u8BDD\u540E\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u3002",
110
+ loadFailed: "\u52A0\u8F7D\u5F52\u6863\u5217\u8868\u5931\u8D25",
111
+ selectAll: "\u5168\u9009",
112
+ selected: "\u5DF2\u9009 {n} \u9879",
113
+ restore: "\u6062\u590D\u6240\u9009",
114
+ restoreDone: "\u5DF2\u6062\u590D {n} \u4E2A\u5F52\u6863\u4F1A\u8BDD",
115
+ restoreFailed: "\u6062\u590D\u5931\u8D25",
116
+ delete: "\u5220\u9664\u6240\u9009",
117
+ deleteConfirm: "\u518D\u6B21\u70B9\u51FB\u786E\u8BA4\u5220\u9664",
118
+ deleteDone: "\u5DF2\u5220\u9664 {n} \u4E2A\u5F52\u6863\u4F1A\u8BDD",
119
+ deleteFailed: "\u90E8\u5206\u5220\u9664\u5931\u8D25\uFF1A{n} \u4E2A",
120
+ confirmAll: "\u786E\u8BA4\u5220\u9664\u5168\u90E8 {n} \u4E2A\uFF1F",
121
+ noSelection: "\u8BF7\u5148\u52FE\u9009\u4F1A\u8BDD",
122
+ view: "\u67E5\u770B",
123
+ collapse: "\u6536\u8D77",
124
+ live: "\u8FD0\u884C\u4E2D",
125
+ detailLoadFailed: "\u8BFB\u53D6\u4F1A\u8BDD\u5185\u5BB9\u5931\u8D25",
126
+ noneTitle: "\uFF08\u65E0\u6807\u9898\uFF09",
127
+ user: "\u7528\u6237",
128
+ assistant: "\u52A9\u624B",
129
+ messages: "\u5171 {n} \u6761\u6D88\u606F",
130
+ noMessages: "\uFF08\u65E0\u6587\u672C\u6D88\u606F\uFF09",
94
131
  sizeBytes: "{n} B",
95
132
  sizeKB: "{n} KB",
96
133
  sizeMB: "{n} MB",
97
- runningHint: "运行中的会话不能删除,请先停止"
134
+ runningHint: "\u8FD0\u884C\u4E2D\u7684\u4F1A\u8BDD\u4E0D\u80FD\u5220\u9664\uFF0C\u8BF7\u5148\u505C\u6B62"
98
135
  };
99
- const en = {
136
+ var en = {
100
137
  badge: "Archive",
101
138
  panelTitle: "Archived sessions",
102
139
  refresh: "Refresh",
@@ -106,11 +143,11 @@ window.__ModuleLoader__.load({
106
143
  selectAll: "Select all",
107
144
  selected: "{n} selected",
108
145
  restore: "Restore",
109
- restoreDone: "Restored {n} sessions",
146
+ restoreDone: "Restored {n} archived sessions",
110
147
  restoreFailed: "Restore failed",
111
148
  delete: "Delete",
112
149
  deleteConfirm: "Click again to confirm delete",
113
- deleteDone: "Deleted {n} sessions",
150
+ deleteDone: "Deleted {n} archived sessions",
114
151
  deleteFailed: "Some deletions failed: {n}",
115
152
  confirmAll: "Delete all {n}?",
116
153
  noSelection: "Select sessions first",
@@ -128,38 +165,54 @@ window.__ModuleLoader__.load({
128
165
  sizeMB: "{n} MB",
129
166
  runningHint: "Running sessions cannot be deleted; stop them first"
130
167
  };
131
-
132
- // ── 工具函数 ─────────────────────────────────────────────────────────
133
168
  function formatBytes(bytes, t) {
134
169
  if (bytes < 1024) return t("sizeBytes").replace("{n}", String(bytes));
135
170
  if (bytes < 1024 * 1024) return t("sizeKB").replace("{n}", (bytes / 1024).toFixed(1));
136
171
  return t("sizeMB").replace("{n}", (bytes / (1024 * 1024)).toFixed(1));
137
172
  }
138
173
  function formatTime(ms) {
139
- try { return new Date(ms).toLocaleString(); } catch { return String(ms); }
174
+ try {
175
+ return new Date(ms).toLocaleString();
176
+ } catch {
177
+ return String(ms);
178
+ }
140
179
  }
141
180
  function shortId(id) {
142
- return id.length > 12 ? id.slice(0, 12) + "" : id;
181
+ return id.length > 12 ? id.slice(0, 12) + "\u2026" : id;
143
182
  }
144
-
145
- // ── 归档面板 ─────────────────────────────────────────────────────────
146
183
  function ArchivePanel(props) {
147
184
  const t = props.t;
148
185
  const call = props.call;
149
- const rootRef = react.useRef(null);
150
- const [open, setOpen] = react.useState(false);
151
- const [items, setItems] = react.useState([]);
152
- const [loading, setLoading] = react.useState(false);
153
- const [error, setError] = react.useState(null);
154
- const [selected, setSelected] = react.useState(new Set());
155
- const [expanded, setExpanded] = react.useState(null);
156
- const [details, setDetails] = react.useState(new Map());
157
- const [detailLoading, setDetailLoading] = react.useState(new Set());
158
- const [busy, setBusy] = react.useState(false);
159
- const [confirmingDelete, setConfirmingDelete] = react.useState(false);
160
- const [notice, setNotice] = react.useState(null);
161
-
162
- const load = react.useCallback(async () => {
186
+ const wideExplicit = typeof props.wide === "boolean" ? props.wide : void 0;
187
+ const [narrow, setNarrow] = React.useState(false);
188
+ const collapsed = wideExplicit === void 0 ? narrow : !wideExplicit;
189
+ const badgeRef = React.useRef(null);
190
+ React.useEffect(() => {
191
+ const el = badgeRef.current?.parentElement;
192
+ if (el === void 0 || el === null || typeof globalThis.ResizeObserver === "undefined") return;
193
+ const ro = new globalThis.ResizeObserver((entries) => {
194
+ for (const entry of entries) {
195
+ const w = entry.contentRect?.width ?? el.clientWidth;
196
+ setNarrow(w > 0 && w < 80);
197
+ }
198
+ });
199
+ ro.observe(el);
200
+ return () => ro.disconnect();
201
+ }, []);
202
+ const iconOnly = collapsed;
203
+ const rootRef = React.useRef(null);
204
+ const [open, setOpen] = React.useState(false);
205
+ const [items, setItems] = React.useState([]);
206
+ const [loading, setLoading] = React.useState(false);
207
+ const [error, setError] = React.useState(null);
208
+ const [selected, setSelected] = React.useState(/* @__PURE__ */ new Set());
209
+ const [expanded, setExpanded] = React.useState(null);
210
+ const [details, setDetails] = React.useState(/* @__PURE__ */ new Map());
211
+ const [detailLoading, setDetailLoading] = React.useState(/* @__PURE__ */ new Set());
212
+ const [busy, setBusy] = React.useState(false);
213
+ const [confirmingDelete, setConfirmingDelete] = React.useState(false);
214
+ const [notice, setNotice] = React.useState(null);
215
+ const load = React.useCallback(async () => {
163
216
  setLoading(true);
164
217
  setError(null);
165
218
  try {
@@ -171,12 +224,38 @@ window.__ModuleLoader__.load({
171
224
  setLoading(false);
172
225
  }
173
226
  }, [call, t]);
174
-
175
- react.useEffect(() => {
176
- if (open) load();
227
+ React.useEffect(() => {
228
+ if (open) {
229
+ setNotice(null);
230
+ load();
231
+ }
177
232
  }, [open, load]);
178
-
179
- react.useEffect(() => {
233
+ const refreshSilently = React.useCallback(async () => {
234
+ try {
235
+ const result = await call("list");
236
+ setItems(Array.isArray(result.items) ? result.items : []);
237
+ } catch {
238
+ }
239
+ }, [call]);
240
+ React.useEffect(() => {
241
+ if (open) return;
242
+ refreshSilently();
243
+ const id = globalThis.setInterval(refreshSilently, 5e3);
244
+ const onVis = () => {
245
+ if (document.visibilityState === "visible") refreshSilently();
246
+ };
247
+ document.addEventListener("visibilitychange", onVis);
248
+ return () => {
249
+ clearInterval(id);
250
+ document.removeEventListener("visibilitychange", onVis);
251
+ };
252
+ }, [open, refreshSilently]);
253
+ React.useEffect(() => {
254
+ if (notice === null) return;
255
+ const id = globalThis.setTimeout(() => setNotice(null), 4e3);
256
+ return () => clearTimeout(id);
257
+ }, [notice]);
258
+ React.useEffect(() => {
180
259
  if (!open) return;
181
260
  const onPointerDown = (event) => {
182
261
  if (rootRef.current !== null && !rootRef.current.contains(event.target)) {
@@ -186,25 +265,23 @@ window.__ModuleLoader__.load({
186
265
  document.addEventListener("pointerdown", onPointerDown);
187
266
  return () => document.removeEventListener("pointerdown", onPointerDown);
188
267
  }, [open]);
189
-
190
268
  const selectable = items.filter((item) => !item.live);
191
269
  const allSelected = selectable.length > 0 && selectable.every((item) => selected.has(item.sessionId));
192
-
193
270
  const toggleAll = (checked) => {
194
271
  if (checked) {
195
272
  setSelected(new Set(selectable.map((item) => item.sessionId)));
196
273
  } else {
197
- setSelected(new Set());
274
+ setSelected(/* @__PURE__ */ new Set());
198
275
  }
199
276
  };
200
277
  const toggleOne = (sessionId, checked) => {
201
278
  setSelected((current) => {
202
279
  const next = new Set(current);
203
- if (checked) next.add(sessionId); else next.delete(sessionId);
280
+ if (checked) next.add(sessionId);
281
+ else next.delete(sessionId);
204
282
  return next;
205
283
  });
206
284
  };
207
-
208
285
  const toggleDetail = (item) => {
209
286
  if (expanded === item.sessionId) {
210
287
  setExpanded(null);
@@ -228,7 +305,6 @@ window.__ModuleLoader__.load({
228
305
  });
229
306
  }
230
307
  };
231
-
232
308
  const runBatch = async (action, doneKey, failKey) => {
233
309
  const ids = [...selected];
234
310
  if (ids.length === 0) {
@@ -250,7 +326,7 @@ window.__ModuleLoader__.load({
250
326
  const done = new Set(doneIds);
251
327
  setItems((current) => current.filter((item) => !done.has(item.sessionId)));
252
328
  }
253
- setSelected(new Set());
329
+ setSelected(/* @__PURE__ */ new Set());
254
330
  setConfirmingDelete(false);
255
331
  await load();
256
332
  } catch (actionError) {
@@ -263,49 +339,60 @@ window.__ModuleLoader__.load({
263
339
  const deleteSelected = () => {
264
340
  if (!confirmingDelete) {
265
341
  setConfirmingDelete(true);
266
- window.setTimeout(() => setConfirmingDelete(false), 4000);
342
+ window.setTimeout(() => setConfirmingDelete(false), 4e3);
267
343
  return;
268
344
  }
269
345
  runBatch("delete", "deleteDone", "deleteFailed");
270
346
  };
271
-
272
347
  const hasSelection = selected.size > 0;
273
-
274
- return react.createElement(
348
+ return React.createElement(
275
349
  "div",
276
350
  { className: "sa_root", ref: rootRef },
277
- react.createElement(
351
+ React.createElement(
278
352
  "button",
279
353
  {
280
- className: "sa_badge",
354
+ ref: badgeRef,
355
+ className: "sa_badge" + (iconOnly ? " sa_badge--collapsed" : ""),
281
356
  type: "button",
282
357
  onClick: () => setOpen(!open),
283
358
  "aria-expanded": open,
284
359
  title: t("badge")
285
360
  },
286
- react.createElement("span", { className: "sa_badgeIcon", "aria-hidden": true }, "🗂"),
287
- react.createElement("span", { className: "sa_badgeLabel" }, t("badge")),
288
- react.createElement("span", { className: "sa_badgeCount" }, String(items.length))
361
+ React.createElement(
362
+ "span",
363
+ { className: "sa_badgeIcon", "aria-hidden": true },
364
+ React.createElement(
365
+ "svg",
366
+ { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" },
367
+ React.createElement("rect", { x: 3, y: 4, width: 18, height: 4, rx: 1 }),
368
+ React.createElement("path", { d: "M5 8v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8" }),
369
+ React.createElement("path", { d: "M10 12h4" })
370
+ )
371
+ ),
372
+ iconOnly ? null : React.createElement("span", { className: "sa_badgeLabel" }, t("badge")),
373
+ iconOnly ? null : React.createElement("span", { className: "sa_badgeCount" }, String(items.length))
289
374
  ),
290
- open ? react.createElement(
375
+ open ? React.createElement(
291
376
  "div",
292
377
  { className: "sa_panel" },
293
- react.createElement(
378
+ React.createElement(
294
379
  "div",
295
380
  { className: "sa_header" },
296
- react.createElement("span", { className: "sa_title" }, t("panelTitle")),
297
- react.createElement(
381
+ React.createElement("span", { className: "sa_title" }, t("panelTitle")),
382
+ React.createElement(
298
383
  "span",
299
384
  { style: { display: "inline-flex", gap: "6px", alignItems: "center" } },
300
- react.createElement("button", { className: "sa_refresh", type: "button", title: t("refresh"), disabled: busy || loading, onClick: load }, t("refresh")),
301
- react.createElement("button", { className: "sa_iconBtn", type: "button", title: t("close"), disabled: busy, onClick: () => setOpen(false) }, "")
385
+ React.createElement("button", { className: "sa_refresh", type: "button", title: t("refresh"), disabled: busy || loading, onClick: load }, t("refresh")),
386
+ React.createElement("button", { className: "sa_iconBtn", type: "button", title: t("close"), disabled: busy, onClick: () => setOpen(false) }, "\u2715")
302
387
  )
303
388
  ),
304
- react.createElement(
389
+ React.createElement(
305
390
  "div",
306
391
  { className: "sa_toolbar" },
307
- react.createElement("label", { className: "sa_toolLabel" },
308
- react.createElement("input", {
392
+ React.createElement(
393
+ "label",
394
+ { className: "sa_toolLabel" },
395
+ React.createElement("input", {
309
396
  className: "sa_check",
310
397
  type: "checkbox",
311
398
  checked: allSelected,
@@ -314,41 +401,41 @@ window.__ModuleLoader__.load({
314
401
  }),
315
402
  t("selectAll")
316
403
  ),
317
- react.createElement("span", { className: "sa_count" }, t("selected").replace("{n}", String(selected.size))),
318
- react.createElement("button", {
404
+ React.createElement("span", { className: "sa_count" }, t("selected").replace("{n}", String(selected.size))),
405
+ React.createElement("button", {
319
406
  className: "sa_action",
320
407
  type: "button",
321
408
  disabled: busy || !hasSelection,
322
409
  onClick: restoreSelected
323
410
  }, t("restore")),
324
- react.createElement("button", {
411
+ React.createElement("button", {
325
412
  className: "sa_action " + (confirmingDelete ? "sa_actionDanger sa_confirm" : "sa_actionDanger"),
326
413
  type: "button",
327
414
  disabled: busy || !hasSelection,
328
415
  onClick: deleteSelected
329
416
  }, confirmingDelete ? t("deleteConfirm") : t("delete"))
330
417
  ),
331
- react.createElement(
418
+ React.createElement(
332
419
  "div",
333
420
  { className: "sa_body" },
334
- notice !== null ? react.createElement("p", { className: "sa_error" + (notice.kind === "ok" ? "" : ""), role: "status" }, notice.text) : null,
335
- error !== null ? react.createElement("p", { className: "sa_error", role: "alert" }, error) : null,
336
- loading && items.length === 0 ? react.createElement("p", { className: "sa_empty" }, "") : null,
337
- !loading && items.length === 0 && error === null ? react.createElement("p", { className: "sa_empty" }, t("empty")) : null,
338
- items.length > 0 ? react.createElement(
421
+ notice !== null ? React.createElement("p", { className: "sa_error" + (notice.kind === "ok" ? "" : ""), role: "status" }, notice.text) : null,
422
+ error !== null ? React.createElement("p", { className: "sa_error", role: "alert" }, error) : null,
423
+ loading && items.length === 0 ? React.createElement("p", { className: "sa_empty" }, "\u2026") : null,
424
+ !loading && items.length === 0 && error === null ? React.createElement("p", { className: "sa_empty" }, t("empty")) : null,
425
+ items.length > 0 ? React.createElement(
339
426
  "ul",
340
427
  { className: "sa_rows" },
341
428
  items.map((item) => {
342
429
  const isExpanded = expanded === item.sessionId;
343
430
  const detail = details.get(item.sessionId);
344
431
  const detailPending = detailLoading.has(item.sessionId);
345
- return react.createElement(
432
+ return React.createElement(
346
433
  "li",
347
434
  { className: "sa_row" + (busy ? " sa_busy" : ""), key: item.sessionId },
348
- react.createElement(
435
+ React.createElement(
349
436
  "div",
350
437
  { className: "sa_rowHead" },
351
- react.createElement("input", {
438
+ React.createElement("input", {
352
439
  className: "sa_check",
353
440
  type: "checkbox",
354
441
  checked: selected.has(item.sessionId),
@@ -356,31 +443,33 @@ window.__ModuleLoader__.load({
356
443
  title: item.live ? t("runningHint") : void 0,
357
444
  onChange: (e) => toggleOne(item.sessionId, e.target.checked)
358
445
  }),
359
- react.createElement(
446
+ React.createElement(
360
447
  "span",
361
448
  { className: "sa_rowTitle", onClick: () => toggleDetail(item), title: t("view") },
362
449
  item.title !== null && item.title !== void 0 && item.title !== "" ? item.title : t("noneTitle")
363
450
  ),
364
- item.live ? react.createElement("span", { className: "sa_live" }, t("live")) : null
451
+ item.live ? React.createElement("span", { className: "sa_live" }, t("live")) : null
365
452
  ),
366
- react.createElement("div", { className: "sa_rowMeta" },
367
- react.createElement("span", null, formatTime(item.updatedAt)),
368
- item.cwd !== null && item.cwd !== void 0 ? react.createElement("span", null, " · ", react.createElement("code", null, item.cwd)) : null,
369
- react.createElement("span", null, " · ", formatBytes(item.size, t))
453
+ React.createElement(
454
+ "div",
455
+ { className: "sa_rowMeta" },
456
+ React.createElement("span", null, formatTime(item.updatedAt)),
457
+ item.cwd !== null && item.cwd !== void 0 ? React.createElement("span", null, " \xB7 ", React.createElement("code", null, item.cwd)) : null,
458
+ React.createElement("span", null, " \xB7 ", formatBytes(item.size, t))
370
459
  ),
371
- react.createElement(
460
+ React.createElement(
372
461
  "div",
373
462
  { className: "sa_rowFoot" },
374
- react.createElement("span", { className: "sa_rowMeta" },
375
- react.createElement("code", null, shortId(item.sessionId)),
376
- detail !== void 0 && !detail.error && detail.messageCount !== void 0
377
- ? " · " + t("messages").replace("{n}", String(detail.messageCount))
378
- : null
463
+ React.createElement(
464
+ "span",
465
+ { className: "sa_rowMeta" },
466
+ React.createElement("code", null, shortId(item.sessionId)),
467
+ detail !== void 0 && !detail.error && detail.messageCount !== void 0 ? " \xB7 " + t("messages").replace("{n}", String(detail.messageCount)) : null
379
468
  ),
380
- react.createElement(
469
+ React.createElement(
381
470
  "div",
382
471
  { className: "sa_rowActions" },
383
- react.createElement("button", {
472
+ React.createElement("button", {
384
473
  className: "sa_action",
385
474
  type: "button",
386
475
  disabled: busy || detailPending,
@@ -388,19 +477,16 @@ window.__ModuleLoader__.load({
388
477
  }, isExpanded ? t("collapse") : t("view"))
389
478
  )
390
479
  ),
391
- isExpanded ? react.createElement(
480
+ isExpanded ? React.createElement(
392
481
  "div",
393
482
  { className: "sa_detail" },
394
- detailPending ? react.createElement("p", { className: "sa_empty" }, "") : null,
395
- detail === void 0 ? null :
396
- detail.error !== void 0 ? react.createElement("p", { className: "sa_error" }, detail.error) :
397
- detail.messages.length === 0 ? react.createElement("p", { className: "sa_empty" }, t("noMessages")) :
398
- detail.messages.map((message, index) => react.createElement(
399
- "div",
400
- { className: "sa_msg", key: index },
401
- react.createElement("span", { className: "sa_msgRole" }, message.role === "user" ? t("user") : t("assistant") + " · " + formatTime(message.time)),
402
- react.createElement("span", { className: "sa_msgText" + (message.role === "user" ? " sa_msgTextUser" : "") }, message.text)
403
- ))
483
+ detailPending ? React.createElement("p", { className: "sa_empty" }, "\u2026") : null,
484
+ detail === void 0 ? null : detail.error !== void 0 ? React.createElement("p", { className: "sa_error" }, detail.error) : detail.messages.length === 0 ? React.createElement("p", { className: "sa_empty" }, t("noMessages")) : detail.messages.map((message, index) => React.createElement(
485
+ "div",
486
+ { className: "sa_msg", key: index },
487
+ React.createElement("span", { className: "sa_msgRole" }, message.role === "user" ? t("user") : t("assistant") + " \xB7 " + formatTime(message.time)),
488
+ React.createElement("span", { className: "sa_msgText" + (message.role === "user" ? " sa_msgTextUser" : "") }, message.text)
489
+ ))
404
490
  ) : null
405
491
  );
406
492
  })
@@ -409,13 +495,9 @@ window.__ModuleLoader__.load({
409
495
  ) : null
410
496
  );
411
497
  }
412
-
413
- // ── 插件 apply ───────────────────────────────────────────────────────
414
- // DSH 客户端的 remote.<ns> 服务不会自动生成:必须由客户端代码用
415
- // ctx.remote.$mount(contribution) 显式挂载(官方 dsh-api-remotes 即如此)。
416
- const inject = ["slots", "locale", "remote"];
417
- const passthroughSchema = { parse: (value) => value };
418
- const REMOTE_CONTRIBUTION = {
498
+ var inject = ["slots", "locale", "remote"];
499
+ var passthroughSchema = { parse: (value) => value };
500
+ var REMOTE_CONTRIBUTION = {
419
501
  package: "@chaoset/session-archive",
420
502
  descriptors: [
421
503
  { id: "@chaoset/session-archive#sessionArchive/list", service: "sessionArchive", namespace: "sessionArchive", method: "list", invocation: { kind: "direct" }, parameters: [], result: { mode: "strict", typeSymbol: "sessionArchive/list:result", schema: passthroughSchema } },
@@ -443,8 +525,6 @@ window.__ModuleLoader__.load({
443
525
  }, ArchivePanel));
444
526
  }
445
527
 
446
- exports.apply = apply;
447
- exports.inject = inject;
448
528
  return module.exports;
449
529
  }
450
530
  });