@kenz1117/dsh-engram 0.4.7 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +16 -14
- package/README.md +22 -15
- package/lib/client.js +1 -2
- package/lib/client.js.map +1 -1
- package/lib/index.js +1019 -110
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -30,11 +30,11 @@ Zero configuration after install (stores and model cache default to `~/.dsh/engr
|
|
|
30
30
|
|
|
31
31
|
## Features
|
|
32
32
|
|
|
33
|
-
- **Cross-session memory**: a user memory profile is injected at session start (
|
|
34
|
-
- **Dual-scope stores**: `user.db` shared globally; `project-<
|
|
35
|
-
- **Hybrid retrieval**: FTS5 (unicode61 + Chinese 2-gram pre-tokenization) fused with local vectors (`Xenova/bge-small-zh-v1.5`, 512-dim, q8) via RRF, plus one-hop expansion over relation edges; the embedding model runs offline, and a failed download degrades to keyword-only retrieval with an explicit marker.
|
|
33
|
+
- **Cross-session memory**: a user memory profile is injected at session start (entry cap plus token budget, both configurable), so the agent naturally knows who you are and what you are building; tools recall facts across sessions.
|
|
34
|
+
- **Dual-scope stores**: `user.db` shared globally; `project-<hash>.db` isolated per git origin identity (falls back to a working-directory encoding without git; legacy stores migrate automatically) — personal preferences follow the person, project conventions follow the repo.
|
|
35
|
+
- **Hybrid retrieval**: FTS5 (unicode61 + Chinese 2-gram pre-tokenization) fused with local vectors (`Xenova/bge-small-zh-v1.5`, 512-dim, q8) via RRF, plus one-hop expansion over relation edges and a multiplicative recency/proof ranking boost; the embedding model runs offline, and a failed download degrades to keyword-only retrieval with an explicit marker.
|
|
36
36
|
- **Knowledge flywheel**: capture/save → contradiction candidates (high-similarity neighbors create `contradicts` edges on write, for the model/user to adjudicate) → hit reinforcement (confidence +0.05) → distillation (topic clusters merge into higher-level rules, supersedes chains, confidence inheritance) → decay (low-importance, long-unaccessed entries archive; restorable).
|
|
37
|
-
- **Automatic capture** (when `ingest` is enabled): each new turn's first step extracts candidate facts from the previous turn out of the session log, written with low confidence and deduplicated by embedding — memories accumulate without you saying "remember this".
|
|
37
|
+
- **Automatic capture** (when `ingest` is enabled): each new turn's first step extracts candidate facts from the previous turn out of the session log, and session end captures the final turn too (failures leave a pending key that the next session replays; capture is idempotent per session+turn), written with low confidence and deduplicated by embedding — memories accumulate without you saying "remember this".
|
|
38
38
|
- **Provenance audit**: every memory records its source session, turn, and event seq; `engram_review` traces the full source chain, supersede chain, contradictions, and operation log; all writes/edits/forgets/distills/decays land in the operation log table.
|
|
39
39
|
- **Web management panel**: a "Memory Library" tab in Settings — stat cards, filter by status/kind/content, inline detail and edit (through the supersede chain), forget/restore, Markdown/JSON export. UI copy is bilingual zh/en and follows the host language setting live.
|
|
40
40
|
- **Portable data**: `engram_export` exports Markdown / JSON files in one step.
|
|
@@ -63,15 +63,18 @@ Optional configuration (cordis.yml):
|
|
|
63
63
|
config:
|
|
64
64
|
dbDir: '~/.dsh/engram' # store and model-cache root directory
|
|
65
65
|
injectProfile: true # inject the user memory profile at session start
|
|
66
|
-
profileTopN: 8 # injection cap (1-64)
|
|
66
|
+
profileTopN: 8 # injection entry cap (1-64)
|
|
67
|
+
injectTokenBudget: 1024 # injection token budget (128-8192, estimated ceil(len/4); over-budget entries degrade to index lines)
|
|
67
68
|
modelCacheDir: '~/.dsh/engram/models' # embedding model cache directory
|
|
68
69
|
hfEndpoint: 'https://huggingface.co' # model download endpoint; set a mirror behind restricted networks
|
|
69
70
|
ingest: 'off' # automatic capture: off | light (user messages only, ≤2/turn) | eager (assistant messages too, ≤5/turn)
|
|
70
71
|
# provider and model must be given as a pair: aux-LLM route override for capture/distill (parsed from the session log by default)
|
|
71
72
|
# provider: 'deepseek'
|
|
72
73
|
# model: 'deepseek-v4-flash'
|
|
73
|
-
decayAfterDays: 30 # decay: last access older than this many days
|
|
74
|
+
decayAfterDays: 30 # decay: last access older than this many days (also the recency-boost decay window)
|
|
74
75
|
decayImportanceBelow: 0.3 # decay: and importance below this → archive (restorable)
|
|
76
|
+
rankRecencyWeight: 0.2 # retrieval recency boost weight (0-2, 0 disables)
|
|
77
|
+
rankProofWeight: 0.1 # retrieval hit-count boost weight (0-2, 0 disables)
|
|
75
78
|
```
|
|
76
79
|
|
|
77
80
|
## How It Works
|
|
@@ -82,15 +85,15 @@ The plugin has a host half (Node) and a browser half (React):
|
|
|
82
85
|
Session agent Host half (Node)
|
|
83
86
|
│ │
|
|
84
87
|
├─ every turn, first step ◀─────────────── ├─ user memory profile injection (plugin-source user snapshot)
|
|
85
|
-
├─ engram_save / search / review … ──────▶ ├─ SQLite dual stores (user.db / project-<
|
|
86
|
-
│ ├─ FTS5 keyword track + local vector track, RRF fusion
|
|
88
|
+
├─ engram_save / search / review … ──────▶ ├─ SQLite dual stores (user.db / project-<origin hash>.db)
|
|
89
|
+
│ ├─ FTS5 keyword track + local vector track, RRF fusion + ranking boost
|
|
87
90
|
├─ engram_distill ───────────────────────▶ ├─ aux-LLM distillation (cluster merge → supersedes chain)
|
|
88
|
-
│ └─ automatic capture: session log → candidate facts (ingest on)
|
|
91
|
+
│ └─ automatic capture: session log → candidate facts (incl. the final turn at session end; ingest on)
|
|
89
92
|
└─ Settings "Memory Library" tab ◀──────── ─── loopback API /api/engram/* (writes verify loopback Origin)
|
|
90
93
|
```
|
|
91
94
|
|
|
92
|
-
- **Dual-scope stores**: `user.db` shared globally; `project-<
|
|
93
|
-
- **Automatic capture** (when `ingest` is on): each new turn's first step extracts candidate facts from the previous turn out of the session log (the read source is the session log; aux-call request auditing goes to the plugin's own operation log — unknown events are never appended to the session log
|
|
95
|
+
- **Dual-scope stores**: `user.db` shared globally; `project-<hash>.db` named by the normalized git origin URL hash (`git@github.com:a/b.git` and `https://github.com/a/b` share one store; worktrees resolve through the pointer to the main repo's origin); without git or an origin it falls back to a working-directory encoding, and legacy cwd-named stores are renamed in place at startup (if both exist, nothing moves and a warning is logged).
|
|
96
|
+
- **Automatic capture** (when `ingest` is on): each new turn's first step extracts candidate facts from the previous turn out of the session log; session end (`session/disposed`) captures the final turn with a 5-second timeout, and on failure/timeout a pending key lands in the operation log for the next session's first step to replay; captured (session, turn) pairs are idempotent. The read source is the session log; aux-call request auditing goes to the plugin's own operation log — unknown events are never appended to the session log. Candidates are written with low confidence and deduplicated by embedding.
|
|
94
97
|
- **Provenance chain**: every memory records its source session, turn, and event seq, fully traceable via `engram_review`; the operation log table records every write/edit/forget/distill/decay.
|
|
95
98
|
- **Offline embeddings**: the model downloads once (q8, ~50MB; mirror endpoint configurable), then runs fully offline; on failure the plugin keeps working and retrieval degrades to keyword-only with an explicit marker.
|
|
96
99
|
- **UI localization**: the client half registers zh/en dictionaries through the host locale service and follows the host language setting live; data-level enums (status/kind) are mapped only in the display layer and stay English in storage.
|
|
@@ -114,11 +117,11 @@ pnpm bundle
|
|
|
114
117
|
|
|
115
118
|
#### What the model sees
|
|
116
119
|
|
|
117
|
-
Each turn's first step appends a plugin-source user snapshot: `User memory profile (dsh-engram, cross-session):` followed by the user-scope memory list (up to 8 by default; `injectProfile: false` disables). Tool results are plain text lines (with `id=`, scope/kind annotations, contradiction-candidate hints, and degradation notes). Automatic capture and distillation each make one aux-LLM call (billed independently of the main conversation path, with purpose attribution).
|
|
120
|
+
Each turn's first step appends a plugin-source user snapshot: `User memory profile (dsh-engram, cross-session):` followed by the user-scope memory list (up to 8 entries within a 1024-token budget by default; over-budget entries degrade to `#id` index lines; `injectProfile: false` disables). Tool results are plain text lines (with `id=`, scope/kind annotations, contradiction-candidate hints, and degradation notes). Automatic capture and distillation each make one aux-LLM call (billed independently of the main conversation path, with purpose attribution).
|
|
118
121
|
|
|
119
122
|
#### Token effect
|
|
120
123
|
|
|
121
|
-
Profile injection is a conditional fixed cost (
|
|
124
|
+
Profile injection is a conditional fixed cost (bounded by both the entry cap and the token budget); the tool schemas are a standing cost (9 narrow-parameter tools).
|
|
122
125
|
|
|
123
126
|
#### KV Cache effect
|
|
124
127
|
|
|
@@ -126,7 +129,6 @@ Profile text changes as the memory store changes — changes only land at turn b
|
|
|
126
129
|
|
|
127
130
|
## Known Limitations and Deferred Work
|
|
128
131
|
|
|
129
|
-
- **Last-turn blind spot in automatic capture** — capture is triggered by the next turn's first step, so a session's final turn is never captured; a session-end event hook is future work.
|
|
130
132
|
- **No LLM adjudication of contradiction candidates** — writes only report candidates by vector similarity (≥0.88) and create edges; semantic-contradiction confirmation is left to model/user adjudication and distillation.
|
|
131
133
|
- **Memories written during embedder degradation have no vectors** — memories written before the model is ready do not participate in the semantic track; after semantics come online run `pnpm backfill` once to backfill existing vectors (after `pnpm build` has warmed the model cache; `HF_ENDPOINT` configurable).
|
|
132
134
|
|
package/README.md
CHANGED
|
@@ -30,20 +30,24 @@ dsh plugin --profile web add @kenz1117/dsh-engram
|
|
|
30
30
|
|
|
31
31
|
## 特性
|
|
32
32
|
|
|
33
|
-
-
|
|
34
|
-
- **双层分库**:`user.db` 全局共享;`project-<
|
|
35
|
-
- **混合检索**:FTS5(unicode61 + 中文 2-gram 预切词)与本地向量(`Xenova/bge-small-zh-v1.5`,512 维,q8)RRF 融合 +
|
|
33
|
+
- **跨会话记忆**:会话开始注入用户画像摘要(条数 + token 预算双重上限,可配),Agent 天然"记得"你是谁、在做什么;工具检索跨会话召回历史事实。
|
|
34
|
+
- **双层分库**:`user.db` 全局共享;`project-<hash>.db` 按 git origin 标识隔离(无 git 时回退工作目录编码,旧库自动迁移)——个人偏好跟人走,项目约定跟仓库走。
|
|
35
|
+
- **混合检索**:FTS5(unicode61 + 中文 2-gram 预切词)与本地向量(`Xenova/bge-small-zh-v1.5`,512 维,q8)RRF 融合 + 关系边一跳扩展 + 新鲜度/命中次数乘性排序 boost;嵌入模型离线运行,下载失败自动降级纯关键词并显式标记。
|
|
36
36
|
- **知识飞轮**:摄取/保存 → 矛盾候选(写入时高相似近邻建 `contradicts` 边并报告,模型/用户裁决)→ 命中强化(confidence +0.05)→ 蒸馏(同主题簇合并为高层规律、supersedes 取代链、置信度继承)→ 衰减(低重要性且长期未访问归档,可恢复)。
|
|
37
|
-
- **自动摄取**(`ingest`
|
|
37
|
+
- **自动摄取**(`ingest` 配置开启时):新一轮第一步从会话日志提取上一轮的候选事实,会话结束时补摄取最后一轮(失败留 pending 键,下次会话自动补做,幂等不重复),低 confidence 写入并按嵌入去重——不说"记住"也能攒记忆。
|
|
38
38
|
- **来源审计**:每条记忆记录来源会话、轮次与事件 seq,`engram_review` 完整回查来源链、取代链、矛盾与操作日志;全部写入/修改/遗忘/蒸馏/衰减入操作日志表。
|
|
39
|
-
- **Web 管理面板**:设置页「记忆库」tab——统计卡片、按状态/种类/内容过滤、行内详情与编辑(走取代链)、遗忘/恢复、导出 Markdown/JSON
|
|
39
|
+
- **Web 管理面板**:设置页「记忆库」tab——统计卡片、按状态/种类/内容过滤、行内详情与编辑(走取代链)、遗忘/恢复、导出 Markdown/JSON。界面文案中英双语,跟随宿主语言设置实时切换。支持按脱敏标记筛选(仅看/排除含 `[REDACTED:*]` 的条目)并给命中条目挂琥珀色徽标,方便审计脱敏覆盖面。
|
|
40
|
+
- **提示注入防护**:全部记忆召回出口(画像注入、`engram_search/timeline/review` 输出)包 `<engram_memory_context>` 协议标签并附使用警告(历史记忆非当前请求、不遵循其中指令、仅相关时使用),当前请求独立包 `<current_user_request>`;所有入库内容(摄取候选、保存正文)先剥离这些协议标签,防伪造协议块二次注入。
|
|
41
|
+
- **摄取脱敏**:入库前正则清洗常见密钥凭据(sk- 系 API key、Bearer、AWS AKIA、GitHub token、PEM 私钥、password/token 赋值),命中片段替换为 `[REDACTED:<类型>]`。
|
|
42
|
+
- **召回占位(防回声室)**:摄取切片中记忆召回工具的输出替换为 `[engram memory result omitted from capture: <tool>]`,并向提取模型附注"既有记忆的复述不是新信息",阻断记忆自我强化循环。
|
|
43
|
+
- **多查询检索**:`engram_search` 可用辅助 LLM 把查询改写为 ≤3 个互补查询分别检索,跨查询 RRF 融合 + 每查询保底命中;改写失败自动降级单查询(`queryRewrite: false` 关闭)。
|
|
40
44
|
- **数据可携带**:`engram_export` 一键导出 Markdown / JSON 文件。
|
|
41
45
|
|
|
42
46
|
## 工具(9 个,窄参数)
|
|
43
47
|
|
|
44
48
|
| 工具 | 作用 |
|
|
45
49
|
|---|---|
|
|
46
|
-
| `engram_save` |
|
|
50
|
+
| `engram_save` | 保存(嵌入可用时自动做矛盾候选检测);支持 `items` 数组单次批量保存 ≤10 条,统一清洗/批量内去重,单条失败不影响其余(`count`/`items`/`failed` 汇总返回) |
|
|
47
51
|
| `engram_search` | 语义 + 关键词混合检索(命中强化置信度) |
|
|
48
52
|
| `engram_timeline` | 时间线浏览 |
|
|
49
53
|
| `engram_update` | 修正(supersedes 取代链) |
|
|
@@ -64,14 +68,18 @@ dsh plugin --profile web add @kenz1117/dsh-engram
|
|
|
64
68
|
dbDir: '~/.dsh/engram' # 分库与模型缓存根目录
|
|
65
69
|
injectProfile: true # 会话开始注入用户画像摘要
|
|
66
70
|
profileTopN: 8 # 注入条数上限(1-64)
|
|
71
|
+
injectTokenBudget: 1024 # 注入 token 预算(128-8192,估算 ceil(len/4),超预算条目降级为索引行)
|
|
67
72
|
modelCacheDir: '~/.dsh/engram/models' # 嵌入模型缓存目录
|
|
68
73
|
hfEndpoint: 'https://huggingface.co' # 模型下载端点,网络受限可配镜像
|
|
69
74
|
ingest: 'off' # 自动摄取:off | light(仅用户消息,每轮≤2条)| eager(含助手消息,每轮≤5条)
|
|
70
75
|
# provider 与 model 必须成对提供:摄取/蒸馏的辅助 LLM 路由覆盖(缺省从会话日志解析)
|
|
71
76
|
# provider: 'deepseek'
|
|
72
77
|
# model: 'deepseek-v4-flash'
|
|
73
|
-
decayAfterDays: 30 #
|
|
78
|
+
decayAfterDays: 30 # 衰减:最近访问超过该天数(同时是检索 recency boost 的衰减窗口)
|
|
74
79
|
decayImportanceBelow: 0.3 # 衰减:且 importance 低于该值 → 归档(可恢复)
|
|
80
|
+
rankRecencyWeight: 0.2 # 检索排序新鲜度因子权重(0-2,0 关闭)
|
|
81
|
+
rankProofWeight: 0.1 # 检索排序命中次数因子权重(0-2,0 关闭)
|
|
82
|
+
queryRewrite: true # engram_search 用辅助 LLM 改写 ≤3 个查询做 RRF 融合(失败自动降级单查询)
|
|
75
83
|
```
|
|
76
84
|
|
|
77
85
|
## 工作原理
|
|
@@ -82,15 +90,15 @@ dsh plugin --profile web add @kenz1117/dsh-engram
|
|
|
82
90
|
会话 Agent 宿主半(Node)
|
|
83
91
|
│ │
|
|
84
92
|
├─ 每轮第一步 ◀─────────────────────────── ├─ 用户画像快照注入(plugin 来源 user 快照)
|
|
85
|
-
├─ engram_save / search / review … ──────▶ ├─ SQLite 双库(user.db / project-<
|
|
86
|
-
│ ├─ FTS5 关键词道 + 本地向量道 RRF 融合
|
|
93
|
+
├─ engram_save / search / review … ──────▶ ├─ SQLite 双库(user.db / project-<origin hash>.db)
|
|
94
|
+
│ ├─ FTS5 关键词道 + 本地向量道 RRF 融合 + 排序 boost
|
|
87
95
|
├─ engram_distill ───────────────────────▶ ├─ 辅助 LLM 蒸馏(簇合并 → supersedes 链)
|
|
88
|
-
│ └─ 自动摄取:会话日志 →
|
|
96
|
+
│ └─ 自动摄取:会话日志 → 候选事实(含会话结束的末轮,ingest 开启时)
|
|
89
97
|
└─ 设置页「记忆库」tab ◀────────────────── ─── 回环 API /api/engram/*(写操作校验回环 Origin)
|
|
90
98
|
```
|
|
91
99
|
|
|
92
|
-
- **双层分库**:`user.db` 全局共享;`project-<
|
|
93
|
-
- **自动摄取**(`ingest`
|
|
100
|
+
- **双层分库**:`user.db` 全局共享;`project-<hash>.db` 按 git origin URL 归一化哈希命名(`git@github.com:a/b.git` 与 `https://github.com/a/b` 同库;worktree 沿指针解析到主仓库 origin);无 git 或无 origin 时回退工作目录编码命名,旧的 cwd 命名库在启动时自动 rename 迁移(新旧并存则不动并告警)。
|
|
101
|
+
- **自动摄取**(`ingest` 开启时):新一轮第一步从会话日志提取上一轮的候选事实;会话结束(session/disposed)补摄取最后一轮,5 秒超时,失败/超时把 pending 键写入操作日志,下次会话首步自动重放补做;已摄取的 (会话, 轮次) 幂等去重。读取源是会话日志;辅助调用的请求审计走插件自身操作日志,不向会话日志 append 未知事件。候选以低 confidence 写入并按嵌入去重。
|
|
94
102
|
- **来源链**:每条记忆记录来源会话、轮次与事件 seq,`engram_review` 可完整回查;操作日志表记录全部写入/修改/遗忘/蒸馏/衰减。
|
|
95
103
|
- **嵌入离线**:模型首次使用需联网下载(q8 约 50MB,端点可配镜像),此后完全离线;失败时插件照常工作,检索降级纯关键词并显式标记。
|
|
96
104
|
- **界面本地化**:client 半经宿主 locale 服务注册 zh/en 词典,跟随宿主语言设置实时切换;状态/种类等数据枚举仅在显示层映射,存储值保持英文。
|
|
@@ -114,11 +122,11 @@ pnpm bundle
|
|
|
114
122
|
|
|
115
123
|
#### What the model sees
|
|
116
124
|
|
|
117
|
-
会话每轮第一步追加一条 plugin 来源的 user 快照:`User memory profile (dsh-engram, cross-session):` 加用户级记忆列表(默认至多 8
|
|
125
|
+
会话每轮第一步追加一条 plugin 来源的 user 快照:`User memory profile (dsh-engram, cross-session):` 加用户级记忆列表(默认至多 8 条且整段不超过 1024 token 预算,超预算条目降级为 `#id` 索引行,`injectProfile: false` 关闭)。工具调用结果为纯文本行列表(含 `id=`、scope/kind 标注、矛盾候选提示与降级说明)。自动摄取与蒸馏各产生一次辅助 LLM 调用(独立于主对话计费路径,带 purpose 归因)。
|
|
118
126
|
|
|
119
127
|
#### Token effect
|
|
120
128
|
|
|
121
|
-
|
|
129
|
+
画像注入为条件性固定成本(受条数上限与 token 预算双重约束);工具 schema 为常驻成本(9 个窄参数工具)。
|
|
122
130
|
|
|
123
131
|
#### KV Cache effect
|
|
124
132
|
|
|
@@ -126,7 +134,6 @@ pnpm bundle
|
|
|
126
134
|
|
|
127
135
|
## Known Limitations and Deferred Work
|
|
128
136
|
|
|
129
|
-
- **自动摄取的最后一轮盲区** —— 摄取由下一轮的第一步触发,会话最后一轮不摄取;会话结束事件钩子是后续工作。
|
|
130
137
|
- **矛盾候选无 LLM 判定** —— 写入时仅按向量相似度(≥0.88)报告候选并建边,语义矛盾的确认留给模型/用户裁决与蒸馏。
|
|
131
138
|
- **嵌入器降级期间的记忆无向量** —— 模型未就绪时写入的记忆不参与语义道;语义上线后跑一次 `pnpm backfill` 补算存量向量(`pnpm build` 的模型缓存就绪后执行,可经 `HF_ENDPOINT` 配镜像)。
|
|
132
139
|
|
package/lib/client.js
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
window.__ModuleLoader__.load({id:`@kenz1117/dsh-engram`,factory:e=>{var t={exports:{}},n=t.exports;Object.defineProperty(n,Symbol.toStringTag,{value:`Module`});let r=e("react"),i=e("react/jsx-runtime"),a=`@kenz1117/dsh-engram/panel.module.css`;if(typeof document<`u`&&document.querySelector(`style[data-plugin-css=`+JSON.stringify(a)+`]`)===null){let e=document.createElement(`style`);e.dataset.plugin=`@kenz1117/dsh-engram`,e.dataset.pluginCss=a,e.textContent=`.bWwFiW_panel{max-width:980px}.bWwFiW_toolbar{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:14px;display:flex}.bWwFiW_scopeTabs{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:flex-end;gap:18px;margin-right:auto;display:flex}.bWwFiW_scopeOn,.bWwFiW_scopeOff{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;padding:7px 1px 9px;font-size:13px;line-height:20px;position:relative}.bWwFiW_scopeOn,.bWwFiW_scopeOff:hover{color:var(--dsw-alias-label-primary)}.bWwFiW_scopeOn:after{background:var(--dsw-alias-brand-primary);content:"";border-radius:2px 2px 0 0;height:2px;position:absolute;bottom:-1px;left:0;right:0}.bWwFiW_button{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;border-radius:8px;padding:5px 12px;font-size:13px}.bWwFiW_button:hover{border-color:var(--dsw-alias-brand-primary)}.bWwFiW_button:disabled{opacity:.45;cursor:default}.bWwFiW_primary{background:var(--dsw-alias-button-primary-fill);border-color:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.bWwFiW_cards{grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:14px;display:grid}.bWwFiW_card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);text-align:left;cursor:pointer;color:var(--dsw-alias-label-primary);font:inherit;border-radius:10px;padding:10px 14px}.bWwFiW_card b{font-size:20px;display:block}.bWwFiW_card span{color:var(--dsw-alias-label-secondary);font-size:12px}.bWwFiW_filters{flex-wrap:wrap;gap:8px;margin-bottom:14px;display:flex}.bWwFiW_input{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:6px 10px;font-size:13px}.bWwFiW_filters .bWwFiW_input{flex:1;min-width:160px}.bWwFiW_item{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;margin-bottom:10px;padding:12px 14px;transition:border-color .12s}.bWwFiW_itemSelected{border-color:var(--dsw-alias-brand-primary)}.bWwFiW_row1{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:6px;display:flex}.bWwFiW_badge{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-fill-tsp-secondary);border-radius:99px;padding:1px 8px;font-size:11px}.bWwFiW_active{color:var(--dsw-alias-state-success-primary);border-color:var(--dsw-alias-state-success-primary)}.bWwFiW_archived{color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-primary)}.bWwFiW_forgotten{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}.bWwFiW_content{white-space:pre-wrap;word-break:break-word;color:var(--dsw-alias-label-primary)}.bWwFiW_meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:14px;margin-top:6px;font-size:12px;display:flex}.bWwFiW_ops{gap:8px;margin-top:8px;display:flex}.bWwFiW_itemCheck{accent-color:var(--dsw-alias-brand-primary);cursor:pointer;flex:none;width:15px;height:15px;margin-top:2px}.bWwFiW_batchBar{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:12px;padding:8px 12px;display:flex}.bWwFiW_batchBar span{color:var(--dsw-alias-label-primary);margin-right:auto;font-size:13px}.bWwFiW_danger{background:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-label-primary-foreground)}.bWwFiW_pager{color:var(--dsw-alias-label-secondary);justify-content:center;align-items:center;gap:10px;margin-top:14px;display:flex}.bWwFiW_empty{text-align:center;color:var(--dsw-alias-label-secondary);padding:40px 0}.bWwFiW_expand{border-top:1px dashed var(--dsw-alias-border-l2);margin-top:10px;padding-top:10px}.bWwFiW_expandLoading{color:var(--dsw-alias-label-secondary);font-size:13px}.bWwFiW_editForm{flex-direction:column;display:flex}.bWwFiW_editForm .bWwFiW_input{box-sizing:border-box;width:100%}.bWwFiW_fieldLabel{color:var(--dsw-alias-label-secondary);margin:10px 0 4px;font-size:12px;display:block}.bWwFiW_modalFoot{justify-content:flex-end;gap:8px;margin-top:12px;display:flex}.bWwFiW_review{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);white-space:pre-wrap;max-height:320px;color:var(--dsw-alias-label-primary);border-radius:8px;margin:0;padding:10px;font-size:12px;overflow:auto}`,document.head.appendChild(e)}var o={cards:`bWwFiW_cards`,ops:`bWwFiW_ops`,scopeTabs:`bWwFiW_scopeTabs`,button:`bWwFiW_button`,modalFoot:`bWwFiW_modalFoot`,expandLoading:`bWwFiW_expandLoading`,editForm:`bWwFiW_editForm`,active:`bWwFiW_active`,expand:`bWwFiW_expand`,toolbar:`bWwFiW_toolbar`,scopeOn:`bWwFiW_scopeOn`,input:`bWwFiW_input`,meta:`bWwFiW_meta`,pager:`bWwFiW_pager`,fieldLabel:`bWwFiW_fieldLabel`,primary:`bWwFiW_primary`,itemCheck:`bWwFiW_itemCheck`,forgotten:`bWwFiW_forgotten`,panel:`bWwFiW_panel`,batchBar:`bWwFiW_batchBar`,empty:`bWwFiW_empty`,review:`bWwFiW_review`,card:`bWwFiW_card`,content:`bWwFiW_content`,item:`bWwFiW_item`,scopeOff:`bWwFiW_scopeOff`,row1:`bWwFiW_row1`,danger:`bWwFiW_danger`,filters:`bWwFiW_filters`,archived:`bWwFiW_archived`,itemSelected:`bWwFiW_itemSelected`,badge:`bWwFiW_badge`};let s=[`fact`,`preference`,`decision`,`episode`,`skill`],c={active:`statusActive`,archived:`statusArchived`,forgotten:`statusForgotten`},l={fact:`kindFact`,preference:`kindPreference`,decision:`kindDecision`,episode:`kindEpisode`,skill:`kindSkill`},u={user:`scopeUser`,project:`scopeProject`},d={write:`opWrite`,update:`opUpdate`,forget:`opForget`,restore:`opRestore`,decay:`opDecay`,superseded:`opSuperseded`,"ingest-request":`opIngestRequest`,"distill-request":`opDistillRequest`},f={supersededBy:`relSupersededBy`,supersedes:`relSupersedes`,contradicts:`relContradicts`,related:`relRelated`},p={kind:`labelKind`,scope:`labelScope`,status:`labelStatus`};function m(e,t){let n=l[t];return n===void 0?t:e(n)}function h(e,t){let n=d[t];return n===void 0?t:e(n)}function g(e,t,n){if(n===null)return``;if(t!==`write`&&t!==`update`&&t!==`superseded`)return` ${n}`;try{let t=JSON.parse(n),r=Object.entries(t);return r.length===0?` ${n}`:` `+r.map(([t,n])=>{let r=p[t];return r===void 0?`${t}=${String(n)}`:t===`kind`&&typeof n==`string`?`${e(r)}=${m(e,n)}`:t===`scope`&&(n===`user`||n===`project`)?`${e(r)}=${e(u[n])}`:`${e(r)}=${String(n)}`}).join(` · `)}catch{return` ${n}`}}function _(e,t){if(t.sourceSessionId===null)return e(`sourceExplicit`);let n=`${t.sourceSessionId.slice(0,16)}…`;return e(`sourceSession`,{id:t.sourceRound===null?n:`${n} ${e(`round`,{n:t.sourceRound})}`})}async function v(e,t){let n=await fetch(`/api/engram/${e}`,t),r=await n.json();if(!n.ok)throw Error(r.error??`HTTP ${String(n.status)}`);return r}function y(e){return new Date(e).toLocaleString(navigator.language||`zh-CN`,{hour12:!1})}function b({t:e,recordId:t,scope:n}){let[a,s]=(0,r.useState)(null);return(0,r.useEffect)(()=>{let r=!1;return v(`review?scope=${n}&id=${encodeURIComponent(t)}`).then(t=>{if(r)return;let n=t.record,i=[`${e(`labelContent`)}: ${n.content}`,`${e(`detailAttributes`)}: ${e(`labelKind`)}=${m(e,n.kind)}, ${e(`labelStatus`)}=${e(c[n.status])}, ${e(`importance`)}=${n.importance.toFixed(2)}, ${e(`confidence`)}=${n.confidence.toFixed(2)}, ${e(`accessCount`,{n:n.accessCount})}`,`${e(`detailSource`)}: ${_(e,n)}`];for(let n of[`supersededBy`,`supersedes`,`contradicts`,`related`]){let r=t[n],a=f[n];r!==void 0&&r.length>0&&i.push(`${a===void 0?n:e(a)}: ${r.join(`, `)}`)}let a=t.operations;a!==void 0&&a.length>0&&(i.push(`${e(`detailOperations`)}:`),a.forEach(t=>i.push(` ${y(t.at)} ${h(e,t.op)}${g(e,t.op,t.detail)}`))),s(i.join(`
|
|
2
|
-
`))}).catch(t=>{r||s(e(`loadFailed`,{msg:t.message}))}),()=>{r=!0}},[t,n,e]),a===null?(0,i.jsx)(`div`,{className:o.expandLoading,children:e(`loading`)}):(0,i.jsx)(`pre`,{className:o.review,children:a})}function x({t:e,record:t,onClose:n,onSaved:a}){let[c,l]=(0,r.useState)(t.content),[u,d]=(0,r.useState)(t.kind),[f,p]=(0,r.useState)(t.importance);return(0,i.jsxs)(`div`,{className:o.editForm,children:[(0,i.jsx)(`label`,{className:o.fieldLabel,children:e(`labelContent`)}),(0,i.jsx)(`textarea`,{className:o.input,rows:3,value:c,onChange:e=>l(e.target.value)}),(0,i.jsx)(`label`,{className:o.fieldLabel,children:e(`labelKind`)}),(0,i.jsx)(`select`,{className:o.input,value:u,onChange:e=>d(e.target.value),children:s.map(t=>(0,i.jsx)(`option`,{value:t,children:m(e,t)},t))}),(0,i.jsx)(`label`,{className:o.fieldLabel,children:`${e(`importance`)} ${f.toFixed(2)}`}),(0,i.jsx)(`input`,{type:`range`,min:0,max:1,step:.05,value:f,onChange:e=>p(Number(e.target.value))}),(0,i.jsxs)(`div`,{className:o.modalFoot,children:[(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:n,children:e(`cancel`)}),(0,i.jsx)(`button`,{type:`button`,className:`${o.button} ${o.primary}`,onClick:()=>{v(`update`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({id:t.id,scope:t.scope,content:c,kind:u,importance:f})}).then(a).catch(e=>{alert(e.message)})},children:e(`saveWithHint`)})]})]})}function S({t:e}){let[t,n]=(0,r.useState)(`user`),[a,l]=(0,r.useState)(`all`),[d,f]=(0,r.useState)(`all`),[p,h]=(0,r.useState)(``),[g,S]=(0,r.useState)(0),[C,w]=(0,r.useState)(null),[T,E]=(0,r.useState)(null),[D,O]=(0,r.useState)(null),[k,A]=(0,r.useState)(null),[j,M]=(0,r.useState)(0),[N,P]=(0,r.useState)(new Set),[F,I]=(0,r.useState)(!1),L=(0,r.useCallback)(()=>{M(e=>e+1)},[]);(0,r.useEffect)(()=>{let e=!1,n=new URLSearchParams({scope:t,status:a,kind:d,limit:`20`,offset:String(g)});return p!==``&&n.set(`q`,p),v(`list?${n.toString()}`).then(t=>{e||(w(t),O(null))}).catch(t=>{e||O(t.message)}),v(`stats`).then(t=>{e||E(t.parts)}).catch(()=>{}),()=>{e=!0}},[t,a,d,p,g,j]);let R=(e,t)=>{v(e,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({id:t.id,scope:t.scope})}).then(L).catch(e=>{alert(e.message)})},z=(e,t)=>{A(n=>n!==null&&n.kind===e&&n.record.id===t.id?null:{kind:e,record:t})},B=C?.records??[],V=B.filter(e=>N.has(e.id)),H=V.filter(e=>e.status===`active`).length,U=V.length-H,W=B.length>0&&B.every(e=>N.has(e.id)),G=e=>{P(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},K=()=>{P(new Set),I(!1)},q=()=>{P(e=>{let t=new Set(e);return W?B.forEach(e=>t.delete(e.id)):B.forEach(e=>t.add(e.id)),t})},J=e=>{let t=e===`forget`?V.filter(e=>e.status===`active`):V.filter(e=>e.status!==`active`);t.length!==0&&Promise.allSettled(t.map(t=>v(e,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({id:t.id,scope:t.scope})}))).then(()=>{K(),L()})},Y=()=>{if(F){J(`forget`);return}I(!0)},X=Math.floor(g/20)+1,Z=C===null?1:Math.max(1,Math.ceil(C.total/20));return(0,i.jsxs)(`div`,{className:o.panel,children:[(0,i.jsxs)(`div`,{className:o.toolbar,children:[(0,i.jsx)(`div`,{className:o.scopeTabs,children:[`user`,`project`].map(r=>(0,i.jsx)(`button`,{type:`button`,className:t===r?o.scopeOn:o.scopeOff,onClick:()=>{n(r),S(0),A(null),K()},children:e(u[r])},r))}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:L,children:e(`refresh`)}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{location.href=`/api/engram/export?scope=${t}&format=markdown`},children:e(`exportMd`)}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{location.href=`/api/engram/export?scope=${t}&format=json`},children:e(`exportJson`)})]}),(0,i.jsx)(`div`,{className:o.cards,children:(T??[]).map(r=>(0,i.jsxs)(`button`,{type:`button`,className:o.card,style:r.scope===t?{borderColor:`var(--dsw-alias-brand-primary)`}:void 0,onClick:()=>{n(r.scope),S(0),K()},children:[(0,i.jsx)(`b`,{children:r.stats.total}),(0,i.jsx)(`span`,{children:`${e(u[r.scope])} · ${e(`cardActive`,{n:r.stats.active})} · ${e(`signalRatio`,{n:Math.round(r.stats.signalRatio*100)})}`})]},r.scope))}),(0,i.jsxs)(`div`,{className:o.filters,children:[(0,i.jsxs)(`select`,{className:o.input,value:a,onChange:e=>{l(e.target.value),S(0),A(null),K()},children:[(0,i.jsx)(`option`,{value:`all`,children:e(`allStatuses`)}),(0,i.jsx)(`option`,{value:`active`,children:e(`statusActive`)}),(0,i.jsx)(`option`,{value:`archived`,children:e(`statusArchived`)}),(0,i.jsx)(`option`,{value:`forgotten`,children:e(`statusForgotten`)})]}),(0,i.jsxs)(`select`,{className:o.input,value:d,onChange:e=>{f(e.target.value),S(0),A(null),K()},children:[(0,i.jsx)(`option`,{value:`all`,children:e(`allKinds`)}),s.map(t=>(0,i.jsx)(`option`,{value:t,children:m(e,t)},t))]}),(0,i.jsx)(`input`,{className:o.input,placeholder:e(`searchPlaceholder`),value:p,onChange:e=>{h(e.target.value.trim()),S(0),A(null),K()}})]}),N.size>0&&(0,i.jsxs)(`div`,{className:o.batchBar,children:[(0,i.jsx)(`span`,{children:e(`selectedCount`,{n:N.size})}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:q,children:e(W?`deselectAll`:`selectAll`)}),(0,i.jsx)(`button`,{type:`button`,className:o.button,disabled:U===0,onClick:()=>J(`restore`),children:e(`batchRestore`,{n:U})}),(0,i.jsx)(`button`,{type:`button`,className:F?`${o.button} ${o.danger}`:o.button,disabled:H===0,onClick:Y,children:e(F?`batchForgetConfirm`:`batchForget`,{n:H})}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:K,children:e(`clearSelection`)})]}),D!==null&&(0,i.jsx)(`div`,{className:o.empty,children:e(`loadFailed`,{msg:D})}),D===null&&C!==null&&C.records.length===0&&(0,i.jsx)(`div`,{className:o.empty,children:e(`empty`)}),(C?.records??[]).map(t=>(0,i.jsxs)(`div`,{className:N.has(t.id)?`${o.item} ${o.itemSelected}`:o.item,children:[(0,i.jsxs)(`div`,{className:o.row1,children:[(0,i.jsx)(`input`,{type:`checkbox`,className:o.itemCheck,checked:N.has(t.id),"aria-label":t.content.slice(0,24),onChange:()=>G(t.id)}),(0,i.jsx)(`span`,{className:`${o.badge} ${o[t.status]}`,children:e(c[t.status])}),(0,i.jsx)(`span`,{className:o.badge,children:m(e,t.kind)}),(0,i.jsx)(`span`,{className:o.badge,children:e(u[t.scope])})]}),(0,i.jsx)(`div`,{className:o.content,children:t.content}),(0,i.jsxs)(`div`,{className:o.meta,children:[(0,i.jsx)(`span`,{children:`${e(`importance`)} ${t.importance.toFixed(2)}`}),(0,i.jsx)(`span`,{children:`${e(`confidence`)} ${t.confidence.toFixed(2)}`}),(0,i.jsx)(`span`,{children:e(`accessCount`,{n:t.accessCount})}),(0,i.jsx)(`span`,{children:_(e,t)}),(0,i.jsx)(`span`,{children:y(t.createdAt)})]}),(0,i.jsxs)(`div`,{className:o.ops,children:[(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{z(`review`,t)},children:e(`detail`)}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{z(`edit`,t)},children:e(`edit`)}),t.status===`active`?(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{R(`forget`,t)},children:e(`forget`)}):(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{R(`restore`,t)},children:e(`restore`)})]}),k!==null&&k.record.id===t.id&&(0,i.jsx)(`div`,{className:o.expand,children:k.kind===`review`?(0,i.jsx)(b,{t:e,recordId:t.id,scope:t.scope}):(0,i.jsx)(x,{t:e,record:t,onClose:()=>{A(null)},onSaved:()=>{A(null),L()}})})]},t.id)),(0,i.jsxs)(`div`,{className:o.pager,children:[(0,i.jsx)(`button`,{type:`button`,className:o.button,disabled:g===0,onClick:()=>{S(Math.max(0,g-20)),A(null),K()},children:e(`prevPage`)}),(0,i.jsx)(`span`,{children:e(`pagerInfo`,{page:X,pages:Z,total:C?.total??0})}),(0,i.jsx)(`button`,{type:`button`,className:o.button,disabled:C===null||g+20>=C.total,onClick:()=>{S(g+20),A(null),K()},children:e(`nextPage`)})]})]})}let C=`engram`,w={nav:`记忆库`,scopeUser:`用户级`,scopeProject:`项目级`,refresh:`刷新`,exportMd:`导出 MD`,exportJson:`导出 JSON`,allStatuses:`全部状态`,allKinds:`全部种类`,searchPlaceholder:`按内容搜索…`,statusActive:`生效中`,statusArchived:`已归档`,statusForgotten:`已遗忘`,kindFact:`事实`,kindPreference:`偏好`,kindDecision:`决策`,kindEpisode:`事件`,kindSkill:`技能`,labelContent:`内容`,labelKind:`种类`,labelStatus:`状态`,labelScope:`作用域`,importance:`重要性`,confidence:`置信`,accessCount:`访问 {n} 次`,sourceExplicit:`显式保存`,sourceSession:`来源 {id}`,round:`第 {n} 轮`,cardActive:`生效 {n}`,signalRatio:`信噪比 {n}%`,detail:`详情`,edit:`编辑`,forget:`遗忘`,restore:`恢复`,empty:`没有符合条件的记忆`,loadFailed:`加载失败:{msg}`,loading:`加载中…`,pagerInfo:`第 {page} / {pages} 页 · 共 {total} 条`,prevPage:`上一页`,nextPage:`下一页`,cancel:`取消`,saveWithHint:`保存(旧条目归档)`,detailAttributes:`属性`,detailSource:`来源`,detailRelations:`关系`,detailOperations:`最近操作`,relSupersededBy:`被取代`,relSupersedes:`取代`,relContradicts:`矛盾`,relRelated:`关联`,opWrite:`写入`,opUpdate:`更新`,opForget:`遗忘`,opRestore:`恢复`,opDecay:`衰减归档`,opSuperseded:`被取代`,opIngestRequest:`摄取请求`,opDistillRequest:`蒸馏请求`,selectAll:`全选本页`,deselectAll:`取消全选`,selectedCount:`已选 {n} 条`,batchForget:`批量遗忘({n})`,batchForgetConfirm:`确认遗忘 {n} 条?`,batchRestore:`批量恢复({n})`,clearSelection:`取消选择`},T={nav:`Memory Library`,scopeUser:`User`,scopeProject:`Project`,refresh:`Refresh`,exportMd:`Export MD`,exportJson:`Export JSON`,allStatuses:`All statuses`,allKinds:`All kinds`,searchPlaceholder:`Search content…`,statusActive:`active`,statusArchived:`archived`,statusForgotten:`forgotten`,kindFact:`fact`,kindPreference:`preference`,kindDecision:`decision`,kindEpisode:`episode`,kindSkill:`skill`,labelContent:`Content`,labelKind:`Kind`,labelStatus:`Status`,labelScope:`Scope`,importance:`Importance`,confidence:`Confidence`,accessCount:`{n}× accessed`,sourceExplicit:`Explicitly saved`,sourceSession:`Source {id}`,round:`round {n}`,cardActive:`{n} active`,signalRatio:`signal {n}%`,detail:`Details`,edit:`Edit`,forget:`Forget`,restore:`Restore`,empty:`No matching memories`,loadFailed:`Failed to load: {msg}`,loading:`Loading…`,pagerInfo:`Page {page} / {pages} · {total} records`,prevPage:`Prev`,nextPage:`Next`,cancel:`Cancel`,saveWithHint:`Save (archives old entry)`,detailAttributes:`Attributes`,detailSource:`Source`,detailRelations:`Relations`,detailOperations:`Recent operations`,relSupersededBy:`Superseded by`,relSupersedes:`Supersedes`,relContradicts:`Contradicts`,relRelated:`Related`,opWrite:`write`,opUpdate:`update`,opForget:`forget`,opRestore:`restore`,opDecay:`decay`,opSuperseded:`superseded`,opIngestRequest:`ingest-request`,opDistillRequest:`distill-request`,selectAll:`Select all on page`,deselectAll:`Deselect all`,selectedCount:`{n} selected`,batchForget:`Forget selected ({n})`,batchForgetConfirm:`Forget {n} entries?`,batchRestore:`Restore selected ({n})`,clearSelection:`Clear selection`},E=[`slots`,`locale`];function D(e){e.effect(()=>e.locale.register(C,{zh:w,en:T}),`dsh-engram: dictionaries`);let t=e.locale.bind(C);e.slots.inject(`settings.section`,()=>e.slots.register({name:`settings.section`,id:`engram`,order:20,label:()=>t(`nav`),locale:C},S))}return n.apply=D,n.inject=E,t.exports}});
|
|
1
|
+
window.__ModuleLoader__.load({id:`@kenz1117/dsh-engram`,factory:e=>{var t={exports:{}},n=t.exports;Object.defineProperty(n,Symbol.toStringTag,{value:`Module`});let r=e("react"),i=e("react/jsx-runtime"),a=`@kenz1117/dsh-engram/panel.module.css`;if(typeof document<`u`&&document.querySelector(`style[data-plugin-css=`+JSON.stringify(a)+`]`)===null){let e=document.createElement(`style`);e.dataset.plugin=`@kenz1117/dsh-engram`,e.dataset.pluginCss=a,e.textContent=`.bWwFiW_panel{max-width:920px;animation:.24s both bWwFiW_rise}@keyframes bWwFiW_rise{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}.bWwFiW_toolbar{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:16px;display:flex}.bWwFiW_scopeTabs{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:flex-end;gap:20px;margin-right:auto;display:flex}.bWwFiW_scopeOn,.bWwFiW_scopeOff{color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;padding:7px 1px 9px;font-size:13px;line-height:20px;transition:color .14s;position:relative}.bWwFiW_scopeOn,.bWwFiW_scopeOff:hover{color:var(--dsw-alias-label-primary)}.bWwFiW_scopeOn:after{background:var(--dsw-alias-brand-primary);content:"";border-radius:2px 2px 0 0;height:2px;position:absolute;bottom:-1px;left:0;right:0}.bWwFiW_button{color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;background:0 0;border-radius:8px;padding:5px 12px;font-size:13px;line-height:18px;transition:color .14s,border-color .14s,background .14s,transform .1s}.bWwFiW_button:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 7%, transparent)}.bWwFiW_button:active{transform:scale(.97)}.bWwFiW_button:disabled{opacity:.4;cursor:default;border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);background:0 0;transform:none}.bWwFiW_primary{background:var(--dsw-alias-button-primary-fill);border-color:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.bWwFiW_primary:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);filter:brightness(1.08)}.bWwFiW_cards{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:16px;display:grid}.bWwFiW_card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);text-align:left;cursor:pointer;color:var(--dsw-alias-label-primary);font:inherit;border-radius:12px;padding:14px 16px 12px;transition:border-color .16s,transform .16s,box-shadow .16s;position:relative;overflow:hidden}.bWwFiW_card:hover{border-color:var(--dsw-alias-border-l1,var(--dsw-alias-brand-primary));box-shadow:0 6px 18px -8px color-mix(in srgb, var(--dsw-alias-label-primary) 22%, transparent);transform:translateY(-1px)}.bWwFiW_card b{font-variant-numeric:tabular-nums;letter-spacing:-.01em;font-size:26px;line-height:1.15;display:block}.bWwFiW_card span{color:var(--dsw-alias-label-secondary);font-size:12px}.bWwFiW_cardOn{border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 55%, transparent);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 5%, var(--dsw-alias-bg-layer-1))}.bWwFiW_cardOn:before{background:var(--dsw-alias-brand-primary);content:"";width:3px;position:absolute;top:0;bottom:0;left:0}.bWwFiW_cardMeter{background:color-mix(in srgb, var(--dsw-alias-label-tertiary) 18%, transparent);border-radius:2px;height:3px;margin-top:10px;display:block;overflow:hidden}.bWwFiW_cardMeter i{background:var(--dsw-alias-brand-primary);border-radius:2px;height:100%;transition:width .3s;display:block}.bWwFiW_filters{flex-wrap:wrap;gap:8px;margin-bottom:16px;display:flex}.bWwFiW_input{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:6px 10px;font-size:13px;transition:border-color .14s,box-shadow .14s}.bWwFiW_input:focus-visible{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 18%, transparent);outline:none}.bWwFiW_filters .bWwFiW_input{flex:1;min-width:160px}.bWwFiW_filters select.bWwFiW_input{flex:0 auto;min-width:120px}.bWwFiW_item{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;margin-bottom:10px;padding:13px 16px 11px;transition:border-color .16s,background .16s,box-shadow .16s;animation:.26s both bWwFiW_rise;position:relative}.bWwFiW_item:hover{border-color:color-mix(in srgb, var(--dsw-alias-brand-primary) 40%, var(--dsw-alias-border-l2));box-shadow:0 8px 22px -12px color-mix(in srgb, var(--dsw-alias-label-primary) 26%, transparent)}.bWwFiW_itemSelected{border-color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 4%, var(--dsw-alias-bg-layer-1))}.bWwFiW_row1{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:7px;display:flex}.bWwFiW_itemCheck{accent-color:var(--dsw-alias-brand-primary);cursor:pointer;flex:none;width:15px;height:15px}.bWwFiW_statusWrap{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:12px;display:inline-flex}.bWwFiW_statusWrap:before{background:var(--dsw-alias-label-tertiary);content:"";border-radius:50%;width:7px;height:7px}.bWwFiW_statusWrap.bWwFiW_active{color:var(--dsw-alias-state-success-primary)}.bWwFiW_statusWrap.bWwFiW_active:before{background:var(--dsw-alias-state-success-primary);box-shadow:0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-success-primary) 16%, transparent)}.bWwFiW_statusWrap.bWwFiW_archived{color:var(--dsw-alias-state-warn-primary)}.bWwFiW_statusWrap.bWwFiW_archived:before{background:var(--dsw-alias-state-warn-primary)}.bWwFiW_statusWrap.bWwFiW_forgotten{color:var(--dsw-alias-state-error-primary)}.bWwFiW_statusWrap.bWwFiW_forgotten:before{background:var(--dsw-alias-state-error-primary)}.bWwFiW_chip{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 10%, transparent);border-radius:6px;padding:0 8px;font-size:11px;line-height:18px}.bWwFiW_redactedChip{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent)}.bWwFiW_scopeTag{color:var(--dsw-alias-label-tertiary);font-size:11px}.bWwFiW_timeTag{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;margin-left:auto;font-size:12px}.bWwFiW_content{white-space:pre-wrap;word-break:break-word;color:var(--dsw-alias-label-primary);-webkit-line-clamp:4;-webkit-box-orient:vertical;font-size:14px;line-height:1.7;display:-webkit-box;overflow:hidden}.bWwFiW_meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:16px;margin-top:8px;font-size:12px;display:flex}.bWwFiW_meterField{font-variant-numeric:tabular-nums;align-items:center;gap:7px;display:inline-flex}.bWwFiW_meter{background:color-mix(in srgb, var(--dsw-alias-label-tertiary) 18%, transparent);border-radius:2px;width:52px;height:4px;overflow:hidden}.bWwFiW_meter i{background:var(--dsw-alias-brand-primary);border-radius:2px;height:100%;display:block}.bWwFiW_meter.bWwFiW_conf i{background:var(--dsw-alias-state-success-primary)}.bWwFiW_ops{opacity:.55;gap:6px;margin-top:9px;transition:opacity .16s;display:flex}.bWwFiW_item:hover .bWwFiW_ops,.bWwFiW_item:focus-within .bWwFiW_ops{opacity:1}.bWwFiW_ops .bWwFiW_button{background:var(--dsw-alias-fill-tsp-secondary);border-color:#0000;padding:3px 10px;font-size:12px}.bWwFiW_ops .bWwFiW_button:hover{border-color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 8%, transparent)}.bWwFiW_batchBar{z-index:5;background:color-mix(in srgb, var(--dsw-alias-bg-layer-1) 82%, transparent);backdrop-filter:blur(10px);border:1px solid color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));box-shadow:0 10px 28px -12px color-mix(in srgb, var(--dsw-alias-label-primary) 30%, transparent);border-radius:12px;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:12px;padding:9px 14px;animation:.2s both bWwFiW_barUp;display:flex;position:sticky;bottom:12px}@keyframes bWwFiW_barUp{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}.bWwFiW_batchBar>span{color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;margin-right:auto;font-size:13px}.bWwFiW_danger{background:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-label-primary-foreground)}.bWwFiW_danger:hover{background:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-label-primary-foreground);filter:brightness(1.08)}.bWwFiW_skeleton{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:12px;margin-bottom:10px;padding:13px 16px}.bWwFiW_skeleton i{background:linear-gradient(90deg, color-mix(in srgb, var(--dsw-alias-label-tertiary) 12%, transparent) 25%, color-mix(in srgb, var(--dsw-alias-label-tertiary) 24%, transparent) 50%, color-mix(in srgb, var(--dsw-alias-label-tertiary) 12%, transparent) 75%);background-size:200% 100%;border-radius:6px;height:12px;margin:8px 0;animation:1.3s linear infinite bWwFiW_shimmer;display:block}.bWwFiW_skeleton i:first-child{width:34%;margin-top:2px}.bWwFiW_skeleton i:last-child{width:62%}@keyframes bWwFiW_shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.bWwFiW_empty{text-align:center;color:var(--dsw-alias-label-secondary);padding:48px 0 52px}.bWwFiW_empty:before{border:1px solid var(--dsw-alias-border-l2);background:linear-gradient(135deg, color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent), transparent 70%);content:"";border-radius:10px;width:34px;height:34px;margin:0 auto 12px;display:block;transform:rotate(45deg)scale(.8)}.bWwFiW_expand{border-top:1px dashed var(--dsw-alias-border-l2);margin-top:10px;padding-top:12px;animation:.18s both bWwFiW_rise}.bWwFiW_expandLoading{color:var(--dsw-alias-label-secondary);font-size:13px}.bWwFiW_attrGrid{grid-template-columns:max-content 1fr;align-items:center;gap:7px 16px;font-size:13px;display:grid}.bWwFiW_attrGrid>dt{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}.bWwFiW_attrGrid>dd{color:var(--dsw-alias-label-primary);flex-wrap:wrap;align-items:center;gap:8px;margin:0;display:flex}.bWwFiW_relBlock{margin-top:12px}.bWwFiW_relBlock>h5{color:var(--dsw-alias-label-tertiary);margin:0 0 6px;font-size:12px;font-weight:500}.bWwFiW_relChips{flex-wrap:wrap;gap:6px;display:flex}.bWwFiW_relChip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-base);border-radius:6px;padding:2px 8px;font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:11px}.bWwFiW_timeline{margin:12px 0 0;padding:0 0 0 14px;list-style:none;position:relative}.bWwFiW_timeline:before{background:var(--dsw-alias-border-l2);content:"";width:1px;position:absolute;top:6px;bottom:6px;left:3px}.bWwFiW_timeline li{color:var(--dsw-alias-label-secondary);padding:5px 0 5px 12px;font-size:12px;line-height:1.6;position:relative}.bWwFiW_timeline li:before{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-brand-primary);content:"";border-radius:50%;width:7px;height:7px;position:absolute;top:10px;left:-14px}.bWwFiW_timeline time{font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-tertiary);margin-right:8px}.bWwFiW_timeline b{color:var(--dsw-alias-label-primary);font-weight:500}.bWwFiW_editForm{flex-direction:column;display:flex}.bWwFiW_editForm .bWwFiW_input{box-sizing:border-box;width:100%}.bWwFiW_fieldLabel{color:var(--dsw-alias-label-secondary);margin:12px 0 5px;font-size:12px;display:block}.bWwFiW_editForm input[type=range]{accent-color:var(--dsw-alias-brand-primary)}.bWwFiW_modalFoot{justify-content:flex-end;gap:8px;margin-top:14px;display:flex}.bWwFiW_pager{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;justify-content:center;align-items:center;gap:12px;margin-top:18px;font-size:13px;display:flex}`,document.head.appendChild(e)}var o={empty:`bWwFiW_empty`,scopeTabs:`bWwFiW_scopeTabs`,item:`bWwFiW_item`,cardMeter:`bWwFiW_cardMeter`,modalFoot:`bWwFiW_modalFoot`,itemCheck:`bWwFiW_itemCheck`,button:`bWwFiW_button`,shimmer:`bWwFiW_shimmer`,expand:`bWwFiW_expand`,relChip:`bWwFiW_relChip`,scopeOn:`bWwFiW_scopeOn`,active:`bWwFiW_active`,barUp:`bWwFiW_barUp`,relChips:`bWwFiW_relChips`,editForm:`bWwFiW_editForm`,attrGrid:`bWwFiW_attrGrid`,input:`bWwFiW_input`,expandLoading:`bWwFiW_expandLoading`,forgotten:`bWwFiW_forgotten`,danger:`bWwFiW_danger`,relBlock:`bWwFiW_relBlock`,content:`bWwFiW_content`,rise:`bWwFiW_rise`,primary:`bWwFiW_primary`,panel:`bWwFiW_panel`,ops:`bWwFiW_ops`,batchBar:`bWwFiW_batchBar`,statusWrap:`bWwFiW_statusWrap`,conf:`bWwFiW_conf`,cardOn:`bWwFiW_cardOn`,timeline:`bWwFiW_timeline`,fieldLabel:`bWwFiW_fieldLabel`,card:`bWwFiW_card`,scopeTag:`bWwFiW_scopeTag`,meta:`bWwFiW_meta`,cards:`bWwFiW_cards`,pager:`bWwFiW_pager`,filters:`bWwFiW_filters`,meter:`bWwFiW_meter`,scopeOff:`bWwFiW_scopeOff`,row1:`bWwFiW_row1`,timeTag:`bWwFiW_timeTag`,redactedChip:`bWwFiW_redactedChip`,toolbar:`bWwFiW_toolbar`,itemSelected:`bWwFiW_itemSelected`,meterField:`bWwFiW_meterField`,skeleton:`bWwFiW_skeleton`,chip:`bWwFiW_chip`,archived:`bWwFiW_archived`};let s=[`fact`,`preference`,`decision`,`episode`,`skill`],c={active:`statusActive`,archived:`statusArchived`,forgotten:`statusForgotten`},l={fact:`kindFact`,preference:`kindPreference`,decision:`kindDecision`,episode:`kindEpisode`,skill:`kindSkill`},u={user:`scopeUser`,project:`scopeProject`},d={write:`opWrite`,update:`opUpdate`,forget:`opForget`,restore:`opRestore`,decay:`opDecay`,superseded:`opSuperseded`,"ingest-request":`opIngestRequest`,"distill-request":`opDistillRequest`},f={supersededBy:`relSupersededBy`,supersedes:`relSupersedes`,contradicts:`relContradicts`,related:`relRelated`},p={kind:`labelKind`,scope:`labelScope`,status:`labelStatus`};function m(e,t){let n=l[t];return n===void 0?t:e(n)}function h(e,t){let n=d[t];return n===void 0?t:e(n)}function g(e,t,n){if(n===null)return``;if(t!==`write`&&t!==`update`&&t!==`superseded`)return` ${n}`;try{let t=JSON.parse(n),r=Object.entries(t);return r.length===0?` ${n}`:` `+r.map(([t,n])=>{let r=p[t];return r===void 0?`${t}=${String(n)}`:t===`kind`&&typeof n==`string`?`${e(r)}=${m(e,n)}`:t===`scope`&&(n===`user`||n===`project`)?`${e(r)}=${e(u[n])}`:`${e(r)}=${String(n)}`}).join(` · `)}catch{return` ${n}`}}function _(e,t){if(t.sourceSessionId===null)return e(`sourceExplicit`);let n=`${t.sourceSessionId.slice(0,16)}…`;return e(`sourceSession`,{id:t.sourceRound===null?n:`${n} ${e(`round`,{n:t.sourceRound})}`})}async function v(e,t){let n=await fetch(`/api/engram/${e}`,t),r=await n.json();if(!n.ok)throw Error(r.error??`HTTP ${String(n.status)}`);return r}function y(e){return new Date(e).toLocaleString(navigator.language||`zh-CN`,{hour12:!1})}function b(e,t){let n=Math.max(0,Date.now()-t),r=Math.floor(n/6e4);if(r<1)return e(`timeJustNow`);if(r<60)return e(`timeMinutesAgo`,{n:r});let i=Math.floor(r/60);return i<48?e(`timeHoursAgo`,{n:i}):e(`timeDaysAgo`,{n:Math.floor(i/24)})}function x({value:e,conf:t}){return(0,i.jsx)(`span`,{className:t===!0?`${o.meter} ${o.conf}`:o.meter,children:(0,i.jsx)(`i`,{style:{width:`${String(Math.round(Math.min(1,Math.max(0,e))*100))}%`}})})}function S({ids:e}){return(0,i.jsx)(`div`,{className:o.relChips,children:e.map(e=>(0,i.jsx)(`span`,{className:o.relChip,title:e,children:e.slice(0,18)},e))})}function C({t:e,recordId:t,scope:n}){let[a,s]=(0,r.useState)(null),[l,u]=(0,r.useState)(null);if((0,r.useEffect)(()=>{let e=!1;return v(`review?scope=${n}&id=${encodeURIComponent(t)}`).then(t=>{e||s(t)}).catch(t=>{e||u(t.message)}),()=>{e=!0}},[t,n,e]),l!==null)return(0,i.jsx)(`div`,{className:o.expandLoading,children:e(`loadFailed`,{msg:l})});if(a===null)return(0,i.jsx)(`div`,{className:o.expandLoading,children:e(`loading`)});let{record:d}=a,p=[`supersededBy`,`supersedes`,`contradicts`,`related`].map(e=>({key:e,label:f[e],ids:a[e]??[]})).filter(e=>e.ids.length>0),b=a.operations??[];return(0,i.jsxs)(`div`,{children:[(0,i.jsxs)(`dl`,{className:o.attrGrid,children:[(0,i.jsx)(`dt`,{children:e(`labelContent`)}),(0,i.jsx)(`dd`,{children:d.content}),(0,i.jsx)(`dt`,{children:e(`labelKind`)}),(0,i.jsx)(`dd`,{children:(0,i.jsx)(`span`,{className:o.chip,children:m(e,d.kind)})}),(0,i.jsx)(`dt`,{children:e(`labelStatus`)}),(0,i.jsx)(`dd`,{children:(0,i.jsx)(`span`,{className:`${o.statusWrap} ${o[d.status]}`,children:e(c[d.status])})}),(0,i.jsx)(`dt`,{children:e(`importance`)}),(0,i.jsxs)(`dd`,{children:[d.importance.toFixed(2),(0,i.jsx)(x,{value:d.importance})]}),(0,i.jsx)(`dt`,{children:e(`confidence`)}),(0,i.jsxs)(`dd`,{children:[d.confidence.toFixed(2),(0,i.jsx)(x,{value:d.confidence,conf:!0})]}),(0,i.jsx)(`dt`,{children:e(`detailSource`)}),(0,i.jsx)(`dd`,{children:_(e,d)}),(0,i.jsx)(`dt`,{children:e(`labelCreated`)}),(0,i.jsx)(`dd`,{children:y(d.createdAt)})]}),p.map(({key:t,label:n,ids:r})=>(0,i.jsxs)(`div`,{className:o.relBlock,children:[(0,i.jsx)(`h5`,{children:n===void 0?t:e(n)}),(0,i.jsx)(S,{ids:r})]},t)),b.length>0&&(0,i.jsxs)(`div`,{className:o.relBlock,children:[(0,i.jsx)(`h5`,{children:e(`detailOperations`)}),(0,i.jsx)(`ul`,{className:o.timeline,children:b.map((t,n)=>(0,i.jsxs)(`li`,{children:[(0,i.jsx)(`time`,{children:y(t.at)}),(0,i.jsx)(`b`,{children:h(e,t.op)}),g(e,t.op,t.detail)]},n))})]})]})}function w({t:e,record:t,onClose:n,onSaved:a}){let[c,l]=(0,r.useState)(t.content),[u,d]=(0,r.useState)(t.kind),[f,p]=(0,r.useState)(t.importance);return(0,i.jsxs)(`div`,{className:o.editForm,children:[(0,i.jsx)(`label`,{className:o.fieldLabel,children:e(`labelContent`)}),(0,i.jsx)(`textarea`,{className:o.input,rows:3,value:c,onChange:e=>l(e.target.value)}),(0,i.jsx)(`label`,{className:o.fieldLabel,children:e(`labelKind`)}),(0,i.jsx)(`select`,{className:o.input,value:u,onChange:e=>d(e.target.value),children:s.map(t=>(0,i.jsx)(`option`,{value:t,children:m(e,t)},t))}),(0,i.jsx)(`label`,{className:o.fieldLabel,children:`${e(`importance`)} ${f.toFixed(2)}`}),(0,i.jsx)(`input`,{type:`range`,min:0,max:1,step:.05,value:f,onChange:e=>p(Number(e.target.value))}),(0,i.jsxs)(`div`,{className:o.modalFoot,children:[(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:n,children:e(`cancel`)}),(0,i.jsx)(`button`,{type:`button`,className:`${o.button} ${o.primary}`,onClick:()=>{v(`update`,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({id:t.id,scope:t.scope,content:c,kind:u,importance:f})}).then(a).catch(e=>{alert(e.message)})},children:e(`saveWithHint`)})]})]})}function T({t:e}){let[t,n]=(0,r.useState)(`user`),[a,l]=(0,r.useState)(`all`),[d,f]=(0,r.useState)(`all`),[p,h]=(0,r.useState)(`all`),[g,S]=(0,r.useState)(``),[T,E]=(0,r.useState)(0),[D,O]=(0,r.useState)(null),[k,A]=(0,r.useState)(null),[j,M]=(0,r.useState)(null),[N,P]=(0,r.useState)(null),[F,I]=(0,r.useState)(0),[L,R]=(0,r.useState)(new Set),[z,B]=(0,r.useState)(!1),V=(0,r.useCallback)(()=>{I(e=>e+1)},[]);(0,r.useEffect)(()=>{let e=!1,n=new URLSearchParams({scope:t,status:a,kind:d,redacted:p,limit:`20`,offset:String(T)});return g!==``&&n.set(`q`,g),v(`list?${n.toString()}`).then(t=>{e||(O(t),M(null))}).catch(t=>{e||M(t.message)}),v(`stats`).then(t=>{e||A(t.parts)}).catch(()=>{}),()=>{e=!0}},[t,a,d,p,g,T,F]);let H=(e,t)=>{v(e,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({id:t.id,scope:t.scope})}).then(V).catch(e=>{alert(e.message)})},U=(e,t)=>{P(n=>n!==null&&n.kind===e&&n.record.id===t.id?null:{kind:e,record:t})},W=D?.records??[],G=W.filter(e=>L.has(e.id)),K=G.filter(e=>e.status===`active`).length,q=G.length-K,J=W.length>0&&W.every(e=>L.has(e.id)),Y=e=>{R(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},X=()=>{R(new Set),B(!1)},Z=()=>{R(e=>{let t=new Set(e);return J?W.forEach(e=>t.delete(e.id)):W.forEach(e=>t.add(e.id)),t})},Q=e=>{let t=e===`forget`?G.filter(e=>e.status===`active`):G.filter(e=>e.status!==`active`);t.length!==0&&Promise.allSettled(t.map(t=>v(e,{method:`POST`,headers:{"content-type":`application/json`},body:JSON.stringify({id:t.id,scope:t.scope})}))).then(()=>{X(),V()})},$=()=>{if(z){Q(`forget`);return}B(!0)},ee=Math.floor(T/20)+1,te=D===null?1:Math.max(1,Math.ceil(D.total/20));return(0,i.jsxs)(`div`,{className:o.panel,children:[(0,i.jsxs)(`div`,{className:o.toolbar,children:[(0,i.jsx)(`div`,{className:o.scopeTabs,children:[`user`,`project`].map(r=>(0,i.jsx)(`button`,{type:`button`,className:t===r?o.scopeOn:o.scopeOff,onClick:()=>{n(r),E(0),P(null),X()},children:e(u[r])},r))}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:V,children:e(`refresh`)}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{location.href=`/api/engram/export?scope=${t}&format=markdown`},children:e(`exportMd`)}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{location.href=`/api/engram/export?scope=${t}&format=json`},children:e(`exportJson`)})]}),(0,i.jsx)(`div`,{className:o.cards,children:(k??[]).map(r=>(0,i.jsxs)(`button`,{type:`button`,className:r.scope===t?`${o.card} ${o.cardOn}`:o.card,onClick:()=>{n(r.scope),E(0),X()},children:[(0,i.jsx)(`b`,{children:r.stats.total}),(0,i.jsx)(`span`,{children:`${e(u[r.scope])} · ${e(`cardActive`,{n:r.stats.active})} · ${e(`signalRatio`,{n:Math.round(r.stats.signalRatio*100)})}`}),(0,i.jsx)(`span`,{className:o.cardMeter,children:(0,i.jsx)(`i`,{style:{width:`${String(Math.round(r.stats.signalRatio*100))}%`}})})]},r.scope))}),(0,i.jsxs)(`div`,{className:o.filters,children:[(0,i.jsxs)(`select`,{className:o.input,value:a,onChange:e=>{l(e.target.value),E(0),P(null),X()},children:[(0,i.jsx)(`option`,{value:`all`,children:e(`allStatuses`)}),(0,i.jsx)(`option`,{value:`active`,children:e(`statusActive`)}),(0,i.jsx)(`option`,{value:`archived`,children:e(`statusArchived`)}),(0,i.jsx)(`option`,{value:`forgotten`,children:e(`statusForgotten`)})]}),(0,i.jsxs)(`select`,{className:o.input,value:d,onChange:e=>{f(e.target.value),E(0),P(null),X()},children:[(0,i.jsx)(`option`,{value:`all`,children:e(`allKinds`)}),s.map(t=>(0,i.jsx)(`option`,{value:t,children:m(e,t)},t))]}),(0,i.jsxs)(`select`,{className:o.input,value:p,onChange:e=>{h(e.target.value),E(0),P(null),X()},children:[(0,i.jsx)(`option`,{value:`all`,children:e(`redactedAll`)}),(0,i.jsx)(`option`,{value:`true`,children:e(`redactedOnly`)}),(0,i.jsx)(`option`,{value:`false`,children:e(`redactedNone`)})]}),(0,i.jsx)(`input`,{className:o.input,placeholder:e(`searchPlaceholder`),value:g,onChange:e=>{S(e.target.value.trim()),E(0),P(null),X()}})]}),L.size>0&&(0,i.jsxs)(`div`,{className:o.batchBar,children:[(0,i.jsx)(`span`,{children:e(`selectedCount`,{n:L.size})}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:Z,children:e(J?`deselectAll`:`selectAll`)}),(0,i.jsx)(`button`,{type:`button`,className:o.button,disabled:q===0,onClick:()=>Q(`restore`),children:e(`batchRestore`,{n:q})}),(0,i.jsx)(`button`,{type:`button`,className:z?`${o.button} ${o.danger}`:o.button,disabled:K===0,onClick:$,children:e(z?`batchForgetConfirm`:`batchForget`,{n:K})}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:X,children:e(`clearSelection`)})]}),j!==null&&(0,i.jsx)(`div`,{className:o.empty,children:e(`loadFailed`,{msg:j})}),j===null&&D===null&&[0,1,2].map(e=>(0,i.jsxs)(`div`,{className:o.skeleton,children:[(0,i.jsx)(`i`,{}),(0,i.jsx)(`i`,{}),(0,i.jsx)(`i`,{})]},e)),j===null&&D!==null&&D.records.length===0&&(0,i.jsx)(`div`,{className:o.empty,children:e(`empty`)}),(D?.records??[]).map((t,n)=>(0,i.jsxs)(`div`,{className:L.has(t.id)?`${o.item} ${o.itemSelected}`:o.item,style:{animationDelay:`${String(Math.min(n,12)*28)}ms`},children:[(0,i.jsxs)(`div`,{className:o.row1,children:[(0,i.jsx)(`input`,{type:`checkbox`,className:o.itemCheck,checked:L.has(t.id),"aria-label":t.content.slice(0,24),onChange:()=>Y(t.id)}),(0,i.jsx)(`span`,{className:`${o.statusWrap} ${o[t.status]}`,children:e(c[t.status])}),(0,i.jsx)(`span`,{className:o.chip,children:m(e,t.kind)}),t.content.includes(`[REDACTED:`)&&(0,i.jsx)(`span`,{className:`${o.chip} ${o.redactedChip}`,children:e(`tagRedacted`)}),(0,i.jsx)(`span`,{className:o.scopeTag,children:e(u[t.scope])}),(0,i.jsx)(`span`,{className:o.timeTag,title:y(t.createdAt),children:b(e,t.createdAt)})]}),(0,i.jsx)(`div`,{className:o.content,children:t.content}),(0,i.jsxs)(`div`,{className:o.meta,children:[(0,i.jsxs)(`span`,{className:o.meterField,children:[e(`importance`),(0,i.jsx)(x,{value:t.importance}),t.importance.toFixed(2)]}),(0,i.jsxs)(`span`,{className:o.meterField,children:[e(`confidence`),(0,i.jsx)(x,{value:t.confidence,conf:!0}),t.confidence.toFixed(2)]}),(0,i.jsx)(`span`,{children:e(`accessCount`,{n:t.accessCount})}),(0,i.jsx)(`span`,{children:_(e,t)})]}),(0,i.jsxs)(`div`,{className:o.ops,children:[(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{U(`review`,t)},children:e(`detail`)}),(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{U(`edit`,t)},children:e(`edit`)}),t.status===`active`?(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{H(`forget`,t)},children:e(`forget`)}):(0,i.jsx)(`button`,{type:`button`,className:o.button,onClick:()=>{H(`restore`,t)},children:e(`restore`)})]}),N!==null&&N.record.id===t.id&&(0,i.jsx)(`div`,{className:o.expand,children:N.kind===`review`?(0,i.jsx)(C,{t:e,recordId:t.id,scope:t.scope}):(0,i.jsx)(w,{t:e,record:t,onClose:()=>{P(null)},onSaved:()=>{P(null),V()}})})]},t.id)),(0,i.jsxs)(`div`,{className:o.pager,children:[(0,i.jsx)(`button`,{type:`button`,className:o.button,disabled:T===0,onClick:()=>{E(Math.max(0,T-20)),P(null),X()},children:e(`prevPage`)}),(0,i.jsx)(`span`,{children:e(`pagerInfo`,{page:ee,pages:te,total:D?.total??0})}),(0,i.jsx)(`button`,{type:`button`,className:o.button,disabled:D===null||T+20>=D.total,onClick:()=>{E(T+20),P(null),X()},children:e(`nextPage`)})]})]})}let E=`engram`,D={nav:`记忆库`,scopeUser:`用户级`,scopeProject:`项目级`,refresh:`刷新`,exportMd:`导出 MD`,exportJson:`导出 JSON`,allStatuses:`全部状态`,allKinds:`全部种类`,redactedAll:`全部脱敏`,redactedOnly:`含脱敏标记`,redactedNone:`无脱敏标记`,tagRedacted:`已脱敏`,searchPlaceholder:`按内容搜索…`,statusActive:`生效中`,statusArchived:`已归档`,statusForgotten:`已遗忘`,kindFact:`事实`,kindPreference:`偏好`,kindDecision:`决策`,kindEpisode:`事件`,kindSkill:`技能`,labelContent:`内容`,labelKind:`种类`,labelStatus:`状态`,labelScope:`作用域`,importance:`重要性`,confidence:`置信`,accessCount:`访问 {n} 次`,labelCreated:`创建时间`,timeJustNow:`刚刚`,timeMinutesAgo:`{n} 分钟前`,timeHoursAgo:`{n} 小时前`,timeDaysAgo:`{n} 天前`,sourceExplicit:`显式保存`,sourceSession:`来源 {id}`,round:`第 {n} 轮`,cardActive:`生效 {n}`,signalRatio:`信噪比 {n}%`,detail:`详情`,edit:`编辑`,forget:`遗忘`,restore:`恢复`,empty:`没有符合条件的记忆`,loadFailed:`加载失败:{msg}`,loading:`加载中…`,pagerInfo:`第 {page} / {pages} 页 · 共 {total} 条`,prevPage:`上一页`,nextPage:`下一页`,cancel:`取消`,saveWithHint:`保存(旧条目归档)`,detailAttributes:`属性`,detailSource:`来源`,detailRelations:`关系`,detailOperations:`最近操作`,relSupersededBy:`被取代`,relSupersedes:`取代`,relContradicts:`矛盾`,relRelated:`关联`,opWrite:`写入`,opUpdate:`更新`,opForget:`遗忘`,opRestore:`恢复`,opDecay:`衰减归档`,opSuperseded:`被取代`,opIngestRequest:`摄取请求`,opDistillRequest:`蒸馏请求`,selectAll:`全选本页`,deselectAll:`取消全选`,selectedCount:`已选 {n} 条`,batchForget:`批量遗忘({n})`,batchForgetConfirm:`确认遗忘 {n} 条?`,batchRestore:`批量恢复({n})`,clearSelection:`取消选择`},O={nav:`Memory Library`,scopeUser:`User`,scopeProject:`Project`,refresh:`Refresh`,exportMd:`Export MD`,exportJson:`Export JSON`,allStatuses:`All statuses`,allKinds:`All kinds`,redactedAll:`All redaction`,redactedOnly:`Redacted only`,redactedNone:`Not redacted`,tagRedacted:`redacted`,searchPlaceholder:`Search content…`,statusActive:`active`,statusArchived:`archived`,statusForgotten:`forgotten`,kindFact:`fact`,kindPreference:`preference`,kindDecision:`decision`,kindEpisode:`episode`,kindSkill:`skill`,labelContent:`Content`,labelKind:`Kind`,labelStatus:`Status`,labelScope:`Scope`,importance:`Importance`,confidence:`Confidence`,accessCount:`{n}× accessed`,labelCreated:`Created`,timeJustNow:`just now`,timeMinutesAgo:`{n} min ago`,timeHoursAgo:`{n} hr ago`,timeDaysAgo:`{n} d ago`,sourceExplicit:`Explicitly saved`,sourceSession:`Source {id}`,round:`round {n}`,cardActive:`{n} active`,signalRatio:`signal {n}%`,detail:`Details`,edit:`Edit`,forget:`Forget`,restore:`Restore`,empty:`No matching memories`,loadFailed:`Failed to load: {msg}`,loading:`Loading…`,pagerInfo:`Page {page} / {pages} · {total} records`,prevPage:`Prev`,nextPage:`Next`,cancel:`Cancel`,saveWithHint:`Save (archives old entry)`,detailAttributes:`Attributes`,detailSource:`Source`,detailRelations:`Relations`,detailOperations:`Recent operations`,relSupersededBy:`Superseded by`,relSupersedes:`Supersedes`,relContradicts:`Contradicts`,relRelated:`Related`,opWrite:`write`,opUpdate:`update`,opForget:`forget`,opRestore:`restore`,opDecay:`decay`,opSuperseded:`superseded`,opIngestRequest:`ingest-request`,opDistillRequest:`distill-request`,selectAll:`Select all on page`,deselectAll:`Deselect all`,selectedCount:`{n} selected`,batchForget:`Forget selected ({n})`,batchForgetConfirm:`Forget {n} entries?`,batchRestore:`Restore selected ({n})`,clearSelection:`Clear selection`},k=[`slots`,`locale`];function A(e){e.effect(()=>e.locale.register(E,{zh:D,en:O}),`dsh-engram: dictionaries`);let t=e.locale.bind(E);e.slots.inject(`settings.section`,()=>e.slots.register({name:`settings.section`,id:`engram`,order:20,label:()=>t(`nav`),locale:E},T))}return n.apply=A,n.inject=k,t.exports}});
|
|
3
2
|
//# sourceMappingURL=client.js.map
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","styles","useCallback"],"sources":["../src/client/EngramPanel.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 记忆库设置面板:统计卡片、过滤列表、批量操作、编辑(取代链)、遗忘/恢复、导出。\n * 数据经回环 API(/api/engram/*)读写;详情与编辑为条目下方行内展开\n * (不嵌套弹窗);文案全部走宿主 locale 词典(zh/en),语言切换自动\n * 重渲染;数据层英文枚举(status/kind/op)只在显示层映射;内容节点\n * 一律 DOM/JSX 构建(防 XSS)。\n * @module @kenz1117/dsh-engram/client/EngramPanel\n */\n\nimport { useCallback, useEffect, useState } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport styles from './panel.module.css'\nimport { NS, type EngramKey } from './locales.ts'\n\n/** 列表/详情用的记忆视图(宿主 /api/engram/list 的行结构)。 */\ninterface MemoryRow {\n readonly id: string\n readonly scope: 'user' | 'project'\n readonly kind: string\n readonly content: string\n readonly importance: number\n readonly confidence: number\n readonly status: 'active' | 'archived' | 'forgotten'\n readonly createdAt: number\n readonly accessCount: number\n readonly sourceSessionId: string | null\n readonly sourceRound: number | null\n}\n\ninterface ListResult {\n readonly records: MemoryRow[]\n readonly total: number\n}\n\ninterface StatsPart {\n readonly scope: 'user' | 'project'\n readonly stats: {\n readonly total: number\n readonly active: number\n readonly archived: number\n readonly forgotten: number\n readonly signalRatio: number\n }\n}\n\n/** 面板内部传递的翻译函数(渲染器按注册的 locale 声明合成的 t 的窄化签名)。 */\ntype T = (key: EngramKey, params?: Record<string, unknown>) => string\n\n/** kind 数据值(存储层英文枚举,编辑表单下拉的 value 保持英文)。 */\nconst KINDS = ['fact', 'preference', 'decision', 'episode', 'skill'] as const\nconst PAGE_SIZE = 20\n\n/** 数据层枚举 → 词典键映射(显示层本地化,存储值不变)。 */\nconst STATUS_KEY: Record<MemoryRow['status'], EngramKey> = {\n active: 'statusActive',\n archived: 'statusArchived',\n forgotten: 'statusForgotten',\n}\nconst KIND_KEY: Record<string, EngramKey> = {\n fact: 'kindFact',\n preference: 'kindPreference',\n decision: 'kindDecision',\n episode: 'kindEpisode',\n skill: 'kindSkill',\n}\nconst SCOPE_KEY: Record<MemoryRow['scope'], EngramKey> = {\n user: 'scopeUser',\n project: 'scopeProject',\n}\nconst OP_KEY: Record<string, EngramKey> = {\n write: 'opWrite',\n update: 'opUpdate',\n forget: 'opForget',\n restore: 'opRestore',\n decay: 'opDecay',\n superseded: 'opSuperseded',\n 'ingest-request': 'opIngestRequest',\n 'distill-request': 'opDistillRequest',\n}\nconst REL_KEY: Record<string, EngramKey> = {\n supersededBy: 'relSupersededBy',\n supersedes: 'relSupersedes',\n contradicts: 'relContradicts',\n related: 'relRelated',\n}\n/** 操作日志 detail JSON 的已知键 → 词典键(write/update/superseded 的小对象)。 */\nconst DETAIL_KEY: Record<string, EngramKey> = {\n kind: 'labelKind',\n scope: 'labelScope',\n status: 'labelStatus',\n}\n\n/** kind 数据值 → 本地化标签(未知值回退原文)。 */\nfunction kindLabel(t: T, kind: string): string {\n const key = KIND_KEY[kind]\n return key === undefined ? kind : t(key)\n}\n\n/** op 数据值 → 本地化标签(未知值回退原文)。 */\nfunction opLabel(t: T, op: string): string {\n const key = OP_KEY[op]\n return key === undefined ? op : t(key)\n}\n\n/** 操作日志 detail 的本地化:write/update/superseded 的小对象转中文键值;摄取/蒸馏请求的完整 JSON 保持原样(审计诚实性优先)。 */\nfunction formatOpDetail(t: T, op: string, detail: string | null): string {\n if (detail === null) return ''\n if (op !== 'write' && op !== 'update' && op !== 'superseded') return ` ${detail}`\n try {\n const parsed = JSON.parse(detail) as Record<string, unknown>\n const entries = Object.entries(parsed)\n if (entries.length === 0) return ` ${detail}`\n return ' ' + entries.map(([key, value]) => {\n const label = DETAIL_KEY[key]\n if (label === undefined) return `${key}=${String(value)}`\n if (key === 'kind' && typeof value === 'string') return `${t(label)}=${kindLabel(t, value)}`\n if (key === 'scope' && (value === 'user' || value === 'project')) return `${t(label)}=${t(SCOPE_KEY[value])}`\n return `${t(label)}=${String(value)}`\n }).join(' · ')\n } catch {\n return ` ${detail}`\n }\n}\n\n/** 来源会话展示片段:截短的会话 id + 可选轮次。 */\nfunction sourceLabel(t: T, record: Pick<MemoryRow, 'sourceSessionId' | 'sourceRound'>): string {\n if (record.sourceSessionId === null) return t('sourceExplicit')\n const id = `${record.sourceSessionId.slice(0, 16)}…`\n const withRound = record.sourceRound === null\n ? id\n : `${id} ${t('round', { n: record.sourceRound })}`\n return t('sourceSession', { id: withRound })\n}\n\nasync function api<T>(path: string, init?: RequestInit): Promise<T> {\n const response = await fetch(`/api/engram/${path}`, init)\n const body = (await response.json()) as T & { error?: string }\n if (!response.ok) throw new Error(body.error ?? `HTTP ${String(response.status)}`)\n return body\n}\n\nfunction fmtTime(ms: number): string {\n // 时间格式跟随浏览器环境语言(宿主界面语言通常与浏览器一致)。\n return new Date(ms).toLocaleString(navigator.language || 'zh-CN', { hour12: false })\n}\n\n/** 行内审计区:来源链、关系与最近操作日志(review API)。 */\nfunction ReviewBody({ t, recordId, scope }: {\n t: T\n recordId: string\n scope: 'user' | 'project'\n}): React.ReactElement {\n const [text, setText] = useState<string | null>(null)\n useEffect(() => {\n let cancelled = false\n api<Record<string, unknown>>(`review?scope=${scope}&id=${encodeURIComponent(recordId)}`)\n .then((view) => {\n if (cancelled) return\n const record = view.record as MemoryRow\n const lines = [\n `${t('labelContent')}: ${record.content}`,\n `${t('detailAttributes')}: ${t('labelKind')}=${kindLabel(t, record.kind)}, ${t('labelStatus')}=${t(STATUS_KEY[record.status])}, ${t('importance')}=${record.importance.toFixed(2)}, ${t('confidence')}=${record.confidence.toFixed(2)}, ${t('accessCount', { n: record.accessCount })}`,\n `${t('detailSource')}: ${sourceLabel(t, record)}`,\n ]\n for (const key of ['supersededBy', 'supersedes', 'contradicts', 'related'] as const) {\n const ids = view[key] as string[] | undefined\n const relKey = REL_KEY[key]\n if (ids !== undefined && ids.length > 0) lines.push(`${relKey === undefined ? key : t(relKey)}: ${ids.join(', ')}`)\n }\n const operations = view.operations as { at: number; op: string; detail: string | null }[] | undefined\n if (operations !== undefined && operations.length > 0) {\n lines.push(`${t('detailOperations')}:`)\n operations.forEach(op => lines.push(` ${fmtTime(op.at)} ${opLabel(t, op.op)}${formatOpDetail(t, op.op, op.detail)}`))\n }\n setText(lines.join('\\n'))\n })\n .catch((error: Error) => { if (!cancelled) setText(t('loadFailed', { msg: error.message })) })\n return () => { cancelled = true }\n }, [recordId, scope, t])\n if (text === null) return <div className={styles.expandLoading}>{t('loading')}</div>\n return <pre className={styles.review}>{text}</pre>\n}\n\n/** 行内编辑表单:内容/种类/重要性 → POST update(旧条目归档,取代链保留)。 */\nfunction EditForm({ t, record, onClose, onSaved }: {\n t: T\n record: MemoryRow\n onClose: () => void\n onSaved: () => void\n}): React.ReactElement {\n const [content, setContent] = useState(record.content)\n const [kind, setKind] = useState(record.kind)\n const [importance, setImportance] = useState(record.importance)\n const save = (): void => {\n api('update', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: record.id, scope: record.scope, content, kind, importance }),\n })\n .then(onSaved)\n .catch((error: Error) => { alert(error.message) })\n }\n return (\n <div className={styles.editForm}>\n <label className={styles.fieldLabel}>{t('labelContent')}</label>\n <textarea className={styles.input} rows={3} value={content} onChange={event => setContent(event.target.value)} />\n <label className={styles.fieldLabel}>{t('labelKind')}</label>\n {/* option 的 value 保持数据层英文枚举,仅显示文本本地化。 */}\n <select className={styles.input} value={kind} onChange={event => setKind(event.target.value)}>\n {KINDS.map(option => <option key={option} value={option}>{kindLabel(t, option)}</option>)}\n </select>\n <label className={styles.fieldLabel}>{`${t('importance')} ${importance.toFixed(2)}`}</label>\n <input type=\"range\" min={0} max={1} step={0.05} value={importance}\n onChange={event => setImportance(Number(event.target.value))} />\n <div className={styles.modalFoot}>\n <button type=\"button\" className={styles.button} onClick={onClose}>{t('cancel')}</button>\n <button type=\"button\" className={`${styles.button} ${styles.primary}`} onClick={save}>{t('saveWithHint')}</button>\n </div>\n </div>\n )\n}\n\n/** 设置页「记忆库」section 主组件(t 由渲染器按 locale: NS 声明合成)。 */\nexport function EngramSection({ t }: PropsLocale<typeof NS>): React.ReactElement {\n const [scope, setScope] = useState<'user' | 'project'>('user')\n const [status, setStatus] = useState('all')\n const [kind, setKind] = useState('all')\n const [q, setQ] = useState('')\n const [offset, setOffset] = useState(0)\n const [list, setList] = useState<ListResult | null>(null)\n const [stats, setStats] = useState<StatsPart[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n /** 行内展开态:互斥的唯一展开条目(review 或 edit)。 */\n const [expanded, setExpanded] = useState<{ kind: 'review' | 'edit'; record: MemoryRow } | null>(null)\n const [reloadTick, setReloadTick] = useState(0)\n /** 批量选择:条目 id 集合(scope/过滤/翻页/搜索变更时清空)。 */\n const [selected, setSelected] = useState<ReadonlySet<string>>(new Set())\n /** 批量遗忘的两段式确认(3 秒未确认自动复位)。 */\n const [confirmForget, setConfirmForget] = useState(false)\n\n const reload = useCallback((): void => { setReloadTick(tick => tick + 1) }, [])\n\n useEffect(() => {\n let cancelled = false\n const qs = new URLSearchParams({\n scope, status, kind, limit: String(PAGE_SIZE), offset: String(offset),\n })\n if (q !== '') qs.set('q', q)\n api<ListResult>(`list?${qs.toString()}`)\n .then((data) => { if (!cancelled) { setList(data); setError(null) } })\n .catch((loadError: Error) => { if (!cancelled) setError(loadError.message) })\n api<{ parts: StatsPart[] }>('stats')\n .then((data) => { if (!cancelled) setStats(data.parts) })\n .catch(() => { /* 统计失败不影响列表 */ })\n return () => { cancelled = true }\n }, [scope, status, kind, q, offset, reloadTick])\n\n const act = (route: string, record: MemoryRow): void => {\n api(route, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: record.id, scope: record.scope }),\n })\n .then(reload)\n .catch((actError: Error) => { alert(actError.message) })\n }\n\n const toggle = (expandKind: 'review' | 'edit', record: MemoryRow): void => {\n setExpanded(current => (current !== null && current.kind === expandKind && current.record.id === record.id)\n ? null\n : { kind: expandKind, record })\n }\n\n const pageRecords = list?.records ?? []\n const selectedRecords = pageRecords.filter(record => selected.has(record.id))\n const forgetable = selectedRecords.filter(record => record.status === 'active').length\n const restorable = selectedRecords.length - forgetable\n const allSelected = pageRecords.length > 0 && pageRecords.every(record => selected.has(record.id))\n\n const toggleSelect = (id: string): void => {\n setSelected(current => {\n const next = new Set(current)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n const clearSelection = (): void => { setSelected(new Set()); setConfirmForget(false) }\n\n const toggleAllPage = (): void => {\n setSelected(current => {\n const next = new Set(current)\n if (allSelected) pageRecords.forEach(record => next.delete(record.id))\n else pageRecords.forEach(record => next.add(record.id))\n return next\n })\n }\n\n /** 批量执行:逐条调单条 API(回环毫秒级),单条失败不阻塞其余;完成即刷新并清空选择。 */\n const runBatch = (mode: 'forget' | 'restore'): void => {\n const targets = mode === 'forget'\n ? selectedRecords.filter(record => record.status === 'active')\n : selectedRecords.filter(record => record.status !== 'active')\n if (targets.length === 0) return\n void Promise.allSettled(targets.map(target => api(mode, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: target.id, scope: target.scope }),\n }))).then(() => { clearSelection(); reload() })\n }\n\n const armBatchForget = (): void => {\n // 确认态保持到用户点击确认执行或清空选择,不做自动复位(3 秒窗口曾导致执行落空)。\n if (confirmForget) { runBatch('forget'); return }\n setConfirmForget(true)\n }\n\n const page = Math.floor(offset / PAGE_SIZE) + 1\n const pages = list === null ? 1 : Math.max(1, Math.ceil(list.total / PAGE_SIZE))\n\n return (\n <div className={styles.panel}>\n <div className={styles.toolbar}>\n <div className={styles.scopeTabs}>\n {(['user', 'project'] as const).map(option => (\n <button key={option} type=\"button\"\n className={scope === option ? styles.scopeOn : styles.scopeOff}\n onClick={() => { setScope(option); setOffset(0); setExpanded(null); clearSelection() }}>\n {t(SCOPE_KEY[option])}\n </button>\n ))}\n </div>\n <button type=\"button\" className={styles.button} onClick={reload}>{t('refresh')}</button>\n <button type=\"button\" className={styles.button}\n onClick={() => { location.href = `/api/engram/export?scope=${scope}&format=markdown` }}>{t('exportMd')}</button>\n <button type=\"button\" className={styles.button}\n onClick={() => { location.href = `/api/engram/export?scope=${scope}&format=json` }}>{t('exportJson')}</button>\n </div>\n <div className={styles.cards}>\n {(stats ?? []).map(part => (\n <button key={part.scope} type=\"button\" className={styles.card}\n style={part.scope === scope ? { borderColor: 'var(--dsw-alias-brand-primary)' } : undefined}\n onClick={() => { setScope(part.scope); setOffset(0); clearSelection() }}>\n <b>{part.stats.total}</b>\n <span>{`${t(SCOPE_KEY[part.scope])} · ${t('cardActive', { n: part.stats.active })} · ${t('signalRatio', { n: Math.round(part.stats.signalRatio * 100) })}`}</span>\n </button>\n ))}\n </div>\n <div className={styles.filters}>\n <select className={styles.input} value={status}\n onChange={event => { setStatus(event.target.value); setOffset(0); setExpanded(null); clearSelection() }}>\n <option value=\"all\">{t('allStatuses')}</option>\n <option value=\"active\">{t('statusActive')}</option>\n <option value=\"archived\">{t('statusArchived')}</option>\n <option value=\"forgotten\">{t('statusForgotten')}</option>\n </select>\n <select className={styles.input} value={kind}\n onChange={event => { setKind(event.target.value); setOffset(0); setExpanded(null); clearSelection() }}>\n <option value=\"all\">{t('allKinds')}</option>\n {KINDS.map(option => <option key={option} value={option}>{kindLabel(t, option)}</option>)}\n </select>\n <input className={styles.input} placeholder={t('searchPlaceholder')} value={q}\n onChange={event => { setQ(event.target.value.trim()); setOffset(0); setExpanded(null); clearSelection() }} />\n </div>\n {selected.size > 0 && (\n <div className={styles.batchBar}>\n <span>{t('selectedCount', { n: selected.size })}</span>\n <button type=\"button\" className={styles.button} onClick={toggleAllPage}>\n {allSelected ? t('deselectAll') : t('selectAll')}\n </button>\n <button type=\"button\" className={styles.button} disabled={restorable === 0}\n onClick={() => runBatch('restore')}>{t('batchRestore', { n: restorable })}</button>\n <button type=\"button\"\n className={confirmForget ? `${styles.button} ${styles.danger}` : styles.button}\n disabled={forgetable === 0}\n onClick={armBatchForget}>\n {confirmForget ? t('batchForgetConfirm', { n: forgetable }) : t('batchForget', { n: forgetable })}\n </button>\n <button type=\"button\" className={styles.button} onClick={clearSelection}>{t('clearSelection')}</button>\n </div>\n )}\n {error !== null && <div className={styles.empty}>{t('loadFailed', { msg: error })}</div>}\n {error === null && list !== null && list.records.length === 0 && (\n <div className={styles.empty}>{t('empty')}</div>\n )}\n {(list?.records ?? []).map(record => (\n <div key={record.id} className={selected.has(record.id) ? `${styles.item} ${styles.itemSelected}` : styles.item}>\n <div className={styles.row1}>\n <input type=\"checkbox\" className={styles.itemCheck} checked={selected.has(record.id)}\n aria-label={record.content.slice(0, 24)} onChange={() => toggleSelect(record.id)} />\n <span className={`${styles.badge} ${styles[record.status]}`}>{t(STATUS_KEY[record.status])}</span>\n <span className={styles.badge}>{kindLabel(t, record.kind)}</span>\n <span className={styles.badge}>{t(SCOPE_KEY[record.scope])}</span>\n </div>\n <div className={styles.content}>{record.content}</div>\n <div className={styles.meta}>\n <span>{`${t('importance')} ${record.importance.toFixed(2)}`}</span>\n <span>{`${t('confidence')} ${record.confidence.toFixed(2)}`}</span>\n <span>{t('accessCount', { n: record.accessCount })}</span>\n <span>{sourceLabel(t, record)}</span>\n <span>{fmtTime(record.createdAt)}</span>\n </div>\n <div className={styles.ops}>\n <button type=\"button\" className={styles.button}\n onClick={() => { toggle('review', record) }}>{t('detail')}</button>\n <button type=\"button\" className={styles.button}\n onClick={() => { toggle('edit', record) }}>{t('edit')}</button>\n {record.status === 'active'\n ? <button type=\"button\" className={styles.button}\n onClick={() => { act('forget', record) }}>{t('forget')}</button>\n : <button type=\"button\" className={styles.button}\n onClick={() => { act('restore', record) }}>{t('restore')}</button>}\n </div>\n {expanded !== null && expanded.record.id === record.id && (\n <div className={styles.expand}>\n {expanded.kind === 'review'\n ? <ReviewBody t={t} recordId={record.id} scope={record.scope} />\n : <EditForm t={t} record={record} onClose={() => { setExpanded(null) }}\n onSaved={() => { setExpanded(null); reload() }} />}\n </div>\n )}\n </div>\n ))}\n <div className={styles.pager}>\n <button type=\"button\" className={styles.button} disabled={offset === 0}\n onClick={() => { setOffset(Math.max(0, offset - PAGE_SIZE)); setExpanded(null); clearSelection() }}>{t('prevPage')}</button>\n <span>{t('pagerInfo', { page, pages, total: list?.total ?? 0 })}</span>\n <button type=\"button\" className={styles.button}\n disabled={list === null || offset + PAGE_SIZE >= list.total}\n onClick={() => { setOffset(offset + PAGE_SIZE); setExpanded(null); clearSelection() }}>{t('nextPage')}</button>\n </div>\n </div>\n )\n}\n","/**\n * 「记忆库」client 词典:zh/en 键并集必须完全一致(类型化注册的编译约束)。\n * 数据层英文枚举(status/kind/op 等)只在显示层映射,存储值保持英文不变。\n * @module @kenz1117/dsh-engram/client/locales\n */\n\n/** 词典命名空间(同时用作 LocaleNamespaceMap 的声明合并键)。 */\nexport const NS = 'engram'\n\n/** 「记忆库」全部界面文案键。 */\nexport type EngramKey =\n | 'nav'\n | 'scopeUser'\n | 'scopeProject'\n | 'refresh'\n | 'exportMd'\n | 'exportJson'\n | 'allStatuses'\n | 'allKinds'\n | 'searchPlaceholder'\n | 'statusActive'\n | 'statusArchived'\n | 'statusForgotten'\n | 'kindFact'\n | 'kindPreference'\n | 'kindDecision'\n | 'kindEpisode'\n | 'kindSkill'\n | 'labelContent'\n | 'labelKind'\n | 'labelStatus'\n | 'labelScope'\n | 'importance'\n | 'confidence'\n | 'accessCount'\n | 'sourceExplicit'\n | 'sourceSession'\n | 'round'\n | 'cardActive'\n | 'signalRatio'\n | 'detail'\n | 'edit'\n | 'forget'\n | 'restore'\n | 'empty'\n | 'loadFailed'\n | 'loading'\n | 'pagerInfo'\n | 'prevPage'\n | 'nextPage'\n | 'cancel'\n | 'saveWithHint'\n | 'detailAttributes'\n | 'detailSource'\n | 'detailRelations'\n | 'detailOperations'\n | 'relSupersededBy'\n | 'relSupersedes'\n | 'relContradicts'\n | 'relRelated'\n | 'opWrite'\n | 'opUpdate'\n | 'opForget'\n | 'opRestore'\n | 'opDecay'\n | 'opSuperseded'\n | 'opIngestRequest'\n | 'opDistillRequest'\n | 'selectAll'\n | 'deselectAll'\n | 'selectedCount'\n | 'batchForget'\n | 'batchForgetConfirm'\n | 'batchRestore'\n | 'clearSelection'\n\n/** 中文词典(宿主默认语言)。 */\nexport const zh: Record<EngramKey, string> = {\n nav: '记忆库',\n scopeUser: '用户级',\n scopeProject: '项目级',\n refresh: '刷新',\n exportMd: '导出 MD',\n exportJson: '导出 JSON',\n allStatuses: '全部状态',\n allKinds: '全部种类',\n searchPlaceholder: '按内容搜索…',\n statusActive: '生效中',\n statusArchived: '已归档',\n statusForgotten: '已遗忘',\n kindFact: '事实',\n kindPreference: '偏好',\n kindDecision: '决策',\n kindEpisode: '事件',\n kindSkill: '技能',\n labelContent: '内容',\n labelKind: '种类',\n labelStatus: '状态',\n labelScope: '作用域',\n importance: '重要性',\n confidence: '置信',\n accessCount: '访问 {n} 次',\n sourceExplicit: '显式保存',\n sourceSession: '来源 {id}',\n round: '第 {n} 轮',\n cardActive: '生效 {n}',\n signalRatio: '信噪比 {n}%',\n detail: '详情',\n edit: '编辑',\n forget: '遗忘',\n restore: '恢复',\n empty: '没有符合条件的记忆',\n loadFailed: '加载失败:{msg}',\n loading: '加载中…',\n pagerInfo: '第 {page} / {pages} 页 · 共 {total} 条',\n prevPage: '上一页',\n nextPage: '下一页',\n cancel: '取消',\n saveWithHint: '保存(旧条目归档)',\n detailAttributes: '属性',\n detailSource: '来源',\n detailRelations: '关系',\n detailOperations: '最近操作',\n relSupersededBy: '被取代',\n relSupersedes: '取代',\n relContradicts: '矛盾',\n relRelated: '关联',\n opWrite: '写入',\n opUpdate: '更新',\n opForget: '遗忘',\n opRestore: '恢复',\n opDecay: '衰减归档',\n opSuperseded: '被取代',\n opIngestRequest: '摄取请求',\n opDistillRequest: '蒸馏请求',\n selectAll: '全选本页',\n deselectAll: '取消全选',\n selectedCount: '已选 {n} 条',\n batchForget: '批量遗忘({n})',\n batchForgetConfirm: '确认遗忘 {n} 条?',\n batchRestore: '批量恢复({n})',\n clearSelection: '取消选择',\n}\n\n/** 英文词典。 */\nexport const en: Record<EngramKey, string> = {\n nav: 'Memory Library',\n scopeUser: 'User',\n scopeProject: 'Project',\n refresh: 'Refresh',\n exportMd: 'Export MD',\n exportJson: 'Export JSON',\n allStatuses: 'All statuses',\n allKinds: 'All kinds',\n searchPlaceholder: 'Search content…',\n statusActive: 'active',\n statusArchived: 'archived',\n statusForgotten: 'forgotten',\n kindFact: 'fact',\n kindPreference: 'preference',\n kindDecision: 'decision',\n kindEpisode: 'episode',\n kindSkill: 'skill',\n labelContent: 'Content',\n labelKind: 'Kind',\n labelStatus: 'Status',\n labelScope: 'Scope',\n importance: 'Importance',\n confidence: 'Confidence',\n accessCount: '{n}× accessed',\n sourceExplicit: 'Explicitly saved',\n sourceSession: 'Source {id}',\n round: 'round {n}',\n cardActive: '{n} active',\n signalRatio: 'signal {n}%',\n detail: 'Details',\n edit: 'Edit',\n forget: 'Forget',\n restore: 'Restore',\n empty: 'No matching memories',\n loadFailed: 'Failed to load: {msg}',\n loading: 'Loading…',\n pagerInfo: 'Page {page} / {pages} · {total} records',\n prevPage: 'Prev',\n nextPage: 'Next',\n cancel: 'Cancel',\n saveWithHint: 'Save (archives old entry)',\n detailAttributes: 'Attributes',\n detailSource: 'Source',\n detailRelations: 'Relations',\n detailOperations: 'Recent operations',\n relSupersededBy: 'Superseded by',\n relSupersedes: 'Supersedes',\n relContradicts: 'Contradicts',\n relRelated: 'Related',\n opWrite: 'write',\n opUpdate: 'update',\n opForget: 'forget',\n opRestore: 'restore',\n opDecay: 'decay',\n opSuperseded: 'superseded',\n opIngestRequest: 'ingest-request',\n opDistillRequest: 'distill-request',\n selectAll: 'Select all on page',\n deselectAll: 'Deselect all',\n selectedCount: '{n} selected',\n batchForget: 'Forget selected ({n})',\n batchForgetConfirm: 'Forget {n} entries?',\n batchRestore: 'Restore selected ({n})',\n clearSelection: 'Clear selection',\n}\n","/**\n * dsh-engram client half: 注册设置页「记忆库」section。\n * 数据经回环 API(/api/engram/*)读取与操作,与 host 半的路由对齐。\n * 文案经宿主 locale 服务(zh/en 词典,语言切换由渲染器自动重渲染)。\n * @module @kenz1117/dsh-engram/client\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only: ctx.locale 服务声明(宿主 locale 插件的 Context merge)。\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: ctx.slots(renderer 的 SlotMap 渲染接线)。\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\n// Type-only: settings.section 槽位的 SlotMap merge(ui-settings 契约)。\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\n// Type-only: LocaleNamespaceMap 声明合并目标(t 席位的键类型检查)。\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { EngramSection } from './EngramPanel.tsx'\nimport { NS, en, zh, type EngramKey } from './locales.ts'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n [NS]: EngramKey\n }\n}\n\n/** 必需服务:slots 渲染接线 + locale 词典注册。 */\nexport const inject = ['slots', 'locale']\n\n/**\n * Client plugin body: 注册 zh/en 词典与设置页「记忆库」section(order 20,\n * 排在 general/models/plugins 之后)。\n * @param ctx - client root context.\n */\nexport function apply(ctx: Context): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-engram: dictionaries')\n const t = ctx.locale.bind(NS)\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'engram',\n order: 20,\n label: () => t('nav'),\n locale: NS,\n }, EngramSection))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["styles","useState","useCallback"],"sources":["../src/client/EngramPanel.tsx","../src/client/locales.ts","../src/client/index.ts"],"sourcesContent":["/**\n * 记忆库设置面板:统计卡片、过滤列表、批量操作、编辑(取代链)、遗忘/恢复、导出。\n * 数据经回环 API(/api/engram/*)读写;详情与编辑为条目下方行内展开\n * (不嵌套弹窗);文案全部走宿主 locale 词典(zh/en),语言切换自动\n * 重渲染;数据层英文枚举(status/kind/op)只在显示层映射;内容节点\n * 一律 DOM/JSX 构建(防 XSS)。\n * @module @kenz1117/dsh-engram/client/EngramPanel\n */\n\nimport { useCallback, useEffect, useState } from 'react'\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots'\nimport styles from './panel.module.css'\nimport { NS, type EngramKey } from './locales.ts'\n\n/** 列表/详情用的记忆视图(宿主 /api/engram/list 的行结构)。 */\ninterface MemoryRow {\n readonly id: string\n readonly scope: 'user' | 'project'\n readonly kind: string\n readonly content: string\n readonly importance: number\n readonly confidence: number\n readonly status: 'active' | 'archived' | 'forgotten'\n readonly createdAt: number\n readonly accessCount: number\n readonly sourceSessionId: string | null\n readonly sourceRound: number | null\n}\n\ninterface ListResult {\n readonly records: MemoryRow[]\n readonly total: number\n}\n\ninterface StatsPart {\n readonly scope: 'user' | 'project'\n readonly stats: {\n readonly total: number\n readonly active: number\n readonly archived: number\n readonly forgotten: number\n readonly signalRatio: number\n }\n}\n\n/** review API 的返回结构(详情行内展开区的数据源)。 */\ninterface ReviewView {\n readonly record: MemoryRow\n readonly supersededBy?: readonly string[]\n readonly supersedes?: readonly string[]\n readonly contradicts?: readonly string[]\n readonly related?: readonly string[]\n readonly operations?: readonly { at: number; op: string; detail: string | null }[]\n}\n\n/** 面板内部传递的翻译函数(渲染器按注册的 locale 声明合成的 t 的窄化签名)。 */\ntype T = (key: EngramKey, params?: Record<string, unknown>) => string\n\n/** kind 数据值(存储层英文枚举,编辑表单下拉的 value 保持英文)。 */\nconst KINDS = ['fact', 'preference', 'decision', 'episode', 'skill'] as const\nconst PAGE_SIZE = 20\n\n/** 数据层枚举 → 词典键映射(显示层本地化,存储值不变)。 */\nconst STATUS_KEY: Record<MemoryRow['status'], EngramKey> = {\n active: 'statusActive',\n archived: 'statusArchived',\n forgotten: 'statusForgotten',\n}\nconst KIND_KEY: Record<string, EngramKey> = {\n fact: 'kindFact',\n preference: 'kindPreference',\n decision: 'kindDecision',\n episode: 'kindEpisode',\n skill: 'kindSkill',\n}\nconst SCOPE_KEY: Record<MemoryRow['scope'], EngramKey> = {\n user: 'scopeUser',\n project: 'scopeProject',\n}\nconst OP_KEY: Record<string, EngramKey> = {\n write: 'opWrite',\n update: 'opUpdate',\n forget: 'opForget',\n restore: 'opRestore',\n decay: 'opDecay',\n superseded: 'opSuperseded',\n 'ingest-request': 'opIngestRequest',\n 'distill-request': 'opDistillRequest',\n}\nconst REL_KEY: Record<string, EngramKey> = {\n supersededBy: 'relSupersededBy',\n supersedes: 'relSupersedes',\n contradicts: 'relContradicts',\n related: 'relRelated',\n}\n/** 操作日志 detail JSON 的已知键 → 词典键(write/update/superseded 的小对象)。 */\nconst DETAIL_KEY: Record<string, EngramKey> = {\n kind: 'labelKind',\n scope: 'labelScope',\n status: 'labelStatus',\n}\n\n/** kind 数据值 → 本地化标签(未知值回退原文)。 */\nfunction kindLabel(t: T, kind: string): string {\n const key = KIND_KEY[kind]\n return key === undefined ? kind : t(key)\n}\n\n/** op 数据值 → 本地化标签(未知值回退原文)。 */\nfunction opLabel(t: T, op: string): string {\n const key = OP_KEY[op]\n return key === undefined ? op : t(key)\n}\n\n/** 操作日志 detail 的本地化:write/update/superseded 的小对象转中文键值;摄取/蒸馏请求的完整 JSON 保持原样(审计诚实性优先)。 */\nfunction formatOpDetail(t: T, op: string, detail: string | null): string {\n if (detail === null) return ''\n if (op !== 'write' && op !== 'update' && op !== 'superseded') return ` ${detail}`\n try {\n const parsed = JSON.parse(detail) as Record<string, unknown>\n const entries = Object.entries(parsed)\n if (entries.length === 0) return ` ${detail}`\n return ' ' + entries.map(([key, value]) => {\n const label = DETAIL_KEY[key]\n if (label === undefined) return `${key}=${String(value)}`\n if (key === 'kind' && typeof value === 'string') return `${t(label)}=${kindLabel(t, value)}`\n if (key === 'scope' && (value === 'user' || value === 'project')) return `${t(label)}=${t(SCOPE_KEY[value])}`\n return `${t(label)}=${String(value)}`\n }).join(' · ')\n } catch {\n return ` ${detail}`\n }\n}\n\n/** 来源会话展示片段:截短的会话 id + 可选轮次。 */\nfunction sourceLabel(t: T, record: Pick<MemoryRow, 'sourceSessionId' | 'sourceRound'>): string {\n if (record.sourceSessionId === null) return t('sourceExplicit')\n const id = `${record.sourceSessionId.slice(0, 16)}…`\n const withRound = record.sourceRound === null\n ? id\n : `${id} ${t('round', { n: record.sourceRound })}`\n return t('sourceSession', { id: withRound })\n}\n\nasync function api<T>(path: string, init?: RequestInit): Promise<T> {\n const response = await fetch(`/api/engram/${path}`, init)\n const body = (await response.json()) as T & { error?: string }\n if (!response.ok) throw new Error(body.error ?? `HTTP ${String(response.status)}`)\n return body\n}\n\n/** 绝对时间(审计时间线等需要精确时刻的位置)。 */\nfunction fmtTime(ms: number): string {\n // 时间格式跟随浏览器环境语言(宿主界面语言通常与浏览器一致)。\n return new Date(ms).toLocaleString(navigator.language || 'zh-CN', { hour12: false })\n}\n\n/** 列表用的相对时间:1 分钟内「刚刚」,其后按分钟/小时/天取整。 */\nfunction relTime(t: T, ms: number): string {\n const diff = Math.max(0, Date.now() - ms)\n const minutes = Math.floor(diff / 60_000)\n if (minutes < 1) return t('timeJustNow')\n if (minutes < 60) return t('timeMinutesAgo', { n: minutes })\n const hours = Math.floor(minutes / 60)\n if (hours < 48) return t('timeHoursAgo', { n: hours })\n return t('timeDaysAgo', { n: Math.floor(hours / 24) })\n}\n\n/** 细仪表条:重要性(品牌色)与置信(成功色)共用,conf 变体换色。 */\nfunction Meter({ value, conf }: { value: number; conf?: boolean }): React.ReactElement {\n return (\n <span className={conf === true ? `${styles.meter} ${styles.conf}` : styles.meter}>\n <i style={{ width: `${String(Math.round(Math.min(1, Math.max(0, value)) * 100))}%` }} />\n </span>\n )\n}\n\n/** 关系 id 列表 → 等宽字体 chips(完整 id 放 title)。 */\nfunction RelChips({ ids }: { ids: readonly string[] }): React.ReactElement {\n return (\n <div className={styles.relChips}>\n {ids.map(id => <span key={id} className={styles.relChip} title={id}>{id.slice(0, 18)}</span>)}\n </div>\n )\n}\n\n/** 行内审计区:属性网格 + 关系 chips + 操作时间线(review API)。 */\nfunction ReviewBody({ t, recordId, scope }: {\n t: T\n recordId: string\n scope: 'user' | 'project'\n}): React.ReactElement {\n const [view, setView] = useState<ReviewView | null>(null)\n const [failed, setFailed] = useState<string | null>(null)\n useEffect(() => {\n let cancelled = false\n api<ReviewView>(`review?scope=${scope}&id=${encodeURIComponent(recordId)}`)\n .then((data) => { if (!cancelled) setView(data) })\n .catch((error: Error) => { if (!cancelled) setFailed(error.message) })\n return () => { cancelled = true }\n }, [recordId, scope, t])\n if (failed !== null) return <div className={styles.expandLoading}>{t('loadFailed', { msg: failed })}</div>\n if (view === null) return <div className={styles.expandLoading}>{t('loading')}</div>\n const { record } = view\n const relations = (['supersededBy', 'supersedes', 'contradicts', 'related'] as const)\n .map(key => ({ key, label: REL_KEY[key], ids: view[key] ?? [] }))\n .filter(entry => entry.ids.length > 0)\n const operations = view.operations ?? []\n return (\n <div>\n <dl className={styles.attrGrid}>\n <dt>{t('labelContent')}</dt>\n <dd>{record.content}</dd>\n <dt>{t('labelKind')}</dt>\n <dd><span className={styles.chip}>{kindLabel(t, record.kind)}</span></dd>\n <dt>{t('labelStatus')}</dt>\n <dd><span className={`${styles.statusWrap} ${styles[record.status]}`}>{t(STATUS_KEY[record.status])}</span></dd>\n <dt>{t('importance')}</dt>\n <dd>{record.importance.toFixed(2)}<Meter value={record.importance} /></dd>\n <dt>{t('confidence')}</dt>\n <dd>{record.confidence.toFixed(2)}<Meter value={record.confidence} conf /></dd>\n <dt>{t('detailSource')}</dt>\n <dd>{sourceLabel(t, record)}</dd>\n <dt>{t('labelCreated')}</dt>\n <dd>{fmtTime(record.createdAt)}</dd>\n </dl>\n {relations.map(({ key, label, ids }) => (\n <div key={key} className={styles.relBlock}>\n <h5>{label === undefined ? key : t(label)}</h5>\n <RelChips ids={ids} />\n </div>\n ))}\n {operations.length > 0 && (\n <div className={styles.relBlock}>\n <h5>{t('detailOperations')}</h5>\n <ul className={styles.timeline}>\n {operations.map((op, index) => (\n <li key={index}>\n <time>{fmtTime(op.at)}</time>\n <b>{opLabel(t, op.op)}</b>\n {formatOpDetail(t, op.op, op.detail)}\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )\n}\n\n/** 行内编辑表单:内容/种类/重要性 → POST update(旧条目归档,取代链保留)。 */\nfunction EditForm({ t, record, onClose, onSaved }: {\n t: T\n record: MemoryRow\n onClose: () => void\n onSaved: () => void\n}): React.ReactElement {\n const [content, setContent] = useState(record.content)\n const [kind, setKind] = useState(record.kind)\n const [importance, setImportance] = useState(record.importance)\n const save = (): void => {\n api('update', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: record.id, scope: record.scope, content, kind, importance }),\n })\n .then(onSaved)\n .catch((error: Error) => { alert(error.message) })\n }\n return (\n <div className={styles.editForm}>\n <label className={styles.fieldLabel}>{t('labelContent')}</label>\n <textarea className={styles.input} rows={3} value={content} onChange={event => setContent(event.target.value)} />\n <label className={styles.fieldLabel}>{t('labelKind')}</label>\n {/* option 的 value 保持数据层英文枚举,仅显示文本本地化。 */}\n <select className={styles.input} value={kind} onChange={event => setKind(event.target.value)}>\n {KINDS.map(option => <option key={option} value={option}>{kindLabel(t, option)}</option>)}\n </select>\n <label className={styles.fieldLabel}>{`${t('importance')} ${importance.toFixed(2)}`}</label>\n <input type=\"range\" min={0} max={1} step={0.05} value={importance}\n onChange={event => setImportance(Number(event.target.value))} />\n <div className={styles.modalFoot}>\n <button type=\"button\" className={styles.button} onClick={onClose}>{t('cancel')}</button>\n <button type=\"button\" className={`${styles.button} ${styles.primary}`} onClick={save}>{t('saveWithHint')}</button>\n </div>\n </div>\n )\n}\n\n/** 设置页「记忆库」section 主组件(t 由渲染器按 locale: NS 声明合成)。 */\nexport function EngramSection({ t }: PropsLocale<typeof NS>): React.ReactElement {\n const [scope, setScope] = useState<'user' | 'project'>('user')\n const [status, setStatus] = useState('all')\n const [kind, setKind] = useState('all')\n /** 脱敏筛选:all 全部 / true 仅含 [REDACTED: 标记 / false 仅不含(审计脱敏命中)。 */\n const [redacted, setRedacted] = useState('all')\n const [q, setQ] = useState('')\n const [offset, setOffset] = useState(0)\n const [list, setList] = useState<ListResult | null>(null)\n const [stats, setStats] = useState<StatsPart[] | null>(null)\n const [error, setError] = useState<string | null>(null)\n /** 行内展开态:互斥的唯一展开条目(review 或 edit)。 */\n const [expanded, setExpanded] = useState<{ kind: 'review' | 'edit'; record: MemoryRow } | null>(null)\n const [reloadTick, setReloadTick] = useState(0)\n /** 批量选择:条目 id 集合(scope/过滤/翻页/搜索变更时清空)。 */\n const [selected, setSelected] = useState<ReadonlySet<string>>(new Set())\n /** 批量遗忘的两段式确认。 */\n const [confirmForget, setConfirmForget] = useState(false)\n\n const reload = useCallback((): void => { setReloadTick(tick => tick + 1) }, [])\n\n useEffect(() => {\n let cancelled = false\n const qs = new URLSearchParams({\n scope, status, kind, redacted, limit: String(PAGE_SIZE), offset: String(offset),\n })\n if (q !== '') qs.set('q', q)\n api<ListResult>(`list?${qs.toString()}`)\n .then((data) => { if (!cancelled) { setList(data); setError(null) } })\n .catch((loadError: Error) => { if (!cancelled) setError(loadError.message) })\n api<{ parts: StatsPart[] }>('stats')\n .then((data) => { if (!cancelled) setStats(data.parts) })\n .catch(() => { /* 统计失败不影响列表 */ })\n return () => { cancelled = true }\n }, [scope, status, kind, redacted, q, offset, reloadTick])\n\n const act = (route: string, record: MemoryRow): void => {\n api(route, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: record.id, scope: record.scope }),\n })\n .then(reload)\n .catch((actError: Error) => { alert(actError.message) })\n }\n\n const toggle = (expandKind: 'review' | 'edit', record: MemoryRow): void => {\n setExpanded(current => (current !== null && current.kind === expandKind && current.record.id === record.id)\n ? null\n : { kind: expandKind, record })\n }\n\n const pageRecords = list?.records ?? []\n const selectedRecords = pageRecords.filter(record => selected.has(record.id))\n const forgetable = selectedRecords.filter(record => record.status === 'active').length\n const restorable = selectedRecords.length - forgetable\n const allSelected = pageRecords.length > 0 && pageRecords.every(record => selected.has(record.id))\n\n const toggleSelect = (id: string): void => {\n setSelected(current => {\n const next = new Set(current)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n }\n\n const clearSelection = (): void => { setSelected(new Set()); setConfirmForget(false) }\n\n const toggleAllPage = (): void => {\n setSelected(current => {\n const next = new Set(current)\n if (allSelected) pageRecords.forEach(record => next.delete(record.id))\n else pageRecords.forEach(record => next.add(record.id))\n return next\n })\n }\n\n /** 批量执行:逐条调单条 API(回环毫秒级),单条失败不阻塞其余;完成即刷新并清空选择。 */\n const runBatch = (mode: 'forget' | 'restore'): void => {\n const targets = mode === 'forget'\n ? selectedRecords.filter(record => record.status === 'active')\n : selectedRecords.filter(record => record.status !== 'active')\n if (targets.length === 0) return\n void Promise.allSettled(targets.map(target => api(mode, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: target.id, scope: target.scope }),\n }))).then(() => { clearSelection(); reload() })\n }\n\n const armBatchForget = (): void => {\n // 确认态保持到用户点击确认执行或清空选择,不做自动复位(3 秒窗口曾导致执行落空)。\n if (confirmForget) { runBatch('forget'); return }\n setConfirmForget(true)\n }\n\n const page = Math.floor(offset / PAGE_SIZE) + 1\n const pages = list === null ? 1 : Math.max(1, Math.ceil(list.total / PAGE_SIZE))\n\n return (\n <div className={styles.panel}>\n <div className={styles.toolbar}>\n <div className={styles.scopeTabs}>\n {(['user', 'project'] as const).map(option => (\n <button key={option} type=\"button\"\n className={scope === option ? styles.scopeOn : styles.scopeOff}\n onClick={() => { setScope(option); setOffset(0); setExpanded(null); clearSelection() }}>\n {t(SCOPE_KEY[option])}\n </button>\n ))}\n </div>\n <button type=\"button\" className={styles.button} onClick={reload}>{t('refresh')}</button>\n <button type=\"button\" className={styles.button}\n onClick={() => { location.href = `/api/engram/export?scope=${scope}&format=markdown` }}>{t('exportMd')}</button>\n <button type=\"button\" className={styles.button}\n onClick={() => { location.href = `/api/engram/export?scope=${scope}&format=json` }}>{t('exportJson')}</button>\n </div>\n <div className={styles.cards}>\n {(stats ?? []).map(part => (\n <button key={part.scope} type=\"button\"\n className={part.scope === scope ? `${styles.card} ${styles.cardOn}` : styles.card}\n onClick={() => { setScope(part.scope); setOffset(0); clearSelection() }}>\n <b>{part.stats.total}</b>\n <span>{`${t(SCOPE_KEY[part.scope])} · ${t('cardActive', { n: part.stats.active })} · ${t('signalRatio', { n: Math.round(part.stats.signalRatio * 100) })}`}</span>\n <span className={styles.cardMeter}>\n <i style={{ width: `${String(Math.round(part.stats.signalRatio * 100))}%` }} />\n </span>\n </button>\n ))}\n </div>\n <div className={styles.filters}>\n <select className={styles.input} value={status}\n onChange={event => { setStatus(event.target.value); setOffset(0); setExpanded(null); clearSelection() }}>\n <option value=\"all\">{t('allStatuses')}</option>\n <option value=\"active\">{t('statusActive')}</option>\n <option value=\"archived\">{t('statusArchived')}</option>\n <option value=\"forgotten\">{t('statusForgotten')}</option>\n </select>\n <select className={styles.input} value={kind}\n onChange={event => { setKind(event.target.value); setOffset(0); setExpanded(null); clearSelection() }}>\n <option value=\"all\">{t('allKinds')}</option>\n {KINDS.map(option => <option key={option} value={option}>{kindLabel(t, option)}</option>)}\n </select>\n <select className={styles.input} value={redacted}\n onChange={event => { setRedacted(event.target.value); setOffset(0); setExpanded(null); clearSelection() }}>\n <option value=\"all\">{t('redactedAll')}</option>\n <option value=\"true\">{t('redactedOnly')}</option>\n <option value=\"false\">{t('redactedNone')}</option>\n </select>\n <input className={styles.input} placeholder={t('searchPlaceholder')} value={q}\n onChange={event => { setQ(event.target.value.trim()); setOffset(0); setExpanded(null); clearSelection() }} />\n </div>\n {selected.size > 0 && (\n <div className={styles.batchBar}>\n <span>{t('selectedCount', { n: selected.size })}</span>\n <button type=\"button\" className={styles.button} onClick={toggleAllPage}>\n {allSelected ? t('deselectAll') : t('selectAll')}\n </button>\n <button type=\"button\" className={styles.button} disabled={restorable === 0}\n onClick={() => runBatch('restore')}>{t('batchRestore', { n: restorable })}</button>\n <button type=\"button\"\n className={confirmForget ? `${styles.button} ${styles.danger}` : styles.button}\n disabled={forgetable === 0}\n onClick={armBatchForget}>\n {confirmForget ? t('batchForgetConfirm', { n: forgetable }) : t('batchForget', { n: forgetable })}\n </button>\n <button type=\"button\" className={styles.button} onClick={clearSelection}>{t('clearSelection')}</button>\n </div>\n )}\n {error !== null && <div className={styles.empty}>{t('loadFailed', { msg: error })}</div>}\n {error === null && list === null && [0, 1, 2].map(index => (\n <div key={index} className={styles.skeleton}><i /><i /><i /></div>\n ))}\n {error === null && list !== null && list.records.length === 0 && (\n <div className={styles.empty}>{t('empty')}</div>\n )}\n {(list?.records ?? []).map((record, index) => (\n <div key={record.id}\n className={selected.has(record.id) ? `${styles.item} ${styles.itemSelected}` : styles.item}\n style={{ animationDelay: `${String(Math.min(index, 12) * 28)}ms` }}>\n <div className={styles.row1}>\n <input type=\"checkbox\" className={styles.itemCheck} checked={selected.has(record.id)}\n aria-label={record.content.slice(0, 24)} onChange={() => toggleSelect(record.id)} />\n <span className={`${styles.statusWrap} ${styles[record.status]}`}>{t(STATUS_KEY[record.status])}</span>\n <span className={styles.chip}>{kindLabel(t, record.kind)}</span>\n {record.content.includes('[REDACTED:') && (\n <span className={`${styles.chip} ${styles.redactedChip}`}>{t('tagRedacted')}</span>\n )}\n <span className={styles.scopeTag}>{t(SCOPE_KEY[record.scope])}</span>\n <span className={styles.timeTag} title={fmtTime(record.createdAt)}>{relTime(t, record.createdAt)}</span>\n </div>\n <div className={styles.content}>{record.content}</div>\n <div className={styles.meta}>\n <span className={styles.meterField}>{t('importance')}<Meter value={record.importance} />{record.importance.toFixed(2)}</span>\n <span className={styles.meterField}>{t('confidence')}<Meter value={record.confidence} conf />{record.confidence.toFixed(2)}</span>\n <span>{t('accessCount', { n: record.accessCount })}</span>\n <span>{sourceLabel(t, record)}</span>\n </div>\n <div className={styles.ops}>\n <button type=\"button\" className={styles.button}\n onClick={() => { toggle('review', record) }}>{t('detail')}</button>\n <button type=\"button\" className={styles.button}\n onClick={() => { toggle('edit', record) }}>{t('edit')}</button>\n {record.status === 'active'\n ? <button type=\"button\" className={styles.button}\n onClick={() => { act('forget', record) }}>{t('forget')}</button>\n : <button type=\"button\" className={styles.button}\n onClick={() => { act('restore', record) }}>{t('restore')}</button>}\n </div>\n {expanded !== null && expanded.record.id === record.id && (\n <div className={styles.expand}>\n {expanded.kind === 'review'\n ? <ReviewBody t={t} recordId={record.id} scope={record.scope} />\n : <EditForm t={t} record={record} onClose={() => { setExpanded(null) }}\n onSaved={() => { setExpanded(null); reload() }} />}\n </div>\n )}\n </div>\n ))}\n <div className={styles.pager}>\n <button type=\"button\" className={styles.button} disabled={offset === 0}\n onClick={() => { setOffset(Math.max(0, offset - PAGE_SIZE)); setExpanded(null); clearSelection() }}>{t('prevPage')}</button>\n <span>{t('pagerInfo', { page, pages, total: list?.total ?? 0 })}</span>\n <button type=\"button\" className={styles.button}\n disabled={list === null || offset + PAGE_SIZE >= list.total}\n onClick={() => { setOffset(offset + PAGE_SIZE); setExpanded(null); clearSelection() }}>{t('nextPage')}</button>\n </div>\n </div>\n )\n}\n","/**\n * 「记忆库」client 词典:zh/en 键并集必须完全一致(类型化注册的编译约束)。\n * 数据层英文枚举(status/kind/op 等)只在显示层映射,存储值保持英文不变。\n * @module @kenz1117/dsh-engram/client/locales\n */\n\n/** 词典命名空间(同时用作 LocaleNamespaceMap 的声明合并键)。 */\nexport const NS = 'engram'\n\n/** 「记忆库」全部界面文案键。 */\nexport type EngramKey =\n | 'nav'\n | 'scopeUser'\n | 'scopeProject'\n | 'refresh'\n | 'exportMd'\n | 'exportJson'\n | 'allStatuses'\n | 'allKinds'\n | 'redactedAll'\n | 'redactedOnly'\n | 'redactedNone'\n | 'tagRedacted'\n | 'searchPlaceholder'\n | 'statusActive'\n | 'statusArchived'\n | 'statusForgotten'\n | 'kindFact'\n | 'kindPreference'\n | 'kindDecision'\n | 'kindEpisode'\n | 'kindSkill'\n | 'labelContent'\n | 'labelKind'\n | 'labelStatus'\n | 'labelScope'\n | 'importance'\n | 'confidence'\n | 'accessCount'\n | 'labelCreated'\n | 'timeJustNow'\n | 'timeMinutesAgo'\n | 'timeHoursAgo'\n | 'timeDaysAgo'\n | 'sourceExplicit'\n | 'sourceSession'\n | 'round'\n | 'cardActive'\n | 'signalRatio'\n | 'detail'\n | 'edit'\n | 'forget'\n | 'restore'\n | 'empty'\n | 'loadFailed'\n | 'loading'\n | 'pagerInfo'\n | 'prevPage'\n | 'nextPage'\n | 'cancel'\n | 'saveWithHint'\n | 'detailAttributes'\n | 'detailSource'\n | 'detailRelations'\n | 'detailOperations'\n | 'relSupersededBy'\n | 'relSupersedes'\n | 'relContradicts'\n | 'relRelated'\n | 'opWrite'\n | 'opUpdate'\n | 'opForget'\n | 'opRestore'\n | 'opDecay'\n | 'opSuperseded'\n | 'opIngestRequest'\n | 'opDistillRequest'\n | 'selectAll'\n | 'deselectAll'\n | 'selectedCount'\n | 'batchForget'\n | 'batchForgetConfirm'\n | 'batchRestore'\n | 'clearSelection'\n\n/** 中文词典(宿主默认语言)。 */\nexport const zh: Record<EngramKey, string> = {\n nav: '记忆库',\n scopeUser: '用户级',\n scopeProject: '项目级',\n refresh: '刷新',\n exportMd: '导出 MD',\n exportJson: '导出 JSON',\n allStatuses: '全部状态',\n allKinds: '全部种类',\n redactedAll: '全部脱敏',\n redactedOnly: '含脱敏标记',\n redactedNone: '无脱敏标记',\n tagRedacted: '已脱敏',\n searchPlaceholder: '按内容搜索…',\n statusActive: '生效中',\n statusArchived: '已归档',\n statusForgotten: '已遗忘',\n kindFact: '事实',\n kindPreference: '偏好',\n kindDecision: '决策',\n kindEpisode: '事件',\n kindSkill: '技能',\n labelContent: '内容',\n labelKind: '种类',\n labelStatus: '状态',\n labelScope: '作用域',\n importance: '重要性',\n confidence: '置信',\n accessCount: '访问 {n} 次',\n labelCreated: '创建时间',\n timeJustNow: '刚刚',\n timeMinutesAgo: '{n} 分钟前',\n timeHoursAgo: '{n} 小时前',\n timeDaysAgo: '{n} 天前',\n sourceExplicit: '显式保存',\n sourceSession: '来源 {id}',\n round: '第 {n} 轮',\n cardActive: '生效 {n}',\n signalRatio: '信噪比 {n}%',\n detail: '详情',\n edit: '编辑',\n forget: '遗忘',\n restore: '恢复',\n empty: '没有符合条件的记忆',\n loadFailed: '加载失败:{msg}',\n loading: '加载中…',\n pagerInfo: '第 {page} / {pages} 页 · 共 {total} 条',\n prevPage: '上一页',\n nextPage: '下一页',\n cancel: '取消',\n saveWithHint: '保存(旧条目归档)',\n detailAttributes: '属性',\n detailSource: '来源',\n detailRelations: '关系',\n detailOperations: '最近操作',\n relSupersededBy: '被取代',\n relSupersedes: '取代',\n relContradicts: '矛盾',\n relRelated: '关联',\n opWrite: '写入',\n opUpdate: '更新',\n opForget: '遗忘',\n opRestore: '恢复',\n opDecay: '衰减归档',\n opSuperseded: '被取代',\n opIngestRequest: '摄取请求',\n opDistillRequest: '蒸馏请求',\n selectAll: '全选本页',\n deselectAll: '取消全选',\n selectedCount: '已选 {n} 条',\n batchForget: '批量遗忘({n})',\n batchForgetConfirm: '确认遗忘 {n} 条?',\n batchRestore: '批量恢复({n})',\n clearSelection: '取消选择',\n}\n\n/** 英文词典。 */\nexport const en: Record<EngramKey, string> = {\n nav: 'Memory Library',\n scopeUser: 'User',\n scopeProject: 'Project',\n refresh: 'Refresh',\n exportMd: 'Export MD',\n exportJson: 'Export JSON',\n allStatuses: 'All statuses',\n allKinds: 'All kinds',\n redactedAll: 'All redaction',\n redactedOnly: 'Redacted only',\n redactedNone: 'Not redacted',\n tagRedacted: 'redacted',\n searchPlaceholder: 'Search content…',\n statusActive: 'active',\n statusArchived: 'archived',\n statusForgotten: 'forgotten',\n kindFact: 'fact',\n kindPreference: 'preference',\n kindDecision: 'decision',\n kindEpisode: 'episode',\n kindSkill: 'skill',\n labelContent: 'Content',\n labelKind: 'Kind',\n labelStatus: 'Status',\n labelScope: 'Scope',\n importance: 'Importance',\n confidence: 'Confidence',\n accessCount: '{n}× accessed',\n labelCreated: 'Created',\n timeJustNow: 'just now',\n timeMinutesAgo: '{n} min ago',\n timeHoursAgo: '{n} hr ago',\n timeDaysAgo: '{n} d ago',\n sourceExplicit: 'Explicitly saved',\n sourceSession: 'Source {id}',\n round: 'round {n}',\n cardActive: '{n} active',\n signalRatio: 'signal {n}%',\n detail: 'Details',\n edit: 'Edit',\n forget: 'Forget',\n restore: 'Restore',\n empty: 'No matching memories',\n loadFailed: 'Failed to load: {msg}',\n loading: 'Loading…',\n pagerInfo: 'Page {page} / {pages} · {total} records',\n prevPage: 'Prev',\n nextPage: 'Next',\n cancel: 'Cancel',\n saveWithHint: 'Save (archives old entry)',\n detailAttributes: 'Attributes',\n detailSource: 'Source',\n detailRelations: 'Relations',\n detailOperations: 'Recent operations',\n relSupersededBy: 'Superseded by',\n relSupersedes: 'Supersedes',\n relContradicts: 'Contradicts',\n relRelated: 'Related',\n opWrite: 'write',\n opUpdate: 'update',\n opForget: 'forget',\n opRestore: 'restore',\n opDecay: 'decay',\n opSuperseded: 'superseded',\n opIngestRequest: 'ingest-request',\n opDistillRequest: 'distill-request',\n selectAll: 'Select all on page',\n deselectAll: 'Deselect all',\n selectedCount: '{n} selected',\n batchForget: 'Forget selected ({n})',\n batchForgetConfirm: 'Forget {n} entries?',\n batchRestore: 'Restore selected ({n})',\n clearSelection: 'Clear selection',\n}\n","/**\n * dsh-engram client half: 注册设置页「记忆库」section。\n * 数据经回环 API(/api/engram/*)读取与操作,与 host 半的路由对齐。\n * 文案经宿主 locale 服务(zh/en 词典,语言切换由渲染器自动重渲染)。\n * @module @kenz1117/dsh-engram/client\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\n// Type-only: ctx.locale 服务声明(宿主 locale 插件的 Context merge)。\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\n// Type-only: ctx.slots(renderer 的 SlotMap 渲染接线)。\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\n// Type-only: settings.section 槽位的 SlotMap merge(ui-settings 契约)。\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\n// Type-only: LocaleNamespaceMap 声明合并目标(t 席位的键类型检查)。\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport { EngramSection } from './EngramPanel.tsx'\nimport { NS, en, zh, type EngramKey } from './locales.ts'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n [NS]: EngramKey\n }\n}\n\n/** 必需服务:slots 渲染接线 + locale 词典注册。 */\nexport const inject = ['slots', 'locale']\n\n/**\n * Client plugin body: 注册 zh/en 词典与设置页「记忆库」section(order 20,\n * 排在 general/models/plugins 之后)。\n * @param ctx - client root context.\n */\nexport function apply(ctx: Context): void {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-engram: dictionaries')\n const t = ctx.locale.bind(NS)\n ctx.slots.inject('settings.section', () => ctx.slots.register({\n name: 'settings.section',\n id: 'engram',\n order: 20,\n label: () => t('nav'),\n locale: NS,\n }, EngramSection))\n}\n"],"mappings":"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"}
|