@max-null/dsh-memory 0.3.7 → 0.3.8

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.
Files changed (2) hide show
  1. package/client.js +549 -0
  2. package/package.json +5 -2
package/client.js ADDED
@@ -0,0 +1,549 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@max-null/dsh-memory",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+
7
+ "use strict";
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
25
+
26
+ // src/client/index.tsx
27
+ var index_exports = {};
28
+ __export(index_exports, {
29
+ apply: () => apply,
30
+ inject: () => inject
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+ var import_react = require("react");
34
+ var inject = ["slots", "locale"];
35
+ var BRAIN_PATHS = [
36
+ "M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z",
37
+ "M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"
38
+ ];
39
+ function brainIcon() {
40
+ return (0, import_react.createElement)("svg", {
41
+ width: 15,
42
+ height: 15,
43
+ viewBox: "0 0 24 24",
44
+ fill: "none",
45
+ stroke: "currentColor",
46
+ strokeWidth: 2,
47
+ strokeLinecap: "round",
48
+ strokeLinejoin: "round"
49
+ }, BRAIN_PATHS.map((d, index) => (0, import_react.createElement)("path", { key: index, d })));
50
+ }
51
+ var SETTINGS_NAV_MARKER = "data-dsh-memory-settings-nav";
52
+ var BRAIN_MASK_SVG = `%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z'/%3E%3Cpath d='M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z'/%3E%3C/svg%3E`;
53
+ var NAV_ICON_CSS = `
54
+ [data-dsh-memory-settings-nav] > svg:first-child { display: none; }
55
+ [data-dsh-memory-settings-nav]::before {
56
+ content: '';
57
+ flex: none;
58
+ width: 16px;
59
+ height: 16px;
60
+ background: currentColor;
61
+ -webkit-mask: url("data:image/svg+xml,${BRAIN_MASK_SVG}") center / contain no-repeat;
62
+ mask: url("data:image/svg+xml,${BRAIN_MASK_SVG}") center / contain no-repeat;
63
+ }
64
+ `;
65
+ var navCssInjected = false;
66
+ function injectNavCss() {
67
+ if (navCssInjected || typeof document === "undefined") return;
68
+ navCssInjected = true;
69
+ const style = document.createElement("style");
70
+ style.setAttribute("data-plugin", "@max-null/dsh-memory");
71
+ style.textContent = NAV_ICON_CSS;
72
+ document.head.append(style);
73
+ }
74
+ function registerSettingsNavIcon(label) {
75
+ let disposed = false;
76
+ const sync = () => {
77
+ if (disposed) return;
78
+ const currentLabel = label().trim();
79
+ const buttons = document.querySelectorAll('[role="dialog"] nav button');
80
+ for (const button of buttons) {
81
+ const matches = currentLabel.length > 0 && button.textContent?.trim() === currentLabel;
82
+ if (matches) button.setAttribute(SETTINGS_NAV_MARKER, "");
83
+ else button.removeAttribute(SETTINGS_NAV_MARKER);
84
+ }
85
+ };
86
+ sync();
87
+ const observer = new MutationObserver(sync);
88
+ observer.observe(document.body, { childList: true, subtree: true, characterData: true });
89
+ return () => {
90
+ disposed = true;
91
+ observer.disconnect();
92
+ document.querySelectorAll(`[${SETTINGS_NAV_MARKER}]`).forEach((element) => {
93
+ element.removeAttribute(SETTINGS_NAV_MARKER);
94
+ });
95
+ };
96
+ }
97
+ var STRINGS = {
98
+ zh: {
99
+ tabMemory: "\u8BB0\u5FC6",
100
+ memorySearch: "\u641C\u7D22\u8BB0\u5FC6\u2026",
101
+ empty: "\u9ED1\u6697\u4E2D\u672A\u89C1\u7075\u5149",
102
+ confirm: "\u786E\u8BA4",
103
+ forget: "\u5220\u9664",
104
+ groupPending: "\u5F85\u5BA1\u6838",
105
+ groupOnDemand: "\u5DF2\u5BA1\u6838 \xB7 \u6309\u9700",
106
+ groupInjected: "\u5E38\u9A7B\u6CE8\u5165",
107
+ injectSwitch: "\u5E38\u9A7B\u6CE8\u5165",
108
+ approveFirst: "\u5BA1\u6838\u901A\u8FC7\u540E\u53EF\u5E38\u9A7B\u6CE8\u5165",
109
+ allNamespaces: "\u5168\u90E8",
110
+ nsGlobal: "\u5168\u5C40",
111
+ nsWorkspace: "\u5DE5\u4F5C\u533A",
112
+ noWorkspace: "\u672A\u9009\u62E9\u5DE5\u4F5C\u533A",
113
+ organizeMemory: "\u6574\u7406\u8BB0\u5FC6",
114
+ confirmAll: "\u5168\u90E8\u786E\u8BA4",
115
+ injectPreview: "\u6CE8\u5165\u9884\u89C8",
116
+ contextUsage: "\u4E0A\u4E0B\u6587\u5360\u7528",
117
+ keywordsLabel: "\u5173\u952E\u8BCD",
118
+ suggested: "\u5F85\u5BA1\u6838",
119
+ approved: "\u5DF2\u5BA1\u6838",
120
+ refresh: "\u5237\u65B0"
121
+ },
122
+ en: {
123
+ tabMemory: "Memory",
124
+ memorySearch: "Search memory\u2026",
125
+ empty: "No spark in the dark",
126
+ confirm: "Confirm",
127
+ forget: "Forget",
128
+ groupPending: "Pending review",
129
+ groupOnDemand: "Approved \xB7 on demand",
130
+ groupInjected: "Always injected",
131
+ injectSwitch: "Inject every turn",
132
+ approveFirst: "Approve to enable injection",
133
+ allNamespaces: "All",
134
+ nsGlobal: "Global",
135
+ nsWorkspace: "Workspace",
136
+ noWorkspace: "No workspace selected",
137
+ organizeMemory: "Organize memory",
138
+ confirmAll: "Approve all",
139
+ injectPreview: "Injection preview",
140
+ contextUsage: "Context usage",
141
+ keywordsLabel: "Keywords",
142
+ suggested: "Suggested",
143
+ approved: "Approved",
144
+ refresh: "Refresh"
145
+ }
146
+ };
147
+ var localeId = "zh";
148
+ var localeListeners = /* @__PURE__ */ new Set();
149
+ function adoptLocale(id) {
150
+ const next = id === "en" ? "en" : "zh";
151
+ if (next === localeId) return;
152
+ localeId = next;
153
+ localeListeners.forEach((l) => l());
154
+ }
155
+ function fmt(tpl, vars = {}) {
156
+ return tpl.replace(/\{(\w+)\}/g, (_, k) => String(vars[k] ?? ""));
157
+ }
158
+ function useT() {
159
+ const [id, setId] = (0, import_react.useState)(localeId);
160
+ (0, import_react.useEffect)(() => {
161
+ const l = () => {
162
+ setId(localeId);
163
+ };
164
+ localeListeners.add(l);
165
+ return () => {
166
+ localeListeners.delete(l);
167
+ };
168
+ }, []);
169
+ return (key, vars) => fmt(STRINGS[id][key] ?? STRINGS.zh[key], vars);
170
+ }
171
+ var ssid = {
172
+ accent: "#4FC3F7",
173
+ wrap: { display: "flex", flexDirection: "column", gap: 8, padding: "10px 12px", overflowY: "auto", height: "100%", boxSizing: "border-box" },
174
+ card: {
175
+ background: "var(--dsw-alias-bg-layer-1, #131a26)",
176
+ border: "1px solid var(--dsw-alias-border-l2, #1e2836)",
177
+ borderRadius: 10,
178
+ padding: "10px 12px"
179
+ },
180
+ title: { fontSize: 12, fontWeight: 600, color: "var(--dsw-alias-label-secondary, #67748a)", marginBottom: 6, display: "flex", justifyContent: "space-between", alignItems: "center" },
181
+ text: { fontSize: 12.5, color: "var(--dsw-alias-label-primary, #d8e0ea)", lineHeight: 1.55 },
182
+ muted: { fontSize: 11, color: "var(--dsw-alias-label-secondary, #67748a)" },
183
+ empty: { padding: "28px 12px", textAlign: "center", fontSize: 12.5, color: "var(--dsw-alias-label-secondary, #67748a)" },
184
+ btn: {
185
+ padding: "3px 12px",
186
+ fontSize: 11.5,
187
+ background: "none",
188
+ border: "1px solid var(--dsw-alias-border-l2, #1e2836)",
189
+ borderRadius: 6,
190
+ color: "var(--dsw-alias-label-primary, #d8e0ea)",
191
+ cursor: "pointer"
192
+ }
193
+ };
194
+ async function api(method, payload) {
195
+ const res = await fetch(`/memory/api/${method}`, {
196
+ method: "POST",
197
+ headers: { "content-type": "application/json" },
198
+ body: JSON.stringify(payload ?? {})
199
+ });
200
+ const body = await res.json();
201
+ if (body.ok !== true) {
202
+ throw new Error(body.error?.message ?? `${method} failed`);
203
+ }
204
+ return body.value;
205
+ }
206
+ var memoryApi = {
207
+ list(filter = {}, cwd) {
208
+ return api("list", { filter, ...cwd === void 0 ? {} : { cwd } });
209
+ },
210
+ reload(cwd) {
211
+ return api("reload", { ...cwd === void 0 ? {} : { cwd } });
212
+ },
213
+ confirm(id, cwd) {
214
+ return api("confirm", { id, ...cwd === void 0 ? {} : { cwd } });
215
+ },
216
+ forget(id, cwd) {
217
+ return api("forget", { id, ...cwd === void 0 ? {} : { cwd } });
218
+ },
219
+ setInjected(id, injected, cwd) {
220
+ return api("setInjected", { id, injected, ...cwd === void 0 ? {} : { cwd } });
221
+ },
222
+ injectionPreview() {
223
+ return api("injectionPreview");
224
+ }
225
+ };
226
+ var ORGANIZE_PROMPT = "\u8BF7\u6574\u7406\u6211\u7684\u8BB0\u5FC6\u5E93\uFF1A\u7528 memory_list \u67E5\u770B\u5168\u90E8\u8BB0\u5FC6\u3002\u6574\u7406\u89C4\u5219\uFF08\u5FC5\u987B\u6267\u884C\uFF09\uFF1A\u2460\u540C\u7C7B\u6761\u76EE\u5408\u5E76\u2014\u2014\u5DE5\u4F5C\u4E60\u60EF/\u7EA6\u5B9A\u7C7B\u591A\u6761\u5408\u5E76\u4E3A\u4E00\u6761\uFF08\u5185\u5BB9\u7528 \u2460\u2461\u2462 \u5E76\u5217\uFF0C\u907F\u514D\u788E\u7247\u5316\uFF09\uFF1B\u2461\u4E0E\u300C[\u8BB0\u5FC6\u7CFB\u7EDF\u81EA\u8FF0]\u300D\uFF08\u4F60\u4E0A\u4E0B\u6587\u4E2D\u7684\u8BB0\u5FC6\u673A\u5236\u8BF4\u660E\uFF09\u5185\u5BB9\u91CD\u590D\u7684\u63D2\u4EF6\u4ECB\u7ECD\u6761\u76EE\uFF08\u5982 dsh-memory \u63D2\u4EF6\u53D1\u5E03\u4FE1\u606F\uFF09\u5E94\u5220\u9664\u2014\u2014\u673A\u5236\u8BF4\u660E\u5DF2\u7531\u7CFB\u7EDF\u5E38\u9A7B\u63D0\u4F9B\uFF0C\u65E0\u9700\u7528\u6237\u5B58\u50A8\uFF1B\u2462\u5BF9\u8FC7\u65F6\u3001\u9519\u8BEF\u6216\u5DF2\u53D8\u5316\u7684\u5185\u5BB9\u7528 memory_update \u4FEE\u6B63\uFF08\u4F1A\u91CD\u7F6E\u4E3A\u5F85\u5BA1\u6838\uFF09\uFF1B\u2463\u7CBE\u7B80\u5197\u957F\u5185\u5BB9\uFF0C\u4E3A\u6BCF\u6761\u8865\u5145\u6216\u4FEE\u6B63 keywords\uFF1B\u2464\u9700\u8981\u5220\u9664\u7684\u7528 memory_forget\uFF0C\u9700\u8981\u65B0\u589E\u7684\u7528 memory_save\u3002\u5224\u65AD\u5185\u5BB9\u662F\u5426\u8FC7\u65F6\u7684\u65B9\u6CD5\uFF1A\u628A\u8BB0\u5FC6\u91CC\u63D0\u5230\u7684\u5DE5\u5177\u540D/\u6570\u91CF\u4E0E\u4F60\u5F53\u524D\u5B9E\u9645\u53EF\u7528\u7684\u8BB0\u5FC6\u5DE5\u5177\u5BF9\u7167\u2014\u2014\u4F60\u5F53\u524D\u53EF\u7528\uFF1Amemory_save / memory_list / memory_search / memory_confirm / memory_forget / memory_update\uFF08\u5171 6 \u4E2A\uFF09\uFF1B\u82E5\u8BB0\u5FC6\u4E2D\u7684\u5DE5\u5177\u5217\u8868\u3001\u6570\u91CF\u3001\u6D41\u7A0B\u4E0E\u6B64\u4E0D\u7B26\u5373\u4E3A\u8FC7\u65F6\uFF0C\u7528 memory_update \u4FEE\u6B63\u3002\u6539\u52A8\u5168\u90E8\u843D\u5728 suggested \u7B49\u5F85\u5BA1\u6838\uFF08\u4E0D\u8981\u8C03\u7528 memory_confirm\uFF09\uFF0C\u5B8C\u6210\u540E\u7528\u4E00\u53E5\u8BDD\u6C47\u62A5\u6574\u7406\u7ED3\u679C\u3002";
227
+ function MemoryView(props) {
228
+ const t = useT();
229
+ const [records, setRecords] = (0, import_react.useState)([]);
230
+ const [query, setQuery] = (0, import_react.useState)("");
231
+ const [namespace, setNamespace] = (0, import_react.useState)(null);
232
+ const [refreshing, setRefreshing] = (0, import_react.useState)(false);
233
+ const [organizing, setOrganizing] = (0, import_react.useState)(false);
234
+ const [preview, setPreview] = (0, import_react.useState)(null);
235
+ const [previewOpen, setPreviewOpen] = (0, import_react.useState)(false);
236
+ const togglePreview = async () => {
237
+ if (previewOpen) {
238
+ setPreviewOpen(false);
239
+ return;
240
+ }
241
+ try {
242
+ setPreview(await memoryApi.injectionPreview());
243
+ } catch {
244
+ setPreview(null);
245
+ }
246
+ setPreviewOpen(true);
247
+ };
248
+ const reload = async () => {
249
+ try {
250
+ setRecords(await memoryApi.list({}, props.cwd));
251
+ } catch {
252
+ }
253
+ };
254
+ const refreshFromDisk = async () => {
255
+ setRefreshing(true);
256
+ try {
257
+ setRecords(await memoryApi.reload(props.cwd));
258
+ } catch {
259
+ await reload();
260
+ } finally {
261
+ setRefreshing(false);
262
+ }
263
+ };
264
+ (0, import_react.useEffect)(() => {
265
+ if (props.visible) void reload();
266
+ }, [props.visible]);
267
+ const toggleInjected = async (record) => {
268
+ if (record.status !== "approved") return;
269
+ try {
270
+ await memoryApi.setInjected(record.id, !record.injected, props.cwd);
271
+ } catch {
272
+ }
273
+ await reload();
274
+ };
275
+ const confirmAll = async () => {
276
+ const pending = records.filter((record) => record.status === "suggested");
277
+ if (pending.length === 0) return;
278
+ await Promise.all(pending.map((record) => memoryApi.confirm(record.id, props.cwd).catch(() => null)));
279
+ await reload();
280
+ };
281
+ const organize = async () => {
282
+ if (organizing) return;
283
+ setOrganizing(true);
284
+ try {
285
+ const sessions = props.ctx.get?.("sessions");
286
+ const conversation = props.ctx.get?.("conversation");
287
+ if (sessions === void 0 || conversation?.input?.for === void 0) {
288
+ throw new Error("sessions/conversation unavailable");
289
+ }
290
+ const sessionId = await sessions.create({});
291
+ sessions.open(sessionId);
292
+ let input;
293
+ for (let i = 0; i < 50; i++) {
294
+ try {
295
+ const actx = sessions.scope(sessionId);
296
+ if (actx !== void 0) {
297
+ input = conversation.input.for(actx);
298
+ if (input !== void 0) break;
299
+ }
300
+ } catch {
301
+ }
302
+ await new Promise((resolve) => setTimeout(resolve, 100));
303
+ }
304
+ if (input === void 0) throw new Error("composer input not ready");
305
+ input.setDraft(ORGANIZE_PROMPT);
306
+ input.submit();
307
+ } catch (error) {
308
+ console.warn("[dsh-memory] organize memory failed:", error);
309
+ } finally {
310
+ setOrganizing(false);
311
+ }
312
+ };
313
+ const q = query.trim().toLowerCase();
314
+ const byNs = namespace === null ? records : namespace === "workspace" ? records.filter((record) => record.namespace === "project") : records.filter((record) => record.namespace === "global");
315
+ const filtered = byNs.filter((record) => q === "" || record.content.toLowerCase().includes(q));
316
+ const groups = [
317
+ { key: "pending", label: t("groupPending"), items: filtered.filter((record) => record.status === "suggested") },
318
+ { key: "ondemand", label: t("groupOnDemand"), items: filtered.filter((record) => record.status === "approved" && !record.injected) },
319
+ { key: "injected", label: t("groupInjected"), items: filtered.filter((record) => record.status === "approved" && record.injected) }
320
+ ].filter((group) => group.items.length > 0);
321
+ return (0, import_react.createElement)(
322
+ "div",
323
+ { style: ssid.wrap },
324
+ (0, import_react.createElement)(
325
+ "div",
326
+ { style: { display: "flex", gap: 6 } },
327
+ (0, import_react.createElement)("button", {
328
+ type: "button",
329
+ title: t("organizeMemory"),
330
+ onClick: () => {
331
+ void organize();
332
+ },
333
+ disabled: organizing,
334
+ style: { ...ssid.btn, color: ssid.accent, borderColor: ssid.accent }
335
+ }, organizing ? "\u2026" : t("organizeMemory")),
336
+ (0, import_react.createElement)("button", {
337
+ type: "button",
338
+ title: t("injectPreview"),
339
+ onClick: () => {
340
+ void togglePreview();
341
+ },
342
+ style: { ...ssid.btn, ...previewOpen ? { color: ssid.accent, borderColor: ssid.accent } : {} }
343
+ }, t("injectPreview")),
344
+ (0, import_react.createElement)("input", {
345
+ value: query,
346
+ onChange: (event) => {
347
+ setQuery(event.target.value);
348
+ },
349
+ placeholder: t("memorySearch"),
350
+ style: {
351
+ flex: 1,
352
+ padding: "6px 10px",
353
+ fontSize: 12.5,
354
+ boxSizing: "border-box",
355
+ background: "var(--dsw-alias-bg-layer-1, #0f141d)",
356
+ border: "1px solid var(--dsw-alias-border-l2, #1e2836)",
357
+ borderRadius: 8,
358
+ color: "var(--dsw-alias-label-primary, #d8e0ea)",
359
+ outline: "none"
360
+ }
361
+ }),
362
+ (0, import_react.createElement)("button", {
363
+ type: "button",
364
+ title: t("refresh"),
365
+ onClick: () => {
366
+ void refreshFromDisk();
367
+ },
368
+ disabled: refreshing,
369
+ style: ssid.btn
370
+ }, refreshing ? "\u2026" : "\u21BB")
371
+ ),
372
+ (0, import_react.createElement)(
373
+ "div",
374
+ { style: { display: "flex", gap: 4 } },
375
+ [null, "global", "workspace"].map((ns) => (0, import_react.createElement)("button", {
376
+ key: ns ?? "all",
377
+ onClick: () => {
378
+ setNamespace(ns);
379
+ },
380
+ style: { flex: 1, ...ssid.btn, ...namespace === ns ? { color: ssid.accent, borderColor: ssid.accent } : {} }
381
+ }, ns === null ? t("allNamespaces") : ns === "global" ? t("nsGlobal") : t("nsWorkspace")))
382
+ ),
383
+ // 注入预览(开发者):self 自述 + 当前注入的记忆 + 上下文占用统计
384
+ previewOpen && preview !== null ? (0, import_react.createElement)(
385
+ "div",
386
+ { style: { ...ssid.card, display: "flex", flexDirection: "column", gap: 6 } },
387
+ (0, import_react.createElement)(
388
+ "div",
389
+ { style: { ...ssid.muted, fontSize: 10.5, display: "flex", justifyContent: "space-between", alignItems: "center" } },
390
+ (0, import_react.createElement)("span", null, t("contextUsage")),
391
+ (0, import_react.createElement)("span", null, (() => {
392
+ const selfChars = preview.self.length;
393
+ const injectedChars = preview.injected.reduce((sum, record) => sum + record.content.length + record.keywords.join("").length, 0);
394
+ const total = selfChars + injectedChars;
395
+ const tokens = Math.ceil(total / 2);
396
+ return `${selfChars}+${injectedChars} \u5B57\u7B26 \u2248 ${tokens} token`;
397
+ })())
398
+ ),
399
+ (0, import_react.createElement)("div", { style: { ...ssid.text, fontSize: 11, whiteSpace: "pre-wrap", wordBreak: "break-all" } }, preview.self),
400
+ preview.injected.length === 0 ? (0, import_react.createElement)("div", { style: ssid.muted }, t("empty")) : preview.injected.map((record) => (0, import_react.createElement)(
401
+ "div",
402
+ { key: record.id, style: { ...ssid.muted, fontSize: 11 } },
403
+ `- [memory:${record.id.slice(0, 8)}] ${record.content}`
404
+ ))
405
+ ) : null,
406
+ // 未选择工作区:工作区视图显示占位(0.3.4 工作区路由语义)
407
+ namespace === "workspace" && (props.cwd === void 0 || props.cwd === "") ? (0, import_react.createElement)("div", { style: ssid.empty }, t("noWorkspace")) : groups.length === 0 ? (0, import_react.createElement)("div", { style: ssid.empty }, t("empty")) : groups.map((group) => (0, import_react.createElement)(
408
+ "div",
409
+ { key: group.key, style: { display: "flex", flexDirection: "column", gap: 6 } },
410
+ (0, import_react.createElement)(
411
+ "div",
412
+ { style: ssid.title },
413
+ (0, import_react.createElement)("span", null, group.label),
414
+ (0, import_react.createElement)(
415
+ "div",
416
+ { style: { display: "flex", alignItems: "center", gap: 6 } },
417
+ group.key === "pending" && group.items.length > 0 ? (0, import_react.createElement)("button", {
418
+ type: "button",
419
+ title: t("confirmAll"),
420
+ onClick: () => {
421
+ void confirmAll();
422
+ },
423
+ style: { ...ssid.btn, padding: "1px 8px", fontSize: 10.5 }
424
+ }, t("confirmAll")) : null,
425
+ (0, import_react.createElement)("span", null, `${group.items.length}`)
426
+ )
427
+ ),
428
+ group.items.map((record) => (0, import_react.createElement)(
429
+ "div",
430
+ { key: record.id, style: ssid.card },
431
+ (0, import_react.createElement)("div", { style: ssid.text }, record.content),
432
+ // keywords 展示(0.3.5:设计约定 UI 显示 keywords)
433
+ record.keywords !== void 0 && record.keywords.length > 0 ? (0, import_react.createElement)(
434
+ "div",
435
+ { style: { display: "flex", flexWrap: "wrap", gap: 4, marginTop: 6 } },
436
+ record.keywords.map((keyword) => (0, import_react.createElement)("span", {
437
+ key: keyword,
438
+ style: {
439
+ fontSize: 10,
440
+ padding: "1px 7px",
441
+ borderRadius: 8,
442
+ background: "var(--dsw-alias-bg-module-platform, rgba(128,148,168,.14))",
443
+ color: "var(--dsw-alias-label-secondary, #67748a)"
444
+ }
445
+ }, keyword))
446
+ ) : null,
447
+ (0, import_react.createElement)(
448
+ "div",
449
+ { style: { ...ssid.muted, marginTop: 6 } },
450
+ `${record.namespace} \xB7 ${record.status === "approved" ? t("approved") : t("suggested")}${record.injected ? ` \xB7 ${t("groupInjected")}` : ""}`
451
+ ),
452
+ (0, import_react.createElement)(
453
+ "div",
454
+ { style: { display: "flex", gap: 6, marginTop: 8, alignItems: "center" } },
455
+ (0, import_react.createElement)("button", {
456
+ type: "button",
457
+ title: record.status === "approved" ? t("injectSwitch") : t("approveFirst"),
458
+ disabled: record.status !== "approved",
459
+ onClick: () => {
460
+ void toggleInjected(record);
461
+ },
462
+ style: {
463
+ ...ssid.btn,
464
+ ...record.injected ? { color: ssid.accent, borderColor: ssid.accent } : {},
465
+ opacity: record.status !== "approved" ? 0.4 : 1,
466
+ cursor: record.status !== "approved" ? "not-allowed" : "pointer"
467
+ }
468
+ }, record.injected ? `\u2713 ${t("injectSwitch")}` : t("injectSwitch")),
469
+ record.status === "suggested" ? (0, import_react.createElement)("button", {
470
+ style: ssid.btn,
471
+ onClick: () => {
472
+ void memoryApi.confirm(record.id, props.cwd).then(() => reload());
473
+ }
474
+ }, t("confirm")) : null,
475
+ (0, import_react.createElement)("button", {
476
+ style: ssid.btn,
477
+ onClick: () => {
478
+ void memoryApi.forget(record.id, props.cwd).then(() => reload());
479
+ }
480
+ }, t("forget"))
481
+ )
482
+ ))
483
+ ))
484
+ );
485
+ }
486
+ function currentSessionCwd(ctx) {
487
+ try {
488
+ const sessions = ctx.get?.("sessions");
489
+ const snapshot = sessions?.list?.getSnapshot?.();
490
+ if (snapshot?.current === void 0) return void 0;
491
+ return snapshot.byId?.[snapshot.current]?.cwd;
492
+ } catch {
493
+ return void 0;
494
+ }
495
+ }
496
+ function SettingsMemoryView(props) {
497
+ return (0, import_react.createElement)(MemoryView, {
498
+ visible: true,
499
+ ctx: props.ctx,
500
+ cwd: currentSessionCwd(props.ctx)
501
+ });
502
+ }
503
+ function apply(ctx) {
504
+ const face = ctx;
505
+ const locale = face.get?.("locale");
506
+ const initial = locale?.getLocale?.()?.active;
507
+ if (typeof initial === "string") adoptLocale(initial);
508
+ face.on?.("locale/change", (snap) => {
509
+ adoptLocale(snap?.active);
510
+ });
511
+ const slots = ctx.slots;
512
+ if (slots?.inject !== void 0) {
513
+ injectNavCss();
514
+ slots.inject("settings.section", () => slots.register({
515
+ name: "settings.section",
516
+ id: "dsh-memory",
517
+ order: 60,
518
+ label: () => STRINGS[localeId].tabMemory,
519
+ inject: () => ({})
520
+ }, () => (0, import_react.createElement)(SettingsMemoryView, {
521
+ ctx
522
+ })));
523
+ face.effect?.(() => registerSettingsNavIcon(() => STRINGS[localeId].tabMemory), "dsh-memory: settings navigation icon");
524
+ }
525
+ const root = ctx;
526
+ if (root.inject === void 0) return;
527
+ root.inject(["betterSidebar"], (sidebarCtx) => {
528
+ const service = sidebarCtx.betterSidebar;
529
+ if (service?.registerTab === void 0) return;
530
+ const tabCtx = ctx;
531
+ service.registerTab({
532
+ id: "@max-null/dsh-memory:memory",
533
+ title: () => STRINGS[localeId].tabMemory,
534
+ icon: brainIcon,
535
+ order: 60,
536
+ single: true,
537
+ component: ({ visible, scope }) => (0, import_react.createElement)(MemoryView, {
538
+ visible,
539
+ // 侧栏场景:当前会话工作区 cwd(TabComponentProps.scope)
540
+ cwd: scope?.cwd,
541
+ ctx: tabCtx
542
+ })
543
+ });
544
+ });
545
+ }
546
+ return module.exports;
547
+ },
548
+ });
549
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@max-null/dsh-memory",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "Cross-session plaintext memory plugin for the DeepSeek Harness — deterministic BM25 recall, human-owned, no embeddings",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -19,7 +19,10 @@
19
19
  },
20
20
  "files": [
21
21
  "dist",
22
- "cordis.patch.yml"
22
+ "client.js",
23
+ "cordis.patch.yml",
24
+ "README.md",
25
+ "LICENSE"
23
26
  ],
24
27
  "dsh": {
25
28
  "bundle": {