dsh-mnemon-source-memory-spaces 0.5.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js ADDED
@@ -0,0 +1,3462 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-mnemon-source-memory-spaces",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let dsh_mnemon_client = require("dsh-mnemon/client");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region src/contracts.ts
11
+ const CATEGORIES = [
12
+ "preference",
13
+ "decision",
14
+ "fact",
15
+ "insight",
16
+ "context",
17
+ "general"
18
+ ];
19
+ //#endregion
20
+ //#region src/client/ProviderIcon.tsx
21
+ function GenericProviderMark() {
22
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
23
+ viewBox: "0 0 36 36",
24
+ fill: "none",
25
+ xmlns: "http://www.w3.org/2000/svg",
26
+ children: [
27
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
28
+ width: "36",
29
+ height: "36",
30
+ rx: "9",
31
+ fill: "#F1F3F7"
32
+ }),
33
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ellipse", {
34
+ cx: "18",
35
+ cy: "11",
36
+ rx: "8",
37
+ ry: "3.5",
38
+ fill: "#68738A"
39
+ }),
40
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
41
+ d: "M10 11v7c0 1.93 3.58 3.5 8 3.5s8-1.57 8-3.5v-7M10 18v7c0 1.93 3.58 3.5 8 3.5s8-1.57 8-3.5v-7",
42
+ stroke: "#68738A",
43
+ strokeWidth: "1.6"
44
+ })
45
+ ]
46
+ });
47
+ }
48
+ /** Only the owning plugin supplies image data; unknown brands get a neutral mark. */
49
+ function ProviderIcon({ providerId, icon, className, title }) {
50
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
51
+ className,
52
+ "data-provider-icon": providerId,
53
+ ...title === void 0 ? { "aria-hidden": true } : {
54
+ role: "img",
55
+ "aria-label": title
56
+ },
57
+ children: icon?.kind === "brand" && icon.value === "mnemon" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.MnemonLogo, {}) : icon?.kind === "glyph" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
58
+ "aria-hidden": "true",
59
+ children: icon.value
60
+ }) : icon?.kind === "data-url" && /^data:image\/(?:png|jpeg|webp|svg\+xml);/u.test(icon.value) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
61
+ src: icon.value,
62
+ alt: ""
63
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GenericProviderMark, {})
64
+ });
65
+ }
66
+ //#endregion
67
+ //#region src/client/provider-presentation.ts
68
+ function providerDisplayLabel(_providerId, label) {
69
+ return label;
70
+ }
71
+ function providerSummary(t, provider) {
72
+ if (provider.summaryI18nKey === void 0) return provider.summary;
73
+ const localized = t(provider.summaryI18nKey);
74
+ return typeof localized !== "string" || localized.length === 0 || localized === provider.summaryI18nKey ? provider.summary : localized;
75
+ }
76
+ function providerFieldLabel(t, field) {
77
+ if (field.i18nKey === void 0) return field.label;
78
+ const localized = t(field.i18nKey);
79
+ return typeof localized !== "string" || localized.length === 0 || localized === field.i18nKey ? field.label : localized;
80
+ }
81
+ function providerOptionLabel(t, option) {
82
+ if (option.i18nKey === void 0) return option.label;
83
+ const localized = t(option.i18nKey);
84
+ return typeof localized !== "string" || localized.length === 0 || localized === option.i18nKey ? option.label : localized;
85
+ }
86
+ //#endregion
87
+ //#region src/client/pages.tsx
88
+ function memoryProviderFields(provider) {
89
+ return provider.fields.filter((field) => field.scope !== "service");
90
+ }
91
+ function providerDefaults(provider) {
92
+ return Object.fromEntries(memoryProviderFields(provider).flatMap((field) => field.defaultValue === void 0 ? [] : [[field.key, field.defaultValue]]));
93
+ }
94
+ function mergeProviderDefaults(providers, current) {
95
+ return Object.fromEntries(providers.map((provider) => [provider.id, {
96
+ ...providerDefaults(provider),
97
+ ...current[provider.id] ?? {}
98
+ }]));
99
+ }
100
+ function providerDraftComplete(provider, connection) {
101
+ if (provider === void 0 || provider.origin === "native") return true;
102
+ return provider.serviceConfigured !== false && memoryProviderFields(provider).every((field) => !field.required || String(connection?.[field.key] ?? "").trim() !== "");
103
+ }
104
+ function nativeBodyProvider(provider) {
105
+ return provider.origin === "native";
106
+ }
107
+ /** Shared memory-level Provider form used by manual creation, editing, and distillation policy. */
108
+ function ProviderMemoryFields(props) {
109
+ const t = (0, dsh_mnemon_client.useT)();
110
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
111
+ className: dsh_mnemon_client.memoryPageStyles.providerFields,
112
+ "data-provider": props.provider.id,
113
+ children: [
114
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
115
+ className: dsh_mnemon_client.memoryPageStyles.providerFieldHeading,
116
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
117
+ className: dsh_mnemon_client.memoryPageStyles.providerFieldIdentity,
118
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderIcon, {
119
+ providerId: props.provider.id,
120
+ icon: props.provider.icon,
121
+ className: dsh_mnemon_client.memoryPageStyles.providerFieldIcon
122
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: props.provider.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: providerSummary(t, props.provider) })] })]
123
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
124
+ props.provider.kind === "local" ? t("overview.providerKindLocal") : t("overview.providerKindRemote"),
125
+ " · ",
126
+ t(`overview.workspaceBinding.${props.provider.workspaceBinding}`)
127
+ ] })]
128
+ }),
129
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
130
+ className: dsh_mnemon_client.memoryPageStyles.providerAdvancedGrid,
131
+ children: memoryProviderFields(props.provider).map((field) => {
132
+ const label = providerFieldLabel(t, field);
133
+ const value = props.connection[field.key] ?? "";
134
+ const savedSecret = props.body?.provider.configuredSecrets.includes(field.key) === true;
135
+ const clearingSecret = props.clearSecrets?.includes(field.key) === true;
136
+ const required = field.required && (!savedSecret || clearingSecret);
137
+ const input = field.input === "boolean" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
138
+ "aria-label": label,
139
+ type: "checkbox",
140
+ checked: Boolean(value),
141
+ onChange: (event) => props.onChange(field.key, event.target.checked)
142
+ }) : field.input === "select" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
143
+ "aria-label": label,
144
+ value: String(value),
145
+ required,
146
+ onChange: (event) => props.onChange(field.key, event.target.value),
147
+ children: field.options?.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
148
+ value: option.value,
149
+ children: providerOptionLabel(t, option)
150
+ }, option.value))
151
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
152
+ "aria-label": label,
153
+ type: field.input === "secret" ? "password" : field.input === "number" ? "number" : field.input === "url" ? "url" : "text",
154
+ value: String(value),
155
+ required,
156
+ autoComplete: field.input === "secret" ? "new-password" : void 0,
157
+ placeholder: savedSecret ? t("overview.providerApiKeyKeep") : field.placeholder ?? (field.input === "secret" ? t("overview.providerApiKeyOptional") : void 0),
158
+ maxLength: field.maxLength ?? (field.input === "secret" ? 8e3 : 2e3),
159
+ min: field.min,
160
+ max: field.max,
161
+ pattern: field.pattern,
162
+ step: field.input === "number" ? "any" : void 0,
163
+ onChange: (event) => props.onChange(field.key, event.target.value)
164
+ });
165
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
166
+ className: dsh_mnemon_client.memoryPageStyles.providerFieldControl,
167
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [label, input] }), props.body !== void 0 && field.input === "secret" && savedSecret && props.onClearSecretsChange !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
168
+ className: dsh_mnemon_client.memoryPageStyles.providerSecretClear,
169
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
170
+ type: "checkbox",
171
+ checked: clearingSecret,
172
+ onChange: (event) => props.onClearSecretsChange(event.target.checked ? [.../* @__PURE__ */ new Set([...props.clearSecrets ?? [], field.key])] : (props.clearSecrets ?? []).filter((key) => key !== field.key))
173
+ }), t("overview.providerSecretClear")]
174
+ })]
175
+ }, field.key);
176
+ })
177
+ }),
178
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", {
179
+ className: dsh_mnemon_client.memoryPageStyles.providerWriteHint,
180
+ children: [
181
+ props.provider.capabilities.writeMode === "exact" ? t("overview.providerWriteExact") : t("overview.providerWriteAsync"),
182
+ " · ",
183
+ props.provider.capabilities.graph ? t("overview.providerGraphReady") : t("overview.providerSearchReady")
184
+ ]
185
+ })
186
+ ]
187
+ });
188
+ }
189
+ const CATEGORY_KEYS = {
190
+ decision: "category.decision",
191
+ preference: "category.preference",
192
+ fact: "category.fact",
193
+ insight: "category.insight",
194
+ context: "category.context",
195
+ general: "category.general"
196
+ };
197
+ function categoryLabel(t, category) {
198
+ return CATEGORY_KEYS[category] === void 0 ? category : t(CATEGORY_KEYS[category]);
199
+ }
200
+ function insightKey(insight) {
201
+ return `${insight.memoryBodyId ?? "memory"}:${insight.id}`;
202
+ }
203
+ function MemoryProviderBadge(props) {
204
+ const label = providerDisplayLabel(props.providerId, props.label);
205
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
206
+ className: dsh_mnemon_client.memoryPageStyles.providerBadge,
207
+ "data-provider": props.providerId,
208
+ title: label,
209
+ children: label
210
+ });
211
+ }
212
+ function ReadSourcePanel(props) {
213
+ const t = (0, dsh_mnemon_client.useT)();
214
+ if (props.sources.length === 0) return null;
215
+ const content = (source) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
217
+ className: dsh_mnemon_client.memoryPageStyles.readSourceSignal,
218
+ "aria-hidden": "true"
219
+ }),
220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
221
+ className: dsh_mnemon_client.memoryPageStyles.readSourceIdentity,
222
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: source.memoryBodyName }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
223
+ className: dsh_mnemon_client.memoryPageStyles.readSourceMeta,
224
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoryProviderBadge, {
225
+ providerId: source.providerId,
226
+ label: source.providerLabel
227
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t(`readSources.model.${source.providerId}`) })]
228
+ })]
229
+ }),
230
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
231
+ className: dsh_mnemon_client.memoryPageStyles.readSourceState,
232
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", { children: t(`readSources.mode.${source.mode}`) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [t(`readSources.status.${source.status}`, { count: source.itemCount }), source.edgeCount === void 0 || source.edgeCount === 0 ? "" : ` · ${t("readSources.edges", { count: source.edgeCount })}`] })]
233
+ })
234
+ ] });
235
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
236
+ className: dsh_mnemon_client.memoryPageStyles.readSources,
237
+ "aria-label": props.title,
238
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: props.title }), props.hint !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: props.hint })] }), props.onSelect !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
239
+ type: "button",
240
+ "aria-pressed": props.selectedBodyId === void 0,
241
+ "data-selected": props.selectedBodyId === void 0 ? "" : void 0,
242
+ onClick: () => props.onSelect?.(void 0),
243
+ children: t("readSources.all")
244
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: props.sources.map((source) => props.onSelect === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {
245
+ className: dsh_mnemon_client.memoryPageStyles.readSourceCard,
246
+ "data-provider": source.providerId,
247
+ "data-mode": source.mode,
248
+ "data-status": source.status,
249
+ title: source.hint,
250
+ children: content(source)
251
+ }, source.memoryBodyId) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
252
+ type: "button",
253
+ className: dsh_mnemon_client.memoryPageStyles.readSourceCard,
254
+ "data-provider": source.providerId,
255
+ "data-mode": source.mode,
256
+ "data-status": source.status,
257
+ "aria-pressed": props.selectedBodyId === source.memoryBodyId,
258
+ "data-selected": props.selectedBodyId === source.memoryBodyId || void 0,
259
+ title: source.hint,
260
+ onClick: () => props.onSelect?.(props.selectedBodyId === source.memoryBodyId ? void 0 : source.memoryBodyId),
261
+ children: content(source)
262
+ }, source.memoryBodyId)) })]
263
+ });
264
+ }
265
+ /** Full-text popup for a selected graph node whose inspector preview is clamped. */
266
+ function ContentPreview(props) {
267
+ const t = (0, dsh_mnemon_client.useT)();
268
+ const meta = [
269
+ props.kind,
270
+ props.node.id,
271
+ props.node.memoryBodyName
272
+ ].filter((entry) => entry !== void 0).join(" · ");
273
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SidebarModal, {
274
+ title: t("overview.previewTitle"),
275
+ description: meta,
276
+ onClose: props.onClose,
277
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
278
+ className: dsh_mnemon_client.memoryPageStyles.previewContent,
279
+ children: props.node.content
280
+ })
281
+ });
282
+ }
283
+ function InsightCard(props) {
284
+ const t = (0, dsh_mnemon_client.useT)();
285
+ const [confirming, setConfirming] = (0, react.useState)(false);
286
+ const [forgetting, setForgetting] = (0, react.useState)(false);
287
+ const { insight } = props;
288
+ const neutralActionClass = (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.ghostButton, dsh_mnemon_client.memorySidebarStyles.itemActionButton);
289
+ const forgetActionClass = (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.dangerButton, (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memorySidebarStyles.itemActionButton, dsh_mnemon_client.memorySidebarStyles.itemDangerAction));
290
+ const providerLabel = insight.memoryProviderLabel ?? insight.memoryProviderId;
291
+ const supportsRelated = insight.memoryCapabilities?.related === true;
292
+ const supportsForget = insight.memoryCapabilities?.forget === true;
293
+ const meta = [
294
+ insight.memoryBodyName,
295
+ providerLabel,
296
+ insight.category !== void 0 ? categoryLabel(t, insight.category) : void 0,
297
+ insight.importance !== void 0 ? t("common.importance", { value: insight.importance }) : void 0,
298
+ insight.score !== void 0 ? `score ${insight.score.toFixed(3)}` : void 0,
299
+ insight.depth !== void 0 ? t("common.hops", { count: insight.depth }) : void 0
300
+ ].filter((entry) => entry !== void 0);
301
+ const forget = async () => {
302
+ setForgetting(true);
303
+ try {
304
+ await props.onForget(insight);
305
+ } finally {
306
+ setForgetting(false);
307
+ setConfirming(false);
308
+ }
309
+ };
310
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
311
+ className: dsh_mnemon_client.memoryPageStyles.insightCard,
312
+ children: [
313
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
314
+ className: dsh_mnemon_client.memoryPageStyles.cardTop,
315
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
316
+ className: dsh_mnemon_client.memoryPageStyles.badges,
317
+ children: meta.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
318
+ className: dsh_mnemon_client.memoryPageStyles.badge,
319
+ children: entry
320
+ }, entry))
321
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
322
+ className: dsh_mnemon_client.memoryPageStyles.id,
323
+ title: insight.id,
324
+ children: insight.id.slice(0, 8)
325
+ })]
326
+ }),
327
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
328
+ className: dsh_mnemon_client.memoryPageStyles.content,
329
+ children: insight.content
330
+ }),
331
+ (insight.tags?.length ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
332
+ className: dsh_mnemon_client.memoryPageStyles.tags,
333
+ children: insight.tags.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: ["#", tag] }, tag))
334
+ }),
335
+ (insight.entities?.length ?? 0) > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
336
+ className: dsh_mnemon_client.memoryPageStyles.entities,
337
+ children: insight.entities.map((entity) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: entity }, entity))
338
+ }),
339
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
340
+ className: dsh_mnemon_client.memoryPageStyles.cardActions,
341
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
342
+ props.onRelated !== void 0 && supportsRelated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
343
+ type: "button",
344
+ className: neutralActionClass,
345
+ onClick: () => props.onRelated?.(insight),
346
+ children: t("card.related")
347
+ }),
348
+ props.onClone !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
349
+ type: "button",
350
+ className: neutralActionClass,
351
+ onClick: () => props.onClone?.(insight),
352
+ children: t("card.clone")
353
+ }),
354
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
355
+ type: "button",
356
+ className: neutralActionClass,
357
+ onClick: () => void navigator.clipboard?.writeText(insight.id),
358
+ children: t("common.copyId")
359
+ }),
360
+ props.writeEnabled && supportsForget && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
361
+ type: "button",
362
+ className: forgetActionClass,
363
+ onClick: () => setConfirming(true),
364
+ children: t("card.forget")
365
+ })
366
+ ] })
367
+ })
368
+ ]
369
+ }), confirming && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SidebarModal, {
370
+ title: t("card.confirmText"),
371
+ description: `${insight.memoryBodyName ?? insight.memoryBodyId ?? ""}${insight.memoryBodyName === void 0 && insight.memoryBodyId === void 0 ? "" : " · "}${insight.id}`,
372
+ busy: forgetting,
373
+ onClose: () => setConfirming(false),
374
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
375
+ className: dsh_mnemon_client.memoryPageStyles.modalFooterActions,
376
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
377
+ type: "button",
378
+ "data-dialog-close": true,
379
+ "data-autofocus": true,
380
+ className: dsh_mnemon_client.memoryPageStyles.ghostButton,
381
+ disabled: forgetting,
382
+ onClick: () => setConfirming(false),
383
+ children: t("common.cancel")
384
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
385
+ type: "button",
386
+ className: dsh_mnemon_client.memoryPageStyles.dangerSolidButton,
387
+ disabled: forgetting,
388
+ onClick: () => void forget(),
389
+ children: forgetting ? t("card.processing") : t("card.confirmForget")
390
+ })]
391
+ }),
392
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
393
+ className: dsh_mnemon_client.memoryPageStyles.bodyDeleteConfirm,
394
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
395
+ className: dsh_mnemon_client.memoryPageStyles.bodyDeleteSummary,
396
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
397
+ className: dsh_mnemon_client.memoryPageStyles.bodyDeleteContent,
398
+ children: insight.content
399
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: meta.join(" · ") })]
400
+ })
401
+ })
402
+ })] });
403
+ }
404
+ const GRAPH_WIDTH = 930;
405
+ const GRAPH_HEIGHT = 520;
406
+ const GRAPH_MARGIN_X = 58;
407
+ const GRAPH_MARGIN_Y = 58;
408
+ const CATEGORY_ORDER = [
409
+ "space",
410
+ "entity",
411
+ "preference",
412
+ "decision",
413
+ "fact",
414
+ "insight",
415
+ "context",
416
+ "general"
417
+ ];
418
+ function hash(value) {
419
+ let result = 2166136261;
420
+ for (const char of value) result = Math.imul(result ^ char.charCodeAt(0), 16777619);
421
+ return result >>> 0;
422
+ }
423
+ function graphNodeKey(node) {
424
+ return node.graphId ?? node.id;
425
+ }
426
+ function graphNodeKind(node) {
427
+ return node.kind ?? "memory";
428
+ }
429
+ function spaceGraphId(id) {
430
+ return `space:${id}`;
431
+ }
432
+ function entityGraphId(entity) {
433
+ return `entity:${encodeURIComponent(normalizeEntity(entity))}`;
434
+ }
435
+ function normalizeEntity(entity) {
436
+ return entity.normalize("NFKC").trim().toLocaleLowerCase();
437
+ }
438
+ /** Add routing scopes and entity indexes without issuing another recall. */
439
+ function enrichMultiSpaceGraph(graph, bodies) {
440
+ if (graph.nodes.length === 0) return graph;
441
+ const memories = graph.nodes.map((node) => ({
442
+ ...node,
443
+ kind: "memory"
444
+ }));
445
+ const memoriesByBody = /* @__PURE__ */ new Map();
446
+ for (const node of memories) {
447
+ if (node.memoryBodyId === void 0) continue;
448
+ memoriesByBody.set(node.memoryBodyId, [...memoriesByBody.get(node.memoryBodyId) ?? [], node]);
449
+ }
450
+ const activeBodies = bodies.filter((body) => body.active && ((memoriesByBody.get(body.id)?.length ?? 0) > 0 || (body.stats?.topEntities.length ?? 0) > 0));
451
+ const spaceNodes = activeBodies.map((body) => ({
452
+ id: body.id,
453
+ graphId: spaceGraphId(body.id),
454
+ kind: "space",
455
+ category: "space",
456
+ content: body.name,
457
+ color: "#22a879",
458
+ memoryBodyId: body.id,
459
+ memoryBodyName: body.name,
460
+ memoryProviderId: body.provider.id,
461
+ occurrenceCount: body.stats?.totalInsights ?? memoriesByBody.get(body.id)?.length ?? 0
462
+ }));
463
+ const edges = graph.edges.filter((edge) => edge.type !== "entity");
464
+ for (const body of activeBodies) for (const memory of memoriesByBody.get(body.id) ?? []) edges.push({
465
+ sourceId: spaceGraphId(body.id),
466
+ targetId: graphNodeKey(memory),
467
+ label: "scope",
468
+ color: "#708199",
469
+ type: "scope"
470
+ });
471
+ const bodiesById = new Map(activeBodies.map((body) => [body.id, body]));
472
+ const indexedEntities = /* @__PURE__ */ new Map();
473
+ for (const memory of memories) {
474
+ const body = memory.memoryBodyId === void 0 ? void 0 : bodiesById.get(memory.memoryBodyId);
475
+ if (body === void 0) continue;
476
+ const seen = /* @__PURE__ */ new Set();
477
+ for (const rawEntity of memory.entities ?? []) {
478
+ const entity = rawEntity.trim();
479
+ const key = normalizeEntity(entity);
480
+ if (key === "" || seen.has(key)) continue;
481
+ seen.add(key);
482
+ const current = indexedEntities.get(key);
483
+ if (current === void 0) indexedEntities.set(key, {
484
+ entity,
485
+ memories: [memory],
486
+ bodies: [body]
487
+ });
488
+ else {
489
+ current.memories.push(memory);
490
+ if (!current.bodies.some((candidate) => candidate.id === body.id)) current.bodies.push(body);
491
+ }
492
+ }
493
+ }
494
+ const entities = [...indexedEntities.values()].sort((left, right) => right.memories.length - left.memories.length || left.entity.localeCompare(right.entity)).slice(0, 24);
495
+ const entityNodes = entities.map((item) => ({
496
+ id: item.entity,
497
+ graphId: entityGraphId(item.entity),
498
+ kind: "entity",
499
+ category: "entity",
500
+ content: item.entity,
501
+ color: "#2b9db9",
502
+ occurrenceCount: item.memories.length,
503
+ memoryBodyIds: item.bodies.map((body) => body.id),
504
+ memoryBodyNames: item.bodies.map((body) => body.name)
505
+ }));
506
+ for (const item of entities) {
507
+ const key = entityGraphId(item.entity);
508
+ for (const memory of item.memories) edges.push({
509
+ sourceId: key,
510
+ targetId: graphNodeKey(memory),
511
+ label: item.entity,
512
+ color: "#22a879",
513
+ type: "entity"
514
+ });
515
+ }
516
+ return {
517
+ ...graph,
518
+ nodes: [
519
+ ...spaceNodes,
520
+ ...entityNodes,
521
+ ...memories
522
+ ],
523
+ edges
524
+ };
525
+ }
526
+ function graphKindLabel(t, node) {
527
+ const kind = graphNodeKind(node);
528
+ return kind === "space" ? t("graph.kindSpace") : kind === "entity" ? t("graph.kindEntity") : categoryLabel(t, node.category ?? "general");
529
+ }
530
+ function activeCategoryAnchors(grouped) {
531
+ const categories = [...grouped.keys()].sort((left, right) => {
532
+ const leftIndex = CATEGORY_ORDER.indexOf(left);
533
+ const rightIndex = CATEGORY_ORDER.indexOf(right);
534
+ return (leftIndex < 0 ? CATEGORY_ORDER.length : leftIndex) - (rightIndex < 0 ? CATEGORY_ORDER.length : rightIndex);
535
+ });
536
+ const anchors = /* @__PURE__ */ new Map();
537
+ if (categories.length === 1) {
538
+ anchors.set(categories[0], {
539
+ x: GRAPH_WIDTH / 2,
540
+ y: GRAPH_HEIGHT / 2
541
+ });
542
+ return anchors;
543
+ }
544
+ categories.forEach((category, index) => {
545
+ const angle = -Math.PI / 2 + index / categories.length * Math.PI * 2;
546
+ anchors.set(category, {
547
+ x: GRAPH_WIDTH / 2 + Math.cos(angle) * Math.min(250, 115 + categories.length * 23),
548
+ y: GRAPH_HEIGHT / 2 + Math.sin(angle) * Math.min(165, 78 + categories.length * 15)
549
+ });
550
+ });
551
+ return anchors;
552
+ }
553
+ function clampGraphPosition(position) {
554
+ return {
555
+ x: Math.min(872, Math.max(GRAPH_MARGIN_X, position.x)),
556
+ y: Math.min(462, Math.max(GRAPH_MARGIN_Y, position.y))
557
+ };
558
+ }
559
+ function naturalGraphPositions(nodes, edges) {
560
+ const positions = /* @__PURE__ */ new Map();
561
+ const grouped = /* @__PURE__ */ new Map();
562
+ for (const node of nodes) {
563
+ const category = node.category ?? "general";
564
+ grouped.set(category, [...grouped.get(category) ?? [], node]);
565
+ }
566
+ const anchors = activeCategoryAnchors(grouped);
567
+ for (const [category, items] of grouped) {
568
+ const anchor = anchors.get(category) ?? {
569
+ x: GRAPH_WIDTH / 2,
570
+ y: GRAPH_HEIGHT / 2
571
+ };
572
+ items.forEach((node, index) => {
573
+ const seed = hash(graphNodeKey(node));
574
+ const angle = index * 2.399963 + seed % 37 / 37 * .4;
575
+ const radius = items.length === 1 ? 0 : 24 + Math.sqrt(index + 1) * 35;
576
+ positions.set(graphNodeKey(node), clampGraphPosition({
577
+ x: anchor.x + Math.cos(angle) * radius,
578
+ y: anchor.y + Math.sin(angle) * radius
579
+ }));
580
+ });
581
+ }
582
+ const velocities = new Map(nodes.map((node) => [graphNodeKey(node), {
583
+ x: 0,
584
+ y: 0
585
+ }]));
586
+ const visibleIds = new Set(nodes.map(graphNodeKey));
587
+ const visibleEdges = edges.filter((edge) => visibleIds.has(edge.sourceId) && visibleIds.has(edge.targetId));
588
+ for (let iteration = 0; iteration < 150; iteration += 1) {
589
+ const cooling = 1 - iteration / 180;
590
+ for (let leftIndex = 0; leftIndex < nodes.length; leftIndex += 1) {
591
+ const left = nodes[leftIndex];
592
+ const leftPosition = positions.get(graphNodeKey(left));
593
+ const leftVelocity = velocities.get(graphNodeKey(left));
594
+ for (let rightIndex = leftIndex + 1; rightIndex < nodes.length; rightIndex += 1) {
595
+ const right = nodes[rightIndex];
596
+ const rightPosition = positions.get(graphNodeKey(right));
597
+ const rightVelocity = velocities.get(graphNodeKey(right));
598
+ let dx = leftPosition.x - rightPosition.x;
599
+ let dy = leftPosition.y - rightPosition.y;
600
+ if (dx === 0 && dy === 0) {
601
+ dx = hash(graphNodeKey(left)) % 13 - 6 || 1;
602
+ dy = hash(graphNodeKey(right)) % 11 - 5 || -1;
603
+ }
604
+ const distanceSquared = Math.max(100, dx * dx + dy * dy);
605
+ const distance = Math.sqrt(distanceSquared);
606
+ const force = Math.min(9, 18e3 / distanceSquared) * cooling + (distance < 66 ? (66 - distance) * .08 : 0);
607
+ const forceX = dx / distance * force;
608
+ const forceY = dy / distance * force;
609
+ leftVelocity.x += forceX;
610
+ leftVelocity.y += forceY;
611
+ rightVelocity.x -= forceX;
612
+ rightVelocity.y -= forceY;
613
+ }
614
+ }
615
+ for (const edge of visibleEdges) {
616
+ const source = positions.get(edge.sourceId);
617
+ const target = positions.get(edge.targetId);
618
+ const sourceVelocity = velocities.get(edge.sourceId);
619
+ const targetVelocity = velocities.get(edge.targetId);
620
+ const dx = target.x - source.x;
621
+ const dy = target.y - source.y;
622
+ const distance = Math.max(1, Math.hypot(dx, dy));
623
+ const sparseScale = nodes.length <= 3 ? 2 : nodes.length <= 8 ? 1.45 : 1;
624
+ const spring = (distance - (edge.type === "scope" ? 138 : edge.type === "entity" ? 94 : edge.type === "semantic" ? 118 : 106) * sparseScale) * .018 * cooling;
625
+ const forceX = dx / distance * spring;
626
+ const forceY = dy / distance * spring;
627
+ sourceVelocity.x += forceX;
628
+ sourceVelocity.y += forceY;
629
+ targetVelocity.x -= forceX;
630
+ targetVelocity.y -= forceY;
631
+ }
632
+ for (const node of nodes) {
633
+ const key = graphNodeKey(node);
634
+ const position = positions.get(key);
635
+ const velocity = velocities.get(key);
636
+ const anchor = anchors.get(node.category ?? "general") ?? {
637
+ x: GRAPH_WIDTH / 2,
638
+ y: GRAPH_HEIGHT / 2
639
+ };
640
+ velocity.x += (anchor.x - position.x) * .0035 * cooling + (GRAPH_WIDTH / 2 - position.x) * 8e-4;
641
+ velocity.y += (anchor.y - position.y) * .0035 * cooling + (GRAPH_HEIGHT / 2 - position.y) * 8e-4;
642
+ velocity.x = Math.max(-12, Math.min(12, velocity.x * .76));
643
+ velocity.y = Math.max(-12, Math.min(12, velocity.y * .76));
644
+ positions.set(key, clampGraphPosition({
645
+ x: position.x + velocity.x,
646
+ y: position.y + velocity.y
647
+ }));
648
+ }
649
+ }
650
+ return positions;
651
+ }
652
+ function uniformGraphPositions(nodes) {
653
+ const positions = /* @__PURE__ */ new Map();
654
+ const ordered = [...nodes].sort((left, right) => {
655
+ const categoryDifference = CATEGORY_ORDER.indexOf(left.category ?? "general") - CATEGORY_ORDER.indexOf(right.category ?? "general");
656
+ return categoryDifference === 0 ? left.id.localeCompare(right.id) : categoryDifference;
657
+ });
658
+ const columns = Math.max(1, Math.ceil(Math.sqrt(ordered.length * 1.65)));
659
+ const rows = Math.max(1, Math.ceil(ordered.length / columns));
660
+ const cellWidth = 814 / columns;
661
+ const cellHeight = 404 / rows;
662
+ ordered.forEach((node, index) => {
663
+ const row = Math.floor(index / columns);
664
+ const column = index % columns;
665
+ const rowLength = Math.min(columns, ordered.length - row * columns);
666
+ const rowOffset = (columns - rowLength) * cellWidth / 2;
667
+ positions.set(graphNodeKey(node), {
668
+ x: GRAPH_MARGIN_X + rowOffset + cellWidth * (column + .5),
669
+ y: GRAPH_MARGIN_Y + cellHeight * (row + .5)
670
+ });
671
+ });
672
+ return positions;
673
+ }
674
+ function graphPoint(svg, clientX, clientY) {
675
+ const matrix = svg.getScreenCTM?.();
676
+ if (matrix !== null && matrix !== void 0 && typeof svg.createSVGPoint === "function") {
677
+ const point = svg.createSVGPoint();
678
+ point.x = clientX;
679
+ point.y = clientY;
680
+ return clampGraphPosition(point.matrixTransform(matrix.inverse()));
681
+ }
682
+ const bounds = svg.getBoundingClientRect();
683
+ const width = bounds.width || GRAPH_WIDTH;
684
+ const height = bounds.height || GRAPH_HEIGHT;
685
+ return clampGraphPosition({
686
+ x: (clientX - bounds.left) * GRAPH_WIDTH / width,
687
+ y: (clientY - bounds.top) * GRAPH_HEIGHT / height
688
+ });
689
+ }
690
+ function MemoryGraph(props) {
691
+ const t = (0, dsh_mnemon_client.useT)();
692
+ const visibleNodes = (0, react.useMemo)(() => {
693
+ const spaces = props.graph.nodes.filter((node) => graphNodeKind(node) === "space");
694
+ const entities = props.graph.nodes.filter((node) => graphNodeKind(node) === "entity").slice(0, 20);
695
+ const memories = props.graph.nodes.filter((node) => graphNodeKind(node) === "memory").slice(0, Math.max(0, 60 - spaces.length - entities.length));
696
+ return [
697
+ ...spaces,
698
+ ...entities,
699
+ ...memories
700
+ ].slice(0, 60);
701
+ }, [props.graph.nodes]);
702
+ const visibleIds = (0, react.useMemo)(() => new Set(visibleNodes.map(graphNodeKey)), [visibleNodes]);
703
+ const visibleKinds = (0, react.useMemo)(() => new Map(visibleNodes.map((node) => [graphNodeKey(node), graphNodeKind(node)])), [visibleNodes]);
704
+ const edges = (0, react.useMemo)(() => {
705
+ const priority = /* @__PURE__ */ new Map([
706
+ ["entity", 0],
707
+ ["scope", 1],
708
+ ["causal", 2],
709
+ ["semantic", 3],
710
+ ["temporal", 4]
711
+ ]);
712
+ return props.graph.edges.filter((edge) => visibleIds.has(edge.sourceId) && visibleIds.has(edge.targetId)).map((edge, index) => ({
713
+ edge,
714
+ index
715
+ })).sort((left, right) => (priority.get(left.edge.type ?? "temporal") ?? 5) - (priority.get(right.edge.type ?? "temporal") ?? 5) || left.index - right.index).slice(0, 180).map(({ edge }) => edge);
716
+ }, [props.graph.edges, visibleIds]);
717
+ const curvedEdges = (0, react.useMemo)(() => {
718
+ const groups = /* @__PURE__ */ new Map();
719
+ edges.forEach((edge, index) => {
720
+ const key = [edge.sourceId, edge.targetId].sort().join("::");
721
+ groups.set(key, [...groups.get(key) ?? [], index]);
722
+ });
723
+ return edges.map((edge, index) => {
724
+ const key = [edge.sourceId, edge.targetId].sort().join("::");
725
+ const group = groups.get(key) ?? [index];
726
+ return {
727
+ edge,
728
+ offset: (group.indexOf(index) - (group.length - 1) / 2) * 12
729
+ };
730
+ });
731
+ }, [edges]);
732
+ const layoutKey = `${visibleNodes.map((node) => `${graphNodeKey(node)}:${graphNodeKind(node)}:${node.category ?? "general"}`).join("|")}::${edges.map((edge) => `${edge.sourceId}>${edge.targetId}:${edge.type ?? "temporal"}`).join("|")}`;
733
+ const naturalLayout = (0, react.useMemo)(() => naturalGraphPositions(visibleNodes, edges), [layoutKey]);
734
+ const [positions, setPositions] = (0, react.useState)(() => naturalLayout);
735
+ const [layoutMode, setLayoutMode] = (0, react.useState)("natural");
736
+ const positionsRef = (0, react.useRef)(positions);
737
+ const animationRef = (0, react.useRef)(null);
738
+ const dragRef = (0, react.useRef)(null);
739
+ const commitPositions = (0, react.useCallback)((next) => {
740
+ positionsRef.current = next;
741
+ setPositions(next);
742
+ }, []);
743
+ const cancelAnimation = (0, react.useCallback)(() => {
744
+ if (animationRef.current !== null && typeof window.cancelAnimationFrame === "function") window.cancelAnimationFrame(animationRef.current);
745
+ animationRef.current = null;
746
+ }, []);
747
+ const animateTo = (0, react.useCallback)((target, mode) => {
748
+ cancelAnimation();
749
+ setLayoutMode(mode);
750
+ if (typeof window.requestAnimationFrame !== "function") {
751
+ commitPositions(target);
752
+ return;
753
+ }
754
+ const start = new Map(positionsRef.current);
755
+ const startedAt = performance.now();
756
+ const tick = (time) => {
757
+ const progress = Math.min(1, (time - startedAt) / 620);
758
+ const eased = 1 - Math.pow(1 - progress, 3);
759
+ const next = /* @__PURE__ */ new Map();
760
+ for (const [id, destination] of target) {
761
+ const origin = start.get(id) ?? {
762
+ x: GRAPH_WIDTH / 2,
763
+ y: GRAPH_HEIGHT / 2
764
+ };
765
+ next.set(id, {
766
+ x: origin.x + (destination.x - origin.x) * eased,
767
+ y: origin.y + (destination.y - origin.y) * eased
768
+ });
769
+ }
770
+ commitPositions(next);
771
+ if (progress < 1) animationRef.current = window.requestAnimationFrame(tick);
772
+ else animationRef.current = null;
773
+ };
774
+ animationRef.current = window.requestAnimationFrame(tick);
775
+ }, [cancelAnimation, commitPositions]);
776
+ (0, react.useEffect)(() => {
777
+ animateTo(naturalLayout, "natural");
778
+ }, [layoutKey]);
779
+ (0, react.useEffect)(() => () => cancelAnimation(), [cancelAnimation]);
780
+ const beginDrag = (event, nodeId) => {
781
+ cancelAnimation();
782
+ dragRef.current = {
783
+ nodeId,
784
+ pointerId: event.pointerId,
785
+ startX: event.clientX,
786
+ startY: event.clientY,
787
+ moved: false
788
+ };
789
+ event.currentTarget.setPointerCapture?.(event.pointerId);
790
+ };
791
+ const moveDrag = (event) => {
792
+ const drag = dragRef.current;
793
+ const svg = event.currentTarget.ownerSVGElement;
794
+ if (drag === null || svg === null || drag.pointerId !== event.pointerId) return;
795
+ if (!drag.moved && Math.hypot(event.clientX - drag.startX, event.clientY - drag.startY) < 4) return;
796
+ drag.moved = true;
797
+ const point = graphPoint(svg, event.clientX, event.clientY);
798
+ const next = new Map(positionsRef.current);
799
+ next.set(drag.nodeId, point);
800
+ commitPositions(next);
801
+ setLayoutMode("custom");
802
+ };
803
+ const endDrag = (event) => {
804
+ const drag = dragRef.current;
805
+ if (drag === null || drag.pointerId !== event.pointerId) return;
806
+ const svg = event.currentTarget.ownerSVGElement;
807
+ if (drag.moved && svg !== null) {
808
+ const next = new Map(positionsRef.current);
809
+ next.set(drag.nodeId, graphPoint(svg, event.clientX, event.clientY));
810
+ commitPositions(next);
811
+ }
812
+ dragRef.current = null;
813
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
814
+ if (!drag.moved) {
815
+ const node = visibleNodes.find((candidate) => graphNodeKey(candidate) === drag.nodeId);
816
+ if (node !== void 0) props.onSelect(node);
817
+ }
818
+ };
819
+ const cancelDrag = (event) => {
820
+ if (dragRef.current?.pointerId === event.pointerId) dragRef.current = null;
821
+ };
822
+ const nudge = (nodeId, dx, dy) => {
823
+ cancelAnimation();
824
+ const current = positionsRef.current.get(nodeId);
825
+ if (current === void 0) return;
826
+ const next = new Map(positionsRef.current);
827
+ next.set(nodeId, clampGraphPosition({
828
+ x: current.x + dx,
829
+ y: current.y + dy
830
+ }));
831
+ commitPositions(next);
832
+ setLayoutMode("custom");
833
+ };
834
+ const layoutLabel = t(layoutMode === "natural" ? "graph.layoutNatural" : layoutMode === "uniform" ? "graph.layoutUniform" : "graph.layoutCustom");
835
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
836
+ className: dsh_mnemon_client.memoryPageStyles.graphCanvasControls,
837
+ role: "toolbar",
838
+ "aria-label": t("graph.layoutAria"),
839
+ children: [
840
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
841
+ role: "status",
842
+ "aria-label": t("graph.layoutStatus", { layout: layoutLabel }),
843
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {}), t("graph.draggable", { layout: layoutLabel })]
844
+ }),
845
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
846
+ type: "button",
847
+ "data-active": layoutMode === "natural" || void 0,
848
+ onClick: () => animateTo(naturalGraphPositions(visibleNodes, edges), "natural"),
849
+ children: t("graph.naturalAction")
850
+ }),
851
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
852
+ type: "button",
853
+ "data-active": layoutMode === "uniform" || void 0,
854
+ onClick: () => animateTo(uniformGraphPositions(visibleNodes), "uniform"),
855
+ children: t("graph.uniformAction")
856
+ })
857
+ ]
858
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
859
+ className: dsh_mnemon_client.memoryPageStyles.graphSvg,
860
+ viewBox: `0 0 ${GRAPH_WIDTH} ${GRAPH_HEIGHT}`,
861
+ role: "img",
862
+ "data-layout": layoutMode,
863
+ "data-density": visibleNodes.length <= 12 ? "sparse" : "dense",
864
+ "aria-label": t("graph.aria", {
865
+ nodes: props.graph.nodes.length,
866
+ edges: props.graph.edges.length
867
+ }),
868
+ children: [
869
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("defs", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("pattern", {
870
+ id: "mnemon-grid",
871
+ width: "26",
872
+ height: "26",
873
+ patternUnits: "userSpaceOnUse",
874
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
875
+ d: "M 26 0 L 0 0 0 26",
876
+ className: dsh_mnemon_client.memoryPageStyles.graphGridLine,
877
+ fill: "none"
878
+ })
879
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("filter", {
880
+ id: "mnemon-glow",
881
+ x: "-100%",
882
+ y: "-100%",
883
+ width: "300%",
884
+ height: "300%",
885
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("feGaussianBlur", {
886
+ stdDeviation: "4",
887
+ result: "blur"
888
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("feMerge", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("feMergeNode", { in: "blur" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("feMergeNode", { in: "SourceGraphic" })] })]
889
+ })] }),
890
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
891
+ width: GRAPH_WIDTH,
892
+ height: GRAPH_HEIGHT,
893
+ className: dsh_mnemon_client.memoryPageStyles.graphBackdrop
894
+ }),
895
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
896
+ width: GRAPH_WIDTH,
897
+ height: GRAPH_HEIGHT,
898
+ fill: "url(#mnemon-grid)"
899
+ }),
900
+ curvedEdges.map(({ edge, offset }, index) => {
901
+ const source = positions.get(edge.sourceId) ?? naturalLayout.get(edge.sourceId) ?? {
902
+ x: GRAPH_WIDTH / 2,
903
+ y: GRAPH_HEIGHT / 2
904
+ };
905
+ const target = positions.get(edge.targetId) ?? naturalLayout.get(edge.targetId) ?? {
906
+ x: GRAPH_WIDTH / 2,
907
+ y: GRAPH_HEIGHT / 2
908
+ };
909
+ const dx = target.x - source.x;
910
+ const dy = target.y - source.y;
911
+ const distance = Math.max(1, Math.hypot(dx, dy));
912
+ const direction = edge.sourceId.localeCompare(edge.targetId) <= 0 ? 1 : -1;
913
+ const controlX = (source.x + target.x) / 2 - dy / distance * offset * direction;
914
+ const controlY = (source.y + target.y) / 2 + dx / distance * offset * direction;
915
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
916
+ d: `M ${source.x} ${source.y} Q ${controlX} ${controlY} ${target.x} ${target.y}`,
917
+ className: dsh_mnemon_client.memoryPageStyles.graphEdge,
918
+ "data-edge": edge.type ?? "temporal",
919
+ "data-source-id": edge.sourceId,
920
+ "data-target-id": edge.targetId,
921
+ "data-source-kind": visibleKinds.get(edge.sourceId),
922
+ "data-target-kind": visibleKinds.get(edge.targetId)
923
+ }, `${edge.sourceId}-${edge.targetId}-${index}`);
924
+ }),
925
+ visibleNodes.map((node, index) => {
926
+ const nodeKey = graphNodeKey(node);
927
+ const position = positions.get(nodeKey) ?? naturalLayout.get(nodeKey) ?? {
928
+ x: GRAPH_WIDTH / 2,
929
+ y: GRAPH_HEIGHT / 2
930
+ };
931
+ const selected = props.selectedId === nodeKey;
932
+ const showLabel = selected || visibleNodes.length < 22 || index % 3 === 0;
933
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("g", {
934
+ className: dsh_mnemon_client.memoryPageStyles.graphNode,
935
+ "data-node-id": nodeKey,
936
+ "data-provider": node.memoryProviderId,
937
+ "data-category": node.category ?? "general",
938
+ "data-kind": graphNodeKind(node),
939
+ "data-selected": selected || void 0,
940
+ transform: `translate(${position.x} ${position.y})`,
941
+ role: "button",
942
+ tabIndex: 0,
943
+ "aria-label": `${graphKindLabel(t, node)}: ${(0, dsh_mnemon_client.short)(node.content, 80)}`,
944
+ "data-dragging": dragRef.current?.nodeId === nodeKey || void 0,
945
+ onPointerDown: (event) => beginDrag(event, nodeKey),
946
+ onPointerMove: moveDrag,
947
+ onPointerUp: endDrag,
948
+ onPointerCancel: cancelDrag,
949
+ onLostPointerCapture: cancelDrag,
950
+ onClick: () => props.onSelect(node),
951
+ onKeyDown: (event) => {
952
+ if (event.key === "Enter" || event.key === " ") props.onSelect(node);
953
+ else if (event.key === "ArrowLeft") {
954
+ event.preventDefault();
955
+ nudge(nodeKey, -12, 0);
956
+ } else if (event.key === "ArrowRight") {
957
+ event.preventDefault();
958
+ nudge(nodeKey, 12, 0);
959
+ } else if (event.key === "ArrowUp") {
960
+ event.preventDefault();
961
+ nudge(nodeKey, 0, -12);
962
+ } else if (event.key === "ArrowDown") {
963
+ event.preventDefault();
964
+ nudge(nodeKey, 0, 12);
965
+ }
966
+ },
967
+ children: [
968
+ graphNodeKind(node) === "space" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
969
+ x: selected ? -20 : -17,
970
+ y: selected ? -15 : -13,
971
+ width: selected ? 40 : 34,
972
+ height: selected ? 30 : 26,
973
+ rx: "9",
974
+ className: dsh_mnemon_client.memoryPageStyles.nodeHalo,
975
+ filter: selected ? "url(#mnemon-glow)" : void 0
976
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
977
+ r: selected ? 6 : 5,
978
+ className: dsh_mnemon_client.memoryPageStyles.nodeCore
979
+ })] }) : graphNodeKind(node) === "entity" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
980
+ d: selected ? "M 0 -18 L 18 0 L 0 18 L -18 0 Z" : "M 0 -14 L 14 0 L 0 14 L -14 0 Z",
981
+ className: dsh_mnemon_client.memoryPageStyles.nodeHalo,
982
+ filter: selected ? "url(#mnemon-glow)" : void 0
983
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
984
+ r: selected ? 5 : 4,
985
+ className: dsh_mnemon_client.memoryPageStyles.nodeCore
986
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
987
+ r: selected ? 17 : visibleNodes.length <= 12 ? 14 : 11,
988
+ className: dsh_mnemon_client.memoryPageStyles.nodeHalo,
989
+ filter: selected ? "url(#mnemon-glow)" : void 0
990
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
991
+ r: selected ? 7 : visibleNodes.length <= 12 ? 6 : 4.5,
992
+ className: dsh_mnemon_client.memoryPageStyles.nodeCore
993
+ })] }),
994
+ (selected || visibleNodes.length <= 12) && graphNodeKind(node) === "memory" && node.memoryBodyName !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
995
+ x: "0",
996
+ y: "-18",
997
+ textAnchor: "middle",
998
+ className: dsh_mnemon_client.memoryPageStyles.nodeBodyLabel,
999
+ children: (0, dsh_mnemon_client.short)(node.memoryBodyName, 12)
1000
+ }),
1001
+ showLabel && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("text", {
1002
+ x: visibleNodes.length <= 12 ? 19 : 15,
1003
+ y: "4",
1004
+ className: dsh_mnemon_client.memoryPageStyles.nodeLabel,
1005
+ children: (0, dsh_mnemon_client.short)(node.content.replace(/\s+/gu, " "), selected ? 34 : visibleNodes.length <= 12 ? 26 : 19)
1006
+ })
1007
+ ]
1008
+ }, nodeKey);
1009
+ })
1010
+ ]
1011
+ })] });
1012
+ }
1013
+ function OverviewPage(props) {
1014
+ const t = (0, dsh_mnemon_client.useT)();
1015
+ const locale = (0, dsh_mnemon_client.useLocale)();
1016
+ const bodyCreateFormId = (0, react.useId)();
1017
+ const bodyEditFormId = (0, react.useId)();
1018
+ const [graph, setGraph] = (0, react.useState)(null);
1019
+ const [catalog, setCatalog] = (0, react.useState)(null);
1020
+ const [selected, setSelected] = (0, react.useState)(null);
1021
+ const [catalogLoading, setCatalogLoading] = (0, react.useState)(true);
1022
+ const [healthLoading, setHealthLoading] = (0, react.useState)(true);
1023
+ const [graphLoading, setGraphLoading] = (0, react.useState)(true);
1024
+ const [error, setError] = (0, react.useState)(null);
1025
+ const [changing, setChanging] = (0, react.useState)(null);
1026
+ const [reconnectingBody, setReconnectingBody] = (0, react.useState)(null);
1027
+ const [creating, setCreating] = (0, react.useState)(false);
1028
+ const [creatingBodyOpen, setCreatingBodyOpen] = (0, react.useState)(false);
1029
+ const [bodyName, setBodyName] = (0, react.useState)("");
1030
+ const [bodyDescription, setBodyDescription] = (0, react.useState)("");
1031
+ const [bodyProviderId, setBodyProviderId] = (0, react.useState)("mnemon-native");
1032
+ const [providerDrafts, setProviderDrafts] = (0, react.useState)({});
1033
+ const [catalogUnavailable, setCatalogUnavailable] = (0, react.useState)(false);
1034
+ const [editingBody, setEditingBody] = (0, react.useState)(null);
1035
+ const [editName, setEditName] = (0, react.useState)("");
1036
+ const [editDescription, setEditDescription] = (0, react.useState)("");
1037
+ const [editConnection, setEditConnection] = (0, react.useState)({});
1038
+ const [editClearSecrets, setEditClearSecrets] = (0, react.useState)([]);
1039
+ const [savingBody, setSavingBody] = (0, react.useState)(null);
1040
+ const [confirmingDeleteBody, setConfirmingDeleteBody] = (0, react.useState)(null);
1041
+ const [deletingBody, setDeletingBody] = (0, react.useState)(null);
1042
+ const [preview, setPreview] = (0, react.useState)(null);
1043
+ const [metadataOpen, setMetadataOpen] = (0, react.useState)(false);
1044
+ const [metadataSelection, setMetadataSelection] = (0, react.useState)([]);
1045
+ const [metadataTasks, setMetadataTasks] = (0, react.useState)({});
1046
+ const [lastFullSyncAt, setLastFullSyncAt] = (0, react.useState)(null);
1047
+ const [syncClock, setSyncClock] = (0, react.useState)(() => Date.now());
1048
+ const loadRequest = (0, react.useRef)(0);
1049
+ const initialSyncStarted = (0, react.useRef)(false);
1050
+ const directoryRetryStarted = (0, react.useRef)(false);
1051
+ const fullSyncObserved = (0, react.useRef)(true);
1052
+ const load = (0, react.useCallback)(async (quiet = false) => {
1053
+ const request = ++loadRequest.current;
1054
+ setCatalogLoading(true);
1055
+ setHealthLoading(true);
1056
+ setGraphLoading(true);
1057
+ setError(null);
1058
+ let directoryUnavailable = false;
1059
+ try {
1060
+ const nextCatalog = await props.client.bodyDirectory().then((next) => {
1061
+ setCatalogUnavailable(false);
1062
+ return next;
1063
+ }).catch(() => {
1064
+ directoryUnavailable = true;
1065
+ setCatalogUnavailable(!props.catalogKnown);
1066
+ return {
1067
+ items: props.fallbackBodies,
1068
+ providers: [],
1069
+ total: props.fallbackBodies.length,
1070
+ activeCount: props.fallbackBodies.filter((body) => body.active).length,
1071
+ directory: props.fallbackDirectory ?? "",
1072
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
1073
+ };
1074
+ });
1075
+ const normalizedProviders = nextCatalog.providers;
1076
+ const normalizedCatalog = {
1077
+ ...nextCatalog,
1078
+ providers: normalizedProviders,
1079
+ items: nextCatalog.items
1080
+ };
1081
+ if (request !== loadRequest.current) return;
1082
+ setProviderDrafts((current) => mergeProviderDefaults(normalizedCatalog.providers, current));
1083
+ setCatalog(normalizedCatalog);
1084
+ setCatalogLoading(false);
1085
+ props.client.bodies().then((next) => {
1086
+ if (request !== loadRequest.current) return;
1087
+ const full = {
1088
+ ...next,
1089
+ providers: next.providers,
1090
+ items: next.items
1091
+ };
1092
+ setCatalog(full);
1093
+ }).catch((reason) => {
1094
+ if (request === loadRequest.current && !quiet && !directoryUnavailable) setError((0, dsh_mnemon_client.message)(reason));
1095
+ }).finally(() => {
1096
+ if (request === loadRequest.current) setHealthLoading(false);
1097
+ });
1098
+ props.client.graph().then((next) => {
1099
+ if (request !== loadRequest.current) return;
1100
+ const enriched = enrichMultiSpaceGraph(next, normalizedCatalog.items);
1101
+ setGraph(enriched);
1102
+ setSelected((current) => current === null ? null : enriched.nodes.find((node) => graphNodeKey(node) === graphNodeKey(current)) ?? null);
1103
+ }).catch((reason) => {
1104
+ if (request === loadRequest.current && !directoryUnavailable) setError((0, dsh_mnemon_client.message)(reason));
1105
+ }).finally(() => {
1106
+ if (request === loadRequest.current) setGraphLoading(false);
1107
+ });
1108
+ } catch (reason) {
1109
+ if (request === loadRequest.current) {
1110
+ setError((0, dsh_mnemon_client.message)(reason));
1111
+ setCatalogLoading(false);
1112
+ setHealthLoading(false);
1113
+ setGraphLoading(false);
1114
+ }
1115
+ }
1116
+ }, [
1117
+ props.catalogKnown,
1118
+ props.client,
1119
+ props.fallbackBodies,
1120
+ props.fallbackDirectory
1121
+ ]);
1122
+ (0, react.useEffect)(() => {
1123
+ if (initialSyncStarted.current) return;
1124
+ initialSyncStarted.current = true;
1125
+ load();
1126
+ }, [load]);
1127
+ (0, react.useEffect)(() => {
1128
+ if (!catalogUnavailable || !props.catalogKnown || directoryRetryStarted.current) return;
1129
+ directoryRetryStarted.current = true;
1130
+ load(true);
1131
+ }, [
1132
+ catalogUnavailable,
1133
+ load,
1134
+ props.catalogKnown
1135
+ ]);
1136
+ (0, react.useEffect)(() => {
1137
+ const timer = window.setInterval(() => setSyncClock(Date.now()), 1e3);
1138
+ return () => window.clearInterval(timer);
1139
+ }, []);
1140
+ const toggle = async (body) => {
1141
+ setChanging(body.id);
1142
+ setError(null);
1143
+ try {
1144
+ await props.client.updateBody(body.id, { active: !body.active });
1145
+ await load(true);
1146
+ props.onMutate();
1147
+ } catch (reason) {
1148
+ setError((0, dsh_mnemon_client.message)(reason));
1149
+ } finally {
1150
+ setChanging(null);
1151
+ }
1152
+ };
1153
+ const reconnect = async (body) => {
1154
+ if (reconnectingBody !== null || editingBody !== null || deletingBody !== null) return;
1155
+ setReconnectingBody(body.id);
1156
+ setError(null);
1157
+ setCatalog((current) => current === null ? current : {
1158
+ ...current,
1159
+ items: current.items.map((item) => item.id === body.id ? {
1160
+ ...item,
1161
+ statusLoading: true
1162
+ } : item)
1163
+ });
1164
+ try {
1165
+ const next = await props.client.reconnectBody(body.id);
1166
+ setCatalog((current) => current === null ? current : {
1167
+ ...current,
1168
+ items: current.items.map((item) => item.id === next.id ? next : item)
1169
+ });
1170
+ props.onBodyReconnect(next);
1171
+ } catch (reason) {
1172
+ const failure = (0, dsh_mnemon_client.message)(reason);
1173
+ setCatalog((current) => current === null ? current : {
1174
+ ...current,
1175
+ items: current.items.map((item) => item.id === body.id ? {
1176
+ ...item,
1177
+ healthy: false,
1178
+ statusLoading: false,
1179
+ error: failure
1180
+ } : item)
1181
+ });
1182
+ setError(failure);
1183
+ } finally {
1184
+ setReconnectingBody(null);
1185
+ }
1186
+ };
1187
+ const beginEdit = (body) => {
1188
+ setEditingBody(body.id);
1189
+ setEditName(body.name);
1190
+ setEditDescription(body.description ?? "");
1191
+ setError(null);
1192
+ setEditConnection(nativeBodyProvider(body.provider) ? {} : { ...body.provider.settings });
1193
+ setEditClearSecrets([]);
1194
+ };
1195
+ const saveEdit = async (event, body) => {
1196
+ event.preventDefault();
1197
+ if (editName.trim() === "") return;
1198
+ setSavingBody(body.id);
1199
+ setError(null);
1200
+ try {
1201
+ const descriptor = catalog?.providers.find((provider) => provider.id === body.provider.id);
1202
+ const connection = descriptor === void 0 ? {} : Object.fromEntries(Object.entries(editConnection).filter(([key, value]) => {
1203
+ return descriptor.fields.find((candidate) => candidate.key === key)?.input !== "secret" || String(value) !== "";
1204
+ }));
1205
+ await props.client.updateBody(body.id, {
1206
+ name: editName,
1207
+ description: editDescription,
1208
+ ...nativeBodyProvider(body.provider) ? {} : {
1209
+ connection,
1210
+ ...editClearSecrets.length === 0 ? {} : { clearSecrets: editClearSecrets }
1211
+ }
1212
+ });
1213
+ setEditingBody(null);
1214
+ await load(true);
1215
+ props.onMutate();
1216
+ } catch (reason) {
1217
+ setError((0, dsh_mnemon_client.message)(reason));
1218
+ } finally {
1219
+ setSavingBody(null);
1220
+ }
1221
+ };
1222
+ const create = async (event) => {
1223
+ event.preventDefault();
1224
+ const providers = catalog?.providers ?? [];
1225
+ const manualProvider = providers.find((provider) => provider.id === bodyProviderId);
1226
+ if (bodyName.trim() === "" || bodyDescription.trim() === "" || !providerDraftComplete(manualProvider, providerDrafts[bodyProviderId])) return;
1227
+ setCreating(true);
1228
+ setError(null);
1229
+ try {
1230
+ await props.client.createBody({
1231
+ name: bodyName,
1232
+ description: bodyDescription,
1233
+ providerId: bodyProviderId,
1234
+ ...manualProvider?.origin === "native" ? {} : { connection: providerDrafts[bodyProviderId] ?? {} }
1235
+ });
1236
+ setBodyName("");
1237
+ setBodyDescription("");
1238
+ setBodyProviderId(providers.find((provider) => provider.origin === "native")?.id ?? providers[0]?.id ?? "mnemon-native");
1239
+ setProviderDrafts((current) => Object.fromEntries(providers.map((provider) => [provider.id, Object.fromEntries(Object.entries(current[provider.id] ?? {}).map(([key, value]) => [key, provider.fields.some((field) => field.key === key && field.input === "secret") ? "" : value]))])));
1240
+ setCreatingBodyOpen(false);
1241
+ await load(true);
1242
+ props.onMutate();
1243
+ } catch (reason) {
1244
+ setError((0, dsh_mnemon_client.message)(reason));
1245
+ } finally {
1246
+ setCreating(false);
1247
+ }
1248
+ };
1249
+ const deleteBody = async (body) => {
1250
+ setDeletingBody(body.id);
1251
+ setError(null);
1252
+ try {
1253
+ await props.client.deleteBody(body.id);
1254
+ setConfirmingDeleteBody(null);
1255
+ await load(true);
1256
+ props.onMutate();
1257
+ } catch (reason) {
1258
+ setError((0, dsh_mnemon_client.message)(reason));
1259
+ } finally {
1260
+ setDeletingBody(null);
1261
+ }
1262
+ };
1263
+ const maintainMetadata = () => {
1264
+ if (metadataSelection.length === 0) return;
1265
+ const selectedIds = metadataSelection.filter((id) => metadataTasks[id]?.status !== "running");
1266
+ if (selectedIds.length === 0) return;
1267
+ setError(null);
1268
+ setMetadataSelection([]);
1269
+ setMetadataTasks((current) => ({
1270
+ ...current,
1271
+ ...Object.fromEntries(selectedIds.map((id) => [id, { status: "running" }]))
1272
+ }));
1273
+ for (const id of selectedIds) props.metadataClient.maintainBodyMetadata([id]).then((result) => {
1274
+ const update = result.updates.find((candidate) => candidate.memoryBodyId === id);
1275
+ if (update === void 0) throw new Error(`metadata task Agent omitted Memory Space ${id}`);
1276
+ setCatalog((current) => current === null ? current : {
1277
+ ...current,
1278
+ items: current.items.map((body) => body.id === id ? {
1279
+ ...body,
1280
+ name: update.title,
1281
+ description: update.description
1282
+ } : body)
1283
+ });
1284
+ props.onBodyMetadata([update]);
1285
+ setMetadataTasks((current) => ({
1286
+ ...current,
1287
+ [id]: { status: "success" }
1288
+ }));
1289
+ }).catch((reason) => {
1290
+ setMetadataTasks((current) => ({
1291
+ ...current,
1292
+ [id]: {
1293
+ status: "error",
1294
+ error: (0, dsh_mnemon_client.message)(reason)
1295
+ }
1296
+ }));
1297
+ });
1298
+ };
1299
+ const generated = graph === null ? t("overview.waitingSnapshot") : t("overview.updatedAt", { time: new Date(graph.generatedAt).toLocaleTimeString(locale, {
1300
+ hour: "2-digit",
1301
+ minute: "2-digit",
1302
+ second: "2-digit"
1303
+ }) });
1304
+ const graphSpaces = graph?.nodes.filter((node) => graphNodeKind(node) === "space").length ?? 0;
1305
+ const graphEntities = graph?.nodes.filter((node) => graphNodeKind(node) === "entity").length ?? 0;
1306
+ const graphMemories = graph?.nodes.filter((node) => graphNodeKind(node) === "memory").length ?? 0;
1307
+ const graphSources = graph?.sources ?? [];
1308
+ const onlyQueryOrUnsupported = graphSources.length > 0 && graphSources.every((source) => source.mode === "query-only" || source.mode === "unsupported" || source.status === "unavailable");
1309
+ const selectedKind = selected === null ? null : graphNodeKind(selected);
1310
+ const editingBodyView = editingBody === null ? void 0 : catalog?.items.find((body) => body.id === editingBody);
1311
+ const deletingBodyView = confirmingDeleteBody === null ? void 0 : catalog?.items.find((body) => body.id === confirmingDeleteBody);
1312
+ const providers = catalog?.providers ?? [];
1313
+ const metadataCandidates = (catalog?.items ?? props.fallbackBodies).filter((body) => body.active && body.providerEnabled !== false);
1314
+ const metadataRunningCount = Object.values(metadataTasks).filter((task) => task.status === "running").length;
1315
+ const metadataBusy = metadataRunningCount > 0;
1316
+ const metadataSelectable = metadataCandidates.filter((body) => metadataTasks[body.id]?.status !== "running");
1317
+ const metadataAllSelected = metadataSelectable.length > 0 && metadataSelectable.every((body) => metadataSelection.includes(body.id));
1318
+ const loading = catalogLoading || healthLoading || graphLoading;
1319
+ (0, react.useEffect)(() => {
1320
+ if (loading) {
1321
+ fullSyncObserved.current = true;
1322
+ return;
1323
+ }
1324
+ if (!fullSyncObserved.current) return;
1325
+ fullSyncObserved.current = false;
1326
+ if (error !== null) return;
1327
+ const completedAt = Date.now();
1328
+ setLastFullSyncAt(completedAt);
1329
+ setSyncClock(completedAt);
1330
+ }, [error, loading]);
1331
+ const fullSyncAge = lastFullSyncAt === null ? t("overview.fullSyncPending") : (() => {
1332
+ const seconds = Math.max(0, Math.floor((syncClock - lastFullSyncAt) / 1e3));
1333
+ if (seconds < 5) return t("overview.fullSyncJustNow");
1334
+ if (seconds < 60) return t("overview.fullSyncSeconds", { count: seconds });
1335
+ const minutes = Math.floor(seconds / 60);
1336
+ if (minutes < 60) return t("overview.fullSyncMinutes", { count: minutes });
1337
+ const hours = Math.floor(minutes / 60);
1338
+ if (hours < 24) return t("overview.fullSyncHours", { count: hours });
1339
+ return t("overview.fullSyncDays", { count: Math.floor(hours / 24) });
1340
+ })();
1341
+ const selectedProvider = providers.find((provider) => provider.id === bodyProviderId);
1342
+ const nativeBodyCount = catalog?.items.filter((body) => nativeBodyProvider(body.provider)).length ?? 0;
1343
+ const canDeleteBody = (body) => !nativeBodyProvider(body.provider) || nativeBodyCount > 1;
1344
+ const updateProviderDraft = (providerId, key, value) => setProviderDrafts((current) => ({
1345
+ ...current,
1346
+ [providerId]: {
1347
+ ...current[providerId] ?? {},
1348
+ [key]: value
1349
+ }
1350
+ }));
1351
+ const placementReceipt = (body) => body.placement === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1352
+ className: dsh_mnemon_client.memoryPageStyles.placementReceipt,
1353
+ title: body.placement.reason,
1354
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1355
+ "aria-hidden": "true",
1356
+ children: "✦"
1357
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
1358
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t(body.placement.decidedBy === "llm" ? "overview.placementByLlm" : "overview.placementByRules") }),
1359
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("overview.placementConfidence", { confidence: t(`overview.confidence.${body.placement.confidence}`) }) }),
1360
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: body.placement.reason })
1361
+ ] })]
1362
+ });
1363
+ const bodyEditForm = (body) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
1364
+ id: bodyEditFormId,
1365
+ className: dsh_mnemon_client.memoryPageStyles.bodyEdit,
1366
+ onSubmit: (event) => void saveEdit(event, body),
1367
+ children: [
1368
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("overview.editName"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1369
+ "aria-label": t("overview.editName"),
1370
+ value: editName,
1371
+ onChange: (event) => setEditName(event.target.value),
1372
+ maxLength: 100,
1373
+ required: true
1374
+ })] }),
1375
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("overview.editDescription"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1376
+ "aria-label": t("overview.editDescription"),
1377
+ value: editDescription,
1378
+ onChange: (event) => setEditDescription(event.target.value),
1379
+ rows: 4,
1380
+ maxLength: 1e3
1381
+ })] }),
1382
+ !nativeBodyProvider(body.provider) && (() => {
1383
+ const descriptor = providers.find((provider) => provider.id === body.provider.id);
1384
+ return descriptor === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderMemoryFields, {
1385
+ provider: descriptor,
1386
+ connection: editConnection,
1387
+ onChange: (key, value) => setEditConnection((current) => ({
1388
+ ...current,
1389
+ [key]: value
1390
+ })),
1391
+ body,
1392
+ clearSecrets: editClearSecrets,
1393
+ onClearSecretsChange: setEditClearSecrets
1394
+ });
1395
+ })()
1396
+ ]
1397
+ });
1398
+ const bodyCreateForm = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
1399
+ id: bodyCreateFormId,
1400
+ className: (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.bodyEdit, dsh_mnemon_client.memoryPageStyles.bodyCreateForm),
1401
+ onSubmit: (event) => void create(event),
1402
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1403
+ className: dsh_mnemon_client.memoryPageStyles.createSection,
1404
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1405
+ className: dsh_mnemon_client.memoryPageStyles.createSectionHeading,
1406
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "01" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("overview.createIdentityTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("overview.createIdentityHint") })] })]
1407
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1408
+ className: dsh_mnemon_client.memoryPageStyles.createIdentityGrid,
1409
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("overview.createName"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1410
+ "data-autofocus": true,
1411
+ "aria-label": t("overview.createName"),
1412
+ value: bodyName,
1413
+ onChange: (event) => setBodyName(event.target.value),
1414
+ placeholder: t("overview.createNamePlaceholder"),
1415
+ maxLength: 100,
1416
+ required: true
1417
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("overview.createDescription"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1418
+ "aria-label": t("overview.createDescription"),
1419
+ value: bodyDescription,
1420
+ onChange: (event) => setBodyDescription(event.target.value),
1421
+ placeholder: t("overview.createDescriptionPlaceholder"),
1422
+ rows: 3,
1423
+ maxLength: 1e3,
1424
+ required: true
1425
+ })] })]
1426
+ })]
1427
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1428
+ className: dsh_mnemon_client.memoryPageStyles.createSection,
1429
+ children: [
1430
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1431
+ className: dsh_mnemon_client.memoryPageStyles.createSectionHeading,
1432
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "02" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("overview.createPlacementTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("overview.createPlacementHint") })] })]
1433
+ }),
1434
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
1435
+ className: dsh_mnemon_client.memoryPageStyles.providerChoice,
1436
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", { children: t("overview.providerLabel") }), providers.map((provider) => {
1437
+ const serviceMissing = provider.origin !== "native" && provider.serviceConfigured === false;
1438
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1439
+ "data-selected": bodyProviderId === provider.id || void 0,
1440
+ "data-native": provider.origin === "native" || void 0,
1441
+ "data-disabled": serviceMissing || void 0,
1442
+ children: [
1443
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1444
+ type: "radio",
1445
+ name: "memory-provider",
1446
+ value: provider.id,
1447
+ checked: bodyProviderId === provider.id,
1448
+ disabled: serviceMissing,
1449
+ onChange: () => setBodyProviderId(provider.id)
1450
+ }),
1451
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderIcon, {
1452
+ providerId: provider.id,
1453
+ icon: provider.icon,
1454
+ className: dsh_mnemon_client.memoryPageStyles.providerChoiceIcon
1455
+ }),
1456
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [provider.label, provider.origin === "native" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", { children: t("overview.nativeOfficial") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: serviceMissing ? t("overview.providerServiceRequired") : `${t(`overview.workspaceBinding.${provider.workspaceBinding}`)} · ${providerSummary(t, provider)}` })] }),
1457
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
1458
+ className: dsh_mnemon_client.memoryPageStyles.choiceControl,
1459
+ "data-kind": "radio",
1460
+ "aria-hidden": "true"
1461
+ })
1462
+ ]
1463
+ }, provider.id);
1464
+ })]
1465
+ }),
1466
+ selectedProvider !== void 0 && selectedProvider.origin !== "native" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderMemoryFields, {
1467
+ provider: selectedProvider,
1468
+ connection: providerDrafts[selectedProvider.id] ?? {},
1469
+ onChange: (key, value) => updateProviderDraft(selectedProvider.id, key, value)
1470
+ })
1471
+ ]
1472
+ })]
1473
+ });
1474
+ const bodyToggle = (body) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1475
+ type: "button",
1476
+ className: dsh_mnemon_client.memoryPageStyles.bodySwitch,
1477
+ role: "switch",
1478
+ "aria-checked": body.active,
1479
+ "aria-label": t("overview.toggleAria", { name: body.name }),
1480
+ disabled: !props.activationEnabled || changing === body.id || deletingBody === body.id,
1481
+ onClick: () => void toggle(body),
1482
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1483
+ className: dsh_mnemon_client.memoryPageStyles.bodySwitchTrack,
1484
+ "aria-hidden": "true",
1485
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {})
1486
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: changing === body.id ? t("overview.toggling") : body.active ? t("common.active") : t("common.inactive") })]
1487
+ });
1488
+ const bodyEditActionClass = (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.ghostButton, (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memorySidebarStyles.itemActionButton, dsh_mnemon_client.memorySidebarStyles.itemEditAction));
1489
+ const bodyDeleteActionClass = (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.dangerButton, (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memorySidebarStyles.itemActionButton, dsh_mnemon_client.memorySidebarStyles.itemDangerAction));
1490
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1491
+ className: dsh_mnemon_client.memoryPageStyles.page,
1492
+ children: [
1493
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.PageHeader, {
1494
+ title: t("nav.overview"),
1495
+ description: t("overview.pageDescription"),
1496
+ meta: fullSyncAge,
1497
+ ...loading ? { loadingLabel: catalogLoading ? t("overview.directoryLoading") : graphLoading ? t("overview.snapshotLoading") : t("overview.healthLoading") } : {},
1498
+ action: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1499
+ type: "button",
1500
+ className: dsh_mnemon_client.memoryPageStyles.secondaryButton,
1501
+ disabled: loading,
1502
+ onClick: () => void load(),
1503
+ children: loading ? t("overview.syncing") : t("overview.syncNow")
1504
+ })
1505
+ }),
1506
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1507
+ className: dsh_mnemon_client.memoryPageStyles.inlineError,
1508
+ role: "alert",
1509
+ children: error
1510
+ }),
1511
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1512
+ className: dsh_mnemon_client.memoryPageStyles.bodyDirectory,
1513
+ "aria-label": t("overview.directory"),
1514
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1515
+ className: dsh_mnemon_client.memoryPageStyles.bodyDirectoryHeader,
1516
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
1517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("overview.directory") }),
1518
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("overview.directory.description") }),
1519
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
1520
+ className: dsh_mnemon_client.memoryPageStyles.bodyDirectoryPath,
1521
+ children: catalogUnavailable ? t("overview.directory.unsynced") : catalog?.directory || props.fallbackDirectory || t("overview.directory.waiting")
1522
+ })
1523
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1524
+ className: (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.bodyDirectoryControls, dsh_mnemon_client.memorySidebarStyles.bodyDirectoryActions),
1525
+ children: [
1526
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: catalogUnavailable ? t("overview.directory.unsyncedBadge") : `${catalog?.activeCount ?? "—"} / ${catalog?.total ?? "—"} ${t("common.active")}` }),
1527
+ props.writeEnabled && !catalogUnavailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1528
+ type: "button",
1529
+ className: bodyEditActionClass,
1530
+ title: !props.agentAvailable ? t("overview.metadataUnavailable") : void 0,
1531
+ onClick: () => {
1532
+ setMetadataSelection([]);
1533
+ setMetadataTasks({});
1534
+ setMetadataOpen(true);
1535
+ if (!props.agentAvailable) props.onAgentRefresh();
1536
+ },
1537
+ children: t("overview.metadataAction")
1538
+ }),
1539
+ props.writeEnabled && !catalogUnavailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1540
+ type: "button",
1541
+ className: bodyEditActionClass,
1542
+ onClick: () => setCreatingBodyOpen(true),
1543
+ children: t("overview.createTitle")
1544
+ })
1545
+ ]
1546
+ })]
1547
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1548
+ className: dsh_mnemon_client.memoryPageStyles.bodyGrid,
1549
+ children: [catalog?.items.map((body) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {
1550
+ className: dsh_mnemon_client.memoryPageStyles.bodyCard,
1551
+ "data-provider": body.provider.id,
1552
+ "data-active": body.active || void 0,
1553
+ "data-healthy": !body.statusLoading && body.healthy || void 0,
1554
+ "data-status-loading": body.statusLoading || void 0,
1555
+ "data-reconnectable": "",
1556
+ "data-reconnecting": reconnectingBody === body.id || void 0,
1557
+ "data-mnemon-default": body.mnemonDefault || void 0,
1558
+ "data-editing": void 0,
1559
+ tabIndex: 0,
1560
+ "aria-label": t("overview.reconnectAria", { name: body.name }),
1561
+ title: reconnectingBody === body.id ? t("overview.reconnecting") : body.error ?? t("overview.reconnectHint"),
1562
+ onClick: (event) => {
1563
+ if (event.target instanceof Element && event.target.closest("button, input, textarea, select, label, a, [role=\"switch\"]") !== null) return;
1564
+ reconnect(body);
1565
+ },
1566
+ onKeyDown: (event) => {
1567
+ if (event.target !== event.currentTarget || event.key !== "Enter" && event.key !== " ") return;
1568
+ event.preventDefault();
1569
+ reconnect(body);
1570
+ },
1571
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1572
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1573
+ className: dsh_mnemon_client.memorySidebarStyles.bodyCardHeader,
1574
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1575
+ className: dsh_mnemon_client.memorySidebarStyles.bodyCardIdentity,
1576
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: dsh_mnemon_client.memoryPageStyles.bodySignal }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: body.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1577
+ className: dsh_mnemon_client.memorySidebarStyles.bodyCardMeta,
1578
+ children: [
1579
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: body.id }),
1580
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoryProviderBadge, {
1581
+ providerId: body.provider.id,
1582
+ label: body.provider.label
1583
+ }),
1584
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1585
+ className: dsh_mnemon_client.memoryPageStyles.bodyHealth,
1586
+ children: reconnectingBody === body.id ? t("overview.reconnecting") : body.statusLoading ? t("overview.storageChecking") : body.healthy ? t("overview.storageHealthy") : t("overview.storageUnhealthy")
1587
+ }),
1588
+ body.mnemonDefault && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1589
+ className: dsh_mnemon_client.memoryPageStyles.mnemonDefaultBadge,
1590
+ children: t("overview.mnemonDefault")
1591
+ })
1592
+ ]
1593
+ })] })]
1594
+ }), bodyToggle(body)]
1595
+ }),
1596
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1597
+ title: body.description || t("overview.noDescription"),
1598
+ children: body.description || t("overview.noDescription")
1599
+ }),
1600
+ placementReceipt(body),
1601
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
1602
+ className: dsh_mnemon_client.memorySidebarStyles.bodyCardFooter,
1603
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1604
+ className: dsh_mnemon_client.memorySidebarStyles.bodyCardStats,
1605
+ children: !nativeBodyProvider(body.provider) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1606
+ className: dsh_mnemon_client.memoryPageStyles.bodyFooterBlock,
1607
+ title: t(body.provider.kind === "remote" ? "overview.providerRemote" : "overview.providerLocal"),
1608
+ children: t(body.provider.kind === "remote" ? "overview.providerRemote" : "overview.providerLocal")
1609
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1610
+ className: `${dsh_mnemon_client.memoryPageStyles.bodyFooterBlock} ${dsh_mnemon_client.memoryPageStyles.bodyFooterGrow}`,
1611
+ title: body.provider.location || body.provider.label,
1612
+ children: body.provider.location || body.provider.label
1613
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1614
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1615
+ className: dsh_mnemon_client.memoryPageStyles.bodyFooterBlock,
1616
+ title: t("common.memories", { count: body.stats?.totalInsights ?? 0 }),
1617
+ children: t("common.memories", { count: body.stats?.totalInsights ?? 0 })
1618
+ }),
1619
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1620
+ className: dsh_mnemon_client.memoryPageStyles.bodyFooterBlock,
1621
+ title: t("common.edges", { count: body.stats?.edgeCount ?? 0 }),
1622
+ children: t("common.edges", { count: body.stats?.edgeCount ?? 0 })
1623
+ }),
1624
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1625
+ className: dsh_mnemon_client.memoryPageStyles.bodyFooterBlock,
1626
+ title: (0, dsh_mnemon_client.humanBytes)(body.stats?.dbSizeBytes ?? 0),
1627
+ children: (0, dsh_mnemon_client.humanBytes)(body.stats?.dbSizeBytes ?? 0)
1628
+ })
1629
+ ] })
1630
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1631
+ className: dsh_mnemon_client.memoryPageStyles.bodyCardActions,
1632
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1633
+ type: "button",
1634
+ className: bodyEditActionClass,
1635
+ "aria-label": t("overview.editBodyAria", { name: body.name }),
1636
+ disabled: !props.writeEnabled || deletingBody === body.id,
1637
+ onClick: () => beginEdit(body),
1638
+ children: t("overview.editBody")
1639
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1640
+ type: "button",
1641
+ className: bodyDeleteActionClass,
1642
+ "aria-label": t(!nativeBodyProvider(body.provider) ? "overview.disconnectBodyAria" : "overview.deleteBodyAria", { name: body.name }),
1643
+ title: canDeleteBody(body) ? void 0 : t("overview.lastStoreDeleteHint"),
1644
+ disabled: !props.writeEnabled || deletingBody === body.id || !canDeleteBody(body),
1645
+ onClick: () => setConfirmingDeleteBody(body.id),
1646
+ children: !nativeBodyProvider(body.provider) ? t("overview.disconnectBody") : t("overview.deleteBody")
1647
+ })]
1648
+ })]
1649
+ })
1650
+ ] })
1651
+ }, body.id)), catalog?.total === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1652
+ className: dsh_mnemon_client.memoryPageStyles.bodyDirectoryEmpty,
1653
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "◇" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: catalogUnavailable ? t("overview.unsyncedTitle") : t("overview.emptyTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: catalogUnavailable ? t("overview.unsyncedShort") : t("overview.emptyShort") })] })]
1654
+ })]
1655
+ })]
1656
+ }),
1657
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1658
+ className: dsh_mnemon_client.memoryPageStyles.asyncRegion,
1659
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadSourcePanel, {
1660
+ title: t("overview.snapshotSources"),
1661
+ hint: t("overview.snapshotSourcesHint"),
1662
+ sources: graphSources
1663
+ })
1664
+ }),
1665
+ creatingBodyOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SidebarModal, {
1666
+ title: t("overview.createTitle"),
1667
+ description: t("overview.createDialogHint"),
1668
+ busy: creating,
1669
+ wide: true,
1670
+ onClose: () => setCreatingBodyOpen(false),
1671
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1672
+ className: dsh_mnemon_client.memoryPageStyles.modalFooterActions,
1673
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1674
+ type: "button",
1675
+ "data-dialog-close": true,
1676
+ className: dsh_mnemon_client.memoryPageStyles.ghostButton,
1677
+ disabled: creating,
1678
+ onClick: () => setCreatingBodyOpen(false),
1679
+ children: t("common.cancel")
1680
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1681
+ type: "submit",
1682
+ form: bodyCreateFormId,
1683
+ className: dsh_mnemon_client.memoryPageStyles.primaryButton,
1684
+ disabled: creating || bodyName.trim() === "" || bodyDescription.trim() === "" || !providerDraftComplete(selectedProvider, providerDrafts[bodyProviderId]),
1685
+ children: creating ? t("overview.creating") : t("overview.createAction")
1686
+ })]
1687
+ }),
1688
+ children: bodyCreateForm
1689
+ }),
1690
+ metadataOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SidebarModal, {
1691
+ title: t("overview.metadataTitle"),
1692
+ description: t("overview.metadataDescription"),
1693
+ busy: metadataBusy,
1694
+ wide: true,
1695
+ onClose: () => setMetadataOpen(false),
1696
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1697
+ className: dsh_mnemon_client.memoryPageStyles.modalFooterNote,
1698
+ children: t("overview.metadataSafety")
1699
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1700
+ className: dsh_mnemon_client.memoryPageStyles.modalFooterActions,
1701
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1702
+ type: "button",
1703
+ "data-dialog-close": true,
1704
+ className: dsh_mnemon_client.memoryPageStyles.ghostButton,
1705
+ disabled: metadataBusy,
1706
+ onClick: () => setMetadataOpen(false),
1707
+ children: t("common.cancel")
1708
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1709
+ type: "button",
1710
+ className: dsh_mnemon_client.memoryPageStyles.primaryButton,
1711
+ disabled: !props.agentAvailable || metadataSelection.length === 0,
1712
+ title: !props.agentAvailable ? t("overview.metadataUnavailable") : void 0,
1713
+ onClick: maintainMetadata,
1714
+ children: t("overview.metadataGenerate", { count: metadataSelection.length })
1715
+ })]
1716
+ })] }),
1717
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1718
+ className: dsh_mnemon_client.memoryPageStyles.metadataDialog,
1719
+ children: [
1720
+ !props.agentAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1721
+ className: dsh_mnemon_client.memoryPageStyles.inlineError,
1722
+ role: "status",
1723
+ children: t("overview.metadataUnavailable")
1724
+ }),
1725
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1726
+ className: dsh_mnemon_client.memoryPageStyles.metadataToolbar,
1727
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [t("overview.metadataSelected", { count: metadataSelection.length }), metadataRunningCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", { children: t("overview.metadataRunningCount", { count: metadataRunningCount }) })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1728
+ type: "button",
1729
+ className: dsh_mnemon_client.memoryPageStyles.ghostButton,
1730
+ disabled: metadataSelectable.length === 0,
1731
+ onClick: () => setMetadataSelection(metadataAllSelected ? [] : metadataSelectable.map((body) => body.id)),
1732
+ children: metadataAllSelected ? t("overview.metadataClear") : t("overview.metadataSelectAll")
1733
+ })]
1734
+ }),
1735
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1736
+ className: dsh_mnemon_client.memoryPageStyles.metadataList,
1737
+ "aria-live": "polite",
1738
+ children: [metadataCandidates.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1739
+ className: dsh_mnemon_client.memoryPageStyles.metadataEmpty,
1740
+ children: catalogLoading ? t("overview.metadataLoading") : t("overview.metadataEmpty")
1741
+ }), metadataCandidates.map((body) => {
1742
+ const selected = metadataSelection.includes(body.id);
1743
+ const task = metadataTasks[body.id];
1744
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1745
+ "data-provider": body.provider.id,
1746
+ "data-selected": selected || void 0,
1747
+ "data-refreshing": task?.status === "running" || void 0,
1748
+ "data-refreshed": task?.status === "success" || void 0,
1749
+ "data-failed": task?.status === "error" || void 0,
1750
+ children: [
1751
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1752
+ type: "checkbox",
1753
+ checked: selected,
1754
+ disabled: task?.status === "running",
1755
+ onChange: (event) => setMetadataSelection((current) => event.target.checked ? [.../* @__PURE__ */ new Set([...current, body.id])] : current.filter((id) => id !== body.id))
1756
+ }),
1757
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
1758
+ className: dsh_mnemon_client.memoryPageStyles.choiceControl,
1759
+ "data-kind": "check",
1760
+ "aria-hidden": "true"
1761
+ }),
1762
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1763
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: body.name }),
1764
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: body.description || t("overview.noDescription") }),
1765
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoryProviderBadge, {
1766
+ providerId: body.provider.id,
1767
+ label: body.provider.label
1768
+ }), task === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: body.id }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
1769
+ className: dsh_mnemon_client.memoryPageStyles.metadataTaskStatus,
1770
+ "data-status": task.status,
1771
+ title: task.error,
1772
+ children: task.status === "running" ? t("overview.metadataTaskRunning") : task.status === "success" ? t("overview.metadataTaskSuccess") : t("overview.metadataTaskError", { error: task.error ?? t("overview.metadataTaskUnknown") })
1773
+ })] })
1774
+ ] })
1775
+ ]
1776
+ }, body.id);
1777
+ })]
1778
+ })
1779
+ ]
1780
+ })
1781
+ }),
1782
+ editingBodyView !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SidebarModal, {
1783
+ title: t("overview.editBodyAria", { name: editingBodyView.name }),
1784
+ description: editingBodyView.id,
1785
+ busy: savingBody === editingBodyView.id,
1786
+ onClose: () => setEditingBody(null),
1787
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1788
+ className: dsh_mnemon_client.memoryPageStyles.modalFooterActions,
1789
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1790
+ type: "button",
1791
+ "data-dialog-close": true,
1792
+ className: dsh_mnemon_client.memoryPageStyles.ghostButton,
1793
+ disabled: savingBody === editingBodyView.id,
1794
+ onClick: () => setEditingBody(null),
1795
+ children: t("common.cancel")
1796
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1797
+ type: "submit",
1798
+ form: bodyEditFormId,
1799
+ className: dsh_mnemon_client.memoryPageStyles.primaryButton,
1800
+ disabled: savingBody === editingBodyView.id || editName.trim() === "",
1801
+ children: savingBody === editingBodyView.id ? t("overview.savingBody") : t("overview.saveBody")
1802
+ })]
1803
+ }),
1804
+ children: bodyEditForm(editingBodyView)
1805
+ }),
1806
+ deletingBodyView !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SidebarModal, {
1807
+ title: t(!nativeBodyProvider(deletingBodyView.provider) ? "overview.disconnectTitle" : "overview.deleteTitle", { name: deletingBodyView.name }),
1808
+ description: deletingBodyView.id,
1809
+ busy: deletingBody === deletingBodyView.id,
1810
+ onClose: () => setConfirmingDeleteBody(null),
1811
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1812
+ className: dsh_mnemon_client.memoryPageStyles.modalFooterActions,
1813
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1814
+ type: "button",
1815
+ "data-dialog-close": true,
1816
+ "data-autofocus": true,
1817
+ className: dsh_mnemon_client.memoryPageStyles.ghostButton,
1818
+ disabled: deletingBody === deletingBodyView.id,
1819
+ onClick: () => setConfirmingDeleteBody(null),
1820
+ children: t("common.cancel")
1821
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1822
+ type: "button",
1823
+ className: dsh_mnemon_client.memoryPageStyles.dangerSolidButton,
1824
+ title: canDeleteBody(deletingBodyView) ? void 0 : t("overview.lastStoreDeleteHint"),
1825
+ disabled: deletingBody === deletingBodyView.id || !canDeleteBody(deletingBodyView),
1826
+ onClick: () => void deleteBody(deletingBodyView),
1827
+ children: deletingBody === deletingBodyView.id ? t("overview.deletingBody") : t(!nativeBodyProvider(deletingBodyView.provider) ? "overview.disconnectAction" : "overview.deleteAction")
1828
+ })]
1829
+ }),
1830
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1831
+ className: dsh_mnemon_client.memoryPageStyles.bodyDeleteConfirm,
1832
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t(!nativeBodyProvider(deletingBodyView.provider) ? "overview.disconnectWarning" : "overview.deleteWarning", { provider: deletingBodyView.provider.label }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1833
+ className: dsh_mnemon_client.memoryPageStyles.bodyDeleteSummary,
1834
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: deletingBodyView.name }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1835
+ deletingBodyView.provider.label,
1836
+ " · ",
1837
+ deletingBodyView.provider.location || t("common.memories", { count: deletingBodyView.stats?.totalInsights ?? 0 })
1838
+ ] })]
1839
+ })]
1840
+ })
1841
+ }),
1842
+ !catalogUnavailable && graph !== null && graph.nodes.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1843
+ className: dsh_mnemon_client.memoryPageStyles.graphLayout,
1844
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1845
+ className: dsh_mnemon_client.memoryPageStyles.graphPanel,
1846
+ children: [
1847
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1848
+ className: dsh_mnemon_client.memoryPageStyles.graphToolbar,
1849
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
1850
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: dsh_mnemon_client.memoryPageStyles.liveDot }),
1851
+ t("overview.snapshot"),
1852
+ " ",
1853
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: generated })
1854
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1855
+ className: dsh_mnemon_client.memoryPageStyles.graphLegend,
1856
+ children: [
1857
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1858
+ "data-edge": "scope",
1859
+ children: t("overview.edgeScope")
1860
+ }),
1861
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1862
+ "data-edge": "temporal",
1863
+ children: t("overview.edgeTemporal")
1864
+ }),
1865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1866
+ "data-edge": "semantic",
1867
+ children: t("overview.edgeSemantic")
1868
+ }),
1869
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1870
+ "data-edge": "causal",
1871
+ children: t("overview.edgeCausal")
1872
+ }),
1873
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1874
+ "data-edge": "entity",
1875
+ children: t("overview.edgeEntity")
1876
+ })
1877
+ ]
1878
+ })]
1879
+ }),
1880
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1881
+ className: dsh_mnemon_client.memoryPageStyles.graphViewport,
1882
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoryGraph, {
1883
+ graph,
1884
+ selectedId: selected === null ? void 0 : graphNodeKey(selected),
1885
+ onSelect: setSelected
1886
+ })
1887
+ }),
1888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1889
+ className: dsh_mnemon_client.memoryPageStyles.graphFooter,
1890
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("overview.graphComposition", {
1891
+ spaces: graphSpaces,
1892
+ memories: graphMemories,
1893
+ entities: graphEntities
1894
+ }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
1895
+ t("overview.graphCount", {
1896
+ visible: Math.min(graph.nodes.length, 60),
1897
+ total: graph.nodes.length
1898
+ }),
1899
+ " · ",
1900
+ t("overview.graphEdges", { count: graph.edges.length })
1901
+ ] })]
1902
+ })
1903
+ ]
1904
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("aside", {
1905
+ className: dsh_mnemon_client.memoryPageStyles.graphInspector,
1906
+ "data-empty": selected === null || void 0,
1907
+ children: selected === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1908
+ className: dsh_mnemon_client.memoryPageStyles.inspectorEmpty,
1909
+ children: [
1910
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1911
+ className: (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.inspectorLogo, dsh_mnemon_client.memorySidebarStyles.inspectorGlyph),
1912
+ "aria-hidden": "true",
1913
+ children: "◇"
1914
+ }),
1915
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("overview.selectNode") }),
1916
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("overview.selectNodeText") })
1917
+ ]
1918
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
1919
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1920
+ className: dsh_mnemon_client.memoryPageStyles.inspectorHeading,
1921
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(selectedKind === "space" ? "overview.inspectorSpace" : selectedKind === "entity" ? "overview.inspectorEntity" : "overview.inspector") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1922
+ type: "button",
1923
+ onClick: () => setSelected(null),
1924
+ "aria-label": t("overview.closeInspector"),
1925
+ children: "×"
1926
+ })]
1927
+ }),
1928
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1929
+ className: dsh_mnemon_client.memoryPageStyles.inspectorChips,
1930
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1931
+ className: dsh_mnemon_client.memoryPageStyles.categoryChip,
1932
+ children: graphKindLabel(t, selected)
1933
+ }), selected.memoryProviderId !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoryProviderBadge, {
1934
+ providerId: selected.memoryProviderId,
1935
+ label: selected.memoryProviderLabel ?? selected.memoryProviderId
1936
+ })]
1937
+ }),
1938
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1939
+ className: dsh_mnemon_client.memoryPageStyles.inspectorTitleRow,
1940
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
1941
+ className: dsh_mnemon_client.memoryPageStyles.inspectorTitle,
1942
+ children: selected.content
1943
+ }), selectedKind === "memory" && selected.content.length > 140 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1944
+ type: "button",
1945
+ className: dsh_mnemon_client.memoryPageStyles.inspectorEye,
1946
+ onClick: () => setPreview(selected),
1947
+ "aria-label": t("overview.previewAria"),
1948
+ title: t("overview.previewAria"),
1949
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
1950
+ viewBox: "0 0 16 16",
1951
+ width: "13",
1952
+ height: "13",
1953
+ "aria-hidden": "true",
1954
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1955
+ d: "M1 8s2.6-4.4 7-4.4S15 8 15 8s-2.6 4.4-7 4.4S1 8 1 8z",
1956
+ fill: "none",
1957
+ stroke: "currentColor",
1958
+ strokeWidth: "1.5"
1959
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
1960
+ cx: "8",
1961
+ cy: "8",
1962
+ r: "2.1",
1963
+ fill: "currentColor"
1964
+ })]
1965
+ })
1966
+ })]
1967
+ }),
1968
+ selectedKind === "space" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
1969
+ className: dsh_mnemon_client.memoryPageStyles.inspectorMeta,
1970
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("overview.spaceId") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: selected.memoryBodyId ?? selected.id }) })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("overview.containedMemories") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: selected.occurrenceCount ?? 0 })] })]
1971
+ }) : selectedKind === "entity" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
1972
+ className: dsh_mnemon_client.memoryPageStyles.inspectorMeta,
1973
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("overview.entityMentions") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: selected.occurrenceCount ?? 0 })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("term.spaces") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: selected.memoryBodyNames?.join(" · ") || "—" })] })]
1974
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
1975
+ className: dsh_mnemon_client.memoryPageStyles.inspectorMeta,
1976
+ children: [
1977
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("term.space") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dd", { children: [
1978
+ selected.memoryBodyName ?? "—",
1979
+ " ",
1980
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: selected.memoryBodyId ?? "" })
1981
+ ] })] }),
1982
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("overview.memoryId") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: selected.id }) })] }),
1983
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("common.category") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: categoryLabel(t, selected.category ?? "general") })] })
1984
+ ]
1985
+ }),
1986
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1987
+ className: dsh_mnemon_client.memoryPageStyles.inspectorActions,
1988
+ children: [selectedKind !== "space" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1989
+ type: "button",
1990
+ className: dsh_mnemon_client.memoryPageStyles.primaryButton,
1991
+ onClick: () => props.onExplore(selected.content),
1992
+ children: t("overview.exploreNode")
1993
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1994
+ type: "button",
1995
+ className: dsh_mnemon_client.memoryPageStyles.secondaryButton,
1996
+ onClick: () => void navigator.clipboard?.writeText(selected.id),
1997
+ children: t("common.copyId")
1998
+ })]
1999
+ })
2000
+ ] })
2001
+ })]
2002
+ }) : !graphLoading && error === null ? catalogUnavailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
2003
+ glyph: "◇",
2004
+ title: t("overview.unsyncedTitle"),
2005
+ children: t("overview.unsyncedLong")
2006
+ }) : catalog?.total === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
2007
+ glyph: "◇",
2008
+ title: t("overview.emptyTitle"),
2009
+ children: t("overview.emptyLong")
2010
+ }) : catalog?.activeCount === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
2011
+ glyph: "◇",
2012
+ title: t("overview.noActiveTitle"),
2013
+ children: t("overview.noActiveText")
2014
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
2015
+ glyph: "◇",
2016
+ title: t(onlyQueryOrUnsupported ? "overview.noVisualTitle" : "overview.noContentTitle"),
2017
+ children: t(onlyQueryOrUnsupported ? "overview.noVisualText" : "overview.noContentText")
2018
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2019
+ className: dsh_mnemon_client.memoryPageStyles.asyncPlaceholder,
2020
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("overview.loading") })
2021
+ }),
2022
+ preview !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContentPreview, {
2023
+ node: preview,
2024
+ kind: graphKindLabel(t, preview),
2025
+ onClose: () => setPreview(null)
2026
+ })
2027
+ ]
2028
+ });
2029
+ }
2030
+ function ExplorePage(props) {
2031
+ const t = (0, dsh_mnemon_client.useT)();
2032
+ const pageSize = 6;
2033
+ const [query, setQuery] = (0, react.useState)(props.seed);
2034
+ const [mode, setMode] = (0, react.useState)("smart");
2035
+ const [category, setCategory] = (0, react.useState)("");
2036
+ const [results, setResults] = (0, react.useState)([]);
2037
+ const [sources, setSources] = (0, react.useState)([]);
2038
+ const [searchKind, setSearchKind] = (0, react.useState)(null);
2039
+ const [agentAnswer, setAgentAnswer] = (0, react.useState)(null);
2040
+ const [searched, setSearched] = (0, react.useState)(false);
2041
+ const [error, setError] = (0, react.useState)(null);
2042
+ const [relatedTo, setRelatedTo] = (0, react.useState)(null);
2043
+ const [related, setRelated] = (0, react.useState)([]);
2044
+ const [relatedLoading, setRelatedLoading] = (0, react.useState)(false);
2045
+ const [visibleResultLimit, setVisibleResultLimit] = (0, react.useState)(pageSize);
2046
+ const [visibleRelatedLimit, setVisibleRelatedLimit] = (0, react.useState)(pageSize);
2047
+ const relatedRequests = (0, dsh_mnemon_client.useRequestVersion)();
2048
+ (0, react.useEffect)(() => {
2049
+ if (props.seed !== "") setQuery(props.seed);
2050
+ }, [props.seed]);
2051
+ const runSearch = async (withAgent) => {
2052
+ if (query.trim() === "") return;
2053
+ relatedRequests.begin();
2054
+ setSearchKind(withAgent ? "agent" : "direct");
2055
+ setSearched(true);
2056
+ setError(null);
2057
+ setRelatedTo(null);
2058
+ setAgentAnswer(null);
2059
+ setVisibleResultLimit(pageSize);
2060
+ setVisibleRelatedLimit(pageSize);
2061
+ try {
2062
+ const request = {
2063
+ query,
2064
+ mode,
2065
+ ...category === "" ? {} : { category },
2066
+ limit: props.status?.defaultRecallLimit ?? 10
2067
+ };
2068
+ if (withAgent) {
2069
+ const response = await props.agentClient.agentSearch(request);
2070
+ setResults(response.results);
2071
+ setSources(response.sources ?? []);
2072
+ setAgentAnswer({
2073
+ answer: response.answer,
2074
+ citations: response.citations,
2075
+ runId: response.delegation.runId
2076
+ });
2077
+ } else {
2078
+ const response = await props.client.search(request);
2079
+ setResults(response.results);
2080
+ setSources(response.sources ?? []);
2081
+ }
2082
+ } catch (reason) {
2083
+ setError((0, dsh_mnemon_client.message)(reason));
2084
+ setResults([]);
2085
+ setSources([]);
2086
+ setAgentAnswer(null);
2087
+ } finally {
2088
+ setSearchKind(null);
2089
+ }
2090
+ };
2091
+ const search = (event) => {
2092
+ event.preventDefault();
2093
+ runSearch(false);
2094
+ };
2095
+ const searching = searchKind !== null;
2096
+ const showRelated = async (insight) => {
2097
+ const request = relatedRequests.begin();
2098
+ setRelatedTo(insight);
2099
+ setRelated([]);
2100
+ setRelatedLoading(true);
2101
+ setError(null);
2102
+ setVisibleRelatedLimit(pageSize);
2103
+ if (typeof window.requestAnimationFrame === "function") window.requestAnimationFrame(() => document.getElementById("mnemon-related-pane")?.scrollIntoView?.({
2104
+ block: "nearest",
2105
+ behavior: "smooth"
2106
+ }));
2107
+ try {
2108
+ const response = await props.client.related(insight.id, insight.memoryBodyId);
2109
+ if (relatedRequests.isCurrent(request)) setRelated(response);
2110
+ } catch (reason) {
2111
+ if (relatedRequests.isCurrent(request)) setError((0, dsh_mnemon_client.message)(reason));
2112
+ } finally {
2113
+ if (relatedRequests.isCurrent(request)) setRelatedLoading(false);
2114
+ }
2115
+ };
2116
+ const forget = async (insight) => {
2117
+ await props.onForget(insight);
2118
+ setResults((items) => items.filter((item) => insightKey(item) !== insightKey(insight)));
2119
+ setRelated((items) => items.filter((item) => insightKey(item) !== insightKey(insight)));
2120
+ if (relatedTo !== null && insightKey(relatedTo) === insightKey(insight)) setRelatedTo(null);
2121
+ };
2122
+ const visibleResults = results.slice(0, visibleResultLimit);
2123
+ const visibleRelated = related.slice(0, visibleRelatedLimit);
2124
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2125
+ className: dsh_mnemon_client.memoryPageStyles.page,
2126
+ children: [
2127
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.PageHeader, {
2128
+ title: t("search.title"),
2129
+ description: t("search.description"),
2130
+ meta: t("search.maxResults", { count: props.status?.defaultRecallLimit ?? "—" })
2131
+ }),
2132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
2133
+ className: dsh_mnemon_client.memoryPageStyles.searchBar,
2134
+ onSubmit: (event) => void search(event),
2135
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2136
+ className: dsh_mnemon_client.memoryPageStyles.queryField,
2137
+ children: [
2138
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2139
+ "aria-hidden": "true",
2140
+ children: "⌕"
2141
+ }),
2142
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2143
+ value: query,
2144
+ onChange: (event) => setQuery(event.target.value),
2145
+ placeholder: t("search.placeholder"),
2146
+ "aria-label": t("search.queryAria")
2147
+ }),
2148
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("kbd", { children: "↵" })
2149
+ ]
2150
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2151
+ className: dsh_mnemon_client.memoryPageStyles.searchControls,
2152
+ children: [
2153
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("common.category"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2154
+ value: category,
2155
+ onChange: (event) => setCategory(event.target.value),
2156
+ "aria-label": t("search.categoryAria"),
2157
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2158
+ value: "",
2159
+ children: t("common.allCategories")
2160
+ }), CATEGORIES.map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2161
+ value,
2162
+ children: categoryLabel(t, value)
2163
+ }, value))]
2164
+ })] }),
2165
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("search.strategy"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2166
+ value: mode,
2167
+ onChange: (event) => setMode(event.target.value),
2168
+ "aria-label": t("search.modeAria"),
2169
+ children: [
2170
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2171
+ value: "smart",
2172
+ children: t("search.modeSmart")
2173
+ }),
2174
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2175
+ value: "keyword",
2176
+ children: t("search.modeKeyword")
2177
+ }),
2178
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2179
+ value: "basic",
2180
+ children: t("search.modeBasic")
2181
+ })
2182
+ ]
2183
+ })] }),
2184
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2185
+ className: dsh_mnemon_client.memoryPageStyles.searchActions,
2186
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2187
+ type: "submit",
2188
+ className: dsh_mnemon_client.memoryPageStyles.secondaryButton,
2189
+ disabled: searching || query.trim() === "",
2190
+ children: searchKind === "direct" ? t("search.searching") : t("search.action")
2191
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2192
+ type: "button",
2193
+ className: dsh_mnemon_client.memoryPageStyles.primaryButton,
2194
+ disabled: searching || query.trim() === "" || !props.agentAvailable,
2195
+ onClick: () => void runSearch(true),
2196
+ children: searchKind === "agent" ? t("search.agentSearching") : t("search.agentAction")
2197
+ })]
2198
+ })
2199
+ ]
2200
+ })]
2201
+ }),
2202
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadSourcePanel, {
2203
+ title: t("search.sourcesTitle"),
2204
+ sources
2205
+ }),
2206
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2207
+ className: dsh_mnemon_client.memoryPageStyles.asyncResults,
2208
+ children: [
2209
+ searching && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SectionSpinner, { label: searchKind === "agent" ? t("search.agentSearching") : t("search.searching") }),
2210
+ agentAnswer !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2211
+ className: dsh_mnemon_client.memoryPageStyles.agentAnswer,
2212
+ "aria-label": t("search.agentAnswer"),
2213
+ children: [
2214
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2215
+ className: dsh_mnemon_client.memoryPageStyles.agentAnswerHeading,
2216
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("search.agentAnswerHint") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("search.agentAnswer") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: agentAnswer.runId.slice(0, 8) })]
2217
+ }),
2218
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: agentAnswer.answer }),
2219
+ agentAnswer.citations.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2220
+ className: dsh_mnemon_client.memoryPageStyles.agentCitations,
2221
+ children: agentAnswer.citations.map((citation) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: citation }, citation))
2222
+ })
2223
+ ]
2224
+ }),
2225
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2226
+ className: dsh_mnemon_client.memoryPageStyles.inlineError,
2227
+ role: "alert",
2228
+ children: error
2229
+ }),
2230
+ !searched && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
2231
+ glyph: "⌕",
2232
+ title: t("search.startTitle"),
2233
+ children: t("search.startText")
2234
+ }),
2235
+ searched && !searching && results.length === 0 && error === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
2236
+ glyph: "0",
2237
+ title: t("search.emptyTitle"),
2238
+ children: t("search.emptyText")
2239
+ }),
2240
+ results.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2241
+ className: relatedTo === null ? dsh_mnemon_client.memoryPageStyles.singleColumn : dsh_mnemon_client.memoryPageStyles.resultLayout,
2242
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2243
+ className: dsh_mnemon_client.memoryPageStyles.results,
2244
+ children: [
2245
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2246
+ className: dsh_mnemon_client.memoryPageStyles.sectionHeading,
2247
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("search.results") }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: results.length })]
2248
+ }),
2249
+ visibleResults.map((insight) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InsightCard, {
2250
+ insight,
2251
+ writeEnabled: props.writeEnabled,
2252
+ onForget: forget,
2253
+ onRelated: (item) => void showRelated(item)
2254
+ }, insightKey(insight))),
2255
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.ProgressiveFooter, {
2256
+ visible: visibleResults.length,
2257
+ total: results.length,
2258
+ pageSize,
2259
+ onMore: () => setVisibleResultLimit((value) => value + pageSize)
2260
+ })
2261
+ ]
2262
+ }), relatedTo !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
2263
+ id: "mnemon-related-pane",
2264
+ className: dsh_mnemon_client.memoryPageStyles.relatedPane,
2265
+ children: [
2266
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2267
+ className: dsh_mnemon_client.memoryPageStyles.sectionHeading,
2268
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("search.related") }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2269
+ type: "button",
2270
+ onClick: () => {
2271
+ relatedRequests.begin();
2272
+ setRelatedTo(null);
2273
+ setRelatedLoading(false);
2274
+ },
2275
+ "aria-label": t("search.closeRelated"),
2276
+ children: "×"
2277
+ })]
2278
+ }),
2279
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2280
+ className: dsh_mnemon_client.memoryPageStyles.relatedSource,
2281
+ children: relatedTo.content
2282
+ }),
2283
+ relatedLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2284
+ className: dsh_mnemon_client.memoryPageStyles.loading,
2285
+ children: t("search.traversing")
2286
+ }),
2287
+ !relatedLoading && related.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2288
+ className: dsh_mnemon_client.memoryPageStyles.muted,
2289
+ children: t("search.noRelated")
2290
+ }),
2291
+ visibleRelated.map((insight) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InsightCard, {
2292
+ insight,
2293
+ writeEnabled: props.writeEnabled,
2294
+ onForget: forget,
2295
+ onRelated: (item) => void showRelated(item)
2296
+ }, insightKey(insight))),
2297
+ !relatedLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.ProgressiveFooter, {
2298
+ visible: visibleRelated.length,
2299
+ total: related.length,
2300
+ pageSize,
2301
+ onMore: () => setVisibleRelatedLimit((value) => value + pageSize)
2302
+ })
2303
+ ]
2304
+ })]
2305
+ })
2306
+ ]
2307
+ })
2308
+ ]
2309
+ });
2310
+ }
2311
+ function EntitiesPage(props) {
2312
+ const t = (0, dsh_mnemon_client.useT)();
2313
+ const entityPageSize = 10;
2314
+ const insightPageSize = 6;
2315
+ const [view, setView] = (0, react.useState)({
2316
+ items: [],
2317
+ insights: []
2318
+ });
2319
+ const [entity, setEntity] = (0, react.useState)("");
2320
+ const [entityFilter, setEntityFilter] = (0, react.useState)("");
2321
+ const [loading, setLoading] = (0, react.useState)(true);
2322
+ const [error, setError] = (0, react.useState)(null);
2323
+ const [visibleEntityLimit, setVisibleEntityLimit] = (0, react.useState)(entityPageSize);
2324
+ const [visibleInsightLimit, setVisibleInsightLimit] = (0, react.useState)(insightPageSize);
2325
+ const entityRequests = (0, dsh_mnemon_client.useRequestVersion)();
2326
+ const load = (0, react.useCallback)(async (selected) => {
2327
+ const request = entityRequests.begin();
2328
+ setLoading(true);
2329
+ setError(null);
2330
+ setVisibleInsightLimit(insightPageSize);
2331
+ if (selected === void 0) setVisibleEntityLimit(entityPageSize);
2332
+ try {
2333
+ const response = await props.client.entities(selected, 20);
2334
+ if (entityRequests.isCurrent(request)) setView(response);
2335
+ } catch (reason) {
2336
+ if (entityRequests.isCurrent(request)) setError((0, dsh_mnemon_client.message)(reason));
2337
+ } finally {
2338
+ if (entityRequests.isCurrent(request)) setLoading(false);
2339
+ }
2340
+ }, [
2341
+ entityPageSize,
2342
+ entityRequests,
2343
+ insightPageSize,
2344
+ props.client
2345
+ ]);
2346
+ (0, react.useEffect)(() => {
2347
+ load();
2348
+ }, [load, props.revision]);
2349
+ const submit = (event) => {
2350
+ event.preventDefault();
2351
+ if (entity.trim() !== "") load(entity);
2352
+ };
2353
+ const visibleEntities = (0, react.useMemo)(() => {
2354
+ const query = entityFilter.trim().toLocaleLowerCase();
2355
+ return (query === "" ? view.items : view.items.filter((item) => item.entity.toLocaleLowerCase().includes(query))).slice(0, visibleEntityLimit);
2356
+ }, [
2357
+ view.items,
2358
+ visibleEntityLimit,
2359
+ entityFilter
2360
+ ]);
2361
+ const filteredTotal = (0, react.useMemo)(() => {
2362
+ const query = entityFilter.trim().toLocaleLowerCase();
2363
+ return query === "" ? view.items.length : view.items.filter((item) => item.entity.toLocaleLowerCase().includes(query)).length;
2364
+ }, [view.items, entityFilter]);
2365
+ const visibleInsights = view.insights.slice(0, visibleInsightLimit);
2366
+ const sources = view.sources ?? [];
2367
+ const hasEntityProvider = sources.length === 0 || sources.some((source) => source.mode === "entities" && source.status !== "unavailable");
2368
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2369
+ className: dsh_mnemon_client.memoryPageStyles.page,
2370
+ children: [
2371
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.PageHeader, {
2372
+ title: t("entities.title"),
2373
+ description: t("entities.description"),
2374
+ meta: t("entities.count", { count: view.items.length })
2375
+ }),
2376
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadSourcePanel, {
2377
+ title: t("entities.sourcesTitle"),
2378
+ sources
2379
+ }),
2380
+ !loading && !hasEntityProvider ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
2381
+ glyph: "◎",
2382
+ title: t("entities.unsupportedTitle"),
2383
+ children: t("entities.unsupportedText")
2384
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2385
+ className: dsh_mnemon_client.memoryPageStyles.entityLayout,
2386
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("aside", {
2387
+ className: dsh_mnemon_client.memoryPageStyles.entityRail,
2388
+ children: [
2389
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
2390
+ className: dsh_mnemon_client.memoryPageStyles.entitySearch,
2391
+ onSubmit: submit,
2392
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2393
+ "aria-label": t("entities.nameAria"),
2394
+ value: entity,
2395
+ onChange: (event) => {
2396
+ setEntity(event.target.value);
2397
+ setEntityFilter(event.target.value);
2398
+ },
2399
+ onKeyDown: (event) => {
2400
+ if (event.key === "Escape" && entityFilter !== "") {
2401
+ event.preventDefault();
2402
+ setEntityFilter("");
2403
+ }
2404
+ },
2405
+ placeholder: t("entities.placeholder")
2406
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2407
+ type: "submit",
2408
+ className: dsh_mnemon_client.memoryPageStyles.primaryButton,
2409
+ disabled: loading || entity.trim() === "",
2410
+ children: t("entities.action")
2411
+ })]
2412
+ }),
2413
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2414
+ className: dsh_mnemon_client.memoryPageStyles.entityHeading,
2415
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("entities.top") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: entityFilter === "" ? t("entities.frequency") : t("entities.filterHint") })]
2416
+ }),
2417
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2418
+ className: dsh_mnemon_client.memoryPageStyles.entityList,
2419
+ children: visibleEntities.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2420
+ type: "button",
2421
+ "aria-pressed": view.selected === item.entity,
2422
+ onClick: () => {
2423
+ setEntity(item.entity);
2424
+ load(item.entity);
2425
+ },
2426
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: item.entity }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: item.count })]
2427
+ }, item.entity))
2428
+ }),
2429
+ visibleEntities.length > 0 && filteredTotal > visibleEntityLimit && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.ProgressiveFooter, {
2430
+ compact: true,
2431
+ visible: visibleEntities.length,
2432
+ total: filteredTotal,
2433
+ pageSize: entityPageSize,
2434
+ onMore: () => setVisibleEntityLimit((value) => value + entityPageSize)
2435
+ }),
2436
+ !loading && view.items.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2437
+ className: dsh_mnemon_client.memoryPageStyles.muted,
2438
+ children: t("entities.emptyRail")
2439
+ }),
2440
+ !loading && view.items.length > 0 && visibleEntities.length === 0 && entityFilter !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2441
+ className: dsh_mnemon_client.memoryPageStyles.muted,
2442
+ children: t("entities.filterEmpty")
2443
+ })
2444
+ ]
2445
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2446
+ className: (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.entityResults, dsh_mnemon_client.memoryPageStyles.asyncResults),
2447
+ children: [
2448
+ loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SectionSpinner, { label: t("entities.loading") }),
2449
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2450
+ className: dsh_mnemon_client.memoryPageStyles.inlineError,
2451
+ role: "alert",
2452
+ children: error
2453
+ }),
2454
+ !loading && view.selected === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
2455
+ glyph: "◎",
2456
+ title: t("entities.selectTitle"),
2457
+ children: t("entities.selectText")
2458
+ }),
2459
+ view.selected !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2460
+ className: dsh_mnemon_client.memoryPageStyles.sectionHeading,
2461
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: view.selected }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: view.insights.length })]
2462
+ }), !loading && view.insights.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
2463
+ glyph: "0",
2464
+ title: t("entities.emptyTitle"),
2465
+ children: t("entities.emptyText")
2466
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [visibleInsights.map((insight) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InsightCard, {
2467
+ insight,
2468
+ writeEnabled: props.writeEnabled,
2469
+ onForget: props.onForget,
2470
+ onRelated: () => props.onExplore(insight.content)
2471
+ }, insightKey(insight))), !loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.ProgressiveFooter, {
2472
+ visible: visibleInsights.length,
2473
+ total: view.insights.length,
2474
+ pageSize: insightPageSize,
2475
+ onMore: () => setVisibleInsightLimit((value) => value + insightPageSize)
2476
+ })] })] })
2477
+ ]
2478
+ })]
2479
+ })
2480
+ ]
2481
+ });
2482
+ }
2483
+ function PersistenceStrategyDialog(props) {
2484
+ const t = (0, dsh_mnemon_client.useT)();
2485
+ const strategyFormId = (0, react.useId)();
2486
+ const configured = props.config?.persistenceStrategy;
2487
+ const [mode, setMode] = (0, react.useState)(configured?.mode ?? "manual");
2488
+ const [providerId, setProviderId] = (0, react.useState)(configured?.providerId ?? "mnemon-native");
2489
+ const [prompt, setPrompt] = (0, react.useState)(configured?.prompt ?? "");
2490
+ const [dataBoundary, setDataBoundary] = (0, react.useState)(configured?.rules?.dataBoundary ?? "allow-remote");
2491
+ const [preference, setPreference] = (0, react.useState)(configured?.rules?.preference ?? "balanced");
2492
+ const [requiredCapabilities, setRequiredCapabilities] = (0, react.useState)(configured?.rules?.requiredCapabilities ?? []);
2493
+ const [automaticProviderIds, setAutomaticProviderIds] = (0, react.useState)(configured?.rules?.allowedProviderIds ?? ["mnemon-native"]);
2494
+ const [providerDrafts, setProviderDrafts] = (0, react.useState)(configured?.providerConnections ?? {});
2495
+ const [providers, setProviders] = (0, react.useState)([]);
2496
+ const [loading, setLoading] = (0, react.useState)(true);
2497
+ const [saving, setSaving] = (0, react.useState)(false);
2498
+ const [error, setError] = (0, react.useState)(null);
2499
+ (0, react.useEffect)(() => {
2500
+ let current = true;
2501
+ props.client.bodyDirectory().then((catalog) => {
2502
+ if (!current) return;
2503
+ const next = catalog.providers;
2504
+ setProviders(next);
2505
+ setProviderDrafts((previous) => mergeProviderDefaults(next, previous));
2506
+ setProviderId((currentProviderId) => next.some((provider) => provider.id === currentProviderId && (provider.origin === "native" || provider.serviceConfigured !== false)) ? currentProviderId : next.find((provider) => provider.origin === "native")?.id ?? next[0]?.id ?? "mnemon-native");
2507
+ }).catch((reason) => {
2508
+ if (current) setError((0, dsh_mnemon_client.message)(reason));
2509
+ }).finally(() => {
2510
+ if (current) setLoading(false);
2511
+ });
2512
+ return () => {
2513
+ current = false;
2514
+ };
2515
+ }, [props.client]);
2516
+ const selectedProvider = providers.find((provider) => provider.id === providerId);
2517
+ const selectedAutomaticProviders = automaticProviderIds.map((id) => providers.find((provider) => provider.id === id)).filter((provider) => provider !== void 0);
2518
+ const selectedProvidersValid = mode === "manual" ? providerDraftComplete(selectedProvider, providerDrafts[providerId]) : automaticProviderIds.length > 0 && selectedAutomaticProviders.length === automaticProviderIds.length && selectedAutomaticProviders.every((provider) => providerDraftComplete(provider, providerDrafts[provider.id]));
2519
+ const updateDraft = (id, key, value) => setProviderDrafts((current) => ({
2520
+ ...current,
2521
+ [id]: {
2522
+ ...current[id] ?? {},
2523
+ [key]: value
2524
+ }
2525
+ }));
2526
+ const toggleCapability = (capability) => setRequiredCapabilities((current) => current.includes(capability) ? current.filter((value) => value !== capability) : [...current, capability]);
2527
+ const toggleProvider = (id, selected) => setAutomaticProviderIds((current) => selected ? [.../* @__PURE__ */ new Set([...current, id])] : current.filter((value) => value !== id));
2528
+ const save = async (event) => {
2529
+ event.preventDefault();
2530
+ if (loading || saving || !props.writable || !selectedProvidersValid) return;
2531
+ const connections = Object.fromEntries((mode === "manual" ? [providerId] : automaticProviderIds).filter((id) => providers.find((provider) => provider.id === id)?.origin !== "native").map((id) => [id, providerDrafts[id] ?? {}]));
2532
+ setSaving(true);
2533
+ setError(null);
2534
+ try {
2535
+ await props.settingsScope.setPath(["persistenceStrategy"], {
2536
+ mode,
2537
+ providerId,
2538
+ prompt,
2539
+ rules: {
2540
+ allowedProviderIds: automaticProviderIds,
2541
+ dataBoundary,
2542
+ requiredCapabilities,
2543
+ preference
2544
+ },
2545
+ providerConnections: connections
2546
+ });
2547
+ props.onClose();
2548
+ } catch (reason) {
2549
+ setError((0, dsh_mnemon_client.message)(reason));
2550
+ } finally {
2551
+ setSaving(false);
2552
+ }
2553
+ };
2554
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SidebarModal, {
2555
+ title: t("strategy.title"),
2556
+ description: t("strategy.description"),
2557
+ busy: saving,
2558
+ contentReady: !loading,
2559
+ wide: true,
2560
+ onClose: props.onClose,
2561
+ footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2562
+ className: dsh_mnemon_client.memoryPageStyles.modalFooterActions,
2563
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2564
+ type: "button",
2565
+ "data-dialog-close": true,
2566
+ className: dsh_mnemon_client.memoryPageStyles.ghostButton,
2567
+ disabled: saving,
2568
+ onClick: props.onClose,
2569
+ children: t("common.cancel")
2570
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2571
+ type: "submit",
2572
+ form: strategyFormId,
2573
+ className: dsh_mnemon_client.memoryPageStyles.primaryButton,
2574
+ disabled: loading || saving || !props.writable || !selectedProvidersValid,
2575
+ children: saving ? t("strategy.saving") : t("strategy.save")
2576
+ })]
2577
+ }),
2578
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
2579
+ id: strategyFormId,
2580
+ className: (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.bodyEdit, dsh_mnemon_client.memoryPageStyles.strategyForm),
2581
+ onSubmit: (event) => void save(event),
2582
+ children: [
2583
+ loading && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2584
+ className: dsh_mnemon_client.memoryPageStyles.strategyLoading,
2585
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SectionSpinner, { label: t("strategy.loading") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("strategy.loading") })]
2586
+ }),
2587
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2588
+ className: dsh_mnemon_client.memoryPageStyles.inlineError,
2589
+ role: "alert",
2590
+ children: error
2591
+ }),
2592
+ !loading && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2593
+ className: dsh_mnemon_client.memoryPageStyles.createSection,
2594
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2595
+ className: dsh_mnemon_client.memoryPageStyles.createSectionHeading,
2596
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "01" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("strategy.modeTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("strategy.modeHint") })] })]
2597
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
2598
+ className: dsh_mnemon_client.memoryPageStyles.placementMode,
2599
+ children: [
2600
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", { children: t("overview.placementMode") }),
2601
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2602
+ "data-selected": mode === "manual" || void 0,
2603
+ children: [
2604
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2605
+ type: "radio",
2606
+ name: "persistence-mode",
2607
+ value: "manual",
2608
+ checked: mode === "manual",
2609
+ "data-autofocus": mode === "manual" || void 0,
2610
+ onChange: () => setMode("manual")
2611
+ }),
2612
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
2613
+ className: dsh_mnemon_client.memoryPageStyles.choiceControl,
2614
+ "data-kind": "radio",
2615
+ "aria-hidden": "true"
2616
+ }),
2617
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("overview.placementManual") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("strategy.manualHint") })] })
2618
+ ]
2619
+ }),
2620
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2621
+ "data-selected": mode === "automatic" || void 0,
2622
+ children: [
2623
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2624
+ type: "radio",
2625
+ name: "persistence-mode",
2626
+ value: "automatic",
2627
+ checked: mode === "automatic",
2628
+ "data-autofocus": mode === "automatic" || void 0,
2629
+ onChange: () => setMode("automatic")
2630
+ }),
2631
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
2632
+ className: dsh_mnemon_client.memoryPageStyles.choiceControl,
2633
+ "data-kind": "radio",
2634
+ "aria-hidden": "true"
2635
+ }),
2636
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [
2637
+ t("overview.placementAutomatic"),
2638
+ " ",
2639
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", { children: t("overview.recommended") })
2640
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("strategy.automaticHint") })] })
2641
+ ]
2642
+ })
2643
+ ]
2644
+ })]
2645
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2646
+ className: dsh_mnemon_client.memoryPageStyles.createSection,
2647
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2648
+ className: dsh_mnemon_client.memoryPageStyles.createSectionHeading,
2649
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "02" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t(mode === "manual" ? "strategy.manualTitle" : "strategy.automaticTitle") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t(mode === "manual" ? "strategy.manualDescription" : "strategy.automaticDescription") })] })]
2650
+ }), mode === "manual" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
2651
+ className: dsh_mnemon_client.memoryPageStyles.providerChoice,
2652
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", { children: t("overview.providerLabel") }), providers.map((provider) => {
2653
+ const disabled = provider.origin !== "native" && provider.serviceConfigured === false;
2654
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2655
+ "data-selected": providerId === provider.id || void 0,
2656
+ "data-native": provider.origin === "native" || void 0,
2657
+ "data-disabled": disabled || void 0,
2658
+ children: [
2659
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2660
+ type: "radio",
2661
+ name: "strategy-provider",
2662
+ value: provider.id,
2663
+ checked: providerId === provider.id,
2664
+ disabled,
2665
+ onChange: () => setProviderId(provider.id)
2666
+ }),
2667
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderIcon, {
2668
+ providerId: provider.id,
2669
+ icon: provider.icon,
2670
+ className: dsh_mnemon_client.memoryPageStyles.providerChoiceIcon
2671
+ }),
2672
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [provider.label, provider.origin === "native" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", { children: t("overview.nativeOfficial") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: disabled ? t("overview.providerServiceRequired") : `${t(`overview.workspaceBinding.${provider.workspaceBinding}`)} · ${providerSummary(t, provider)}` })] }),
2673
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
2674
+ className: dsh_mnemon_client.memoryPageStyles.choiceControl,
2675
+ "data-kind": "radio",
2676
+ "aria-hidden": "true"
2677
+ })
2678
+ ]
2679
+ }, provider.id);
2680
+ })]
2681
+ }), selectedProvider !== void 0 && selectedProvider.origin !== "native" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderMemoryFields, {
2682
+ provider: selectedProvider,
2683
+ connection: providerDrafts[selectedProvider.id] ?? {},
2684
+ onChange: (key, value) => updateDraft(selectedProvider.id, key, value)
2685
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2686
+ className: dsh_mnemon_client.memoryPageStyles.placementPolicy,
2687
+ "aria-label": t("overview.placementPolicy"),
2688
+ children: [
2689
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2690
+ className: dsh_mnemon_client.memoryPageStyles.placementPolicyHeading,
2691
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("overview.placementPolicy") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("overview.placementPolicyHint") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: props.agentAvailable ? t("strategy.taskAgentReady") : t("strategy.taskAgentUnavailable") })]
2692
+ }),
2693
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("overview.placementPrompt"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2694
+ "aria-label": t("overview.placementPrompt"),
2695
+ value: prompt,
2696
+ onChange: (event) => setPrompt(event.target.value),
2697
+ placeholder: t("overview.placementPromptPlaceholder"),
2698
+ rows: 3,
2699
+ maxLength: 4e3
2700
+ })] }),
2701
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2702
+ className: dsh_mnemon_client.memoryPageStyles.placementRuleGrid,
2703
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("overview.dataBoundary"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2704
+ "aria-label": t("overview.dataBoundary"),
2705
+ value: dataBoundary,
2706
+ onChange: (event) => {
2707
+ const value = event.target.value;
2708
+ setDataBoundary(value);
2709
+ if (value === "local-only") setAutomaticProviderIds((current) => current.filter((id) => providers.find((provider) => provider.id === id)?.kind === "local"));
2710
+ },
2711
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2712
+ value: "allow-remote",
2713
+ children: t("overview.dataBoundaryRemote")
2714
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2715
+ value: "local-only",
2716
+ children: t("overview.dataBoundaryLocal")
2717
+ })]
2718
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("overview.preference"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
2719
+ "aria-label": t("overview.preference"),
2720
+ value: preference,
2721
+ onChange: (event) => setPreference(event.target.value),
2722
+ children: [
2723
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2724
+ value: "balanced",
2725
+ children: t("overview.preferenceBalanced")
2726
+ }),
2727
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2728
+ value: "local-first",
2729
+ children: t("overview.preferenceLocal")
2730
+ }),
2731
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2732
+ value: "shared-first",
2733
+ children: t("overview.preferenceShared")
2734
+ })
2735
+ ]
2736
+ })] })]
2737
+ }),
2738
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
2739
+ className: dsh_mnemon_client.memoryPageStyles.capabilityRules,
2740
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", { children: t("overview.requiredCapabilities") }), [
2741
+ "graph",
2742
+ "exact-write",
2743
+ "forget"
2744
+ ].map((capability) => {
2745
+ const selected = requiredCapabilities.includes(capability);
2746
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2747
+ "data-selected": selected || void 0,
2748
+ children: [
2749
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2750
+ type: "checkbox",
2751
+ checked: selected,
2752
+ onChange: () => toggleCapability(capability)
2753
+ }),
2754
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
2755
+ className: dsh_mnemon_client.memoryPageStyles.choiceControl,
2756
+ "data-kind": "check",
2757
+ "aria-hidden": "true"
2758
+ }),
2759
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t(`overview.capability.${capability}`) })
2760
+ ]
2761
+ }, capability);
2762
+ })]
2763
+ }),
2764
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2765
+ className: dsh_mnemon_client.memoryPageStyles.placementCandidates,
2766
+ children: providers.map((provider) => {
2767
+ const disabled = provider.serviceConfigured === false || dataBoundary === "local-only" && provider.kind === "remote";
2768
+ const selected = automaticProviderIds.includes(provider.id);
2769
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2770
+ "data-selected": selected || void 0,
2771
+ "data-disabled": disabled || void 0,
2772
+ children: [
2773
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2774
+ type: "checkbox",
2775
+ checked: selected,
2776
+ disabled,
2777
+ onChange: (event) => toggleProvider(provider.id, event.target.checked)
2778
+ }),
2779
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderIcon, {
2780
+ providerId: provider.id,
2781
+ icon: provider.icon,
2782
+ className: dsh_mnemon_client.memoryPageStyles.candidateIcon
2783
+ }),
2784
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: provider.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: provider.serviceConfigured === false ? t("overview.providerServiceRequired") : provider.origin === "native" ? t("overview.candidateNativeReady") : provider.kind === "local" ? t("overview.candidateLocal") : t("overview.candidateRemote") })] }),
2785
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", {
2786
+ className: dsh_mnemon_client.memoryPageStyles.choiceControl,
2787
+ "data-kind": "check",
2788
+ "aria-hidden": "true"
2789
+ })
2790
+ ]
2791
+ }, provider.id);
2792
+ })
2793
+ }),
2794
+ automaticProviderIds.map((id) => {
2795
+ const provider = providers.find((candidate) => candidate.id === id);
2796
+ return provider === void 0 || provider.origin === "native" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderMemoryFields, {
2797
+ provider,
2798
+ connection: providerDrafts[id] ?? {},
2799
+ onChange: (key, value) => updateDraft(id, key, value)
2800
+ }, id);
2801
+ })
2802
+ ]
2803
+ })]
2804
+ })] })
2805
+ ]
2806
+ })
2807
+ });
2808
+ }
2809
+ function RememberPage(props) {
2810
+ const t = (0, dsh_mnemon_client.useT)();
2811
+ const rememberFormId = (0, react.useId)();
2812
+ const [content, setContent] = (0, react.useState)(props.seed);
2813
+ const [category, setCategory] = (0, react.useState)("general");
2814
+ const [importance, setImportance] = (0, react.useState)(3);
2815
+ const [tags, setTags] = (0, react.useState)("");
2816
+ const [entities, setEntities] = (0, react.useState)("");
2817
+ const [memoryBodyId, setMemoryBodyId] = (0, react.useState)("");
2818
+ const [supervising, setSupervising] = (0, react.useState)(false);
2819
+ const [saving, setSaving] = (0, react.useState)(false);
2820
+ const [result, setResult] = (0, react.useState)(null);
2821
+ (0, react.useEffect)(() => {
2822
+ if (props.seed !== "") setContent(props.seed);
2823
+ }, [props.seed]);
2824
+ (0, react.useEffect)(() => {
2825
+ if (memoryBodyId === "" && props.memoryBodies.length > 0) setMemoryBodyId((props.memoryBodies.find((body) => body.active) ?? props.memoryBodies[0]).id);
2826
+ }, [memoryBodyId, props.memoryBodies]);
2827
+ const selectedMemoryBody = props.memoryBodies.find((body) => body.id === memoryBodyId);
2828
+ const supervise = async (event) => {
2829
+ event.preventDefault();
2830
+ if (content.trim() === "" || !props.agentAvailable) return;
2831
+ setSupervising(true);
2832
+ setResult(null);
2833
+ try {
2834
+ const response = await props.client.supervise(content);
2835
+ setResult(`${t(response.action === "skipped" ? "remember.skipped" : "remember.completed")}${response.memoryBodyIds.length === 0 ? "" : ` · ${response.memoryBodyIds.join(", ")}`}${response.summary === "" ? "" : ` · ${response.summary}`}`);
2836
+ props.onMutate();
2837
+ if (response.action !== "skipped") {
2838
+ setContent("");
2839
+ props.onComplete?.();
2840
+ }
2841
+ } catch (reason) {
2842
+ setResult(t("remember.dispatchFailed", { error: (0, dsh_mnemon_client.message)(reason) }));
2843
+ } finally {
2844
+ setSupervising(false);
2845
+ }
2846
+ };
2847
+ const manualSave = async (event) => {
2848
+ event.preventDefault();
2849
+ if (content.trim() === "") return;
2850
+ setSaving(true);
2851
+ setResult(null);
2852
+ try {
2853
+ const response = await props.client.remember({
2854
+ content,
2855
+ category,
2856
+ importance,
2857
+ tags: tags.split(",").map((value) => value.trim()).filter(Boolean),
2858
+ entities: entities.split(",").map((value) => value.trim()).filter(Boolean),
2859
+ source: "user",
2860
+ ...memoryBodyId === "" ? {} : { memoryBodyId }
2861
+ });
2862
+ const action = typeof response.action === "string" ? response.action : "saved";
2863
+ const summary = typeof response.summary === "string" ? response.summary : "";
2864
+ setResult(action === "skipped" ? `${t("remember.skipped")}${summary === "" ? "" : ` · ${summary}`}` : `${t("remember.processed", { action })}${summary === "" ? "" : ` · ${summary}`}`);
2865
+ if (action !== "skipped") {
2866
+ setContent("");
2867
+ setTags("");
2868
+ setEntities("");
2869
+ props.onMutate();
2870
+ props.onComplete?.();
2871
+ }
2872
+ } catch (reason) {
2873
+ setResult(t("remember.saveFailed", { error: (0, dsh_mnemon_client.message)(reason) }));
2874
+ } finally {
2875
+ setSaving(false);
2876
+ }
2877
+ };
2878
+ const composer = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
2879
+ className: dsh_mnemon_client.memoryPageStyles.supervisedComposer,
2880
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
2881
+ id: rememberFormId,
2882
+ className: dsh_mnemon_client.memoryPageStyles.supervisedForm,
2883
+ onSubmit: (event) => void supervise(event),
2884
+ children: [
2885
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2886
+ className: dsh_mnemon_client.memoryPageStyles.supervisedHeading,
2887
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("remember.delegateTitle") }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2888
+ className: !props.agentAvailable ? dsh_mnemon_client.memoryPageStyles.sessionMissing : dsh_mnemon_client.memoryPageStyles.sessionReady,
2889
+ children: !props.agentAvailable ? t("remember.noTaskAgent") : t("remember.taskAgentReady")
2890
+ })]
2891
+ }),
2892
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2893
+ className: dsh_mnemon_client.memoryPageStyles.fieldWide,
2894
+ children: [t("remember.candidate"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
2895
+ "aria-label": t("remember.candidateAria"),
2896
+ value: content,
2897
+ onChange: (event) => setContent(event.target.value),
2898
+ maxLength: 8e3,
2899
+ rows: 8,
2900
+ placeholder: t("remember.placeholder")
2901
+ })]
2902
+ }),
2903
+ !props.agentAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2904
+ className: dsh_mnemon_client.memoryPageStyles.sessionHint,
2905
+ children: t("remember.taskAgentHint")
2906
+ }),
2907
+ result !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2908
+ className: dsh_mnemon_client.memoryPageStyles.modalInlineStatus,
2909
+ role: "status",
2910
+ children: result
2911
+ })
2912
+ ]
2913
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
2914
+ className: dsh_mnemon_client.memoryPageStyles.advancedWrite,
2915
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("remember.advanced") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("remember.advancedHint") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("remember.expand") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
2916
+ className: dsh_mnemon_client.memoryPageStyles.manualForm,
2917
+ onSubmit: (event) => void manualSave(event),
2918
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2919
+ className: dsh_mnemon_client.memoryPageStyles.formGrid,
2920
+ children: [
2921
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2922
+ className: dsh_mnemon_client.memoryPageStyles.fieldWide,
2923
+ children: [
2924
+ t("remember.target"),
2925
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
2926
+ "aria-label": t("remember.target"),
2927
+ value: memoryBodyId,
2928
+ onChange: (event) => setMemoryBodyId(event.target.value),
2929
+ children: props.memoryBodies.map((body) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
2930
+ value: body.id,
2931
+ children: [
2932
+ body.name,
2933
+ " · ",
2934
+ body.provider.label,
2935
+ body.active ? ` · ${t("common.active")}` : ""
2936
+ ]
2937
+ }, body.id))
2938
+ }),
2939
+ selectedMemoryBody?.provider.capabilities.writeMode === "async-extracting" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
2940
+ className: dsh_mnemon_client.memoryPageStyles.providerWriteHint,
2941
+ children: t("remember.asyncProviderHint")
2942
+ })
2943
+ ]
2944
+ }),
2945
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("common.category"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
2946
+ value: category,
2947
+ onChange: (event) => setCategory(event.target.value),
2948
+ children: CATEGORIES.map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
2949
+ value,
2950
+ children: categoryLabel(t, value)
2951
+ }, value))
2952
+ })] }),
2953
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t("common.importanceLabel"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
2954
+ value: importance,
2955
+ onChange: (event) => setImportance(Number(event.target.value)),
2956
+ children: [
2957
+ 1,
2958
+ 2,
2959
+ 3,
2960
+ 4,
2961
+ 5
2962
+ ].map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
2963
+ value,
2964
+ children: [value, " / 5"]
2965
+ }, value))
2966
+ })] }),
2967
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2968
+ className: dsh_mnemon_client.memoryPageStyles.fieldWide,
2969
+ children: [t("remember.entities"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2970
+ value: entities,
2971
+ onChange: (event) => setEntities(event.target.value),
2972
+ placeholder: "SQLite, DSH"
2973
+ })]
2974
+ }),
2975
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2976
+ className: dsh_mnemon_client.memoryPageStyles.fieldWide,
2977
+ children: [t("remember.tags"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2978
+ value: tags,
2979
+ onChange: (event) => setTags(event.target.value),
2980
+ placeholder: "architecture, local-first"
2981
+ })]
2982
+ })
2983
+ ]
2984
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2985
+ className: dsh_mnemon_client.memoryPageStyles.manualActions,
2986
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("remember.advancedText") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2987
+ type: "submit",
2988
+ className: dsh_mnemon_client.memoryPageStyles.secondaryButton,
2989
+ disabled: saving || content.trim() === "" || memoryBodyId === "",
2990
+ children: saving ? t("remember.saving") : t("remember.advancedAction")
2991
+ })]
2992
+ })]
2993
+ })]
2994
+ })]
2995
+ });
2996
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SidebarModal, {
2997
+ title: t("remember.title"),
2998
+ description: t("remember.description"),
2999
+ busy: supervising || saving,
3000
+ onClose: props.onClose,
3001
+ footer: props.writeEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3002
+ className: dsh_mnemon_client.memoryPageStyles.modalFooterActions,
3003
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3004
+ type: "button",
3005
+ "data-dialog-close": true,
3006
+ className: dsh_mnemon_client.memoryPageStyles.ghostButton,
3007
+ disabled: supervising || saving,
3008
+ onClick: props.onClose,
3009
+ children: t("common.cancel")
3010
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3011
+ type: "submit",
3012
+ form: rememberFormId,
3013
+ className: dsh_mnemon_client.memoryPageStyles.primaryButton,
3014
+ disabled: supervising || content.trim() === "" || !props.agentAvailable,
3015
+ children: supervising ? t("remember.processing") : t("remember.action")
3016
+ })]
3017
+ }) : void 0,
3018
+ children: props.writeEnabled ? composer : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
3019
+ glyph: "⊘",
3020
+ title: t("remember.readOnlyTitle"),
3021
+ children: t("remember.readOnlyText")
3022
+ })
3023
+ });
3024
+ }
3025
+ function ListPage(props) {
3026
+ const t = (0, dsh_mnemon_client.useT)();
3027
+ const pageSize = 12;
3028
+ const [query, setQuery] = (0, react.useState)("");
3029
+ const [category, setCategory] = (0, react.useState)("");
3030
+ const [view, setView] = (0, react.useState)(null);
3031
+ const [loading, setLoading] = (0, react.useState)(true);
3032
+ const [error, setError] = (0, react.useState)(null);
3033
+ const [visibleLimit, setVisibleLimit] = (0, react.useState)(pageSize);
3034
+ const [selectedBodyId, setSelectedBodyId] = (0, react.useState)();
3035
+ const load = (0, react.useCallback)(async () => {
3036
+ setLoading(true);
3037
+ setError(null);
3038
+ try {
3039
+ setView(await props.client.list({
3040
+ ...query.trim() === "" ? {} : { query },
3041
+ ...category === "" ? {} : { category },
3042
+ limit: 1e3
3043
+ }));
3044
+ } catch (reason) {
3045
+ setError((0, dsh_mnemon_client.message)(reason));
3046
+ } finally {
3047
+ setLoading(false);
3048
+ }
3049
+ }, [
3050
+ category,
3051
+ props.client,
3052
+ query
3053
+ ]);
3054
+ (0, react.useEffect)(() => {
3055
+ setVisibleLimit(pageSize);
3056
+ load();
3057
+ }, [pageSize, props.revision]);
3058
+ const submit = (event) => {
3059
+ event.preventDefault();
3060
+ setVisibleLimit(pageSize);
3061
+ load();
3062
+ };
3063
+ const forget = async (insight) => {
3064
+ await props.onForget(insight);
3065
+ setView((current) => current === null ? current : {
3066
+ ...current,
3067
+ total: Math.max(0, current.total - 1),
3068
+ items: current.items.filter((item) => insightKey(item) !== insightKey(insight))
3069
+ });
3070
+ };
3071
+ const filteredItems = view?.items.filter((item) => selectedBodyId === void 0 || item.memoryBodyId === selectedBodyId) ?? [];
3072
+ const visibleItems = filteredItems.slice(0, visibleLimit);
3073
+ const sources = view?.sources ?? [];
3074
+ const waitingForQuery = query.trim() === "" && sources.some((source) => source.status === "query-required" && (selectedBodyId === void 0 || source.memoryBodyId === selectedBodyId));
3075
+ const selectBody = (memoryBodyId) => {
3076
+ setSelectedBodyId(memoryBodyId);
3077
+ setVisibleLimit(pageSize);
3078
+ };
3079
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3080
+ className: dsh_mnemon_client.memoryPageStyles.page,
3081
+ children: [
3082
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.PageHeader, {
3083
+ title: t("content.title"),
3084
+ description: t("content.description"),
3085
+ meta: t("content.count", { count: view === null ? "—" : selectedBodyId === void 0 ? view.total : filteredItems.length })
3086
+ }),
3087
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
3088
+ className: dsh_mnemon_client.memoryPageStyles.listToolbar,
3089
+ onSubmit: submit,
3090
+ children: [
3091
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3092
+ "aria-label": t("content.filterAria"),
3093
+ value: query,
3094
+ onChange: (event) => setQuery(event.target.value),
3095
+ placeholder: t("content.filterPlaceholder")
3096
+ }),
3097
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3098
+ "aria-label": t("content.categoryAria"),
3099
+ value: category,
3100
+ onChange: (event) => setCategory(event.target.value),
3101
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3102
+ value: "",
3103
+ children: t("common.allCategories")
3104
+ }), CATEGORIES.map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3105
+ value,
3106
+ children: categoryLabel(t, value)
3107
+ }, value))]
3108
+ }),
3109
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3110
+ type: "submit",
3111
+ className: dsh_mnemon_client.memoryPageStyles.primaryButton,
3112
+ disabled: loading,
3113
+ children: loading ? t("common.loading") : t("content.apply")
3114
+ })
3115
+ ]
3116
+ }),
3117
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3118
+ className: dsh_mnemon_client.memoryPageStyles.listNotice,
3119
+ children: t("content.notice")
3120
+ }),
3121
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReadSourcePanel, {
3122
+ title: t("content.sourcesTitle"),
3123
+ sources,
3124
+ selectedBodyId,
3125
+ onSelect: selectBody
3126
+ }),
3127
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3128
+ className: dsh_mnemon_client.memoryPageStyles.inlineError,
3129
+ role: "alert",
3130
+ children: error
3131
+ }),
3132
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3133
+ className: dsh_mnemon_client.memoryPageStyles.asyncResults,
3134
+ children: [
3135
+ loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.SectionSpinner, { label: t("common.loading") }),
3136
+ !loading && filteredItems.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.EmptyState, {
3137
+ glyph: "≡",
3138
+ title: t(waitingForQuery ? "content.queryRequiredTitle" : "content.emptyTitle"),
3139
+ children: t(waitingForQuery ? "content.queryRequiredText" : "content.emptyText")
3140
+ }),
3141
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3142
+ className: dsh_mnemon_client.memoryPageStyles.memoryList,
3143
+ children: visibleItems.map((insight) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InsightCard, {
3144
+ insight,
3145
+ writeEnabled: props.writeEnabled,
3146
+ onForget: forget,
3147
+ onClone: props.onClone,
3148
+ onRelated: () => props.onExplore(insight.content)
3149
+ }, insightKey(insight)))
3150
+ }),
3151
+ view !== null && !loading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.ProgressiveFooter, {
3152
+ visible: visibleItems.length,
3153
+ total: filteredItems.length,
3154
+ pageSize,
3155
+ onMore: () => setVisibleLimit((value) => value + pageSize)
3156
+ })
3157
+ ]
3158
+ })
3159
+ ]
3160
+ });
3161
+ }
3162
+ //#endregion
3163
+ //#region src/client/ui.tsx
3164
+ /** The adapter never gets a transport or chooses another Source instance. */
3165
+ function memorySpacesPageClient(management) {
3166
+ const client = (0, dsh_mnemon_client.createMemorySourcePageClient)(management);
3167
+ const input = (value) => JSON.parse(JSON.stringify(value));
3168
+ return {
3169
+ canAssist: client.canAssist,
3170
+ status: () => client.read("status-summary"),
3171
+ bodies: () => client.read("bodies"),
3172
+ bodyDirectory: () => client.read("body-directory"),
3173
+ graph: (memoryBodyIds) => client.read("graph", input({ memoryBodyIds })),
3174
+ list: (request) => client.read("list", input(request ?? {})),
3175
+ entities: (entity, limit) => client.read("entities", input({
3176
+ entity,
3177
+ limit
3178
+ })),
3179
+ search: (request) => client.read("search", input(request)),
3180
+ related: (id, memoryBodyId) => client.read("related", input({
3181
+ id,
3182
+ memoryBodyId
3183
+ })),
3184
+ reconnectBody: (memoryBodyId) => client.read("body-reconnect", { memoryBodyId }),
3185
+ remember: (request) => client.mutate("remember", input(request), true),
3186
+ forget: (id, memoryBodyId) => client.mutate("forget", input({
3187
+ id,
3188
+ memoryBodyId
3189
+ }), true),
3190
+ createBody: (request) => request.placement !== void 0 && client.canAssist("body-create") ? client.assist("body-create", input(request), true) : client.mutate("body-create", input(request), true),
3191
+ updateBody: (memoryBodyId, request) => typeof request.active === "boolean" && Object.keys(request).every((key) => key === "active") && client.canAssist("activation") ? client.assist("activation", {
3192
+ memoryBodyId,
3193
+ active: request.active
3194
+ }, true) : client.mutate("body-update", input({
3195
+ memoryBodyId,
3196
+ ...request
3197
+ }), true),
3198
+ deleteBody: (memoryBodyId) => client.mutate("body-delete", { memoryBodyId }, true),
3199
+ agentSearch: (request) => client.assist("agent-search", input(request), false),
3200
+ supervise: (content, idempotencyKey) => client.assist("supervise", input({
3201
+ content,
3202
+ idempotencyKey
3203
+ }), true),
3204
+ maintainBodyMetadata: (memoryBodyIds) => client.assist("body-metadata-maintain", { memoryBodyIds }, true)
3205
+ };
3206
+ }
3207
+ const rememberedPages = /* @__PURE__ */ new WeakMap();
3208
+ const TABS = [
3209
+ {
3210
+ id: "spaces",
3211
+ key: "nav.overview"
3212
+ },
3213
+ {
3214
+ id: "explore",
3215
+ key: "nav.search"
3216
+ },
3217
+ {
3218
+ id: "content",
3219
+ key: "nav.content"
3220
+ },
3221
+ {
3222
+ id: "entities",
3223
+ key: "nav.entities"
3224
+ }
3225
+ ];
3226
+ function MemorySpacesSourceView(props) {
3227
+ const t = (0, dsh_mnemon_client.useT)();
3228
+ const client = (0, react.useMemo)(() => props.management === void 0 ? void 0 : memorySpacesPageClient(props.management), [props.management]);
3229
+ const [revision, setRevision] = (0, react.useState)(0);
3230
+ const [status, setStatus] = (0, react.useState)(null);
3231
+ const [page, setPage] = (0, react.useState)(() => props.management !== void 0 ? rememberedPages.get(props.management) ?? (props.page === "remember" ? "spaces" : props.page) : props.page);
3232
+ const [seed, setSeed] = (0, react.useState)("");
3233
+ const [rememberOpen, setRememberOpen] = (0, react.useState)(props.page === "remember");
3234
+ const [strategyOpen, setStrategyOpen] = (0, react.useState)(false);
3235
+ const [error, setError] = (0, react.useState)(null);
3236
+ const refresh = (0, react.useCallback)(() => {
3237
+ setRevision((value) => value + 1);
3238
+ props.onRefresh?.();
3239
+ }, [props.onRefresh]);
3240
+ (0, react.useEffect)(() => {
3241
+ setPage(props.management !== void 0 && props.navigationInput === void 0 ? rememberedPages.get(props.management) ?? (props.page === "remember" ? "spaces" : props.page) : props.page === "remember" ? "spaces" : props.page);
3242
+ setRememberOpen(props.page === "remember");
3243
+ const value = props.navigationInput;
3244
+ setSeed(typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.seed === "string" ? value.seed : "");
3245
+ }, [props.page, props.navigationInput]);
3246
+ (0, react.useEffect)(() => {
3247
+ if (props.management !== void 0) rememberedPages.set(props.management, page);
3248
+ }, [props.management, page]);
3249
+ (0, react.useEffect)(() => {
3250
+ let active = true;
3251
+ setError(null);
3252
+ client?.status().then((value) => {
3253
+ if (active) setStatus(value);
3254
+ }).catch((reason) => {
3255
+ if (active) setError((0, dsh_mnemon_client.message)(reason));
3256
+ });
3257
+ return () => {
3258
+ active = false;
3259
+ };
3260
+ }, [client, revision]);
3261
+ if (client === void 0) return null;
3262
+ const writable = props.writable === true && status?.writeEnabled === true;
3263
+ const activationEnabled = status?.writeEnabled === true && (writable || client.canAssist("activation"));
3264
+ const agentAvailable = client.canAssist("supervise");
3265
+ const explore = (query) => {
3266
+ setSeed(query);
3267
+ setPage("explore");
3268
+ };
3269
+ const remember = (content = "") => {
3270
+ setSeed(content);
3271
+ setRememberOpen(true);
3272
+ };
3273
+ const forget = async (insight) => {
3274
+ await client.forget(insight.id, insight.memoryBodyId);
3275
+ refresh();
3276
+ };
3277
+ const bodies = status?.memoryBodies ?? [];
3278
+ const preferences = props.preferences;
3279
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
3280
+ error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3281
+ className: dsh_mnemon_client.memoryPageStyles.inlineError,
3282
+ role: "alert",
3283
+ children: error
3284
+ }),
3285
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
3286
+ className: dsh_mnemon_client.memorySidebarStyles.memoryWorkspace,
3287
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.PageHeader, {
3288
+ title: t("nav.bodies"),
3289
+ description: t("overview.description"),
3290
+ meta: writable ? t("common.agentSupervised") : activationEnabled ? t("common.activationOnly") : t("common.readOnly"),
3291
+ action: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3292
+ className: dsh_mnemon_client.memoryPageStyles.memoryHeaderActions,
3293
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3294
+ type: "button",
3295
+ className: (0, dsh_mnemon_client.appearanceClass)(dsh_mnemon_client.memoryPageStyles.primaryButton, dsh_mnemon_client.memorySidebarStyles.memoryWriteButton),
3296
+ disabled: !writable,
3297
+ onClick: () => remember(),
3298
+ children: t("nav.rememberAction")
3299
+ }), preferences !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3300
+ type: "button",
3301
+ className: dsh_mnemon_client.memoryPageStyles.secondaryButton,
3302
+ onClick: () => setStrategyOpen(true),
3303
+ children: t("strategy.action")
3304
+ })]
3305
+ })
3306
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3307
+ className: dsh_mnemon_client.memorySidebarStyles.memoryNavigation,
3308
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3309
+ className: dsh_mnemon_client.memorySidebarStyles.memoryTabs,
3310
+ role: "tablist",
3311
+ "aria-label": t("nav.memory.aria"),
3312
+ children: TABS.map((tab) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3313
+ type: "button",
3314
+ role: "tab",
3315
+ "aria-selected": page === tab.id,
3316
+ "data-active": page === tab.id ? "" : void 0,
3317
+ onClick: () => setPage(tab.id),
3318
+ children: t(tab.key)
3319
+ }, tab.id))
3320
+ })
3321
+ })]
3322
+ }),
3323
+ page === "spaces" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewPage, {
3324
+ client,
3325
+ metadataClient: client,
3326
+ revision,
3327
+ activationEnabled,
3328
+ writeEnabled: writable,
3329
+ agentAvailable,
3330
+ fallbackBodies: bodies,
3331
+ fallbackDirectory: status?.memoryBodyDirectory,
3332
+ catalogKnown: status?.memoryBodies !== void 0,
3333
+ onMutate: refresh,
3334
+ onAgentRefresh: refresh,
3335
+ onBodyReconnect: refresh,
3336
+ onBodyMetadata: refresh,
3337
+ onExplore: explore
3338
+ }),
3339
+ page === "explore" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExplorePage, {
3340
+ client,
3341
+ agentClient: client,
3342
+ agentAvailable: client.canAssist("agent-search"),
3343
+ status,
3344
+ seed,
3345
+ writeEnabled: writable,
3346
+ onForget: forget
3347
+ }),
3348
+ page === "entities" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntitiesPage, {
3349
+ client,
3350
+ revision,
3351
+ writeEnabled: writable,
3352
+ onForget: forget,
3353
+ onExplore: explore
3354
+ }),
3355
+ page === "content" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ListPage, {
3356
+ client,
3357
+ revision,
3358
+ writeEnabled: writable,
3359
+ onForget: forget,
3360
+ onClone: (insight) => remember(insight.content),
3361
+ onExplore: explore
3362
+ }),
3363
+ rememberOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RememberPage, {
3364
+ client,
3365
+ agentAvailable,
3366
+ memoryBodies: bodies,
3367
+ writeEnabled: writable,
3368
+ seed,
3369
+ onMutate: refresh,
3370
+ onClose: () => setRememberOpen(false),
3371
+ onComplete: () => setRememberOpen(false)
3372
+ }),
3373
+ strategyOpen && preferences !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PersistenceStrategyDialog, {
3374
+ client,
3375
+ settingsScope: { setPath: async (path, value) => {
3376
+ if (path.length !== 1 || path[0] !== "persistenceStrategy") throw new Error("Preference path is outside this Source");
3377
+ await preferences.replace(JSON.parse(JSON.stringify({ persistenceStrategy: value })));
3378
+ } },
3379
+ config: preferences.value,
3380
+ writable: preferences.writable,
3381
+ agentAvailable,
3382
+ onClose: () => setStrategyOpen(false)
3383
+ })
3384
+ ] });
3385
+ }
3386
+ function MemorySpacesSourcePage(props) {
3387
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(dsh_mnemon_client.MemorySourcePageFrame, {
3388
+ locale: props.locale,
3389
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemorySpacesSourceView, { ...props }, props.sourceInstanceKey)
3390
+ });
3391
+ }
3392
+ function installMemorySpacesUI(ctx, t = dsh_mnemon_client.translateEn) {
3393
+ return (0, dsh_mnemon_client.installMemorySourceUI)(ctx, {
3394
+ sourceTypeId: "memory-spaces",
3395
+ pages: [
3396
+ {
3397
+ id: "spaces",
3398
+ key: "nav.bodies",
3399
+ order: 300,
3400
+ glyph: "◇"
3401
+ },
3402
+ {
3403
+ id: "remember",
3404
+ key: "nav.rememberAction",
3405
+ order: 400,
3406
+ glyph: "+"
3407
+ },
3408
+ {
3409
+ id: "explore",
3410
+ key: "nav.search",
3411
+ order: 500,
3412
+ glyph: "⌕"
3413
+ },
3414
+ {
3415
+ id: "entities",
3416
+ key: "nav.entities",
3417
+ order: 600,
3418
+ glyph: "◎"
3419
+ },
3420
+ {
3421
+ id: "content",
3422
+ key: "nav.content",
3423
+ order: 700,
3424
+ glyph: "≡"
3425
+ }
3426
+ ].map((page) => ({
3427
+ id: page.id,
3428
+ order: page.order,
3429
+ label: () => t(page.key),
3430
+ navigation: {
3431
+ stickyHeader: false,
3432
+ group: page.id === "spaces" ? "storage" : "tools",
3433
+ primary: page.id === "spaces",
3434
+ glyph: page.glyph
3435
+ },
3436
+ component: (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemorySpacesSourcePage, {
3437
+ ...props,
3438
+ page: page.id
3439
+ })
3440
+ }))
3441
+ });
3442
+ }
3443
+ const inject = ["slots", "locale"];
3444
+ function apply(ctx) {
3445
+ installMemorySpacesUI(ctx, ctx.locale?.bind("mnemon") ?? dsh_mnemon_client.translateEn);
3446
+ }
3447
+ //#endregion
3448
+ exports.EntitiesPage = EntitiesPage;
3449
+ exports.ExplorePage = ExplorePage;
3450
+ exports.ListPage = ListPage;
3451
+ exports.MemorySpacesSourcePage = MemorySpacesSourcePage;
3452
+ exports.OverviewPage = OverviewPage;
3453
+ exports.PersistenceStrategyDialog = PersistenceStrategyDialog;
3454
+ exports.RememberPage = RememberPage;
3455
+ exports.apply = apply;
3456
+ exports.inject = inject;
3457
+ exports.installMemorySpacesUI = installMemorySpacesUI;
3458
+ exports.memorySpacesPageClient = memorySpacesPageClient;
3459
+ exports.nativeBodyProvider = nativeBodyProvider;
3460
+ return module.exports;
3461
+ }
3462
+ });