@flowingspring/dsh-workspace-memory 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client/index.js +106 -51
- package/lib/client/index.js.map +1 -1
- package/lib/client.js +138 -60
- package/lib/client.js.map +1 -1
- package/lib/core.js +57 -1
- package/lib/core.js.map +1 -1
- package/lib/index.js +95 -3
- package/lib/index.js.map +1 -1
- package/lib/types/client/index.d.ts +0 -9
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/core.d.ts +9 -0
- package/lib/types/core.d.ts.map +1 -1
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -13,7 +13,7 @@ window.__ModuleLoader__.load({
|
|
|
13
13
|
refresh: "刷新",
|
|
14
14
|
global: "全局记忆",
|
|
15
15
|
workspace: "项目记忆",
|
|
16
|
-
|
|
16
|
+
scope: "选择记忆范围",
|
|
17
17
|
emptyScopes: "还没有已记录的项目记忆",
|
|
18
18
|
summary: "摘要记忆",
|
|
19
19
|
entries: "结构化记忆",
|
|
@@ -24,6 +24,11 @@ window.__ModuleLoader__.load({
|
|
|
24
24
|
loading: "正在读取…",
|
|
25
25
|
failed: "读取记忆失败",
|
|
26
26
|
retry: "重试",
|
|
27
|
+
activeView: "记忆",
|
|
28
|
+
archiveView: "回收区",
|
|
29
|
+
archiveEmpty: "回收区为空",
|
|
30
|
+
purge: "永久删除",
|
|
31
|
+
purgeConfirm: "确定永久删除这个项目的全部记忆吗?此操作无法撤销。",
|
|
27
32
|
pending: "待整理",
|
|
28
33
|
checkpoints: "整理次数",
|
|
29
34
|
updated: "最近更新",
|
|
@@ -36,7 +41,7 @@ window.__ModuleLoader__.load({
|
|
|
36
41
|
refresh: "Refresh",
|
|
37
42
|
global: "Global memory",
|
|
38
43
|
workspace: "Project memory",
|
|
39
|
-
|
|
44
|
+
scope: "Memory scope",
|
|
40
45
|
emptyScopes: "No project memories recorded yet",
|
|
41
46
|
summary: "Summary memory",
|
|
42
47
|
entries: "Structured memory",
|
|
@@ -47,21 +52,20 @@ window.__ModuleLoader__.load({
|
|
|
47
52
|
loading: "Loading…",
|
|
48
53
|
failed: "Unable to read memory",
|
|
49
54
|
retry: "Retry",
|
|
55
|
+
activeView: "Memory",
|
|
56
|
+
archiveView: "Recycle bin",
|
|
57
|
+
archiveEmpty: "Recycle bin is empty",
|
|
58
|
+
purge: "Delete permanently",
|
|
59
|
+
purgeConfirm: "Permanently delete all memory for this project? This cannot be undone.",
|
|
50
60
|
pending: "Pending",
|
|
51
61
|
checkpoints: "Checkpoints",
|
|
52
62
|
updated: "Last updated",
|
|
53
63
|
all: "All",
|
|
54
64
|
count: ""
|
|
55
65
|
};
|
|
56
|
-
function useSnapshot(source) {
|
|
57
|
-
return (0, react.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
|
|
58
|
-
}
|
|
59
66
|
function workspaceName(cwd, fallback) {
|
|
60
67
|
return cwd.replaceAll("\\", "/").replace(/\/+$/u, "").split("/").filter(Boolean).at(-1) ?? fallback;
|
|
61
68
|
}
|
|
62
|
-
function normalizeCwd(cwd) {
|
|
63
|
-
return cwd.replaceAll("\\", "/").replace(/\/+$/u, "").toLocaleLowerCase();
|
|
64
|
-
}
|
|
65
69
|
function formatDate(value) {
|
|
66
70
|
if (value === void 0 || value === "") return "";
|
|
67
71
|
const date = new Date(value);
|
|
@@ -88,12 +92,14 @@ window.__ModuleLoader__.load({
|
|
|
88
92
|
strokeLinejoin: "round"
|
|
89
93
|
}));
|
|
90
94
|
}
|
|
91
|
-
function MemorySection({ t
|
|
92
|
-
const sessionList = useSnapshot(sessions.list);
|
|
93
|
-
const currentCwd = sessionList.current === void 0 ? "" : sessionList.byId[sessionList.current]?.cwd ?? "";
|
|
95
|
+
function MemorySection({ t }) {
|
|
94
96
|
const [scopes, setScopes] = (0, react.useState)([]);
|
|
95
97
|
const [selectedKey, setSelectedKey] = (0, react.useState)("global");
|
|
96
98
|
const [payload, setPayload] = (0, react.useState)();
|
|
99
|
+
const [archivedScopes, setArchivedScopes] = (0, react.useState)([]);
|
|
100
|
+
const [archivedSelectedKey, setArchivedSelectedKey] = (0, react.useState)("");
|
|
101
|
+
const [archivedPayload, setArchivedPayload] = (0, react.useState)();
|
|
102
|
+
const [view, setView] = (0, react.useState)("active");
|
|
97
103
|
const [query, setQuery] = (0, react.useState)("");
|
|
98
104
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
99
105
|
const [error, setError] = (0, react.useState)(false);
|
|
@@ -108,13 +114,24 @@ window.__ModuleLoader__.load({
|
|
|
108
114
|
if (!response.ok) throw new Error(`scope read failed (${response.status})`);
|
|
109
115
|
return await response.json();
|
|
110
116
|
};
|
|
117
|
+
const loadArchivedScopes = async () => {
|
|
118
|
+
const response = await fetch("/workspace-memory/api/v1/archived-scopes");
|
|
119
|
+
if (!response.ok) throw new Error(`archived scope list failed (${response.status})`);
|
|
120
|
+
const result = await response.json();
|
|
121
|
+
return Array.isArray(result.scopes) ? result.scopes : [];
|
|
122
|
+
};
|
|
123
|
+
const loadArchivedScope = async (key) => {
|
|
124
|
+
const response = await fetch(`/workspace-memory/api/v1/archived-scope?key=${encodeURIComponent(key)}`);
|
|
125
|
+
if (!response.ok) throw new Error(`archived scope read failed (${response.status})`);
|
|
126
|
+
return await response.json();
|
|
127
|
+
};
|
|
111
128
|
const reload = async (cwd) => {
|
|
112
129
|
setLoading(true);
|
|
113
130
|
setError(false);
|
|
114
131
|
try {
|
|
115
132
|
const nextScopes = await loadScopes();
|
|
116
133
|
setScopes(nextScopes);
|
|
117
|
-
const next = await loadScope(cwd ?? payload?.scope.cwd ??
|
|
134
|
+
const next = await loadScope(cwd ?? payload?.scope.cwd ?? "");
|
|
118
135
|
setPayload(next);
|
|
119
136
|
setSelectedKey(next.scope.key);
|
|
120
137
|
} catch {
|
|
@@ -123,52 +140,76 @@ window.__ModuleLoader__.load({
|
|
|
123
140
|
setLoading(false);
|
|
124
141
|
}
|
|
125
142
|
};
|
|
143
|
+
const reloadArchived = async (key) => {
|
|
144
|
+
setLoading(true);
|
|
145
|
+
setError(false);
|
|
146
|
+
try {
|
|
147
|
+
const nextScopes = await loadArchivedScopes();
|
|
148
|
+
setArchivedScopes(nextScopes);
|
|
149
|
+
const nextKey = key ?? (archivedSelectedKey !== "" ? archivedSelectedKey : nextScopes[0]?.key) ?? "";
|
|
150
|
+
if (nextKey === "") {
|
|
151
|
+
setArchivedSelectedKey("");
|
|
152
|
+
setArchivedPayload(void 0);
|
|
153
|
+
} else {
|
|
154
|
+
const next = await loadArchivedScope(nextKey);
|
|
155
|
+
setArchivedSelectedKey(next.scope.key);
|
|
156
|
+
setArchivedPayload(next);
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
setError(true);
|
|
160
|
+
} finally {
|
|
161
|
+
setLoading(false);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
126
164
|
(0, react.useEffect)(() => {
|
|
127
|
-
reload(
|
|
165
|
+
reload();
|
|
128
166
|
}, []);
|
|
129
167
|
const options = (0, react.useMemo)(() => {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
hasSummary: payload.summary.trim() !== "",
|
|
137
|
-
pending: payload.state.pending,
|
|
138
|
-
checkpointCount: payload.state.checkpointCount
|
|
139
|
-
});
|
|
140
|
-
const currentKey = `current:${currentCwd.toLocaleLowerCase()}`;
|
|
141
|
-
if (currentCwd !== "" && !known.has(currentKey) && ![...known.values()].some((scope) => normalizeCwd(scope.cwd) === normalizeCwd(currentCwd))) known.set(currentKey, {
|
|
142
|
-
key: currentKey,
|
|
143
|
-
cwd: currentCwd,
|
|
144
|
-
kind: "workspace",
|
|
145
|
-
entryCount: 0,
|
|
146
|
-
hasSummary: false,
|
|
147
|
-
pending: 0,
|
|
148
|
-
checkpointCount: 0
|
|
149
|
-
});
|
|
150
|
-
return [...known.values()];
|
|
151
|
-
}, [
|
|
152
|
-
scopes,
|
|
153
|
-
currentCwd,
|
|
154
|
-
payload
|
|
155
|
-
]);
|
|
168
|
+
return [...new Map(scopes.map((scope) => [scope.key, scope])).values()];
|
|
169
|
+
}, [scopes]);
|
|
170
|
+
const archivedOptions = (0, react.useMemo)(() => archivedScopes, [archivedScopes]);
|
|
171
|
+
const displayedPayload = view === "active" ? payload : archivedPayload;
|
|
172
|
+
const displayedOptions = view === "active" ? options : archivedOptions;
|
|
173
|
+
const displayedKey = view === "active" ? selectedKey : archivedSelectedKey;
|
|
156
174
|
const filteredEntries = (0, react.useMemo)(() => {
|
|
157
175
|
const normalized = query.trim().toLocaleLowerCase();
|
|
158
|
-
if (normalized === "") return
|
|
159
|
-
return (
|
|
176
|
+
if (normalized === "") return displayedPayload?.entries ?? [];
|
|
177
|
+
return (displayedPayload?.entries ?? []).filter((entry) => [
|
|
160
178
|
entry.title,
|
|
161
179
|
entry.description,
|
|
162
180
|
entry.content,
|
|
163
181
|
...entry.tags
|
|
164
182
|
].join(" ").toLocaleLowerCase().includes(normalized));
|
|
165
|
-
}, [
|
|
183
|
+
}, [displayedPayload, query]);
|
|
166
184
|
const onSelect = (key) => {
|
|
167
185
|
const option = options.find((scope) => scope.key === key);
|
|
168
186
|
if (option === void 0) return;
|
|
169
187
|
setSelectedKey(key);
|
|
170
188
|
reload(option.cwd);
|
|
171
189
|
};
|
|
190
|
+
const onSelectArchived = (key) => {
|
|
191
|
+
if (!archivedOptions.some((scope) => scope.key === key)) return;
|
|
192
|
+
setArchivedSelectedKey(key);
|
|
193
|
+
reloadArchived(key);
|
|
194
|
+
};
|
|
195
|
+
const showArchived = () => {
|
|
196
|
+
setView("archived");
|
|
197
|
+
if (archivedPayload === void 0) reloadArchived();
|
|
198
|
+
};
|
|
199
|
+
const purge = async () => {
|
|
200
|
+
const confirm = globalThis.confirm;
|
|
201
|
+
if (archivedSelectedKey === "" || confirm !== void 0 && !confirm(t("purgeConfirm"))) return;
|
|
202
|
+
setLoading(true);
|
|
203
|
+
setError(false);
|
|
204
|
+
try {
|
|
205
|
+
const response = await fetch(`/workspace-memory/api/v1/archived-scope?key=${encodeURIComponent(archivedSelectedKey)}`, { method: "DELETE" });
|
|
206
|
+
if (!response.ok) throw new Error(`archived scope delete failed (${response.status})`);
|
|
207
|
+
await reloadArchived();
|
|
208
|
+
} catch {
|
|
209
|
+
setError(true);
|
|
210
|
+
setLoading(false);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
172
213
|
const colors = {
|
|
173
214
|
text: "var(--ds-color-text-primary, #f1f1f1)",
|
|
174
215
|
secondary: "var(--ds-color-text-secondary, #a7a7ad)",
|
|
@@ -195,12 +236,12 @@ window.__ModuleLoader__.load({
|
|
|
195
236
|
color: colors.secondary,
|
|
196
237
|
fontSize: 12,
|
|
197
238
|
wordBreak: "break-all"
|
|
198
|
-
} },
|
|
239
|
+
} }, displayedPayload?.scope.cwd || t("global"))), (0, react.createElement)("button", {
|
|
199
240
|
type: "button",
|
|
200
241
|
title: t("refresh"),
|
|
201
242
|
"aria-label": t("refresh"),
|
|
202
243
|
onClick: () => {
|
|
203
|
-
reload();
|
|
244
|
+
view === "active" ? reload() : reloadArchived();
|
|
204
245
|
},
|
|
205
246
|
style: {
|
|
206
247
|
display: "inline-flex",
|
|
@@ -215,16 +256,46 @@ window.__ModuleLoader__.load({
|
|
|
215
256
|
cursor: "pointer"
|
|
216
257
|
}
|
|
217
258
|
}, iconRefresh())), (0, react.createElement)("div", { style: {
|
|
259
|
+
display: "flex",
|
|
260
|
+
gap: 6,
|
|
261
|
+
marginBottom: 16
|
|
262
|
+
} }, (0, react.createElement)("button", {
|
|
263
|
+
type: "button",
|
|
264
|
+
onClick: () => setView("active"),
|
|
265
|
+
"aria-pressed": view === "active",
|
|
266
|
+
style: {
|
|
267
|
+
padding: "7px 11px",
|
|
268
|
+
border: `1px solid ${colors.border}`,
|
|
269
|
+
borderRadius: 6,
|
|
270
|
+
background: view === "active" ? colors.surface : "transparent",
|
|
271
|
+
color: colors.text,
|
|
272
|
+
cursor: "pointer"
|
|
273
|
+
}
|
|
274
|
+
}, t("activeView")), (0, react.createElement)("button", {
|
|
275
|
+
type: "button",
|
|
276
|
+
onClick: showArchived,
|
|
277
|
+
"aria-pressed": view === "archived",
|
|
278
|
+
style: {
|
|
279
|
+
padding: "7px 11px",
|
|
280
|
+
border: `1px solid ${colors.border}`,
|
|
281
|
+
borderRadius: 6,
|
|
282
|
+
background: view === "archived" ? colors.surface : "transparent",
|
|
283
|
+
color: colors.text,
|
|
284
|
+
cursor: "pointer"
|
|
285
|
+
}
|
|
286
|
+
}, t("archiveView"))), (0, react.createElement)("div", { style: {
|
|
218
287
|
display: "grid",
|
|
219
288
|
gap: 8,
|
|
220
289
|
marginBottom: 18
|
|
221
290
|
} }, (0, react.createElement)("label", { style: {
|
|
222
291
|
color: colors.secondary,
|
|
223
292
|
fontSize: 12
|
|
224
|
-
} }, t("
|
|
225
|
-
value:
|
|
293
|
+
} }, t("scope")), (0, react.createElement)("select", {
|
|
294
|
+
value: displayedKey,
|
|
226
295
|
onChange: (event) => {
|
|
227
|
-
|
|
296
|
+
const key = event.target.value;
|
|
297
|
+
if (view === "active") onSelect(key);
|
|
298
|
+
else onSelectArchived(key);
|
|
228
299
|
},
|
|
229
300
|
style: {
|
|
230
301
|
minHeight: 38,
|
|
@@ -234,7 +305,7 @@ window.__ModuleLoader__.load({
|
|
|
234
305
|
border: `1px solid ${colors.border}`,
|
|
235
306
|
borderRadius: 6
|
|
236
307
|
}
|
|
237
|
-
},
|
|
308
|
+
}, displayedOptions.length === 0 ? (0, react.createElement)("option", { value: "" }, view === "active" ? t("emptyScopes") : t("archiveEmpty")) : displayedOptions.map((scope) => (0, react.createElement)("option", {
|
|
238
309
|
key: scope.key,
|
|
239
310
|
value: scope.key
|
|
240
311
|
}, scope.kind === "global" ? t("global") : workspaceName(scope.cwd, t("workspace")))))), error && (0, react.createElement)("div", {
|
|
@@ -252,7 +323,7 @@ window.__ModuleLoader__.load({
|
|
|
252
323
|
}, (0, react.createElement)("span", null, t("failed")), (0, react.createElement)("button", {
|
|
253
324
|
type: "button",
|
|
254
325
|
onClick: () => {
|
|
255
|
-
reload();
|
|
326
|
+
view === "active" ? reload() : reloadArchived();
|
|
256
327
|
},
|
|
257
328
|
style: {
|
|
258
329
|
color: colors.accent,
|
|
@@ -269,7 +340,21 @@ window.__ModuleLoader__.load({
|
|
|
269
340
|
color: colors.secondary,
|
|
270
341
|
fontSize: 12,
|
|
271
342
|
flexWrap: "wrap"
|
|
272
|
-
} }, (0, react.createElement)("span", null, `${
|
|
343
|
+
} }, (0, react.createElement)("span", null, `${displayedPayload?.entries.length ?? 0}${t("count")}`), (0, react.createElement)("span", null, `${t("pending")}: ${displayedPayload?.state.pending ?? 0}`), (0, react.createElement)("span", null, `${t("checkpoints")}: ${displayedPayload?.state.checkpointCount ?? 0}`)), view === "archived" && displayedPayload !== void 0 && (0, react.createElement)("button", {
|
|
344
|
+
type: "button",
|
|
345
|
+
onClick: () => {
|
|
346
|
+
purge();
|
|
347
|
+
},
|
|
348
|
+
style: {
|
|
349
|
+
justifySelf: "start",
|
|
350
|
+
color: "#ff8f8f",
|
|
351
|
+
background: "transparent",
|
|
352
|
+
border: `1px solid ${colors.border}`,
|
|
353
|
+
borderRadius: 6,
|
|
354
|
+
padding: "7px 11px",
|
|
355
|
+
cursor: "pointer"
|
|
356
|
+
}
|
|
357
|
+
}, t("purge")), (0, react.createElement)("article", { style: {
|
|
273
358
|
border: `1px solid ${colors.border}`,
|
|
274
359
|
borderRadius: 6,
|
|
275
360
|
padding: 14,
|
|
@@ -284,7 +369,7 @@ window.__ModuleLoader__.load({
|
|
|
284
369
|
wordBreak: "break-word",
|
|
285
370
|
font: "inherit",
|
|
286
371
|
lineHeight: 1.55
|
|
287
|
-
} },
|
|
372
|
+
} }, displayedPayload?.summary.trim() || t("noSummary"))), (0, react.createElement)("div", { style: {
|
|
288
373
|
display: "grid",
|
|
289
374
|
gap: 10
|
|
290
375
|
} }, (0, react.createElement)("div", { style: {
|
|
@@ -348,11 +433,7 @@ window.__ModuleLoader__.load({
|
|
|
348
433
|
fontSize: 11
|
|
349
434
|
} }, `${t("updated")}: ${formatDate(entry.updatedAt)}`))))));
|
|
350
435
|
}
|
|
351
|
-
const inject = [
|
|
352
|
-
"slots",
|
|
353
|
-
"locale",
|
|
354
|
-
"sessions"
|
|
355
|
-
];
|
|
436
|
+
const inject = ["slots", "locale"];
|
|
356
437
|
function apply(ctx) {
|
|
357
438
|
ctx.effect(() => ctx.locale.register(NS, {
|
|
358
439
|
zh,
|
|
@@ -368,10 +449,7 @@ window.__ModuleLoader__.load({
|
|
|
368
449
|
order: 50,
|
|
369
450
|
label: () => t("nav"),
|
|
370
451
|
locale: NS
|
|
371
|
-
}, () => (0, react.createElement)(MemorySection, {
|
|
372
|
-
t,
|
|
373
|
-
sessions: ctx.sessions
|
|
374
|
-
})));
|
|
452
|
+
}, () => (0, react.createElement)(MemorySection, { t })));
|
|
375
453
|
}
|
|
376
454
|
//#endregion
|
|
377
455
|
exports.apply = apply;
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useSyncExternalStore","h","useState","useMemo"],"sources":["../src/client/index.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport { createElement as h } from 'react'\nimport type { SettingsSectionOwnerProps } from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type { SessionListState } from '@deepseek-ai/dsh-client-runtime/client'\n\nconst NS = 'workspace-memory'\n\nconst zh = {\n nav: '记忆',\n title: '记忆',\n refresh: '刷新',\n global: '全局记忆',\n workspace: '项目记忆',\n current: '当前项目',\n emptyScopes: '还没有已记录的项目记忆',\n summary: '摘要记忆',\n entries: '结构化记忆',\n search: '筛选记忆',\n searchPlaceholder: '搜索标题、内容或标签',\n noSummary: '暂无摘要',\n noEntries: '暂无结构化记忆',\n loading: '正在读取…',\n failed: '读取记忆失败',\n retry: '重试',\n pending: '待整理',\n checkpoints: '整理次数',\n updated: '最近更新',\n all: '全部',\n count: '条',\n}\n\nconst en = {\n nav: 'Memory',\n title: 'Memory',\n refresh: 'Refresh',\n global: 'Global memory',\n workspace: 'Project memory',\n current: 'Current project',\n emptyScopes: 'No project memories recorded yet',\n summary: 'Summary memory',\n entries: 'Structured memory',\n search: 'Filter memory',\n searchPlaceholder: 'Search title, content, or tags',\n noSummary: 'No summary yet',\n noEntries: 'No structured memories yet',\n loading: 'Loading…',\n failed: 'Unable to read memory',\n retry: 'Retry',\n pending: 'Pending',\n checkpoints: 'Checkpoints',\n updated: 'Last updated',\n all: 'All',\n count: '',\n}\n\ntype Translate = (key: keyof typeof zh) => string\n\ninterface ScopeInfo {\n key: string\n cwd: string\n kind: 'global' | 'workspace'\n entryCount: number\n hasSummary: boolean\n pending: number\n checkpointCount: number\n updatedAt?: string\n}\n\ninterface MemoryEntry {\n id: string\n scope: string\n type: string\n title: string\n description: string\n content: string\n tags: string[]\n importance: number\n createdAt?: string\n updatedAt?: string\n}\n\ninterface ScopePayload {\n scope: { key: string; cwd: string; kind: 'global' | 'workspace' }\n summary: string\n entries: MemoryEntry[]\n state: { pending: number; checkpointCount: number; lastCheckpointAt: number }\n}\n\ninterface Snapshot<T> {\n getSnapshot(): T\n subscribe(listener: () => void): () => void\n}\n\ninterface SessionsLike {\n list: Snapshot<SessionListState>\n}\n\ninterface SlotsLike {\n inject(slot: string, register: () => unknown): void\n register(options: Record<string, unknown>, component: () => unknown): unknown\n}\n\ninterface LocaleLike {\n register(namespace: string, dictionaries: { zh: Record<string, string>; en: Record<string, string> }): unknown\n bind(namespace: string): (key: string) => string\n}\n\ninterface MemoryClientContext {\n effect(callback: () => unknown, label?: string): void\n slots: SlotsLike\n locale: LocaleLike\n sessions: SessionsLike\n}\n\nfunction useSnapshot<T>(source: Snapshot<T>): T {\n return useSyncExternalStore(source.subscribe, source.getSnapshot, source.getSnapshot)\n}\n\nfunction workspaceName(cwd: string, fallback: string): string {\n const normalized = cwd.replaceAll('\\\\', '/').replace(/\\/+$/u, '')\n const name = normalized.split('/').filter(Boolean).at(-1)\n return name ?? fallback\n}\n\nfunction normalizeCwd(cwd: string): string {\n return cwd.replaceAll('\\\\', '/').replace(/\\/+$/u, '').toLocaleLowerCase()\n}\n\nfunction formatDate(value: string | undefined): string {\n if (value === undefined || value === '') return ''\n const date = new Date(value)\n return Number.isFinite(date.getTime()) ? date.toLocaleString() : value\n}\n\nfunction iconRefresh(): ReturnType<typeof h> {\n return h('svg', { width: 16, height: 16, viewBox: '0 0 16 16', 'aria-hidden': true },\n h('path', { d: 'M13 4.8A5.5 5.5 0 1 0 13.5 9', fill: 'none', stroke: 'currentColor', strokeWidth: 1.4, strokeLinecap: 'round' }),\n h('path', { d: 'M10.8 2.8h2.8v2.8', fill: 'none', stroke: 'currentColor', strokeWidth: 1.4, strokeLinecap: 'round', strokeLinejoin: 'round' }),\n )\n}\n\nfunction MemorySection({ t, sessions }: { t: Translate; sessions: SessionsLike } & Partial<SettingsSectionOwnerProps>) {\n const sessionList = useSnapshot(sessions.list)\n const currentCwd = sessionList.current === undefined ? '' : sessionList.byId[sessionList.current]?.cwd ?? ''\n const [scopes, setScopes] = useState<ScopeInfo[]>([])\n const [selectedKey, setSelectedKey] = useState('global')\n const [payload, setPayload] = useState<ScopePayload | undefined>()\n const [query, setQuery] = useState('')\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState(false)\n\n const loadScopes = async (): Promise<ScopeInfo[]> => {\n const response = await fetch('/workspace-memory/api/v1/scopes')\n if (!response.ok) throw new Error(`scope list failed (${response.status})`)\n const result = await response.json() as { scopes?: ScopeInfo[] }\n return Array.isArray(result.scopes) ? result.scopes : []\n }\n\n const loadScope = async (cwd: string): Promise<ScopePayload> => {\n const response = await fetch(`/workspace-memory/api/v1/scope?cwd=${encodeURIComponent(cwd)}`)\n if (!response.ok) throw new Error(`scope read failed (${response.status})`)\n return await response.json() as ScopePayload\n }\n\n const reload = async (cwd?: string): Promise<void> => {\n setLoading(true)\n setError(false)\n try {\n const nextScopes = await loadScopes()\n setScopes(nextScopes)\n const next = await loadScope(cwd ?? payload?.scope.cwd ?? currentCwd)\n setPayload(next)\n setSelectedKey(next.scope.key)\n } catch {\n setError(true)\n } finally {\n setLoading(false)\n }\n }\n\n useEffect(() => {\n void reload(currentCwd)\n // The initial project follows the current session; later changes are user-selected.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const options = useMemo(() => {\n const known = new Map(scopes.map(scope => [scope.key, scope]))\n if (payload !== undefined && !known.has(payload.scope.key)) {\n known.set(payload.scope.key, {\n key: payload.scope.key,\n cwd: payload.scope.cwd,\n kind: payload.scope.kind,\n entryCount: payload.entries.length,\n hasSummary: payload.summary.trim() !== '',\n pending: payload.state.pending,\n checkpointCount: payload.state.checkpointCount,\n })\n }\n const currentKey = `current:${currentCwd.toLocaleLowerCase()}`\n if (currentCwd !== '' && !known.has(currentKey) && ![...known.values()].some(scope => normalizeCwd(scope.cwd) === normalizeCwd(currentCwd))) {\n known.set(currentKey, {\n key: currentKey,\n cwd: currentCwd,\n kind: 'workspace',\n entryCount: 0,\n hasSummary: false,\n pending: 0,\n checkpointCount: 0,\n })\n }\n return [...known.values()]\n }, [scopes, currentCwd, payload])\n\n const filteredEntries = useMemo(() => {\n const normalized = query.trim().toLocaleLowerCase()\n if (normalized === '') return payload?.entries ?? []\n return (payload?.entries ?? []).filter(entry => [entry.title, entry.description, entry.content, ...entry.tags]\n .join(' ').toLocaleLowerCase().includes(normalized))\n }, [payload, query])\n\n const onSelect = (key: string): void => {\n const option = options.find(scope => scope.key === key)\n if (option === undefined) return\n setSelectedKey(key)\n void reload(option.cwd)\n }\n\n const colors = {\n text: 'var(--ds-color-text-primary, #f1f1f1)',\n secondary: 'var(--ds-color-text-secondary, #a7a7ad)',\n border: 'var(--ds-color-border, rgba(255,255,255,.12))',\n surface: 'var(--ds-color-bg-secondary, rgba(255,255,255,.045))',\n accent: 'var(--ds-color-primary, #8ab4ff)',\n }\n\n return h('section', { style: { color: colors.text, maxWidth: 820, paddingBottom: 24 } },\n h('header', { style: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16, marginBottom: 18 } },\n h('div', null,\n h('h2', { style: { margin: 0, fontSize: 20, fontWeight: 650 } }, t('title')),\n h('p', { style: { margin: '6px 0 0', color: colors.secondary, fontSize: 12, wordBreak: 'break-all' } }, payload?.scope.cwd || t('global')),\n ),\n h('button', {\n type: 'button', title: t('refresh'), 'aria-label': t('refresh'), onClick: () => { void reload() },\n style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 34, height: 34, border: `1px solid ${colors.border}`, borderRadius: 6, background: 'transparent', color: colors.text, cursor: 'pointer' },\n }, iconRefresh()),\n ),\n h('div', { style: { display: 'grid', gap: 8, marginBottom: 18 } },\n h('label', { style: { color: colors.secondary, fontSize: 12 } }, t('current')),\n h('select', {\n value: selectedKey, onChange: (event: Event) => { onSelect((event.target as unknown as { value: string }).value) },\n style: { minHeight: 38, padding: '0 10px', color: colors.text, background: colors.surface, border: `1px solid ${colors.border}`, borderRadius: 6 },\n }, options.length === 0\n ? h('option', { value: '' }, t('emptyScopes'))\n : options.map(scope => h('option', { key: scope.key, value: scope.key }, scope.kind === 'global' ? t('global') : workspaceName(scope.cwd, t('workspace')))),\n ),\n ),\n error && h('div', { role: 'alert', style: { padding: 12, border: `1px solid ${colors.border}`, borderRadius: 6, color: colors.secondary, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12 } },\n h('span', null, t('failed')),\n h('button', { type: 'button', onClick: () => { void reload() }, style: { color: colors.accent, background: 'transparent', border: 0, cursor: 'pointer' } }, t('retry')),\n ),\n loading && h('p', { style: { color: colors.secondary } }, t('loading')),\n !loading && !error && h('div', { style: { display: 'grid', gap: 16 } },\n h('div', { style: { display: 'flex', gap: 16, color: colors.secondary, fontSize: 12, flexWrap: 'wrap' } },\n h('span', null, `${payload?.entries.length ?? 0}${t('count')}`),\n h('span', null, `${t('pending')}: ${payload?.state.pending ?? 0}`),\n h('span', null, `${t('checkpoints')}: ${payload?.state.checkpointCount ?? 0}`),\n ),\n h('article', { style: { border: `1px solid ${colors.border}`, borderRadius: 6, padding: 14, background: colors.surface } },\n h('h3', { style: { margin: '0 0 10px', fontSize: 14 } }, t('summary')),\n h('pre', { style: { margin: 0, color: colors.secondary, whiteSpace: 'pre-wrap', wordBreak: 'break-word', font: 'inherit', lineHeight: 1.55 } }, payload?.summary.trim() || t('noSummary')),\n ),\n h('div', { style: { display: 'grid', gap: 10 } },\n h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 } },\n h('h3', { style: { margin: 0, fontSize: 14 } }, `${t('entries')} (${filteredEntries.length})`),\n h('input', { type: 'search', value: query, placeholder: t('searchPlaceholder'), 'aria-label': t('search'), onChange: (event: Event) => { setQuery((event.target as unknown as { value: string }).value) }, style: { minHeight: 32, width: 230, maxWidth: '48%', padding: '0 9px', color: colors.text, background: 'transparent', border: `1px solid ${colors.border}`, borderRadius: 6 } }),\n ),\n filteredEntries.length === 0 && h('p', { style: { color: colors.secondary, margin: 0 } }, t('noEntries')),\n filteredEntries.map(entry => h('details', { key: entry.id, style: { border: `1px solid ${colors.border}`, borderRadius: 6, padding: '10px 12px', background: colors.surface } },\n h('summary', { style: { cursor: 'pointer', display: 'flex', gap: 8, alignItems: 'baseline' } },\n h('strong', { style: { fontSize: 13 } }, entry.title),\n h('span', { style: { color: colors.secondary, fontSize: 11 } }, entry.type),\n ),\n h('p', { style: { margin: '10px 0 0', color: colors.secondary, lineHeight: 1.5, whiteSpace: 'pre-wrap', wordBreak: 'break-word' } }, entry.content),\n entry.tags.length > 0 && h('div', { style: { marginTop: 10, color: colors.accent, fontSize: 11 } }, entry.tags.join(' · ')),\n entry.updatedAt !== undefined && h('div', { style: { marginTop: 8, color: colors.secondary, fontSize: 11 } }, `${t('updated')}: ${formatDate(entry.updatedAt)}`),\n )),\n ),\n ),\n )\n}\n\nexport const inject = ['slots', 'locale', 'sessions']\n\nexport function apply(ctx: MemoryClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'workspace-memory: dictionaries')\n const translate = ctx.locale.bind(NS)\n const t = (key: keyof typeof zh): string => {\n const translated = translate(key)\n return translated || zh[key]\n }\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'workspace-memory',\n order: 50,\n label: () => t('nav'),\n locale: NS,\n }, () => h(MemorySection, { t, sessions: ctx.sessions })))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["h","useState","useMemo"],"sources":["../src/client/index.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { createElement as h } from 'react'\nimport type { SettingsSectionOwnerProps } from '@deepseek-ai/dsh-client-ui-settings/client'\n\nconst NS = 'workspace-memory'\n\nconst zh = {\n nav: '记忆',\n title: '记忆',\n refresh: '刷新',\n global: '全局记忆',\n workspace: '项目记忆',\n scope: '选择记忆范围',\n emptyScopes: '还没有已记录的项目记忆',\n summary: '摘要记忆',\n entries: '结构化记忆',\n search: '筛选记忆',\n searchPlaceholder: '搜索标题、内容或标签',\n noSummary: '暂无摘要',\n noEntries: '暂无结构化记忆',\n loading: '正在读取…',\n failed: '读取记忆失败',\n retry: '重试',\n activeView: '记忆',\n archiveView: '回收区',\n archiveEmpty: '回收区为空',\n purge: '永久删除',\n purgeConfirm: '确定永久删除这个项目的全部记忆吗?此操作无法撤销。',\n pending: '待整理',\n checkpoints: '整理次数',\n updated: '最近更新',\n all: '全部',\n count: '条',\n}\n\nconst en = {\n nav: 'Memory',\n title: 'Memory',\n refresh: 'Refresh',\n global: 'Global memory',\n workspace: 'Project memory',\n scope: 'Memory scope',\n emptyScopes: 'No project memories recorded yet',\n summary: 'Summary memory',\n entries: 'Structured memory',\n search: 'Filter memory',\n searchPlaceholder: 'Search title, content, or tags',\n noSummary: 'No summary yet',\n noEntries: 'No structured memories yet',\n loading: 'Loading…',\n failed: 'Unable to read memory',\n retry: 'Retry',\n activeView: 'Memory',\n archiveView: 'Recycle bin',\n archiveEmpty: 'Recycle bin is empty',\n purge: 'Delete permanently',\n purgeConfirm: 'Permanently delete all memory for this project? This cannot be undone.',\n pending: 'Pending',\n checkpoints: 'Checkpoints',\n updated: 'Last updated',\n all: 'All',\n count: '',\n}\n\ntype Translate = (key: keyof typeof zh) => string\n\ninterface ScopeInfo {\n key: string\n cwd: string\n kind: 'global' | 'workspace'\n entryCount: number\n hasSummary: boolean\n pending: number\n checkpointCount: number\n updatedAt?: string\n}\n\ninterface MemoryEntry {\n id: string\n scope: string\n type: string\n title: string\n description: string\n content: string\n tags: string[]\n importance: number\n createdAt?: string\n updatedAt?: string\n}\n\ninterface ScopePayload {\n scope: { key: string; cwd: string; kind: 'global' | 'workspace' }\n summary: string\n entries: MemoryEntry[]\n state: { pending: number; checkpointCount: number; lastCheckpointAt: number }\n}\n\ntype MemoryView = 'active' | 'archived'\n\ninterface SlotsLike {\n inject(slot: string, register: () => unknown): void\n register(options: Record<string, unknown>, component: () => unknown): unknown\n}\n\ninterface LocaleLike {\n register(namespace: string, dictionaries: { zh: Record<string, string>; en: Record<string, string> }): unknown\n bind(namespace: string): (key: string) => string\n}\n\ninterface MemoryClientContext {\n effect(callback: () => unknown, label?: string): void\n slots: SlotsLike\n locale: LocaleLike\n}\n\nfunction workspaceName(cwd: string, fallback: string): string {\n const normalized = cwd.replaceAll('\\\\', '/').replace(/\\/+$/u, '')\n const name = normalized.split('/').filter(Boolean).at(-1)\n return name ?? fallback\n}\n\nfunction formatDate(value: string | undefined): string {\n if (value === undefined || value === '') return ''\n const date = new Date(value)\n return Number.isFinite(date.getTime()) ? date.toLocaleString() : value\n}\n\nfunction iconRefresh(): ReturnType<typeof h> {\n return h('svg', { width: 16, height: 16, viewBox: '0 0 16 16', 'aria-hidden': true },\n h('path', { d: 'M13 4.8A5.5 5.5 0 1 0 13.5 9', fill: 'none', stroke: 'currentColor', strokeWidth: 1.4, strokeLinecap: 'round' }),\n h('path', { d: 'M10.8 2.8h2.8v2.8', fill: 'none', stroke: 'currentColor', strokeWidth: 1.4, strokeLinecap: 'round', strokeLinejoin: 'round' }),\n )\n}\n\nfunction MemorySection({ t }: { t: Translate } & Partial<SettingsSectionOwnerProps>) {\n const [scopes, setScopes] = useState<ScopeInfo[]>([])\n const [selectedKey, setSelectedKey] = useState('global')\n const [payload, setPayload] = useState<ScopePayload | undefined>()\n const [archivedScopes, setArchivedScopes] = useState<ScopeInfo[]>([])\n const [archivedSelectedKey, setArchivedSelectedKey] = useState('')\n const [archivedPayload, setArchivedPayload] = useState<ScopePayload | undefined>()\n const [view, setView] = useState<MemoryView>('active')\n const [query, setQuery] = useState('')\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState(false)\n\n const loadScopes = async (): Promise<ScopeInfo[]> => {\n const response = await fetch('/workspace-memory/api/v1/scopes')\n if (!response.ok) throw new Error(`scope list failed (${response.status})`)\n const result = await response.json() as { scopes?: ScopeInfo[] }\n return Array.isArray(result.scopes) ? result.scopes : []\n }\n\n const loadScope = async (cwd: string): Promise<ScopePayload> => {\n const response = await fetch(`/workspace-memory/api/v1/scope?cwd=${encodeURIComponent(cwd)}`)\n if (!response.ok) throw new Error(`scope read failed (${response.status})`)\n return await response.json() as ScopePayload\n }\n\n const loadArchivedScopes = async (): Promise<ScopeInfo[]> => {\n const response = await fetch('/workspace-memory/api/v1/archived-scopes')\n if (!response.ok) throw new Error(`archived scope list failed (${response.status})`)\n const result = await response.json() as { scopes?: ScopeInfo[] }\n return Array.isArray(result.scopes) ? result.scopes : []\n }\n\n const loadArchivedScope = async (key: string): Promise<ScopePayload> => {\n const response = await fetch(`/workspace-memory/api/v1/archived-scope?key=${encodeURIComponent(key)}`)\n if (!response.ok) throw new Error(`archived scope read failed (${response.status})`)\n return await response.json() as ScopePayload\n }\n\n const reload = async (cwd?: string): Promise<void> => {\n setLoading(true)\n setError(false)\n try {\n const nextScopes = await loadScopes()\n setScopes(nextScopes)\n const next = await loadScope(cwd ?? payload?.scope.cwd ?? '')\n setPayload(next)\n setSelectedKey(next.scope.key)\n } catch {\n setError(true)\n } finally {\n setLoading(false)\n }\n }\n\n const reloadArchived = async (key?: string): Promise<void> => {\n setLoading(true)\n setError(false)\n try {\n const nextScopes = await loadArchivedScopes()\n setArchivedScopes(nextScopes)\n const nextKey = key ?? (archivedSelectedKey !== '' ? archivedSelectedKey : nextScopes[0]?.key) ?? ''\n if (nextKey === '') {\n setArchivedSelectedKey('')\n setArchivedPayload(undefined)\n } else {\n const next = await loadArchivedScope(nextKey)\n setArchivedSelectedKey(next.scope.key)\n setArchivedPayload(next)\n }\n } catch {\n setError(true)\n } finally {\n setLoading(false)\n }\n }\n\n useEffect(() => {\n void reload()\n // Memory browsing is independent from the currently open session.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const options = useMemo(() => {\n const known = new Map(scopes.map(scope => [scope.key, scope]))\n return [...known.values()]\n }, [scopes])\n\n const archivedOptions = useMemo(() => archivedScopes, [archivedScopes])\n\n const displayedPayload = view === 'active' ? payload : archivedPayload\n const displayedOptions = view === 'active' ? options : archivedOptions\n const displayedKey = view === 'active' ? selectedKey : archivedSelectedKey\n\n const filteredEntries = useMemo(() => {\n const normalized = query.trim().toLocaleLowerCase()\n if (normalized === '') return displayedPayload?.entries ?? []\n return (displayedPayload?.entries ?? []).filter(entry => [entry.title, entry.description, entry.content, ...entry.tags]\n .join(' ').toLocaleLowerCase().includes(normalized))\n }, [displayedPayload, query])\n\n const onSelect = (key: string): void => {\n const option = options.find(scope => scope.key === key)\n if (option === undefined) return\n setSelectedKey(key)\n void reload(option.cwd)\n }\n\n const onSelectArchived = (key: string): void => {\n if (!archivedOptions.some(scope => scope.key === key)) return\n setArchivedSelectedKey(key)\n void reloadArchived(key)\n }\n\n const showArchived = (): void => {\n setView('archived')\n if (archivedPayload === undefined) void reloadArchived()\n }\n\n const purge = async (): Promise<void> => {\n const confirm = (globalThis as unknown as { confirm?: (message: string) => boolean }).confirm\n if (archivedSelectedKey === '' || (confirm !== undefined && !confirm(t('purgeConfirm')))) return\n setLoading(true)\n setError(false)\n try {\n const response = await fetch(`/workspace-memory/api/v1/archived-scope?key=${encodeURIComponent(archivedSelectedKey)}`, { method: 'DELETE' })\n if (!response.ok) throw new Error(`archived scope delete failed (${response.status})`)\n await reloadArchived()\n } catch {\n setError(true)\n setLoading(false)\n }\n }\n\n const colors = {\n text: 'var(--ds-color-text-primary, #f1f1f1)',\n secondary: 'var(--ds-color-text-secondary, #a7a7ad)',\n border: 'var(--ds-color-border, rgba(255,255,255,.12))',\n surface: 'var(--ds-color-bg-secondary, rgba(255,255,255,.045))',\n accent: 'var(--ds-color-primary, #8ab4ff)',\n }\n\n return h('section', { style: { color: colors.text, maxWidth: 820, paddingBottom: 24 } },\n h('header', { style: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16, marginBottom: 18 } },\n h('div', null,\n h('h2', { style: { margin: 0, fontSize: 20, fontWeight: 650 } }, t('title')),\n h('p', { style: { margin: '6px 0 0', color: colors.secondary, fontSize: 12, wordBreak: 'break-all' } }, displayedPayload?.scope.cwd || t('global')),\n ),\n h('button', {\n type: 'button', title: t('refresh'), 'aria-label': t('refresh'), onClick: () => { void (view === 'active' ? reload() : reloadArchived()) },\n style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 34, height: 34, border: `1px solid ${colors.border}`, borderRadius: 6, background: 'transparent', color: colors.text, cursor: 'pointer' },\n }, iconRefresh()),\n ),\n h('div', { style: { display: 'flex', gap: 6, marginBottom: 16 } },\n h('button', { type: 'button', onClick: () => setView('active'), 'aria-pressed': view === 'active', style: { padding: '7px 11px', border: `1px solid ${colors.border}`, borderRadius: 6, background: view === 'active' ? colors.surface : 'transparent', color: colors.text, cursor: 'pointer' } }, t('activeView')),\n h('button', { type: 'button', onClick: showArchived, 'aria-pressed': view === 'archived', style: { padding: '7px 11px', border: `1px solid ${colors.border}`, borderRadius: 6, background: view === 'archived' ? colors.surface : 'transparent', color: colors.text, cursor: 'pointer' } }, t('archiveView')),\n ),\n h('div', { style: { display: 'grid', gap: 8, marginBottom: 18 } },\n h('label', { style: { color: colors.secondary, fontSize: 12 } }, t('scope')),\n h('select', {\n value: displayedKey, onChange: (event: Event) => { const key = (event.target as unknown as { value: string }).value; if (view === 'active') onSelect(key); else onSelectArchived(key) },\n style: { minHeight: 38, padding: '0 10px', color: colors.text, background: colors.surface, border: `1px solid ${colors.border}`, borderRadius: 6 },\n }, displayedOptions.length === 0\n ? h('option', { value: '' }, view === 'active' ? t('emptyScopes') : t('archiveEmpty'))\n : displayedOptions.map(scope => h('option', { key: scope.key, value: scope.key }, scope.kind === 'global' ? t('global') : workspaceName(scope.cwd, t('workspace')))),\n ),\n ),\n error && h('div', { role: 'alert', style: { padding: 12, border: `1px solid ${colors.border}`, borderRadius: 6, color: colors.secondary, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12 } },\n h('span', null, t('failed')),\n h('button', { type: 'button', onClick: () => { void (view === 'active' ? reload() : reloadArchived()) }, style: { color: colors.accent, background: 'transparent', border: 0, cursor: 'pointer' } }, t('retry')),\n ),\n loading && h('p', { style: { color: colors.secondary } }, t('loading')),\n !loading && !error && h('div', { style: { display: 'grid', gap: 16 } },\n h('div', { style: { display: 'flex', gap: 16, color: colors.secondary, fontSize: 12, flexWrap: 'wrap' } },\n h('span', null, `${displayedPayload?.entries.length ?? 0}${t('count')}`),\n h('span', null, `${t('pending')}: ${displayedPayload?.state.pending ?? 0}`),\n h('span', null, `${t('checkpoints')}: ${displayedPayload?.state.checkpointCount ?? 0}`),\n ),\n view === 'archived' && displayedPayload !== undefined && h('button', { type: 'button', onClick: () => { void purge() }, style: { justifySelf: 'start', color: '#ff8f8f', background: 'transparent', border: `1px solid ${colors.border}`, borderRadius: 6, padding: '7px 11px', cursor: 'pointer' } }, t('purge')),\n h('article', { style: { border: `1px solid ${colors.border}`, borderRadius: 6, padding: 14, background: colors.surface } },\n h('h3', { style: { margin: '0 0 10px', fontSize: 14 } }, t('summary')),\n h('pre', { style: { margin: 0, color: colors.secondary, whiteSpace: 'pre-wrap', wordBreak: 'break-word', font: 'inherit', lineHeight: 1.55 } }, displayedPayload?.summary.trim() || t('noSummary')),\n ),\n h('div', { style: { display: 'grid', gap: 10 } },\n h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 } },\n h('h3', { style: { margin: 0, fontSize: 14 } }, `${t('entries')} (${filteredEntries.length})`),\n h('input', { type: 'search', value: query, placeholder: t('searchPlaceholder'), 'aria-label': t('search'), onChange: (event: Event) => { setQuery((event.target as unknown as { value: string }).value) }, style: { minHeight: 32, width: 230, maxWidth: '48%', padding: '0 9px', color: colors.text, background: 'transparent', border: `1px solid ${colors.border}`, borderRadius: 6 } }),\n ),\n filteredEntries.length === 0 && h('p', { style: { color: colors.secondary, margin: 0 } }, t('noEntries')),\n filteredEntries.map(entry => h('details', { key: entry.id, style: { border: `1px solid ${colors.border}`, borderRadius: 6, padding: '10px 12px', background: colors.surface } },\n h('summary', { style: { cursor: 'pointer', display: 'flex', gap: 8, alignItems: 'baseline' } },\n h('strong', { style: { fontSize: 13 } }, entry.title),\n h('span', { style: { color: colors.secondary, fontSize: 11 } }, entry.type),\n ),\n h('p', { style: { margin: '10px 0 0', color: colors.secondary, lineHeight: 1.5, whiteSpace: 'pre-wrap', wordBreak: 'break-word' } }, entry.content),\n entry.tags.length > 0 && h('div', { style: { marginTop: 10, color: colors.accent, fontSize: 11 } }, entry.tags.join(' · ')),\n entry.updatedAt !== undefined && h('div', { style: { marginTop: 8, color: colors.secondary, fontSize: 11 } }, `${t('updated')}: ${formatDate(entry.updatedAt)}`),\n )),\n ),\n ),\n )\n}\n\nexport const inject = ['slots', 'locale']\n\nexport function apply(ctx: MemoryClientContext): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'workspace-memory: dictionaries')\n const translate = ctx.locale.bind(NS)\n const t = (key: keyof typeof zh): string => {\n const translated = translate(key)\n return translated || zh[key]\n }\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'workspace-memory',\n order: 50,\n label: () => t('nav'),\n locale: NS,\n }, () => h(MemorySection, { t })))\n}\n"],"mappings":"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"}
|
package/lib/core.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
|
-
import { mkdir, readFile, readdir, rename, unlink, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { mkdir, readFile, readdir, rename, rm, unlink, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { dirname, isAbsolute, join, normalize, resolve } from 'node:path';
|
|
5
5
|
export const SUMMARY_FILE = 'memory_summary.md';
|
|
6
6
|
export const ENTRIES_FILE = 'memory_entries.json';
|
|
7
7
|
export const STATE_FILE = 'state.json';
|
|
8
8
|
export const CHECKPOINT_DIR = 'checkpoints';
|
|
9
9
|
export const HISTORY_DIR = 'summary_history';
|
|
10
|
+
export const ARCHIVE_DIR = 'archived';
|
|
10
11
|
const DEFAULT_STATE = Object.freeze({
|
|
11
12
|
version: 0,
|
|
12
13
|
checkpointCount: 0,
|
|
@@ -187,6 +188,9 @@ function validMessage(value) {
|
|
|
187
188
|
function scopeDirectory(root, key) {
|
|
188
189
|
return key === 'global' ? join(root, 'global') : join(root, 'scopes', key);
|
|
189
190
|
}
|
|
191
|
+
function archivedScopeDirectory(root, key) {
|
|
192
|
+
return join(root, ARCHIVE_DIR, key);
|
|
193
|
+
}
|
|
190
194
|
function deterministicSummary(entries, maxBytes, heading = '# Workspace memory') {
|
|
191
195
|
const active = entries
|
|
192
196
|
.filter(entry => entry.status !== 'deleted')
|
|
@@ -322,6 +326,54 @@ export class WorkspaceMemoryStore {
|
|
|
322
326
|
}
|
|
323
327
|
return result.sort((left, right) => left.key === 'global' ? -1 : right.key === 'global' ? 1 : left.cwd.localeCompare(right.cwd));
|
|
324
328
|
}
|
|
329
|
+
/** List workspace scopes moved to the recoverable archive. */
|
|
330
|
+
async listArchivedScopes() {
|
|
331
|
+
const result = [];
|
|
332
|
+
const directories = await readdir(join(this.root, ARCHIVE_DIR), { withFileTypes: true }).catch(() => []);
|
|
333
|
+
for (const directory of directories) {
|
|
334
|
+
if (!directory.isDirectory() || !/^ws-[a-f0-9]{20}$/u.test(directory.name))
|
|
335
|
+
continue;
|
|
336
|
+
const descriptor = join(archivedScopeDirectory(this.root, directory.name), 'scope.json');
|
|
337
|
+
const raw = await readFile(descriptor, 'utf8').catch(() => '');
|
|
338
|
+
try {
|
|
339
|
+
const value = JSON.parse(raw);
|
|
340
|
+
if (!isRecord(value) || typeof value.cwd !== 'string' || value.cwd.trim() === '')
|
|
341
|
+
continue;
|
|
342
|
+
const scope = this.scope(value.cwd);
|
|
343
|
+
if (scope.key === directory.name)
|
|
344
|
+
result.push(scope);
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
// Ignore incomplete archive descriptors.
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return result.sort((left, right) => left.cwd.localeCompare(right.cwd));
|
|
351
|
+
}
|
|
352
|
+
/** Move one workspace scope to the recoverable archive. */
|
|
353
|
+
async archiveScope(scope) {
|
|
354
|
+
if (scope.key === 'global' || !existsSync(scope.dir))
|
|
355
|
+
return false;
|
|
356
|
+
return this.withScope(scope, async () => {
|
|
357
|
+
if (!existsSync(scope.dir))
|
|
358
|
+
return false;
|
|
359
|
+
const destination = archivedScopeDirectory(this.root, scope.key);
|
|
360
|
+
await mkdir(join(this.root, ARCHIVE_DIR), { recursive: true });
|
|
361
|
+
if (existsSync(destination))
|
|
362
|
+
return false;
|
|
363
|
+
await rename(scope.dir, destination);
|
|
364
|
+
return true;
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
/** Permanently remove one archived scope and all of its history/checkpoints. */
|
|
368
|
+
async purgeArchivedScope(scope) {
|
|
369
|
+
if (scope.key === 'global')
|
|
370
|
+
return false;
|
|
371
|
+
const directory = archivedScopeDirectory(this.root, scope.key);
|
|
372
|
+
if (!existsSync(directory))
|
|
373
|
+
return false;
|
|
374
|
+
await rm(directory, { recursive: true, force: true });
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
325
377
|
/** Read one scope without forcing initialization or writing any files. */
|
|
326
378
|
async readSnapshot(scope) {
|
|
327
379
|
const [summary, entries, state] = await Promise.all([
|
|
@@ -331,6 +383,10 @@ export class WorkspaceMemoryStore {
|
|
|
331
383
|
]);
|
|
332
384
|
return { scope, summary, entries, state };
|
|
333
385
|
}
|
|
386
|
+
/** Read a scope that currently lives in the recoverable archive. */
|
|
387
|
+
async readArchivedSnapshot(scope) {
|
|
388
|
+
return this.readSnapshot({ ...scope, dir: archivedScopeDirectory(this.root, scope.key) });
|
|
389
|
+
}
|
|
334
390
|
async writeEntries(scope, entries) {
|
|
335
391
|
await this.writeAtomic(join(scope.dir, ENTRIES_FILE), JSON.stringify(entries, null, 2) + '\n');
|
|
336
392
|
}
|