@modusensus/dsh-mneme 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -16
- package/bin/cli.mjs +8 -2
- package/docs/CONFIGURATION.md +25 -0
- package/docs/SEMANTIC.md +3 -3
- package/lib/api-standalone.js +78 -12
- package/lib/api.js +282 -57
- package/lib/candidates.js +454 -454
- package/lib/client.js +598 -248
- package/lib/config.js +33 -2
- package/lib/embedding.js +116 -15
- package/lib/index.js +32 -0
- package/lib/inject.js +34 -3
- package/lib/lang.js +124 -0
- package/lib/pipeline-activities.js +178 -0
- package/lib/pipeline-attribution.js +165 -0
- package/lib/pipeline-command.js +279 -0
- package/lib/pipeline-confirm.js +142 -0
- package/lib/pipeline-exec.js +174 -0
- package/lib/pipeline-nudge.js +1281 -0
- package/lib/pipeline-observations.js +181 -0
- package/lib/pipeline-observe.js +133 -0
- package/lib/pipeline-runner.js +346 -0
- package/lib/pipeline-runs.js +442 -0
- package/lib/pipeline.js +128 -7
- package/lib/semantic.js +66 -10
- package/lib/settings.js +132 -1
- package/lib/store.js +77 -3
- package/lib/summarize.js +2 -3
- package/package.json +3 -3
- package/scripts/changelog-prep.mjs +62 -7
- package/scripts/check-sync.js +8 -6
- package/scripts/release-docs.mjs +204 -0
- package/scripts/release-notes.mjs +162 -0
- package/src/api-standalone.js +78 -12
- package/src/api.js +282 -57
- package/src/candidates.js +454 -454
- package/src/config.js +33 -2
- package/src/embedding.js +116 -15
- package/src/index.js +32 -0
- package/src/inject.js +34 -3
- package/src/lang.js +124 -0
- package/src/pipeline-activities.js +178 -0
- package/src/pipeline-attribution.js +165 -0
- package/src/pipeline-command.js +279 -0
- package/src/pipeline-confirm.js +142 -0
- package/src/pipeline-exec.js +174 -0
- package/src/pipeline-nudge.js +1281 -0
- package/src/pipeline-observations.js +181 -0
- package/src/pipeline-observe.js +133 -0
- package/src/pipeline-runner.js +346 -0
- package/src/pipeline-runs.js +442 -0
- package/src/pipeline.js +128 -7
- package/src/semantic.js +66 -10
- package/src/settings.js +132 -1
- package/src/store.js +77 -3
- package/src/summarize.js +2 -3
- package/test/api-hardening.test.js +173 -0
- package/test/api.test.js +98 -1
- package/test/changelog-prep.test.js +74 -8
- package/test/cli-enums.test.js +37 -0
- package/test/client.test.js +152 -3
- package/test/docs-config-keys.test.js +65 -0
- package/test/helpers/write-admission-samples.js +6 -2
- package/test/lib-smoke.test.js +20 -0
- package/test/persona-api.test.js +172 -0
- package/test/persona-flag.test.js +36 -0
- package/test/persona-inject.test.js +145 -0
- package/test/persona-settings.test.js +145 -0
- package/test/pipeline-activities.test.js +155 -0
- package/test/pipeline-attribution.test.js +175 -0
- package/test/pipeline-command.test.js +243 -0
- package/test/pipeline-confirm.test.js +148 -0
- package/test/pipeline-exec.test.js +172 -0
- package/test/pipeline-nudge.test.js +1036 -0
- package/test/pipeline-observations.test.js +157 -0
- package/test/pipeline-observe.test.js +127 -0
- package/test/pipeline-r1r2.test.js +149 -0
- package/test/pipeline-runner.test.js +442 -0
- package/test/pipeline-runs.test.js +767 -0
- package/test/pipeline.test.js +137 -6
- package/test/reindex-backfill.test.js +83 -4
- package/test/release-docs.test.js +130 -0
- package/test/release-notes.test.js +240 -0
- package/test/release-workflow.test.js +16 -0
- package/test/route-policy.test.js +113 -0
- package/test/security-doc.test.js +139 -0
- package/test/sensitive-scan.test.js +44 -0
- package/test/workflow-guards.test.js +156 -0
package/lib/client.js
CHANGED
|
@@ -147,7 +147,11 @@ window.__ModuleLoader__.load({
|
|
|
147
147
|
flame: [["path", { d: "M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z" }]],
|
|
148
148
|
// 意见与反馈入口的行首图标(Lucide 数据,同上 ISC 惯例内联)
|
|
149
149
|
bug: [["path", { d: "m8 2 1.88 1.88" }], ["path", { d: "M14.12 3.88 16 2" }], ["path", { d: "M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" }], ["path", { d: "M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6" }], ["path", { d: "M12 20v-9" }], ["path", { d: "M6.53 9C4.6 8.8 3 7.1 3 5" }], ["path", { d: "M6 13H2" }], ["path", { d: "M3 21c0-2.1 1.7-3.9 3.8-4" }], ["path", { d: "M20.97 5c0 2.1-1.6 3.8-3.5 4" }], ["path", { d: "M22 13h-4" }], ["path", { d: "M17.2 17c2.1.1 3.8 1.9 3.8 4" }]],
|
|
150
|
-
mail: [["rect", { width: "20", height: "16", x: "2", y: "4", rx: "2" }], ["path", { d: "m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" }]]
|
|
150
|
+
mail: [["rect", { width: "20", height: "16", x: "2", y: "4", rx: "2" }], ["path", { d: "m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" }]],
|
|
151
|
+
// star 是设置页鼓励入口的行首图标(Lucide 数据,同上 ISC 惯例内联)。
|
|
152
|
+
// Icon 对未注册的名字静默 return null——漏了它就是一行缺图标、闸门全绿,
|
|
153
|
+
// 所以这条表与用法必须同处出现(见 test/client.test.js 的图标名锁)。
|
|
154
|
+
star: [["polygon", { points: "12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" }]]
|
|
151
155
|
};
|
|
152
156
|
const Icon = ({ name, size = 16, className }) => {
|
|
153
157
|
const parts = ICON_PATHS[name];
|
|
@@ -277,6 +281,31 @@ window.__ModuleLoader__.load({
|
|
|
277
281
|
"memory.settings.profileHint": "写一段自我介绍(角色、背景、偏好),Agent 每轮对话都会自动带上。",
|
|
278
282
|
"memory.settings.profileSave": "保存画像",
|
|
279
283
|
"memory.settings.profileSaved": "画像已保存",
|
|
284
|
+
"memory.settings.persona": "人格定义(beta)",
|
|
285
|
+
"memory.settings.personaHint": "给这位 Agent 一张角色卡:名字、性格、回复要求。开关打开且下方「人格定义」总闸开启时,每轮对话都会注入。",
|
|
286
|
+
"memory.settings.personaName": "名字",
|
|
287
|
+
"memory.settings.personaNamePlaceholder": "例如:Nyx",
|
|
288
|
+
"memory.settings.personaPersonality": "性格设定",
|
|
289
|
+
"memory.settings.personaPersonalityPlaceholder": "例如:轻松幽默,讲难题时用类比帮用户快速理解",
|
|
290
|
+
"memory.settings.personaReplyStyle": "回复要求",
|
|
291
|
+
"memory.settings.personaReplyStylePlaceholder": "例如:先给结论再解释;用人在用的词,不用机制名",
|
|
292
|
+
"memory.settings.personaSave": "保存人格",
|
|
293
|
+
"memory.settings.personaSaved": "人格已保存",
|
|
294
|
+
"memory.settings.personaSaveFailed": "保存失败",
|
|
295
|
+
"memory.settings.personaEnabledToggle": "启用这张卡",
|
|
296
|
+
"memory.settings.personaEnabledOn": "已启用——身份命中的会话会注入这张卡",
|
|
297
|
+
"memory.settings.personaEnabledOff": "未启用——这个身份的会话将使用默认卡",
|
|
298
|
+
"memory.settings.personaDefaultCard": "默认卡(兜底)",
|
|
299
|
+
"memory.settings.personaAdd": "添加卡",
|
|
300
|
+
"memory.settings.personaAddPlaceholder": "输入 agent 身份名(如 nyx),按它寻址注入",
|
|
301
|
+
"memory.settings.personaEditingDefault": "正在编辑:默认卡——没有命中任何身份卡的会话用它",
|
|
302
|
+
"memory.settings.personaEditingFor": "正在编辑身份卡:",
|
|
303
|
+
"memory.settings.personaDeleteCard": "删除这张卡",
|
|
304
|
+
"memory.settings.personaView": "Agent 眼中的你(注入生效文本)",
|
|
305
|
+
"memory.settings.personaViewHint": "以下是每一轮实际注入的用户画像与规则,只读。",
|
|
306
|
+
"memory.settings.personaViewEmpty": "画像与规则均为空——Agent 每轮只会看到记忆库。",
|
|
307
|
+
"memory.features.personaEnabled": "人格定义(beta)",
|
|
308
|
+
"memory.features.personaEnabled.hint": "把「设置 → 个性化」里的人格卡注入到每轮对话。默认关(beta);总闸与人格卡内的开关同时打开才生效。",
|
|
280
309
|
"memory.settings.rules": "规则",
|
|
281
310
|
"memory.settings.rulesHint": "给 Agent 立下必须遵守的规矩,随时增删,下一轮即生效。",
|
|
282
311
|
"memory.settings.ruleAdd": "添加规则",
|
|
@@ -299,12 +328,14 @@ window.__ModuleLoader__.load({
|
|
|
299
328
|
"memory.explorer.tabStatus": "状态",
|
|
300
329
|
"memory.entities.count": "{n} 个实体",
|
|
301
330
|
"memory.entities.pick": "从左侧选择一个实体,查看它的属性与关系",
|
|
331
|
+
// 搜索框有字但一条没筛出来时用它;与 entities.none(库里真没实体)分开,
|
|
332
|
+
// 否则「暂无实体」会把打错一个字误导成「你还没开抽取」。
|
|
333
|
+
"memory.entities.noMatch": "没有匹配的实体,换个词试试",
|
|
302
334
|
"memory.entities.none": "暂无实体:开启实体抽取后,记忆里的人物 / 项目 / 概念会自动沉淀到这里",
|
|
303
335
|
"memory.entities.attrs": "属性",
|
|
304
336
|
"memory.entities.relations": "关系",
|
|
305
337
|
"memory.entities.mentions": "{n} 次提及",
|
|
306
338
|
"memory.entities.lastSeen": "最近出现",
|
|
307
|
-
"memory.entities.graph": "关系图谱",
|
|
308
339
|
"memory.entity.person": "人物",
|
|
309
340
|
"memory.entity.project": "项目",
|
|
310
341
|
"memory.entity.concept": "概念",
|
|
@@ -329,8 +360,6 @@ window.__ModuleLoader__.load({
|
|
|
329
360
|
"memory.explorer.tags": "标签",
|
|
330
361
|
"memory.explorer.importance": "重要性",
|
|
331
362
|
"memory.explorer.heat": "热度",
|
|
332
|
-
"memory.explorer.sourceFilter": "来源",
|
|
333
|
-
"memory.explorer.sort": "排序",
|
|
334
363
|
"memory.explorer.heatSort": "热度优先",
|
|
335
364
|
"memory.explorer.heatSortHint": "对已加载条目按热度降序(页内排序,非全局序;时间树保持时间序)",
|
|
336
365
|
"memory.explorer.filterDeposited": "沉淀",
|
|
@@ -351,7 +380,6 @@ window.__ModuleLoader__.load({
|
|
|
351
380
|
"memory.graph.depth": "跳数",
|
|
352
381
|
"memory.graph.empty": "图谱待积累:随对话记忆的沉淀,实体与关系会自动进入图谱",
|
|
353
382
|
"memory.graph.notFound": "未找到该实体",
|
|
354
|
-
"memory.graph.attrs": "属性",
|
|
355
383
|
"memory.graph.related": "关联记忆",
|
|
356
384
|
"memory.graph.relation": "关系",
|
|
357
385
|
"memory.graph.sourceMemory": "查看来源记忆",
|
|
@@ -373,8 +401,8 @@ window.__ModuleLoader__.load({
|
|
|
373
401
|
"memory.settings.vectorReindexing": "索引中…",
|
|
374
402
|
"memory.settings.vectorReindexDone": "已索引 {n} 条",
|
|
375
403
|
"memory.settings.apiTokenTitle": "API Token(可选)",
|
|
376
|
-
"memory.settings.apiTokenHint": "
|
|
377
|
-
"memory.settings.apiTokenPlaceholder": "
|
|
404
|
+
"memory.settings.apiTokenHint": "这里只把 Token 存在当前浏览器(localStorage),供面板请求携带;服务端的锁是插件配置里的 apiToken,两边填同一个值才真正上锁。只填这里不改服务端 = 没上锁。",
|
|
405
|
+
"memory.settings.apiTokenPlaceholder": "与插件配置的 apiToken 保持一致",
|
|
378
406
|
"memory.settings.apiTokenSave": "保存 Token",
|
|
379
407
|
"memory.settings.apiTokenSaved": "Token 已保存",
|
|
380
408
|
"memory.settings.feedback.title": "意见与反馈",
|
|
@@ -386,8 +414,16 @@ window.__ModuleLoader__.load({
|
|
|
386
414
|
"memory.settings.feedback.browse": "浏览已知问题",
|
|
387
415
|
"memory.settings.feedback.browse.sub": "先搜搜,省一份重复 issue",
|
|
388
416
|
"memory.settings.feedback.hint": "反馈前先搜搜是否已有相同问题,能省一份重复的 issue~",
|
|
389
|
-
"memory.settings.
|
|
417
|
+
"memory.settings.update.check": "检测更新",
|
|
418
|
+
"memory.settings.update.latest": "已是最新版本 ✓",
|
|
419
|
+
"memory.settings.update.ahead": "当前版本比 registry 还新(本地开发版)",
|
|
420
|
+
"memory.settings.update.error": "检测失败:稍后再试",
|
|
421
|
+
// 检测更新点出的第三种结果(发现新版本);横幅形态已删,它现在只是
|
|
422
|
+
// 按需那一行的文案,占位 {v}/{c} 由 checkResultText 填。
|
|
423
|
+
"memory.settings.version.outdated": "有新版本 {v}(当前运行 {c})",
|
|
390
424
|
"memory.settings.version.outdatedHint": "若安装时指定过版本号,常规升级不会跨大/小版本(pnpm 钉子)——请重新安装或在升级命令加 --latest;npm 已发布而市场暂未收录属正常(约 1 天延迟)。",
|
|
425
|
+
"memory.settings.star": "给项目点个 Star",
|
|
426
|
+
"memory.settings.star.sub": "它对你是好用的,就鼓励一下作者吧 (●'◡'●)",
|
|
391
427
|
"memory.settings.mode.title": "运行模式",
|
|
392
428
|
"memory.settings.mode.desc": "轻量模式只保留核心能力:记忆读写、自动注入、热记忆;巩固、实体抽取、语义搜索等全部关闭,适合低配机器或只想「记住偏好」的场景。标准模式开启全部功能。",
|
|
393
429
|
"memory.settings.mode.light": "轻量",
|
|
@@ -430,6 +466,9 @@ window.__ModuleLoader__.load({
|
|
|
430
466
|
// 「别的会话能不能看到」由同在首屏的 scope 组回答。
|
|
431
467
|
"memory.features.group.basic": "常用(先动这几个)",
|
|
432
468
|
"memory.features.group.core": "它会记住什么",
|
|
469
|
+
// group.mo 是 v0.9.0 新增的独立分组(座位闸门,不是行为开关);渲染
|
|
470
|
+
// 走 t(`memory.features.${g.key}`),缺这行会把键名直接写上屏。
|
|
471
|
+
"memory.features.group.mo": "它的工作流(M.O.)",
|
|
433
472
|
"memory.features.group.enhance": "它什么时候写进去",
|
|
434
473
|
"memory.features.group.dream": "它怎么用回来",
|
|
435
474
|
"memory.features.group.scope": "不同会话之间",
|
|
@@ -528,10 +567,13 @@ window.__ModuleLoader__.load({
|
|
|
528
567
|
"memory.actions.credit": "复用",
|
|
529
568
|
"memory.actions.creditValue": "{n} 次 · 上次 {d} 天前",
|
|
530
569
|
"memory.actions.dep": "依赖",
|
|
570
|
+
"memory.actions.zoom.value": "缩放 {n}%",
|
|
531
571
|
"memory.actions.zoom.in": "放大",
|
|
532
572
|
"memory.actions.zoom.out": "缩小",
|
|
533
573
|
"memory.actions.zoom.fit": "适应宽度",
|
|
534
574
|
"memory.actions.zoom.hint": "Ctrl/⌘ + 滚轮也能缩放",
|
|
575
|
+
// 缩放百分比指示({n} 由画布工具条填);基线 4568614 起就缺这两行,
|
|
576
|
+
// 工具条上会直接显示键名。
|
|
535
577
|
"memory.actions.list.heading": "这个项目里的工作流",
|
|
536
578
|
"memory.actions.list.count": "工作流 {n} 条",
|
|
537
579
|
"memory.actions.list.meta": "{steps} 步 · {gates} 道闸门 · 反事实 {cf}/{steps}",
|
|
@@ -911,6 +953,31 @@ window.__ModuleLoader__.load({
|
|
|
911
953
|
"memory.settings.profileHint": "Describe yourself once — the agent reads it every turn.",
|
|
912
954
|
"memory.settings.profileSave": "Save Profile",
|
|
913
955
|
"memory.settings.profileSaved": "Profile saved",
|
|
956
|
+
"memory.settings.persona": "Persona (beta)",
|
|
957
|
+
"memory.settings.personaHint": "Give this agent a character card: name, personality, reply style. Injected every turn when the toggle below and the \"Persona\" master switch are both on.",
|
|
958
|
+
"memory.settings.personaName": "Name",
|
|
959
|
+
"memory.settings.personaNamePlaceholder": "e.g. Nyx",
|
|
960
|
+
"memory.settings.personaPersonality": "Personality",
|
|
961
|
+
"memory.settings.personaPersonalityPlaceholder": "e.g. relaxed and humorous; use analogies for hard topics",
|
|
962
|
+
"memory.settings.personaReplyStyle": "Reply style",
|
|
963
|
+
"memory.settings.personaReplyStylePlaceholder": "e.g. conclusion first; plain words over jargon",
|
|
964
|
+
"memory.settings.personaSave": "Save Persona",
|
|
965
|
+
"memory.settings.personaSaved": "Persona saved",
|
|
966
|
+
"memory.settings.personaSaveFailed": "Save failed",
|
|
967
|
+
"memory.settings.personaEnabledToggle": "Enable this card",
|
|
968
|
+
"memory.settings.personaEnabledOn": "On — sessions matching this identity get this card",
|
|
969
|
+
"memory.settings.personaEnabledOff": "Off — sessions with this identity fall back to the default card",
|
|
970
|
+
"memory.settings.personaDefaultCard": "Default (fallback)",
|
|
971
|
+
"memory.settings.personaAdd": "Add card",
|
|
972
|
+
"memory.settings.personaAddPlaceholder": "Agent identity (e.g. nyx) used to address injection",
|
|
973
|
+
"memory.settings.personaEditingDefault": "Editing: default card — used by sessions matching no identity card",
|
|
974
|
+
"memory.settings.personaEditingFor": "Editing identity card:",
|
|
975
|
+
"memory.settings.personaDeleteCard": "Delete this card",
|
|
976
|
+
"memory.settings.personaView": "You, as the agent sees you (injected text)",
|
|
977
|
+
"memory.settings.personaViewHint": "The profile and rules actually injected every turn. Read-only.",
|
|
978
|
+
"memory.settings.personaViewEmpty": "Profile and rules are both empty — the agent only sees the memory store.",
|
|
979
|
+
"memory.features.personaEnabled": "Persona (beta)",
|
|
980
|
+
"memory.features.personaEnabled.hint": "Inject the persona card (Settings → Personalization) into every turn. Off by default (beta); both this switch and the card's own toggle must be on.",
|
|
914
981
|
"memory.settings.rules": "Rules",
|
|
915
982
|
"memory.settings.rulesHint": "Rules the agent must follow — add or remove anytime, effective next turn.",
|
|
916
983
|
"memory.settings.ruleAdd": "Add Rule",
|
|
@@ -933,12 +1000,12 @@ window.__ModuleLoader__.load({
|
|
|
933
1000
|
"memory.explorer.tabStatus": "Status",
|
|
934
1001
|
"memory.entities.count": "{n} entities",
|
|
935
1002
|
"memory.entities.pick": "Select an entity to see its attributes and relations",
|
|
1003
|
+
"memory.entities.noMatch": "No entities match that search — try another word",
|
|
936
1004
|
"memory.entities.none": "No entities yet — turn on entity extraction and people / projects / concepts will accumulate here",
|
|
937
1005
|
"memory.entities.attrs": "Attributes",
|
|
938
1006
|
"memory.entities.relations": "Relations",
|
|
939
1007
|
"memory.entities.mentions": "{n} mentions",
|
|
940
1008
|
"memory.entities.lastSeen": "Last seen",
|
|
941
|
-
"memory.entities.graph": "Relation graph",
|
|
942
1009
|
"memory.entity.person": "People",
|
|
943
1010
|
"memory.entity.project": "Projects",
|
|
944
1011
|
"memory.entity.concept": "Concepts",
|
|
@@ -963,8 +1030,6 @@ window.__ModuleLoader__.load({
|
|
|
963
1030
|
"memory.explorer.tags": "Tags",
|
|
964
1031
|
"memory.explorer.importance": "Importance",
|
|
965
1032
|
"memory.explorer.heat": "Heat",
|
|
966
|
-
"memory.explorer.sourceFilter": "Source",
|
|
967
|
-
"memory.explorer.sort": "Sort",
|
|
968
1033
|
"memory.explorer.heatSort": "By heat",
|
|
969
1034
|
"memory.explorer.heatSortHint": "Sort loaded items by heat, in-page (not global; the month tree keeps chronological order)",
|
|
970
1035
|
"memory.explorer.filterDeposited": "Deposited",
|
|
@@ -985,7 +1050,6 @@ window.__ModuleLoader__.load({
|
|
|
985
1050
|
"memory.graph.depth": "hops",
|
|
986
1051
|
"memory.graph.empty": "The graph is waiting for data: entities and relations accumulate as memories are extracted",
|
|
987
1052
|
"memory.graph.notFound": "Entity not found",
|
|
988
|
-
"memory.graph.attrs": "Attributes",
|
|
989
1053
|
"memory.graph.related": "Related memories",
|
|
990
1054
|
"memory.graph.relation": "Relation",
|
|
991
1055
|
"memory.graph.sourceMemory": "View source memory",
|
|
@@ -1007,8 +1071,8 @@ window.__ModuleLoader__.load({
|
|
|
1007
1071
|
"memory.settings.vectorReindexing": "Indexing…",
|
|
1008
1072
|
"memory.settings.vectorReindexDone": "Indexed {n} items",
|
|
1009
1073
|
"memory.settings.apiTokenTitle": "API Token (optional)",
|
|
1010
|
-
"memory.settings.apiTokenHint": "
|
|
1011
|
-
"memory.settings.apiTokenPlaceholder": "
|
|
1074
|
+
"memory.settings.apiTokenHint": "This field only stores the token in this browser (localStorage) for the panel requests. The server-side lock is the plugin\u0027s apiToken config — set both to the same value; this field alone locks nothing.",
|
|
1075
|
+
"memory.settings.apiTokenPlaceholder": "Must match the plugin\u0027s apiToken",
|
|
1012
1076
|
"memory.settings.apiTokenSave": "Save Token",
|
|
1013
1077
|
"memory.settings.apiTokenSaved": "Token saved",
|
|
1014
1078
|
"memory.settings.feedback.title": "Feedback",
|
|
@@ -1020,8 +1084,14 @@ window.__ModuleLoader__.load({
|
|
|
1020
1084
|
"memory.settings.feedback.browse": "Browse known issues",
|
|
1021
1085
|
"memory.settings.feedback.browse.sub": "Check if it's already reported",
|
|
1022
1086
|
"memory.settings.feedback.hint": "Search for an existing issue first — it saves a duplicate.",
|
|
1023
|
-
"memory.settings.
|
|
1087
|
+
"memory.settings.update.check": "Check for updates",
|
|
1088
|
+
"memory.settings.update.latest": "Up to date ✓",
|
|
1089
|
+
"memory.settings.update.ahead": "Newer than the registry (local build)",
|
|
1090
|
+
"memory.settings.update.error": "Check failed: try again later",
|
|
1091
|
+
"memory.settings.version.outdated": "New version {v} available (running {c})",
|
|
1024
1092
|
"memory.settings.version.outdatedHint": "If the plugin was installed with a pinned version, regular upgrades never cross minor/major lines — reinstall or pass --latest. A fresh npm release may take about a day to appear in the market.",
|
|
1093
|
+
"memory.settings.star": "Star the project",
|
|
1094
|
+
"memory.settings.star.sub": "If it serves you well, cheer the author on (●'◡'●)",
|
|
1025
1095
|
"memory.settings.mode.title": "Runtime mode",
|
|
1026
1096
|
"memory.settings.mode.desc": "Light mode keeps the core loop only: memory read/write, auto-injection and hot memory — consolidation, entity extraction and semantic search are all off. For modest machines or when you just want preferences remembered. Standard mode enables everything.",
|
|
1027
1097
|
"memory.settings.mode.light": "Light",
|
|
@@ -1065,6 +1135,7 @@ window.__ModuleLoader__.load({
|
|
|
1065
1135
|
// "can other sessions see this?" is answerable without opening anything.
|
|
1066
1136
|
"memory.features.group.basic": "Common (start here)",
|
|
1067
1137
|
"memory.features.group.core": "What gets remembered",
|
|
1138
|
+
"memory.features.group.mo": "Its workflow (M.O.)",
|
|
1068
1139
|
"memory.features.group.enhance": "When it gets written",
|
|
1069
1140
|
"memory.features.group.dream": "How it comes back",
|
|
1070
1141
|
"memory.features.group.scope": "Between sessions",
|
|
@@ -1155,6 +1226,7 @@ window.__ModuleLoader__.load({
|
|
|
1155
1226
|
"memory.actions.credit": "Reuse",
|
|
1156
1227
|
"memory.actions.creditValue": "{n} uses · last {d}d ago",
|
|
1157
1228
|
"memory.actions.dep": "depends on",
|
|
1229
|
+
"memory.actions.zoom.value": "Zoom {n}%",
|
|
1158
1230
|
"memory.actions.zoom.in": "Zoom in",
|
|
1159
1231
|
"memory.actions.zoom.out": "Zoom out",
|
|
1160
1232
|
"memory.actions.zoom.fit": "Fit width",
|
|
@@ -1635,17 +1707,26 @@ window.__ModuleLoader__.load({
|
|
|
1635
1707
|
".mneme-xcount{flex:none;font-size:12px;line-height:16px;color:var(--mneme-fg-dim);white-space:nowrap}",
|
|
1636
1708
|
// --- three-column browse layout: hairline separators, no outer box ---
|
|
1637
1709
|
".mneme-xmain{flex:1;min-height:0;display:flex;flex-direction:row;overflow:hidden}",
|
|
1638
|
-
".mneme-xside{flex:none;width:
|
|
1639
|
-
|
|
1710
|
+
".mneme-xside{flex:none;width:264px;min-width:0;min-height:0;overflow-y:auto;overflow-x:hidden;padding:16px 14px;border-right:1px solid var(--dsw-alias-border-l2);box-sizing:border-box;display:flex;flex-direction:column;gap:8px}",
|
|
1711
|
+
// 左栏下段=重要性+来源+热度:紧跟检索段之后(复验修订:不再钉底),
|
|
1712
|
+
// 顶部细线保留分段感;栏宽 264px 让 4 枚星级 chip 单行排布。
|
|
1713
|
+
".mneme-xside-imp{flex:none;margin-top:10px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l1);display:flex;flex-direction:column;gap:8px}",
|
|
1714
|
+
// 内容区列容器 + 顶部工具条:类型 chips 横排 + 计数/刷新靠右;窄容器靠 wrap 换行。
|
|
1715
|
+
".mneme-xcontent{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden}",
|
|
1716
|
+
".mneme-xbrowsebar{flex:none;display:flex;align-items:center;gap:6px;flex-wrap:wrap;padding:8px 16px;border-bottom:1px solid var(--dsw-alias-border-l2)}",
|
|
1717
|
+
// 窄容器(better-sidebar 工作台 ~500px):左栏收窄保内容区可用宽度。
|
|
1718
|
+
"@container (max-width: 640px){.mneme-xside{width:216px}}",
|
|
1719
|
+
// 类型筛选原为第二侧栏的竖排列表(三栏布局遗留,#面板两栏改版):
|
|
1720
|
+
// 两栏化后收进内容区工具条,改为自适应横排 chips,彩点与计数保留。
|
|
1721
|
+
".mneme-xtype{display:inline-flex;justify-content:flex-start;align-items:center;gap:7px;padding:4px 9px;border:none;border-radius:8px;background:none;color:var(--dsw-alias-label-secondary);cursor:pointer;font-family:inherit;font-size:13px;line-height:18px;text-align:left}",
|
|
1640
1722
|
".mneme-xbrowse{flex:1;min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden}",
|
|
1641
1723
|
".mneme-xrow{display:flex;align-items:center;gap:8px;flex-wrap:wrap}",
|
|
1642
1724
|
".mneme-xsearch{width:100%}",
|
|
1643
1725
|
".mneme-xselect{flex:1;min-width:0}",
|
|
1644
1726
|
".mneme-xcolhead{flex:none;font-size:12px;font-weight:500;margin:0;color:var(--mneme-fg-dim);padding:2px 8px 8px}",
|
|
1645
|
-
".mneme-xtype{display:flex;justify-content:flex-start;align-items:center;gap:8px;width:100%;padding:5px 8px;border:none;border-radius:8px;background:none;color:var(--dsw-alias-label-secondary);cursor:pointer;font-family:inherit;font-size:13px;line-height:18px;text-align:left}",
|
|
1646
1727
|
".mneme-xtype:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
1647
1728
|
".mneme-xtype.mneme-active{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);font-weight:500}",
|
|
1648
|
-
".mneme-xcount2{flex:none;
|
|
1729
|
+
".mneme-xcount2{flex:none;font-size:12px;color:var(--mneme-fg-dim)}",
|
|
1649
1730
|
".mneme-xmonth{display:flex;align-items:center;gap:4px;width:100%;padding:6px 8px 4px;border:none;border-radius:8px;background:none;color:var(--dsw-alias-label-primary);cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;line-height:18px;text-align:left}",
|
|
1650
1731
|
".mneme-xmonth:first-child{margin-top:0}",
|
|
1651
1732
|
".mneme-xmonth:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
@@ -1687,20 +1768,26 @@ window.__ModuleLoader__.load({
|
|
|
1687
1768
|
// 虚线态靠 dash 图案区分,不再叠 opacity:50% 的描边再乘 .7 后浅色画布上
|
|
1688
1769
|
// 只剩 2.28,低于图形要求的 3:1(图案已经承担了「没有 memory_id」这个语义)。
|
|
1689
1770
|
".mneme-gedge-dashed{stroke-dasharray:5 4}",
|
|
1690
|
-
|
|
1691
|
-
|
|
1771
|
+
// 图谱与图内侧卡的共同父容器:侧卡改浮层(#面板实体页去冗余)后需要
|
|
1772
|
+
// relative 锚点,否则 absolute 会逃逸到 .mneme-ent 根。
|
|
1773
|
+
".mneme-entgraphwrap{position:relative;min-width:0}",
|
|
1774
|
+
// 侧卡从「SVG 下方文档流堆叠」(把图挤成邮票)改为图内左下浮层:
|
|
1775
|
+
// 反应面板与点击焦点同位,SVG 不再被推挤;背景用不透明 bg-layer-1,
|
|
1776
|
+
// 浮层下透出的图形不参与文字对比度计算。
|
|
1777
|
+
".mneme-graphside{position:absolute;left:12px;bottom:12px;width:min(300px,60%);max-height:60%;overflow-y:auto;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1);padding:10px 12px;z-index:2}",
|
|
1692
1778
|
".mneme-gs-title{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary);margin-bottom:4px;display:flex;justify-content:space-between;align-items:baseline;gap:8px}",
|
|
1693
1779
|
".mneme-gs-meta{font-size:13px;color:var(--mneme-fg-dim);margin-bottom:6px}",
|
|
1694
|
-
".mneme-gs-attr{display:flex;gap:6px;font-size:13px;padding:2px 0}",
|
|
1695
|
-
".mneme-gs-attrkey{flex:none;color:var(--mneme-fg-dim);font-size:13px}",
|
|
1696
|
-
".mneme-gs-attrval{color:var(--dsw-alias-label-secondary);word-break:break-word;font-size:13px}",
|
|
1697
1780
|
".mneme-gs-link{display:block;width:100%;text-align:left;border:none;background:none;color:var(--dsw-alias-label-secondary);cursor:pointer;font-family:inherit;font-size:13px;padding:3px 6px;border-radius:6px;word-break:break-word}",
|
|
1698
1781
|
".mneme-gs-link:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
|
|
1699
1782
|
// --- entity explorer: rail + layered detail (entities → attrs → relations → graph) ---
|
|
1700
1783
|
".mneme-ent{flex:1;min-height:0;display:flex;overflow:hidden}",
|
|
1701
|
-
".mneme-entrail{flex:none;width:224px;min-height:0;overflow
|
|
1702
|
-
|
|
1703
|
-
|
|
1784
|
+
".mneme-entrail{flex:none;width:224px;min-height:0;overflow:hidden;padding:12px 0 0;border-right:1px solid var(--dsw-alias-border-l2);box-sizing:border-box;display:flex;flex-direction:column}",
|
|
1785
|
+
// 复验修订:搜索与计数固定在顶部不随列表滚动,列表独立滚动;
|
|
1786
|
+
// 类型分组头 sticky——长列表滚动时分组归属始终可见。
|
|
1787
|
+
".mneme-entrailhead{flex:none;padding:0 12px 10px;display:flex;flex-direction:column;gap:4px;border-bottom:1px solid var(--dsw-alias-border-l1)}",
|
|
1788
|
+
".mneme-entrailbody{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;padding:4px 12px 12px;display:flex;flex-direction:column;gap:4px}",
|
|
1789
|
+
".mneme-enttype{position:sticky;top:0;z-index:1;background:var(--dsw-alias-bg-layer-1);display:flex;align-items:center;gap:6px;width:100%;box-sizing:border-box;padding:8px 8px 2px;border:none;color:var(--mneme-fg-dim);cursor:pointer;font-family:inherit;font-size:11px;font-weight:600;line-height:16px;text-align:left;letter-spacing:.02em}",
|
|
1790
|
+
".mneme-entitem{display:flex;align-items:center;gap:8px;width:100%;box-sizing:border-box;padding:5px 8px;border:none;border-radius:8px;background:none;color:var(--dsw-alias-label-secondary);cursor:pointer;font-family:inherit;font-size:13px;line-height:18px;text-align:left}",
|
|
1704
1791
|
".mneme-entitem:hover{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
1705
1792
|
".mneme-entitem.mneme-active{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary)}",
|
|
1706
1793
|
".mneme-entname{min-width:0;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
@@ -2005,8 +2092,14 @@ window.__ModuleLoader__.load({
|
|
|
2005
2092
|
".mneme-barcol:hover .mneme-bar{opacity:.8}",
|
|
2006
2093
|
".mneme-barlabels{display:flex;gap:8px;padding:6px 2px 0}",
|
|
2007
2094
|
".mneme-barlabels span{flex:1;min-width:0;text-align:center;font-size:10.5px;color:var(--mneme-fg-dim);font-variant-numeric:tabular-nums}",
|
|
2008
|
-
|
|
2009
|
-
|
|
2095
|
+
// GitHub 贡献图式紧凑网格(复验第三轮「间距太大」):11px 格 + 2px 缝;
|
|
2096
|
+
// heatrows 是左侧星期标签列(一/三/五),heatmonths 是顶部月份标签行
|
|
2097
|
+
// (列主序网格下月份行须与格子列对齐,所以同用 auto-flow:column)。
|
|
2098
|
+
".mneme-heatgrid{display:grid;grid-auto-flow:column;grid-template-rows:repeat(7,11px);gap:2px;margin-top:12px}",
|
|
2099
|
+
".mneme-heatcell{width:11px;height:11px;border-radius:2px;background:var(--dsw-alias-interactive-bg-hover)}",
|
|
2100
|
+
".mneme-heatwrap{display:flex;gap:4px}",
|
|
2101
|
+
".mneme-heatrows{display:grid;grid-template-rows:repeat(7,11px);gap:2px;margin-top:12px;font-size:9.5px;line-height:11px;color:var(--mneme-fg-dim);text-align:right}",
|
|
2102
|
+
".mneme-heatmonths{display:grid;grid-auto-flow:column;grid-auto-columns:11px;gap:2px;margin-top:0;font-size:9.5px;line-height:14px;color:var(--mneme-fg-dim)}",
|
|
2010
2103
|
// 类名编号=视觉档位(l1 最浅…l4 最深),变量编号=business 占比降序,故交叉引用。
|
|
2011
2104
|
".mneme-heatcell.l1{background:var(--mneme-heat-4)}",
|
|
2012
2105
|
".mneme-heatcell.l2{background:var(--mneme-heat-3)}",
|
|
@@ -2563,31 +2656,39 @@ window.__ModuleLoader__.load({
|
|
|
2563
2656
|
const graphStatus = status === "ready" && data && data.nodes.length <= 1 ? "empty" : status;
|
|
2564
2657
|
|
|
2565
2658
|
const rail = h("div", { className: "mneme-entrail" },
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
),
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2659
|
+
// 头部固定段:表头(本页名「实体」——原误用全局页签名「记忆库」,而
|
|
2660
|
+
// memory.view.label 仍被顶部 nav aria-label 使用,字典不动只换这里)
|
|
2661
|
+
// + 搜索 + 计数,不随列表滚动。
|
|
2662
|
+
h("div", { className: "mneme-entrailhead" },
|
|
2663
|
+
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.tabEntities")),
|
|
2664
|
+
h("input", {
|
|
2665
|
+
className: "mneme-search mneme-xsearch",
|
|
2666
|
+
placeholder: t("memory.graph.placeholder"),
|
|
2667
|
+
value: railQuery,
|
|
2668
|
+
onChange: (e) => setRailQuery(e.target.value)
|
|
2669
|
+
}),
|
|
2670
|
+
entities !== null && entities.length > 0 && h("div", { className: "mneme-xcount" },
|
|
2671
|
+
t("memory.entities.count").replace("{n}", String(entities.length)))
|
|
2672
|
+
),
|
|
2673
|
+
h("div", { className: "mneme-entrailbody" },
|
|
2674
|
+
entities === null && h("div", { className: "mneme-xempty" }, "…"),
|
|
2675
|
+
entities !== null && entities.length === 0 && h("div", { className: "mneme-xempty" }, t("memory.entities.none")),
|
|
2676
|
+
entities !== null && entities.length > 0 && visibleEntities.length === 0 && h("div", { className: "mneme-xempty" }, t("memory.entities.noMatch")),
|
|
2677
|
+
groups.map((g) => h("div", { key: g.key },
|
|
2678
|
+
h("div", { className: "mneme-enttype" },
|
|
2679
|
+
h("span", { className: "mneme-xdot", style: { color: typeColor(g.key === "other" ? null : g.key) }, "aria-hidden": "true" }),
|
|
2680
|
+
entityTypeLabel(t, g.key)
|
|
2681
|
+
),
|
|
2682
|
+
g.items.map((e) => h("button", {
|
|
2683
|
+
key: e.id,
|
|
2684
|
+
className: e.name === entityName ? "mneme-entitem mneme-active" : "mneme-entitem",
|
|
2685
|
+
onClick: () => setEntityName(e.name)
|
|
2686
|
+
},
|
|
2687
|
+
h("span", { className: "mneme-entname" }, e.name),
|
|
2688
|
+
h("span", { className: "mneme-entmentions" }, `×${e.mention_count ?? 1}`)
|
|
2689
|
+
))
|
|
2589
2690
|
))
|
|
2590
|
-
)
|
|
2691
|
+
)
|
|
2591
2692
|
);
|
|
2592
2693
|
|
|
2593
2694
|
// side card for a clicked (non-root) node or edge inside the graph layer
|
|
@@ -2603,14 +2704,7 @@ window.__ModuleLoader__.load({
|
|
|
2603
2704
|
selected.node.name !== entityName && h("button", {
|
|
2604
2705
|
className: "mneme-footbtn",
|
|
2605
2706
|
onClick: () => setEntityName(selected.node.name)
|
|
2606
|
-
}, t("memory.graph.viewInGraph"))
|
|
2607
|
-
attrs.length > 0 && h("div", null,
|
|
2608
|
-
h("div", { className: "mneme-gs-meta" }, t("memory.graph.attrs")),
|
|
2609
|
-
attrs.map((a, i) => h("div", { key: i, className: "mneme-gs-attr" },
|
|
2610
|
-
h("span", { className: "mneme-gs-attrkey" }, `${a.key}:`),
|
|
2611
|
-
h("span", { className: "mneme-gs-attrval" }, a.value)
|
|
2612
|
-
))
|
|
2613
|
-
)
|
|
2707
|
+
}, t("memory.graph.viewInGraph"))
|
|
2614
2708
|
)
|
|
2615
2709
|
: selected?.kind === "edge"
|
|
2616
2710
|
? h("div", { className: "mneme-graphside" },
|
|
@@ -2644,46 +2738,104 @@ window.__ModuleLoader__.load({
|
|
|
2644
2738
|
h("span", { title: formatDate(entity.last_seen) },
|
|
2645
2739
|
`${t("memory.entities.lastSeen")}: ${formatRelativeTime(entity.last_seen, t)}`)
|
|
2646
2740
|
),
|
|
2647
|
-
// ①
|
|
2741
|
+
// ① 关系图谱(前置):图形在属性/列表之前,是实体页的第一眼;
|
|
2742
|
+
// 深度/复位 chips 从层头移进图谱工具行(动作靠近作用对象),操作
|
|
2743
|
+
// 提示收进行尾小字不再独占一行;关系列表(跳来源记忆的唯一入口)
|
|
2744
|
+
// 保留为图谱的文本镜像、与图谱同层共享层头,消除信息双轨的分段感。
|
|
2648
2745
|
h("div", { className: "mneme-layer" },
|
|
2649
2746
|
h("div", { className: "mneme-layerhead" },
|
|
2650
|
-
t("memory.entities.
|
|
2651
|
-
h("span", { className: "mneme-layercount" }, String(
|
|
2747
|
+
t("memory.entities.relations"),
|
|
2748
|
+
h("span", { className: "mneme-layercount" }, String(rootEdges.length))
|
|
2652
2749
|
),
|
|
2653
|
-
|
|
2654
|
-
? h("div", { className: "mneme-
|
|
2655
|
-
: h(
|
|
2656
|
-
|
|
2657
|
-
h("
|
|
2658
|
-
|
|
2659
|
-
|
|
2750
|
+
rootEdges.length === 0 && graphStatus === "empty"
|
|
2751
|
+
? h("div", { className: "mneme-hint" }, t("memory.graph.empty"))
|
|
2752
|
+
: h(react.Fragment, null,
|
|
2753
|
+
graphStatus !== "empty" && h("div", { className: "mneme-graphbar" },
|
|
2754
|
+
h("button", {
|
|
2755
|
+
className: depth === 2 ? "mneme-chip mneme-active" : "mneme-chip",
|
|
2756
|
+
title: t("memory.graph.depth"),
|
|
2757
|
+
onClick: () => setDepth(depth === 1 ? 2 : 1)
|
|
2758
|
+
}, `${depth} ${t("memory.graph.depth")}`),
|
|
2759
|
+
h("button", {
|
|
2760
|
+
className: "mneme-chip",
|
|
2761
|
+
title: t("memory.graph.resetView"),
|
|
2762
|
+
onClick: resetView
|
|
2763
|
+
}, t("memory.graph.resetView")),
|
|
2764
|
+
h("span", { style: { marginLeft: "auto", fontSize: 12, color: "var(--mneme-fg-dim)" } }, t("memory.graph.hint"))
|
|
2765
|
+
),
|
|
2766
|
+
graphStatus !== "empty" && h("div", { className: "mneme-entgraphwrap" },
|
|
2767
|
+
h("svg", {
|
|
2768
|
+
ref: svgRef,
|
|
2769
|
+
className: "mneme-graphsvg",
|
|
2770
|
+
viewBox: `0 0 ${VIEW_W} ${VIEW_H}`,
|
|
2771
|
+
style: { height: 440 },
|
|
2772
|
+
role: "img",
|
|
2773
|
+
"aria-label": t("memory.graph.summary")
|
|
2774
|
+
.replace("{entity}", (data.root && (data.root.label || data.root.name)) || "")
|
|
2775
|
+
.replace("{nodes}", String(data.nodes.length))
|
|
2776
|
+
.replace("{edges}", String(data.edges.length)),
|
|
2777
|
+
onMouseDown: onSurfaceMouseDown
|
|
2778
|
+
},
|
|
2779
|
+
h("g", {
|
|
2780
|
+
transform: `translate(${viewRef.current.x},${viewRef.current.y}) scale(${viewRef.current.k})`
|
|
2781
|
+
},
|
|
2782
|
+
data.edges.map((e) => {
|
|
2783
|
+
const a = nodeById.get(e.from), b = nodeById.get(e.to);
|
|
2784
|
+
if (!a || !b) return null;
|
|
2785
|
+
return h("line", {
|
|
2786
|
+
key: e.id,
|
|
2787
|
+
x1: a.x, y1: a.y, x2: b.x, y2: b.y,
|
|
2788
|
+
className: e.memory_id ? "mneme-gedge" : "mneme-gedge mneme-gedge-dashed",
|
|
2789
|
+
onClick: () => onEdgeClick(e)
|
|
2790
|
+
});
|
|
2791
|
+
}),
|
|
2792
|
+
nodes.map((n) => h("g", {
|
|
2793
|
+
key: n.id,
|
|
2794
|
+
className: n.id === data.root.id ? "mneme-gnode mneme-groot" : "mneme-gnode",
|
|
2795
|
+
transform: `translate(${n.x},${n.y})`,
|
|
2796
|
+
onMouseDown: (e) => onNodeMouseDown(e, n),
|
|
2797
|
+
onClick: () => onNodeClick(n),
|
|
2798
|
+
"data-node": n.name
|
|
2799
|
+
},
|
|
2800
|
+
h("circle", { r: nodeRadius(n), fill: typeColor(n.type), fillOpacity: nodeOpacity(n) }),
|
|
2801
|
+
h("text", { className: "mneme-glabel", y: nodeRadius(n) + 13 }, n.name)
|
|
2802
|
+
))
|
|
2803
|
+
)
|
|
2804
|
+
),
|
|
2805
|
+
side
|
|
2806
|
+
),
|
|
2807
|
+
rootEdges.length > 0 && rootEdges.map((e) => {
|
|
2808
|
+
const out = e.from === data.root.id;
|
|
2809
|
+
const other = nodeById.get(out ? e.to : e.from);
|
|
2810
|
+
const otherName = other?.name ?? (out ? e.to : e.from);
|
|
2811
|
+
return h("button", {
|
|
2812
|
+
key: e.id,
|
|
2813
|
+
className: "mneme-relrow",
|
|
2814
|
+
title: e.memory_id ? t("memory.graph.sourceMemory") : undefined,
|
|
2815
|
+
onClick: () => { if (e.memory_id && onJumpMemory) onJumpMemory({ id: e.memory_id }); }
|
|
2816
|
+
},
|
|
2817
|
+
h("span", { className: "mneme-relname", style: { textAlign: "right", flex: 1 } }, entity.name),
|
|
2818
|
+
h("span", { className: "mneme-reltype" }, e.relation_type),
|
|
2819
|
+
h("span", { className: "mneme-relname", style: { flex: 1 } }, otherName)
|
|
2820
|
+
);
|
|
2821
|
+
})
|
|
2660
2822
|
)
|
|
2661
2823
|
),
|
|
2662
|
-
// ②
|
|
2663
|
-
|
|
2824
|
+
// ② 属性 — current snapshot (valid_until IS NULL);空层整层隐藏
|
|
2825
|
+
// (原先渲染一行「暂无内容」占住高度,是零信息的堆积)
|
|
2826
|
+
attrs.length > 0 && h("div", { className: "mneme-layer" },
|
|
2664
2827
|
h("div", { className: "mneme-layerhead" },
|
|
2665
|
-
t("memory.entities.
|
|
2666
|
-
h("span", { className: "mneme-layercount" }, String(
|
|
2828
|
+
t("memory.entities.attrs"),
|
|
2829
|
+
h("span", { className: "mneme-layercount" }, String(attrs.length))
|
|
2667
2830
|
),
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
return h("button", {
|
|
2675
|
-
key: e.id,
|
|
2676
|
-
className: "mneme-relrow",
|
|
2677
|
-
title: e.memory_id ? t("memory.graph.sourceMemory") : undefined,
|
|
2678
|
-
onClick: () => { if (e.memory_id && onJumpMemory) onJumpMemory({ id: e.memory_id }); }
|
|
2679
|
-
},
|
|
2680
|
-
h("span", { className: "mneme-relname", style: { textAlign: "right", flex: 1 } }, entity.name),
|
|
2681
|
-
h("span", { className: "mneme-reltype" }, e.relation_type),
|
|
2682
|
-
h("span", { className: "mneme-relname", style: { flex: 1 } }, otherName)
|
|
2683
|
-
);
|
|
2684
|
-
})
|
|
2831
|
+
h("div", { className: "mneme-attrgrid" },
|
|
2832
|
+
attrs.map((a, i) => h("div", { key: i, className: "mneme-attrcard", title: a.value },
|
|
2833
|
+
h("div", { className: "mneme-attrkey" }, a.key),
|
|
2834
|
+
h("div", { className: "mneme-attrval" }, a.value)
|
|
2835
|
+
))
|
|
2836
|
+
)
|
|
2685
2837
|
),
|
|
2686
|
-
// ③ 关联记忆 — entity: prefix recall
|
|
2838
|
+
// ③ 关联记忆 — entity: prefix recall(entity: 之外的唯一记忆回链入口)
|
|
2687
2839
|
related.length > 0 && h("div", { className: "mneme-layer" },
|
|
2688
2840
|
h("div", { className: "mneme-layerhead" },
|
|
2689
2841
|
t("memory.graph.related"),
|
|
@@ -2696,68 +2848,6 @@ window.__ModuleLoader__.load({
|
|
|
2696
2848
|
title: t("memory.card.open"),
|
|
2697
2849
|
onClick: () => onJumpMemory && onJumpMemory(m)
|
|
2698
2850
|
}, m.title || m.content?.slice(0, 60)))
|
|
2699
|
-
),
|
|
2700
|
-
// ④ 关系图谱 — the ego graph
|
|
2701
|
-
h("div", { className: "mneme-layer" },
|
|
2702
|
-
h("div", { className: "mneme-layerhead" },
|
|
2703
|
-
t("memory.entities.graph"),
|
|
2704
|
-
h("span", { className: "mneme-layercount" },
|
|
2705
|
-
h("button", {
|
|
2706
|
-
className: depth === 2 ? "mneme-chip mneme-active" : "mneme-chip",
|
|
2707
|
-
title: t("memory.graph.depth"),
|
|
2708
|
-
onClick: () => setDepth(depth === 1 ? 2 : 1)
|
|
2709
|
-
}, `${depth} ${t("memory.graph.depth")}`)),
|
|
2710
|
-
h("span", { className: "mneme-layercount" },
|
|
2711
|
-
h("button", {
|
|
2712
|
-
className: "mneme-chip",
|
|
2713
|
-
title: t("memory.graph.resetView"),
|
|
2714
|
-
onClick: resetView
|
|
2715
|
-
}, t("memory.graph.resetView")))
|
|
2716
|
-
),
|
|
2717
|
-
graphStatus === "empty"
|
|
2718
|
-
? h("div", { className: "mneme-hint" }, t("memory.graph.empty"))
|
|
2719
|
-
: h(react.Fragment, null,
|
|
2720
|
-
h("svg", {
|
|
2721
|
-
ref: svgRef,
|
|
2722
|
-
className: "mneme-graphsvg",
|
|
2723
|
-
viewBox: `0 0 ${VIEW_W} ${VIEW_H}`,
|
|
2724
|
-
style: { height: 320 },
|
|
2725
|
-
role: "img",
|
|
2726
|
-
"aria-label": t("memory.graph.summary")
|
|
2727
|
-
.replace("{entity}", (data.root && (data.root.label || data.root.name)) || "")
|
|
2728
|
-
.replace("{nodes}", String(data.nodes.length))
|
|
2729
|
-
.replace("{edges}", String(data.edges.length)),
|
|
2730
|
-
onMouseDown: onSurfaceMouseDown
|
|
2731
|
-
},
|
|
2732
|
-
h("g", {
|
|
2733
|
-
transform: `translate(${viewRef.current.x},${viewRef.current.y}) scale(${viewRef.current.k})`
|
|
2734
|
-
},
|
|
2735
|
-
data.edges.map((e) => {
|
|
2736
|
-
const a = nodeById.get(e.from), b = nodeById.get(e.to);
|
|
2737
|
-
if (!a || !b) return null;
|
|
2738
|
-
return h("line", {
|
|
2739
|
-
key: e.id,
|
|
2740
|
-
x1: a.x, y1: a.y, x2: b.x, y2: b.y,
|
|
2741
|
-
className: e.memory_id ? "mneme-gedge" : "mneme-gedge mneme-gedge-dashed",
|
|
2742
|
-
onClick: () => onEdgeClick(e)
|
|
2743
|
-
});
|
|
2744
|
-
}),
|
|
2745
|
-
nodes.map((n) => h("g", {
|
|
2746
|
-
key: n.id,
|
|
2747
|
-
className: n.id === data.root.id ? "mneme-gnode mneme-groot" : "mneme-gnode",
|
|
2748
|
-
transform: `translate(${n.x},${n.y})`,
|
|
2749
|
-
onMouseDown: (e) => onNodeMouseDown(e, n),
|
|
2750
|
-
onClick: () => onNodeClick(n),
|
|
2751
|
-
"data-node": n.name
|
|
2752
|
-
},
|
|
2753
|
-
h("circle", { r: nodeRadius(n), fill: typeColor(n.type), fillOpacity: nodeOpacity(n) }),
|
|
2754
|
-
h("text", { className: "mneme-glabel", y: nodeRadius(n) + 13 }, n.name)
|
|
2755
|
-
))
|
|
2756
|
-
)
|
|
2757
|
-
),
|
|
2758
|
-
h("div", { className: "mneme-graphhint" }, t("memory.graph.hint"))
|
|
2759
|
-
),
|
|
2760
|
-
side
|
|
2761
2851
|
)
|
|
2762
2852
|
)
|
|
2763
2853
|
)
|
|
@@ -2783,19 +2873,27 @@ window.__ModuleLoader__.load({
|
|
|
2783
2873
|
// 用户敢动的总开关;group.scope 也在首屏——它回答新用户的第二个必答题
|
|
2784
2874
|
// 「它记的东西会不会被别的会话看到」。其余组整组收进「全部设置」折叠
|
|
2785
2875
|
// (showAll)。成员从原组里移走而不是渲染时过滤:同一项永远只渲染一次。
|
|
2786
|
-
// 首屏可点项 = 3 总开关 + autoInject 的 2 个子开关 +
|
|
2787
|
-
// 「全部设置」按钮 =
|
|
2788
|
-
//
|
|
2789
|
-
|
|
2876
|
+
// 首屏可点项 = 3 总开关 + autoInject 的 2 个子开关 + M.O. 1 开关 +
|
|
2877
|
+
// scope 2 开关 + 「全部设置」按钮 = 9。复验修订:产品主人点名 M.O.
|
|
2878
|
+
// 「别藏在里面」,独立成组但仍折在「全部设置」后等于换个地方藏;
|
|
2879
|
+
// ≤8 是本仓库 #427 自设的软预算,owner 点名豁免一项,锁随注释同步改。
|
|
2880
|
+
// RuntimeProvisionBlock 配好即消失,常态首屏更少。
|
|
2881
|
+
const FEATURE_FIRST_SCREEN = ["group.basic", "group.mo", "group.scope"];
|
|
2790
2882
|
const FEATURE_GROUPS = [
|
|
2791
2883
|
{ key: "group.basic", items: ["autoInject", "autoSummarize", "autoDream"] },
|
|
2792
2884
|
{ key: "group.core", items: ["hotMemoryEnabled", "injectTimePrefix", "memoryQualityFilter.enabled", "llmAudit.enabled", "writeAdmission.enabled", "writeAdmission.enforce", "preInjectGate.enabled", "preInjectGate.enforce", "sensitiveScanEnabled",
|
|
2793
2885
|
// #399 的留存总闸(见下方 distillSub:天数行是它的子块)。放在组末,
|
|
2794
2886
|
// 子块才能紧跟在它后面展开——items.map 之后的子块是按组尾渲染的。
|
|
2795
2887
|
"distillRecords.enabled"] },
|
|
2796
|
-
// v0.9.0 M.O.(beta
|
|
2797
|
-
//
|
|
2798
|
-
|
|
2888
|
+
// v0.9.0 M.O.(beta)复验修订:独立分组行——它不是行为开关而是座位闸门
|
|
2889
|
+
// (关=一个座位都不注册,见 apply 内的 M.O. 块)。独立成组只解决「混在
|
|
2890
|
+
// enhance 末位」;可见层由 FEATURE_FIRST_SCREEN 决定(见其注释),
|
|
2891
|
+
// 本轮已把组名提进首屏——组成员资格不决定可见性。
|
|
2892
|
+
{ key: "group.mo", items: ["actionPipelineEnabled"] },
|
|
2893
|
+
{ key: "group.enhance", items: ["entityExtractionEnabled", "codingRetrospect", "rerankEnabled", "resilientModelDownload", "searchSemanticDedup", "bm25SearchEnabled", "heatEnabled", "documentMemoryEnabled",
|
|
2894
|
+
// 人格定义页(beta):注入总闸。分组随 enhance(行为开关,非座位闸门);
|
|
2895
|
+
// 人格内容在设置页「个性化」区的人格卡。
|
|
2896
|
+
"personaEnabled"] },
|
|
2799
2897
|
{ key: "group.dream", items: ["sleepModeEnabled"] },
|
|
2800
2898
|
// v0.8.0 A4(issue #17):作用域隔离组——标注总开关 + 严格硬过滤。
|
|
2801
2899
|
{ key: "group.scope", items: ["scopeEnabled", "strictScope"] }
|
|
@@ -3310,6 +3408,14 @@ window.__ModuleLoader__.load({
|
|
|
3310
3408
|
const [newCmd, setNewCmd] = react.useState({ name: "", description: "", instruction: "" });
|
|
3311
3409
|
const [saved, setSaved] = react.useState(false);
|
|
3312
3410
|
const [cmdError, setCmdError] = react.useState("");
|
|
3411
|
+
// 人格卡(beta,v1.2 多卡):default 卡草稿 + byAgent 卡列表 + 当前编辑位;
|
|
3412
|
+
// provenance 是 v1.5 协作式人格的预留位,v1 只透传不编辑。
|
|
3413
|
+
const [personaDefault, setPersonaDefault] = react.useState({ name: "", personality: "", replyStyle: "", enabled: false });
|
|
3414
|
+
const [personaByAgent, setPersonaByAgent] = react.useState({});
|
|
3415
|
+
const [personaEditing, setPersonaEditing] = react.useState("default");
|
|
3416
|
+
const [personaNewName, setPersonaNewName] = react.useState("");
|
|
3417
|
+
const [personaSaved, setPersonaSaved] = react.useState(false);
|
|
3418
|
+
const [personaError, setPersonaError] = react.useState("");
|
|
3313
3419
|
const [vector, setVector] = react.useState({ enabled: false, baseUrl: "", apiKey: "", model: "" });
|
|
3314
3420
|
const [vectorSaved, setVectorSaved] = react.useState(false);
|
|
3315
3421
|
const [reindexing, setReindexing] = react.useState(false);
|
|
@@ -3320,9 +3426,39 @@ window.__ModuleLoader__.load({
|
|
|
3320
3426
|
const [apiTokenSaved, setApiTokenSaved] = react.useState(false);
|
|
3321
3427
|
// 反馈入口的插件版本(GET /info)。失败保持 "unknown",链接仍可用。
|
|
3322
3428
|
const [pkgVersion, setPkgVersion] = react.useState("unknown");
|
|
3323
|
-
//
|
|
3324
|
-
//
|
|
3429
|
+
// 版本自检(复验修订:原为进入设置页自动请求 + 顶部横幅,现改为底部
|
|
3430
|
+
// 「支持与鼓励」卡里的「检测更新」按钮手动触发,结果内联显示——自动
|
|
3431
|
+
// 请求与横幅常驻都是打扰面)。status 共四种:outdated / latest / error / null(未检测)。
|
|
3325
3432
|
const [updateInfo, setUpdateInfo] = react.useState(null);
|
|
3433
|
+
const [updateChecking, setUpdateChecking] = react.useState(false);
|
|
3434
|
+
function checkForUpdate() {
|
|
3435
|
+
if (updateChecking) return;
|
|
3436
|
+
setUpdateChecking(true);
|
|
3437
|
+
apiFetch("/api/dsh-mneme/version-check")
|
|
3438
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
3439
|
+
.then((j) => {
|
|
3440
|
+
setUpdateInfo(j && typeof j.latest === "string" && typeof j.version === "string"
|
|
3441
|
+
? { status: j.status, latest: j.latest, current: j.version }
|
|
3442
|
+
: { status: "error" });
|
|
3443
|
+
setUpdateChecking(false);
|
|
3444
|
+
})
|
|
3445
|
+
.catch(() => { setUpdateInfo({ status: "error" }); setUpdateChecking(false); });
|
|
3446
|
+
}
|
|
3447
|
+
// 检测结果一行文案:按 status 取既有或新增的 i18n key。
|
|
3448
|
+
function checkResultText() {
|
|
3449
|
+
if (!updateInfo) return "";
|
|
3450
|
+
if (updateInfo.status === "outdated") {
|
|
3451
|
+
return t("memory.settings.version.outdated")
|
|
3452
|
+
.replace("{v}", updateInfo.latest)
|
|
3453
|
+
.replace("{c}", updateInfo.current);
|
|
3454
|
+
}
|
|
3455
|
+
// 后端 classify(src/version-check.js)返回的是 "up-to-date"(连字符)。
|
|
3456
|
+
// 前端状态名必须与它逐字对齐:此前误写 "latest"/"up_to_date" 两个后端
|
|
3457
|
+
// 从未返回过的名字,检测成功也掉进 error 兜底(复验实测踩中)。
|
|
3458
|
+
if (updateInfo.status === "up-to-date") return t("memory.settings.update.latest");
|
|
3459
|
+
if (updateInfo.status === "ahead") return t("memory.settings.update.ahead");
|
|
3460
|
+
return t("memory.settings.update.error");
|
|
3461
|
+
}
|
|
3326
3462
|
// 运行模式 — light vs standard; null = still loading. The card keeps
|
|
3327
3463
|
// its own busy/saved/error state so it never blocks the others.
|
|
3328
3464
|
const [mode, setMode] = react.useState(null);
|
|
@@ -3343,16 +3479,33 @@ window.__ModuleLoader__.load({
|
|
|
3343
3479
|
|
|
3344
3480
|
const load = react.useCallback(async () => {
|
|
3345
3481
|
try {
|
|
3346
|
-
const [p, r, c, v] = await Promise.all([
|
|
3482
|
+
const [p, r, c, v, ps] = await Promise.all([
|
|
3347
3483
|
apiFetch("/api/dsh-mneme/profile").then((res) => res.json()),
|
|
3348
3484
|
apiFetch("/api/dsh-mneme/rules").then((res) => res.json()),
|
|
3349
3485
|
apiFetch("/api/dsh-mneme/commands").then((res) => res.json()),
|
|
3350
|
-
apiFetch("/api/dsh-mneme/vector-config").then((res) => res.json())
|
|
3486
|
+
apiFetch("/api/dsh-mneme/vector-config").then((res) => res.json()),
|
|
3487
|
+
apiFetch("/api/dsh-mneme/persona").then((res) => res.json()).catch(() => null)
|
|
3351
3488
|
]);
|
|
3352
3489
|
setProfile(p.profile || "");
|
|
3353
3490
|
setRules(Array.isArray(r.rules) ? r.rules : []);
|
|
3354
3491
|
setCommands(Array.isArray(c.commands) ? c.commands : []);
|
|
3355
3492
|
setVector(v.config || { enabled: false, baseUrl: "", apiKey: "", model: "" });
|
|
3493
|
+
// 人格卡:旧后端无 /persona(404)时保持默认空卡,不阻塞其它卡片。
|
|
3494
|
+
// v1.2 多卡形 {default, byAgent};旧单对象形读作 default(后端已兼容,
|
|
3495
|
+
// 这里双保险再判一次形状)。
|
|
3496
|
+
const pj = ps && ps.persona && typeof ps.persona === "object" ? ps.persona : {};
|
|
3497
|
+
const isMulti = "default" in pj || "byAgent" in pj;
|
|
3498
|
+
const dRaw = isMulti ? (pj.default ?? {}) : pj;
|
|
3499
|
+
const bRaw = isMulti && pj.byAgent && typeof pj.byAgent === "object" ? pj.byAgent : {};
|
|
3500
|
+
const norm = (c) => ({
|
|
3501
|
+
name: typeof c?.name === "string" ? c.name : "",
|
|
3502
|
+
personality: typeof c?.personality === "string" ? c.personality : "",
|
|
3503
|
+
replyStyle: typeof c?.replyStyle === "string" ? c.replyStyle : "",
|
|
3504
|
+
enabled: c?.enabled === true
|
|
3505
|
+
});
|
|
3506
|
+
setPersonaDefault(norm(dRaw));
|
|
3507
|
+
setPersonaByAgent(Object.fromEntries(Object.entries(bRaw).map(([k, c]) => [k, norm(c)])));
|
|
3508
|
+
setPersonaEditing("default");
|
|
3356
3509
|
} catch { /* ignore */ }
|
|
3357
3510
|
}, []);
|
|
3358
3511
|
|
|
@@ -3368,21 +3521,6 @@ window.__ModuleLoader__.load({
|
|
|
3368
3521
|
return () => { cancelled = true; };
|
|
3369
3522
|
}, []);
|
|
3370
3523
|
|
|
3371
|
-
// 版本自检:只读 /version-check(宿主围栏鉴权)。非 outdated 一律归
|
|
3372
|
-
// null——横幅不出现即是「无需处理」;接口失败也静默,不新增故障面。
|
|
3373
|
-
react.useEffect(() => {
|
|
3374
|
-
let cancelled = false;
|
|
3375
|
-
apiFetch("/api/dsh-mneme/version-check")
|
|
3376
|
-
.then((res) => (res.ok ? res.json() : null))
|
|
3377
|
-
.then((j) => {
|
|
3378
|
-
if (!cancelled && j && j.status === "outdated" && typeof j.latest === "string" && typeof j.version === "string") {
|
|
3379
|
-
setUpdateInfo({ latest: j.latest, current: j.version });
|
|
3380
|
-
}
|
|
3381
|
-
})
|
|
3382
|
-
.catch(() => {});
|
|
3383
|
-
return () => { cancelled = true; };
|
|
3384
|
-
}, []);
|
|
3385
|
-
|
|
3386
3524
|
// 反馈链接预填:环境信息(插件版本 + 平台)+ 问题描述骨架。邮件主题带
|
|
3387
3525
|
// 版本/平台方便归档,正文同款模板;纯链接零后端成本。
|
|
3388
3526
|
const feedbackEnv = `**插件版本**: ${pkgVersion}\n**平台**: ${platformLabel()}\n`;
|
|
@@ -3443,6 +3581,65 @@ window.__ModuleLoader__.load({
|
|
|
3443
3581
|
} catch { /* ignore */ }
|
|
3444
3582
|
}
|
|
3445
3583
|
|
|
3584
|
+
// 人格卡保存(v1.2 多卡):全量多卡形 PUT({default, byAgent},后端全量
|
|
3585
|
+
// 替换语义)。保存失败必须冒提示——v1 的静默 catch 曾把 404 吞成「看起来
|
|
3586
|
+
// 保存了」(后端未重装配时 PUT 打 404),桉桉实测踩中,此处不再静默。
|
|
3587
|
+
async function savePersonaCards(nextDefault, nextByAgent) {
|
|
3588
|
+
try {
|
|
3589
|
+
const res = await apiFetch("/api/dsh-mneme/persona", {
|
|
3590
|
+
method: "PUT",
|
|
3591
|
+
headers: { "Content-Type": "application/json" },
|
|
3592
|
+
body: JSON.stringify({ persona: { default: nextDefault, byAgent: nextByAgent } })
|
|
3593
|
+
});
|
|
3594
|
+
if (!res.ok) throw new Error("HTTP " + res.status);
|
|
3595
|
+
setPersonaSaved(true);
|
|
3596
|
+
setPersonaError("");
|
|
3597
|
+
announce(t("memory.settings.personaSaved"));
|
|
3598
|
+
setTimeout(() => setPersonaSaved(false), 1500);
|
|
3599
|
+
} catch (err) {
|
|
3600
|
+
setPersonaError((err && err.message) || "failed");
|
|
3601
|
+
setTimeout(() => setPersonaError(""), 4000);
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
// 当前编辑位的卡:default 或 byAgent[editing];草稿即编辑位状态本身。
|
|
3605
|
+
const personaEditingCard = personaEditing === "default"
|
|
3606
|
+
? personaDefault
|
|
3607
|
+
: personaByAgent[personaEditing] || { name: "", personality: "", replyStyle: "", enabled: false };
|
|
3608
|
+
const updatePersonaCard = (key, value) => {
|
|
3609
|
+
if (personaEditing === "default") setPersonaDefault((c) => ({ ...c, [key]: value }));
|
|
3610
|
+
else setPersonaByAgent((c) => ({ ...c, [personaEditing]: { ...(c[personaEditing] || { name: "", personality: "", replyStyle: "", enabled: false }), [key]: value } }));
|
|
3611
|
+
};
|
|
3612
|
+
const saveEditingPersona = () => savePersonaCards(personaDefault, personaByAgent);
|
|
3613
|
+
const toggleEditingPersona = (checked) => {
|
|
3614
|
+
if (personaEditing === "default") {
|
|
3615
|
+
const next = { ...personaDefault, enabled: checked };
|
|
3616
|
+
setPersonaDefault(next);
|
|
3617
|
+
savePersonaCards(next, personaByAgent);
|
|
3618
|
+
} else {
|
|
3619
|
+
const next = { ...(personaByAgent[personaEditing] || { name: "", personality: "", replyStyle: "", enabled: false }), enabled: checked };
|
|
3620
|
+
setPersonaByAgent((c) => ({ ...c, [personaEditing]: next }));
|
|
3621
|
+
savePersonaCards(personaDefault, { ...personaByAgent, [personaEditing]: next });
|
|
3622
|
+
}
|
|
3623
|
+
};
|
|
3624
|
+
const addPersonaCard = () => {
|
|
3625
|
+
const id = personaNewName.trim();
|
|
3626
|
+
if (!id || id === "default" || personaByAgent[id]) return;
|
|
3627
|
+
const fresh = { name: id, personality: "", replyStyle: "", enabled: false };
|
|
3628
|
+
const next = { ...personaByAgent, [id]: fresh };
|
|
3629
|
+
setPersonaByAgent(next);
|
|
3630
|
+
setPersonaNewName("");
|
|
3631
|
+
setPersonaEditing(id);
|
|
3632
|
+
savePersonaCards(personaDefault, next);
|
|
3633
|
+
};
|
|
3634
|
+
const removePersonaCard = (id) => {
|
|
3635
|
+
if (id === "default" || !personaByAgent[id]) return;
|
|
3636
|
+
const next = { ...personaByAgent };
|
|
3637
|
+
delete next[id];
|
|
3638
|
+
setPersonaByAgent(next);
|
|
3639
|
+
if (personaEditing === id) setPersonaEditing("default");
|
|
3640
|
+
savePersonaCards(personaDefault, next);
|
|
3641
|
+
};
|
|
3642
|
+
|
|
3446
3643
|
async function putRules(next) {
|
|
3447
3644
|
await apiFetch("/api/dsh-mneme/rules", {
|
|
3448
3645
|
method: "PUT",
|
|
@@ -3624,18 +3821,7 @@ window.__ModuleLoader__.load({
|
|
|
3624
3821
|
}
|
|
3625
3822
|
|
|
3626
3823
|
return h("div", null,
|
|
3627
|
-
//
|
|
3628
|
-
// 全部零渲染)。钉子警示:安装时指定过版本号的 profile 会被 pnpm
|
|
3629
|
-
// 挡住常规升级(#174 报障者的实际成因);市场收录新发布约有 1 天延迟。
|
|
3630
|
-
updateInfo && h("section", { className: "mneme-set-card" },
|
|
3631
|
-
h("div", { className: "mneme-set-title" },
|
|
3632
|
-
t("memory.settings.version.outdated")
|
|
3633
|
-
.replace("{v}", updateInfo.latest)
|
|
3634
|
-
.replace("{c}", updateInfo.current)
|
|
3635
|
-
),
|
|
3636
|
-
h("div", { className: "mneme-featsubhint" }, t("memory.settings.version.outdatedHint"))
|
|
3637
|
-
),
|
|
3638
|
-
// —— 个性化:画像 / 规则 / 自定义指令(Agent 怎么对你) ——
|
|
3824
|
+
// —— 个性化:画像 / 人格 / 规则 / 自定义指令(Agent 怎么对你) ——
|
|
3639
3825
|
h("div", { className: "mneme-set-grouphead" }, t("memory.settings.group.personal")),
|
|
3640
3826
|
// 用户画像 — who the agent is talking to
|
|
3641
3827
|
h("section", { className: "mneme-set-card" },
|
|
@@ -3652,6 +3838,103 @@ window.__ModuleLoader__.load({
|
|
|
3652
3838
|
saved && h("span", { className: "mneme-saved" }, t("memory.settings.profileSaved"))
|
|
3653
3839
|
)
|
|
3654
3840
|
),
|
|
3841
|
+
// 人格卡(beta,v1.2 多卡)— who the agent is:多张角色卡按会话身份注入。
|
|
3842
|
+
// 卡内开关只是该卡 enabled 半边;总闸 personaEnabled 在功能开关组,双闸同真。
|
|
3843
|
+
h("section", { className: "mneme-set-card" },
|
|
3844
|
+
h("div", { className: "mneme-set-title" }, t("memory.settings.persona")),
|
|
3845
|
+
h("div", { className: "mneme-set-desc" }, t("memory.settings.personaHint")),
|
|
3846
|
+
// 卡列表:default + 各 byAgent 卡,点选切换编辑位;default 标注兜底位
|
|
3847
|
+
h("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 } },
|
|
3848
|
+
h("button", {
|
|
3849
|
+
type: "button",
|
|
3850
|
+
className: "mneme-btn" + (personaEditing === "default" ? " mneme-btn--active" : ""),
|
|
3851
|
+
onClick: () => setPersonaEditing("default")
|
|
3852
|
+
}, t("memory.settings.personaDefaultCard")),
|
|
3853
|
+
Object.keys(personaByAgent).map((id) =>
|
|
3854
|
+
h("span", { key: id, style: { display: "inline-flex", alignItems: "center", gap: 2 } },
|
|
3855
|
+
h("button", {
|
|
3856
|
+
type: "button",
|
|
3857
|
+
className: "mneme-btn" + (personaEditing === id ? " mneme-btn--active" : ""),
|
|
3858
|
+
onClick: () => setPersonaEditing(id)
|
|
3859
|
+
}, id),
|
|
3860
|
+
h("button", {
|
|
3861
|
+
type: "button",
|
|
3862
|
+
className: "mneme-set-del",
|
|
3863
|
+
title: t("memory.settings.personaDeleteCard"),
|
|
3864
|
+
"aria-label": t("memory.settings.personaDeleteCard") + " " + id,
|
|
3865
|
+
onClick: () => removePersonaCard(id)
|
|
3866
|
+
}, "×")
|
|
3867
|
+
))
|
|
3868
|
+
),
|
|
3869
|
+
// 添加卡:输入 agent 身份名(注入时按它寻址)
|
|
3870
|
+
h("div", { style: { display: "flex", gap: 8 } },
|
|
3871
|
+
h("input", {
|
|
3872
|
+
className: "mneme-set-input",
|
|
3873
|
+
style: { marginBottom: 0 },
|
|
3874
|
+
value: personaNewName,
|
|
3875
|
+
placeholder: t("memory.settings.personaAddPlaceholder"),
|
|
3876
|
+
onChange: (e) => setPersonaNewName(e.target.value),
|
|
3877
|
+
onKeyDown: (e) => { if (e.key === "Enter") addPersonaCard(); }
|
|
3878
|
+
}),
|
|
3879
|
+
h("button", { className: "mneme-btn", onClick: addPersonaCard }, t("memory.settings.personaAdd"))
|
|
3880
|
+
),
|
|
3881
|
+
// 编辑位标题:default 卡 vs 具体身份卡
|
|
3882
|
+
h("div", { className: "mneme-set-desc" },
|
|
3883
|
+
personaEditing === "default"
|
|
3884
|
+
? t("memory.settings.personaEditingDefault")
|
|
3885
|
+
: t("memory.settings.personaEditingFor") + " " + personaEditing),
|
|
3886
|
+
// 可见字段标签(v1.1 反馈:aria-label 看不见,框上要有字)
|
|
3887
|
+
h("div", { className: "mneme-featnumlabel" }, t("memory.settings.personaEnabledToggle")),
|
|
3888
|
+
h("div", { className: "mneme-set-row" },
|
|
3889
|
+
h("input", {
|
|
3890
|
+
type: "checkbox",
|
|
3891
|
+
checked: personaEditingCard.enabled === true,
|
|
3892
|
+
"aria-label": t("memory.settings.personaEnabledToggle"),
|
|
3893
|
+
onChange: (e) => toggleEditingPersona(e.target.checked)
|
|
3894
|
+
}),
|
|
3895
|
+
h("span", { className: "mneme-set-ruletext" },
|
|
3896
|
+
personaEditingCard.enabled === true
|
|
3897
|
+
? t("memory.settings.personaEnabledOn")
|
|
3898
|
+
: t("memory.settings.personaEnabledOff"))
|
|
3899
|
+
),
|
|
3900
|
+
h("div", { className: "mneme-featnumlabel" }, t("memory.settings.personaName")),
|
|
3901
|
+
h("input", {
|
|
3902
|
+
className: "mneme-set-input",
|
|
3903
|
+
value: personaEditingCard.name,
|
|
3904
|
+
placeholder: t("memory.settings.personaNamePlaceholder"),
|
|
3905
|
+
onChange: (e) => updatePersonaCard("name", e.target.value)
|
|
3906
|
+
}),
|
|
3907
|
+
h("div", { className: "mneme-featnumlabel" }, t("memory.settings.personaPersonality")),
|
|
3908
|
+
h("textarea", {
|
|
3909
|
+
className: "mneme-set-input mneme-set-input--area",
|
|
3910
|
+
value: personaEditingCard.personality,
|
|
3911
|
+
placeholder: t("memory.settings.personaPersonalityPlaceholder"),
|
|
3912
|
+
onChange: (e) => updatePersonaCard("personality", e.target.value)
|
|
3913
|
+
}),
|
|
3914
|
+
h("div", { className: "mneme-featnumlabel" }, t("memory.settings.personaReplyStyle")),
|
|
3915
|
+
h("textarea", {
|
|
3916
|
+
className: "mneme-set-input mneme-set-input--area",
|
|
3917
|
+
value: personaEditingCard.replyStyle,
|
|
3918
|
+
placeholder: t("memory.settings.personaReplyStylePlaceholder"),
|
|
3919
|
+
onChange: (e) => updatePersonaCard("replyStyle", e.target.value)
|
|
3920
|
+
}),
|
|
3921
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: 8 } },
|
|
3922
|
+
h("button", { className: "mneme-btn", onClick: saveEditingPersona }, t("memory.settings.personaSave")),
|
|
3923
|
+
personaSaved && h("span", { className: "mneme-saved" }, t("memory.settings.personaSaved")),
|
|
3924
|
+
personaError && h("span", { className: "mneme-saved", style: { color: "var(--dsw-danger, #d33)" } },
|
|
3925
|
+
t("memory.settings.personaSaveFailed") + " (" + personaError + ")")
|
|
3926
|
+
)
|
|
3927
|
+
),
|
|
3928
|
+
// Agent 眼中的画像(只读)— 注入生效文本:profile + rules 原样展示。
|
|
3929
|
+
// 纯前端拼接(GET 已在 load 里拿到 profile/rules),零后端新请求。
|
|
3930
|
+
h("section", { className: "mneme-set-card" },
|
|
3931
|
+
h("div", { className: "mneme-set-title" }, t("memory.settings.personaView")),
|
|
3932
|
+
h("div", { className: "mneme-set-desc" }, t("memory.settings.personaViewHint")),
|
|
3933
|
+
(profile.trim() || rules.length)
|
|
3934
|
+
? h("pre", { className: "mneme-set-input mneme-set-input--area", style: { whiteSpace: "pre-wrap", minHeight: 0 } },
|
|
3935
|
+
[profile.trim() ? "- " + profile.trim() : "", ...rules.map((r) => "- " + r)].filter(Boolean).join("\n"))
|
|
3936
|
+
: h("div", { className: "mneme-xempty" }, t("memory.settings.personaViewEmpty"))
|
|
3937
|
+
),
|
|
3655
3938
|
// 规则 — numbered rows, hover reveals the delete affordance
|
|
3656
3939
|
h("section", { className: "mneme-set-card" },
|
|
3657
3940
|
h("div", { className: "mneme-set-title" }, t("memory.settings.rules")),
|
|
@@ -3879,7 +4162,7 @@ window.__ModuleLoader__.load({
|
|
|
3879
4162
|
),
|
|
3880
4163
|
apiTokenSaved && h("div", { style: { marginTop: 8 } }, h("span", { className: "mneme-saved" }, t("memory.settings.apiTokenSaved")))
|
|
3881
4164
|
),
|
|
3882
|
-
// ——
|
|
4165
|
+
// —— 帮助:支持与鼓励(图标行 + 检测更新 + star,兼当「关于」收尾) ——
|
|
3883
4166
|
h("div", { className: "mneme-set-grouphead" }, t("memory.settings.group.help")),
|
|
3884
4167
|
// 反馈三入口:新建 issue 预填(版本+平台)/ 邮件 / 浏览已知问题。
|
|
3885
4168
|
// 纯前端链接零后端成本;插件版本来自 /info,平台取 userAgent。GitHub
|
|
@@ -3895,6 +4178,21 @@ window.__ModuleLoader__.load({
|
|
|
3895
4178
|
feedRow("search", t("memory.settings.feedback.browse"), t("memory.settings.feedback.browse.sub"),
|
|
3896
4179
|
{ href: "https://github.com/slow-stack/mneme/issues", target: "_blank", rel: "noopener noreferrer" })
|
|
3897
4180
|
),
|
|
4181
|
+
// 检测更新:手动触发(复验修订——原顶部自动横幅移除),结果内联;
|
|
4182
|
+
// outdated 时提示带 pnpm 钉子警示(#174 报障成因)。
|
|
4183
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 12 } },
|
|
4184
|
+
h("button", { className: "mneme-btn", onClick: checkForUpdate, disabled: updateChecking },
|
|
4185
|
+
updateChecking ? "…" : t("memory.settings.update.check")),
|
|
4186
|
+
updateInfo && h("span", { className: "mneme-feedsub" }, checkResultText())
|
|
4187
|
+
),
|
|
4188
|
+
updateInfo?.status === "outdated" && h("div", { className: "mneme-set-hint", style: { marginTop: 8 } },
|
|
4189
|
+
t("memory.settings.version.outdatedHint")),
|
|
4190
|
+
// star 鼓励入口:链接到仓库主页而非 stargazers——前者右上角就有
|
|
4191
|
+
// Star 按钮,后者只是「看谁点过」的名单页(复验实测反馈)。
|
|
4192
|
+
h("div", { className: "mneme-feedlist", style: { marginTop: 12 } },
|
|
4193
|
+
feedRow("star", t("memory.settings.star"), t("memory.settings.star.sub"),
|
|
4194
|
+
{ href: "https://github.com/slow-stack/mneme", target: "_blank", rel: "noopener noreferrer" })
|
|
4195
|
+
),
|
|
3898
4196
|
h("div", { className: "mneme-set-foot" },
|
|
3899
4197
|
h("span", null, `dsh-mneme v${pkgVersion}`)
|
|
3900
4198
|
)
|
|
@@ -4580,7 +4878,18 @@ window.__ModuleLoader__.load({
|
|
|
4580
4878
|
if (n >= 1e3) return `${Math.round(n / 1e3)}K`;
|
|
4581
4879
|
return String(n);
|
|
4582
4880
|
};
|
|
4583
|
-
|
|
4881
|
+
// 热力分档:固定阈值(75万/150万/250万)在低用量库里整片落在 l1,档位失真
|
|
4882
|
+
// (复验第三轮「颜色不怎么明显」)。改按 56 天窗口内的非零日做三分位切四档
|
|
4883
|
+
// ——GitHub 贡献图同思路,档次随库的实际用量自适应拉开;全零/无数据退化
|
|
4884
|
+
// 为全 0 档(不留空档位)。边界是三个分位值(q 只返回 3 个):n>p75 → l4、
|
|
4885
|
+
// >p50 → l3、>p25 → l2、其余非零 → l1,四档都有 populated。
|
|
4886
|
+
const heatLevel = (n, q) => (n <= 0 ? 0 : n > q[2] ? 4 : n > q[1] ? 3 : n > q[0] ? 2 : 1);
|
|
4887
|
+
const heatQuartiles = (days) => {
|
|
4888
|
+
const pos = days.map((d) => d.total).filter((v) => v > 0).sort((a, b) => a - b);
|
|
4889
|
+
if (!pos.length) return [0, 0, 0];
|
|
4890
|
+
const pick = (p) => pos[Math.min(pos.length - 1, Math.floor(p * pos.length))];
|
|
4891
|
+
return [pick(0.25), pick(0.5), pick(0.75)];
|
|
4892
|
+
};
|
|
4584
4893
|
|
|
4585
4894
|
function TokenFlowSection({ t }) {
|
|
4586
4895
|
const [stats, setStats] = useState(null);
|
|
@@ -4690,6 +4999,33 @@ window.__ModuleLoader__.load({
|
|
|
4690
4999
|
heatCells.push({ key, total: byDayMap.get(key) ?? 0 });
|
|
4691
5000
|
}
|
|
4692
5001
|
}
|
|
5002
|
+
// 分档阈值取自本窗口真实分布(见 heatQuartiles 注释),空窗口退化全 0 档。
|
|
5003
|
+
const heatQ = heatQuartiles(heatCells);
|
|
5004
|
+
// 月份标签:GitHub 同款——按周列贴,列首日期属于哪个月,那列就标那个
|
|
5005
|
+
// 月;连续同月的列合并成一段(gridColumn: from / span)。月不在列首
|
|
5006
|
+
// 开始时其首列仍标上一月(列首归属优先),这是贡献图的通行近似。
|
|
5007
|
+
const monthLabels = (() => {
|
|
5008
|
+
if (!heatCells.length) return [];
|
|
5009
|
+
const spans = [];
|
|
5010
|
+
heatCells.forEach((c, i) => {
|
|
5011
|
+
if (i % 7 !== 0) return; // 只看列首
|
|
5012
|
+
const ym = c.key.slice(0, 7);
|
|
5013
|
+
const col = Math.floor(i / 7) + 1;
|
|
5014
|
+
const label = `${Number(ym.slice(5, 7))}月`;
|
|
5015
|
+
const lastSpan = spans[spans.length - 1];
|
|
5016
|
+
if (lastSpan && lastSpan.key === ym) { lastSpan.last = col; return; }
|
|
5017
|
+
spans.push({ key: ym, label, from: col, last: col });
|
|
5018
|
+
});
|
|
5019
|
+
return spans.map((m) => ({ ...m, span: m.last - m.from + 1 }));
|
|
5020
|
+
})();
|
|
5021
|
+
// 行标签:行 r 的星期 = (首列星期 + r) % 7(getUTCDay 周日=0)。一/三/五
|
|
5022
|
+
// 三行有字(GitHub 同款),其余留空。
|
|
5023
|
+
const heatRowLabels = (() => {
|
|
5024
|
+
const startDow = heatCells.length ? new Date(`${heatCells[0].key}T00:00:00Z`).getUTCDay() : 0;
|
|
5025
|
+
const rows = new Array(7).fill("");
|
|
5026
|
+
for (const d of [1, 3, 5]) rows[(d - startDow + 7) % 7] = ["一", "三", "五"][[1, 3, 5].indexOf(d)];
|
|
5027
|
+
return rows;
|
|
5028
|
+
})();
|
|
4693
5029
|
const kpi = (title, num, cap) =>
|
|
4694
5030
|
h(StatusCard, { t, title, loading, error: statsError, num, cap });
|
|
4695
5031
|
const segClass = (s) =>
|
|
@@ -4783,12 +5119,22 @@ window.__ModuleLoader__.load({
|
|
|
4783
5119
|
days.map((d) => h("span", { key: d.day }, d.day.slice(5)))
|
|
4784
5120
|
),
|
|
4785
5121
|
h("h3", { className: "mneme-xcolhead" }, t("memory.status.token.heat")),
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
5122
|
+
// 月份行与格子列同用列主序网格对齐:每月占的列数 = 该月在这个
|
|
5123
|
+
// 56 天窗口里的列数(按列的起始日期算),标签只在该列起点画一次。
|
|
5124
|
+
h("div", { className: "mneme-heatmonths" },
|
|
5125
|
+
monthLabels.map((m) => h("span", { key: m.key, style: { gridColumn: `${m.from} / span ${m.span}` } }, m.label))
|
|
5126
|
+
),
|
|
5127
|
+
h("div", { className: "mneme-heatwrap" },
|
|
5128
|
+
h("div", { className: "mneme-heatrows", "aria-hidden": "true" },
|
|
5129
|
+
heatRowLabels.map((d, i) => h("span", { key: i }, d))
|
|
5130
|
+
),
|
|
5131
|
+
h("div", { className: "mneme-heatgrid" },
|
|
5132
|
+
heatCells.map((c) => h("div", {
|
|
5133
|
+
key: c.key,
|
|
5134
|
+
className: heatLevel(c.total, heatQ) ? `mneme-heatcell l${heatLevel(c.total, heatQ)}` : "mneme-heatcell",
|
|
5135
|
+
title: `${c.key} · ${c.total.toLocaleString()}`
|
|
5136
|
+
}))
|
|
5137
|
+
)
|
|
4792
5138
|
),
|
|
4793
5139
|
h("div", { className: "mneme-heatlegend" },
|
|
4794
5140
|
h("span", null, t("memory.status.token.heatLess")),
|
|
@@ -6267,6 +6613,9 @@ window.__ModuleLoader__.load({
|
|
|
6267
6613
|
),
|
|
6268
6614
|
),
|
|
6269
6615
|
view === "memory" && h("div", { className: "mneme-xmain" },
|
|
6616
|
+
// 两栏布局(桉桉规格):左栏上段=语义检索(怎么搜),下段=重要性/
|
|
6617
|
+
// 来源/热度(留什么);原第二侧栏的类型列表与计数刷新下沉到内容区
|
|
6618
|
+
// 工具条(.mneme-xbrowsebar),五个功能入口一个不丢。
|
|
6270
6619
|
h("div", { className: "mneme-xside" },
|
|
6271
6620
|
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.searchTitle")),
|
|
6272
6621
|
h("div", { className: "mneme-xsearchwrap" },
|
|
@@ -6307,39 +6656,28 @@ window.__ModuleLoader__.load({
|
|
|
6307
6656
|
["all", "7d", "30d", "90d"].map((k) =>
|
|
6308
6657
|
h("option", { key: k, value: k }, t(`memory.explorer.date.${k}`))))
|
|
6309
6658
|
),
|
|
6310
|
-
h("div", { className: "mneme-
|
|
6311
|
-
h("
|
|
6312
|
-
h("button", { className: "mneme-footbtn", onClick: () => setReloadKey((k) => k + 1) },
|
|
6313
|
-
h(Icon, { name: "refresh", size: 12 }), t("memory.explorer.refresh"))
|
|
6314
|
-
)
|
|
6315
|
-
),
|
|
6316
|
-
h("div", { className: "mneme-xside mneme-xside--filter" },
|
|
6317
|
-
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.importance")),
|
|
6318
|
-
h("div", { className: "mneme-xrow" },
|
|
6319
|
-
[0, 3, 4, 5].map((v) =>
|
|
6320
|
-
h("button", {
|
|
6321
|
-
key: v,
|
|
6322
|
-
className: minImp === v ? "mneme-chip mneme-active" : "mneme-chip",
|
|
6323
|
-
onClick: () => setMinImp(v)
|
|
6324
|
-
}, v === 0 ? t("memory.tab.all") : h(react.Fragment, null, h(StarGlyph, { size: 11 }), ` ${v}+`)))
|
|
6325
|
-
),
|
|
6326
|
-
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.sourceFilter")),
|
|
6327
|
-
h("div", { className: "mneme-xrow" },
|
|
6328
|
-
h("button", {
|
|
6329
|
-
className: depositedOnly ? "mneme-chip mneme-active" : "mneme-chip",
|
|
6330
|
-
title: t("memory.explorer.filterDepositedHint"),
|
|
6331
|
-
onClick: () => setDepositedOnly(!depositedOnly)
|
|
6332
|
-
}, t("memory.explorer.filterDeposited")),
|
|
6333
|
-
h("button", {
|
|
6334
|
-
className: archivedOnly ? "mneme-chip mneme-active" : "mneme-chip",
|
|
6335
|
-
title: t("memory.explorer.filterArchivedHint"),
|
|
6336
|
-
onClick: () => setArchivedOnly(!archivedOnly)
|
|
6337
|
-
}, t("memory.explorer.filterArchived"))
|
|
6338
|
-
),
|
|
6339
|
-
heatAvailable && h(react.Fragment, null,
|
|
6340
|
-
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.sort")),
|
|
6659
|
+
h("div", { className: "mneme-xside-imp" },
|
|
6660
|
+
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.importance")),
|
|
6341
6661
|
h("div", { className: "mneme-xrow" },
|
|
6662
|
+
[0, 3, 4, 5].map((v) =>
|
|
6663
|
+
h("button", {
|
|
6664
|
+
key: v,
|
|
6665
|
+
className: minImp === v ? "mneme-chip mneme-active" : "mneme-chip",
|
|
6666
|
+
onClick: () => setMinImp(v)
|
|
6667
|
+
}, v === 0 ? t("memory.tab.all") : h(react.Fragment, null, h(StarGlyph, { size: 11 }), ` ${v}+`)))
|
|
6668
|
+
),
|
|
6669
|
+
h("div", { className: "mneme-xrow" },
|
|
6670
|
+
h("button", {
|
|
6671
|
+
className: depositedOnly ? "mneme-chip mneme-active" : "mneme-chip",
|
|
6672
|
+
title: t("memory.explorer.filterDepositedHint"),
|
|
6673
|
+
onClick: () => setDepositedOnly(!depositedOnly)
|
|
6674
|
+
}, t("memory.explorer.filterDeposited")),
|
|
6342
6675
|
h("button", {
|
|
6676
|
+
className: archivedOnly ? "mneme-chip mneme-active" : "mneme-chip",
|
|
6677
|
+
title: t("memory.explorer.filterArchivedHint"),
|
|
6678
|
+
onClick: () => setArchivedOnly(!archivedOnly)
|
|
6679
|
+
}, t("memory.explorer.filterArchived")),
|
|
6680
|
+
heatAvailable && h("button", {
|
|
6343
6681
|
className: heatSort ? "mneme-chip mneme-active" : "mneme-chip",
|
|
6344
6682
|
title: t("memory.explorer.heatSortHint"),
|
|
6345
6683
|
onClick: () => {
|
|
@@ -6348,23 +6686,28 @@ window.__ModuleLoader__.load({
|
|
|
6348
6686
|
}
|
|
6349
6687
|
}, t("memory.explorer.heatSort"))
|
|
6350
6688
|
)
|
|
6351
|
-
)
|
|
6352
|
-
h("div", { className: "mneme-xcolhead" }, t("memory.explorer.types")),
|
|
6353
|
-
h("button", {
|
|
6354
|
-
className: type === "all" ? "mneme-xtype mneme-active" : "mneme-xtype",
|
|
6355
|
-
onClick: () => setType("all")
|
|
6356
|
-
}, h("span", { className: "mneme-xdot mneme-xdot--all", "aria-hidden": "true" }), h("span", null, t("memory.tab.all")), h("span", { className: "mneme-xcount2" }, String(items.length))),
|
|
6357
|
-
knownTypes.concat(extraTypes).map((key) =>
|
|
6358
|
-
h("button", {
|
|
6359
|
-
key,
|
|
6360
|
-
className: type === key ? "mneme-xtype mneme-active" : "mneme-xtype",
|
|
6361
|
-
onClick: () => setType(key)
|
|
6362
|
-
},
|
|
6363
|
-
h("span", { className: "mneme-xdot", style: { color: memoryTypeColor(key) }, title: typeLabel(t, key), "aria-hidden": "true" }),
|
|
6364
|
-
h("span", null, typeLabel(t, key)),
|
|
6365
|
-
h("span", { className: "mneme-xcount2" }, String(counts[key]))
|
|
6366
|
-
))
|
|
6689
|
+
)
|
|
6367
6690
|
),
|
|
6691
|
+
h("div", { className: "mneme-xcontent" },
|
|
6692
|
+
h("div", { className: "mneme-xbrowsebar" },
|
|
6693
|
+
h("button", {
|
|
6694
|
+
className: type === "all" ? "mneme-xtype mneme-active" : "mneme-xtype",
|
|
6695
|
+
onClick: () => setType("all")
|
|
6696
|
+
}, h("span", { className: "mneme-xdot mneme-xdot--all", "aria-hidden": "true" }), h("span", null, t("memory.tab.all")), h("span", { className: "mneme-xcount2" }, String(items.length))),
|
|
6697
|
+
knownTypes.concat(extraTypes).map((key) =>
|
|
6698
|
+
h("button", {
|
|
6699
|
+
key,
|
|
6700
|
+
className: type === key ? "mneme-xtype mneme-active" : "mneme-xtype",
|
|
6701
|
+
onClick: () => setType(key)
|
|
6702
|
+
},
|
|
6703
|
+
h("span", { className: "mneme-xdot", style: { color: memoryTypeColor(key) }, title: typeLabel(t, key), "aria-hidden": "true" }),
|
|
6704
|
+
h("span", null, typeLabel(t, key)),
|
|
6705
|
+
h("span", { className: "mneme-xcount2" }, String(counts[key]))
|
|
6706
|
+
)),
|
|
6707
|
+
h("span", { className: "mneme-xcount", style: { marginLeft: "auto" } }, t("memory.explorer.count").replace("{n}", String(visible.length))),
|
|
6708
|
+
h("button", { className: "mneme-footbtn", onClick: () => setReloadKey((k) => k + 1) },
|
|
6709
|
+
h(Icon, { name: "refresh", size: 12 }), t("memory.explorer.refresh"))
|
|
6710
|
+
),
|
|
6368
6711
|
viewMode === "cards"
|
|
6369
6712
|
? h("div", { className: "mneme-cards" },
|
|
6370
6713
|
loading
|
|
@@ -6469,8 +6812,11 @@ window.__ModuleLoader__.load({
|
|
|
6469
6812
|
)
|
|
6470
6813
|
),
|
|
6471
6814
|
),
|
|
6815
|
+
),
|
|
6472
6816
|
// 详情抽屉:卡片/时间线共用,选中即从右侧滑出(替代旧底部面板)。
|
|
6473
6817
|
// 两步删除确认在抽屉内完成;编辑/归档经 /update 落库并同步本地视图。
|
|
6818
|
+
// 保持为 .mneme-xmain 直接子元素:抽屉锚在 xmain 的 relative 层上,随
|
|
6819
|
+
// 两栏改版内容区变宽,抽屉锚点不变。
|
|
6474
6820
|
selected && h(MemoryDrawer, {
|
|
6475
6821
|
key: selected.id,
|
|
6476
6822
|
t,
|
|
@@ -7873,3 +8219,7 @@ window.__ModuleLoader__.load({
|
|
|
7873
8219
|
return module.exports;
|
|
7874
8220
|
}
|
|
7875
8221
|
});
|
|
8222
|
+
|
|
8223
|
+
|
|
8224
|
+
|
|
8225
|
+
|