dsh-memory-eternal 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,275 +1,246 @@
1
- # 🧠 记忆核心(dsh-memory-eternal)— 给 DeepSeek Harness 装上「第二大脑」
2
-
3
- <p align="center">
4
- <img src="https://img.shields.io/badge/DeepSeek%20Harness-plugin-3B82F6" alt="DSH plugin" />
5
- <img src="https://img.shields.io/npm/v/dsh-memory-eternal" alt="npm version" />
6
- <img src="https://img.shields.io/github/stars/EternalNight996/dsh-memory-eternal?style=flat" alt="GitHub stars" />
7
- <img src="https://img.shields.io/github/license/EternalNight996/dsh-memory-eternal" alt="license" />
8
- <img src="https://img.shields.io/badge/%E6%99%BA%E8%83%BD%E5%8E%BB%E9%87%8D-CJK%E6%A3%80%E7%B4%A2-10B981" alt="self-built" />
9
- </p>
10
-
11
- > 🧠 **对话结束自动沉淀、跨会话真实不失忆;召回只取相关小块,省 token、少噪音。** 全自研、零第三方记忆框架、不改 DSH 源码、一条命令安装——**为你装一个自己说了算的、可 git 管理的本地记忆库。**
12
-
13
- <p align="center">
14
- <strong>自研·去重·CJK 检索·知识图谱·双语·本地优先</strong> — ⭐ 觉得好用就点个 Star,让更多 DSH 用户用上这份「第二大脑」
15
- </p>
16
-
17
- <details>
18
- <summary><b>⭐ 为什么值得 Star(30 秒看懂)</b></summary>
19
-
20
- | 一句话 | 为什么打动你 |
21
- | --- | --- |
22
- | 🤖 **全自动,零人工** | 每轮对话结束自动判定+压缩成知识卡,不用点保存 |
23
- | 🧬 **全自研,不抄框架** | 去重/CJK 检索/图谱/界面全部本仓库独立实现,逻辑可逐行核对 |
24
- | 📚 **本地 Markdown Vault** | 普通 `.md`,可读、可编辑、可 git 版本管理、不被锁进私有库 |
25
- | 🚫 **双层去重** | 词法 Jaccard + 语义去重,对 LLM 改写免疫,不堆重复卡 |
26
- | 🔍 **CJK 感知检索** | 中文整词+字符 bigram,短查询也能命中长文 |
27
- | 🕸 **秒级知识图谱** | 力导向/径向布局 + 图例过滤/时间维/框选/删除/合并,几千节点不卡 |
28
- | 💾 **一键备份迁移** | 记忆库 MD/JSON 导出导入,异地迁移随便走 |
29
- | ⚙️ **一条命令安装** | `dsh plugin --profile web add dsh-memory-eternal` |
30
-
31
- </details>
32
-
33
- ---
34
-
35
- <p align="center">
36
- <img src="assets/screen/dsh-memory-eternal.gif" width="880" alt="记忆核心 DSH 插件效果演示(动态)" />
37
- <br/>
38
- <em>记忆核心 · 对话自动沉淀 + 图形化知识库 + 增强知识图谱(动态演示)</em>
39
- </p>
40
-
41
- <p align="center">
42
- <img src="assets/screen/memory-settings.png" width="840" alt="设置 → 记忆:图形化知识库主界面(统计 + 检索 + 卡片/图谱 Tab + 分类)" />
43
- <br/>
44
- <em>设置 记忆:统计概览、CJK 检索、知识卡网格、按 kind 分类(「知识卡 / 知识图谱」Tab 一键切换)</em>
45
- </p>
46
-
47
- <p align="center">
48
- <img src="assets/screen/memory-popup.png" width="840" alt="侧边栏「记忆」按钮一键打开完整记忆库弹窗(含增强知识图谱)" />
49
- <br/>
50
- <em>侧边栏底部「记忆」按钮 → 一键打开<strong>完整记忆库弹窗</strong>:更开阔的查看体验 + 增强版知识图谱</em>
51
- </p>
52
-
53
- <p align="center">
54
- <img src="assets/screen/memory-sidebar.png" width="460" alt="侧边栏底部新增「记忆」入口,位于更新配置下方" />
55
- <br/>
56
- <em>侧边栏底部新增「记忆」入口(位于「设置 / 更新配置」下方),一键直达知识库</em>
57
- </p>
58
-
59
- ---
60
-
61
- ## ✨ 为什么值得装
62
-
63
- | 亮点 | 说明 |
64
- | --- | --- |
65
- | 🧬 **原创自研** | 去重 / CJK 检索 / 知识图谱 / 图形化界面均由本仓库**独立实现**;运行时**不依赖**任何第三方记忆框架,核心逻辑可在 `lib/` 逐行核对 |
66
- | 🤖 **全自动沉淀** | 每轮对话结束自动触发(监听 `agent/turn-stopping`),模型判定「是否值得保存」并压缩成 300-800 字知识卡——**无需任何手动操作** |
67
- | 📚 **本地 Markdown Vault** | 知识卡是带 frontmatter 的普通 `.md` 文件,默认落在 `~/.dsh/memory-vault`,可手动编辑、可 git 版本管理、可被任何工具读取,不锁进私有数据库 |
68
- | 🚫 **智能去重** | 双层去重:**自研词法 Jaccard bigram**(0.62 阈值)+ **语义去重**(把已有卡片索引喂给模型,由模型决定「新建 vs 追加到已有卡」,对 LLM 改写免疫) |
69
- | 🔍 **CJK 感知检索** | 中文整词 + 字符 bigram 命中,短查询「强化学习」「蒸馏」也能在长文里命中,无需全文搜索引擎 |
70
- | 🕸 **知识图谱** | 卡片间的 `[[wikilink]]`、共享标签与**相似度关联**自动连线;Canvas 图谱支持**图例过滤、时间维着色、右键菜单、Shift 框选多选、导出/删除/合并**,交互全面 |
71
- | 🧠 **自动召回** | 注入 system prompt 告知 Agent 它拥有记忆核心,并注册 `memory_recall` 工具——需要项目背景/历史决策/领域知识时自动检索 |
72
- | 🔒 **本地优先** | 所有内容留在你的本机 Vault;不设遥测、不传云端。模型请求仍走你已配置好的 provider |
73
- | ⚙️ **一键安装** | `dsh-ui-three-body` 同款插件机制,`dsh plugin --profile web add dsh-memory-eternal` 即可 |
74
-
75
- **适合**:想要让 DSH 记住项目背景与历史决策的人 / 想给对话沉淀可检索知识库的人 / Obsidian 式 Markdown Vault 爱好者 / 在意数据归属(本地文件 > 云端数据库)的人。
76
-
77
- ---
78
-
79
- ## 🚀 安装
80
-
81
- ```bash
82
- # 已发布后(npm)—— 跨平台通用(无需 cd / 无需平台路径)
83
- dsh plugin --profile web add dsh-memory-eternal
84
-
85
- # 从 GitHub
86
- dsh plugin --profile web add github:EternalNight996/dsh-memory-eternal
87
-
88
- # 本地联调(link 本地目录,改代码即时生效)
89
- dsh plugin --profile web add F:/MyApp/eternal/dsh-memory-eternal
90
- ```
91
-
92
- > 提示:
93
- > - `dsh` 为 DSH CLI(`npm i -g @deepseek-ai/dsh` 后可直接用,跨平台、无需 cd/平台路径)。
94
- > - 要**官方最新**(避免 npmmirror 镜像滞后):`npm_config_registry=https://registry.npmjs.org/ dsh plugin --profile web add dsh-memory-eternal`。
95
- > - **DSH profile 是 pnpm workspace**(本地插件用 `link:`):在 profile 目录用 `pnpm add` 装/更新,`cd ~/.dsh/profiles/web && pnpm add dsh-memory-eternal@latest`(bash/PowerShell);`npm install` 会报 `EUNSUPPORTEDPROTOCOL`。
96
-
97
- 装完**重启 dsh web**:设置 → 记忆 出现知识库页面;侧边栏底部也会出现「记忆」按钮,一键打开完整记忆库弹窗。此后每轮对话自动沉淀知识卡。
98
-
99
- ### 🧭 插件发现 / 收录标准
100
-
101
- 本插件面向 DSH 插件生态,仓库已打 **GitHub `dsh-plugin` topic** 并按社区标准结构发布,可被以下机制**自动发现**(无需手动提交 PR):
102
-
103
- | 机制 | 收录方式 | 状态 |
104
- | --- | --- | --- |
105
- | **dsh-marketplace**(ouyangyipeng) | 实时读取 **GitHub `topic:dsh-plugin`** 的社区仓库,有该 topic 即进入 `设置 → 插件 → Marketplace` | ✅(topic 已打) |
106
- | **dsh-find-plugin**(awesome-dsh-plugin) | 会话内按 `topic:dsh-plugin` + 星数实时搜索 | ✅ |
107
- | **dsh-plugin-marketplace**(YELEBAI) | 2 小时扫描 `topic:dsh-plugin`,静态验证 manifest/patch/入口/npm tarball 后写入中心 Registry;无需人工申请 | ✅(已声明 `dsh.marketplace` 元数据辅助分类/一键安装) |
108
-
109
- > dsh-plugin-marketplace 会读取 `package.json#dsh.marketplace`(`profiles:["web"]`、`requiresBuildApproval:false`、`manualSteps:false`)来给插件归类并开放一键安装。两个市场均为**自动扫描**,非发布动作;插件结构通过验证即被收录。
110
-
111
- ---
112
-
113
- ## 🧠 自动沉淀(零人工干预)
114
-
115
- 对话每轮结束 → 插件提取增量消息 → 调用当前模型压缩 → 双层去重 → 写入 Vault:
116
-
117
- ```
118
- 你的对话
119
-
120
-
121
- agent/turn-stopping ──► 提取本轮 user/assistant 消息
122
-
123
-
124
- LLM 判定(值得保存?)──否──► 丢弃
125
- │是
126
-
127
- 语义去重(喂给模型已有卡片索引)
128
- ├─ 同一主题 ──► append_to:追加「更新记录」到已有卡(不建重复卡)
129
- └─ 新主题 ────► 写新知识卡到 02-08 对应目录
130
- ```
131
-
132
- 知识卡格式(frontmatter + markdown 正文):
133
-
134
- ```markdown
135
- ---
136
- kind: knowledge
137
- title: 数据库索引原理与B+树加速机制
138
- tags: [数据库, 索引, B+树, 查询优化]
139
- created: 2026-08-21T12:01:51.636Z
140
- updated: 2026-08-21T12:01:51.636Z
141
- source: session:session-xxxx
142
- ---
143
- # 数据库索引原理与B+树加速机制
144
-
145
- ## 核心定义
146
- - 索引是数据库为加速查找而额外维护的数据结构...
147
- ```
148
-
149
- Vault 目录结构:
150
-
151
- ```
152
- ~/.dsh/memory-vault/
153
- ├── 00-System/ # 系统(未来扩展:Active-Context 等)
154
- ├── 02-Projects/ # 项目背景 / 进度
155
- ├── 03-Knowledge/ # 通用知识 / 技术方案 / 设计决策(默认)
156
- ├── 04-Content/ # 内容素材 / 资料
157
- ├── 05-Prompts/ # 提示词 / 工作流
158
- ├── 06-Business/ # 业务 / 商业
159
- ├── 07-Tools/ # 工具链 / CLI / 配置 / 环境坑
160
- └── 08-Mistakes/ # 教训:错误 / 反模式 / 踩坑 /「别这么做」
161
- ```
162
-
163
- ## 🕸 图形化知识库(设置 → 记忆 + 侧边栏「记忆」入口)
164
-
165
- - **双入口**:设置 → 记忆 页 + 侧边栏底部「记忆」按钮(一键打开完整记忆库弹窗)
166
- - **统计概览**:总卡数、近 7 天新增、标签数、知识卡数
167
- - **检索**:中文片段即时搜索(280ms 防抖)
168
- - **知识卡网格**:kind 筛选(全部/项目/知识/内容/提示词/业务/工具/教训),点击卡片阅读全文(frontmatter 一并展示)
169
- - **知识图谱(增强版)**:**多环径向 / 力导向**混合布局(按 kind 分扇区、枢纽在内圈、环间距不重叠,数量大也不乱);节点 = 知识卡(按 kind 渐变+发光着色、按连接数自动放大);连线 = `[[wikilink]]`、共享标签或**相似度关联**(曲线渐变,只保留**高影响力主干边**避免毛球);**默认只显示枢纽标签**、放大后全量显示;**几千节点不卡**;**滚轮缩放、按住左键拖动画布**、**左键凸显关联节点并淡化无关节点**、悬停高亮
170
-
171
- ### 🕸 图谱交互速览
172
-
173
- | 操作 | 效果 |
174
- | --- | --- |
175
- | 左键点击节点 | 凸显该节点及关联(淡化无关) |
176
- | **右键节点** | 弹出菜单:打开卡片 / 凸显关联 / 复制名称 / **删除** |
177
- | **Shift + 拖拽** | 方形**框选多节点** → 浮出「导出选中 / 删除选中 / **合并** / 清空」 |
178
- | 滚轮 | 以光标为中心缩放 |
179
- | 左键拖空白 | 平移画布 |
180
- | 点图例 | **按 kind 过滤**(再点取消) |
181
- | **时间维** 开关 | 节点按最后更新时间着色:3 天内绿 / 2 周内琥珀 / 1 月内橙 / 更早灰蓝 |
182
- | 搜索框 | 标题 + 类型名匹配,无结果有提示 |
183
- | **导出** | 一键导出 PNG(预览弹窗:下载 / **另存为** / 复制 / 全屏) |
184
-
185
- ### 📇 知识卡管理
186
-
187
- - 卡片列表搜索命中,标题与摘要**关键词高亮**(`<mark>`)
188
- - 3 天内更新的卡自动标「**新**」角标;排序支持 最近 / 标题 / 热点
189
- - 打开长卡**默认折叠**,可「展开全部 / 收起」
190
- - 卡片行右下角「✕」**删除记忆**(确认后删),阅读器内亦可删除
191
- - 列表**无限滚动懒加载**(滚动到底自动加载下一批)
192
- - 自定义**滚动条**配色,明暗主题都清晰可见
193
- - 阅读全文走**轻量 Markdown 渲染**(标题/加粗/列表/代码块/引用/安全链接,先转义再白名单)
194
- - 记忆库工具栏:**导入 / 导出 MD / 导出 JSON / 刷新**(导出可选**自选位置**另存为,或默认下载)
195
- - 所有操作**顶部醒目 toast** 反馈:✓ 绿 = 成功、✕ 红 = 失败,并显示导出位置 / 文件名
196
- - 导出分两类:**知识卡片**(MD/JSON)与**知识图谱**(PNG),均可选**另存为**自定位置
197
-
198
- ## 🔧 设置
199
-
200
- | 设置项 | 默认 | 说明 |
201
- | --- | --- | --- |
202
- | 启用 | 开 | 总开关 |
203
- | 自动沉淀 | 开 | 每轮对话结束自动捕获 |
204
- | 自动召回 | | 注入 system prompt + `memory_recall` 工具 |
205
- | 记忆库目录 | | 留空 = `~/.dsh/memory-vault`;可指向任意目录(如 Obsidian Vault) |
206
- | 去重阈值 | 0.62 | 词法去重 Jaccard 相似度阈值(自研默认值) |
207
- | 捕获最小长度 | 200 | 少于该字符数的对话不触发捕获 |
208
- | 日配额 | 60 | 每天最多自动写卡数,防止烧 token |
209
-
210
- ## 🛠 开发 / 构建 / 测试
211
-
212
- ```bash
213
- npm i
214
- npm test # 29 个单元测试:vault 去重/检索/图谱 + capture 管线 + API 形状
215
- npm run build # 打包 client(改 src/client 后需要;改 index.js / lib 无需构建)
216
- ```
217
-
218
- 目录结构:
219
-
220
- ```
221
- dsh-memory-eternal/
222
- ├── index.js # host 半边:设置 + turn-stopping 钩子 + 工具 + JSON API
223
- ├── lib/
224
- │ ├── vault.js # Markdown Vault 存储层(frontmatter/去重/检索/图谱,纯 Node 可单测)
225
- │ └── capture.js # 自动沉淀管线(LLM 压缩 + 语义去重)
226
- ├── src/client/index.tsx # client 半边:设置页 + 侧边栏「记忆」按钮 + 完整记忆库弹窗(统计/搜索/卡片/图谱)
227
- ├── build.mjs # esbuild 打包 client → lib/client.js
228
- ├── cordis.patch.yml # bundle 补丁层(自动挂载 host 行)
229
- └── tests/ # node:test 单测
230
- ```
231
-
232
- ## 🗺 Roadmap / 待办
233
-
234
- - [x] 对话自动沉淀(turn-stopping 钩子 + LLM 压缩 + 双层去重)
235
- - [x] CJK 感知检索 + 本地 Markdown Vault(02-08 分类 + frontmatter)
236
- - [x] `memory_recall` 自动召回工具 + system prompt 分段
237
- - [x] 图形化知识库:统计 / 检索 / 知识卡网格
238
- - [x] 侧边栏「记忆」按钮 + 完整记忆库弹窗
239
- - [x] 增强知识图谱:力导向/径向布局、滚轮缩放、左键拖拽、右键凸显关联、大规模抗压
240
- - [x] 图谱交互增强:图例过滤、时间维着色、右键菜单、Shift 框选多选(导出/删除/合并)
241
- - [x] 知识卡管理:Markdown 渲染、长卡折叠、新卡角标、搜索高亮、最近/标题/热点排序
242
- - [x] 记忆库导出/导入(MD/JSON 备份,异地可迁移)
243
- - [x] 知识卡删除 + 无限滚动懒加载 + 自定义滚动条
244
- - [x] 导出可选默认下载 / 自选位置另存为(卡片 MD/JSON 与图谱 PNG)
245
- - [ ] 每日记忆回顾 + 用量统计
246
- - [ ] 多语言模板与自定义去重规则
247
- - [ ] 卡片拖拽归类 / 批量合并更智能(按相似度预分组)
248
- - [ ] Market 收录:dsh-marketplace / dsh-plugin-marketplace(待社区审核)
249
-
250
- ## 📦 发布记录
251
-
252
- - **v0.4.3**(待发布):记忆侧**省 token / 提质量**——**沉淀预筛**:turn-stopping 先做启发式「可复用信号」判定(结构化标记/决策/代码/事实关键词/列表密度),纯闲聊或无可复用内容**直接跳过、不触发 LLM**(省掉每轮昂贵的判定调用);**召回相关性 + 精简**:`memory_recall` 现按 `minScore` 过滤弱命中(降噪声)、注入片段截断到 130 字(降体积),召回更准、更省。
253
- - **v0.4.2**(已发布):修复 GitHub/npm 顶部产品演示 GIF 无法显示——压缩至 GitHub README 10MB 渲染阈值以下(8MB)。
254
- - **v0.4.1**(已发布):**Marketplace 收录标准**落地——声明 `package.json#dsh.marketplace`(`profiles:["web"]` 等),README 收录规则表对齐 dsh-marketplace / dsh-find-plugin / dsh-plugin-marketplace 的**自动扫描**机制;产品展示头部 GIF(`assets/screen/dsh-memory-eternal.gif`)置顶。
255
- - **v0.4.0**(已发布):图谱交互全面增强——**图例过滤**、**时间维着色**、**右键菜单**、**Shift 框选多选**、**单删/批量删除**、**多卡合并**、**一键导出 PNG**(预览:下载/另存为/复制/全屏);知识卡——**Markdown 渲染**、**长卡折叠**、**新卡角标**、**搜索命中高亮**、**卡片行删除**、**无限滚动懒加载**、最近/标题/热点排序、自定义滚动条;**相似度关联建议**(similar 边);**记忆库导出/导入**(MD/JSON 备份,导出可选**自选位置**);操作**顶部醒目绿/红 toast**(反馈结果 + 导出位置/文件名);默认视图与节点调大。
256
- - **v0.3.0**:知识图谱**科学布局**升级——多环径向/力导向混合布局(按 kind 分扇区、枢纽在内圈、环间距不重叠,数量大也不乱);只保留**高影响力主干边**(避免毛球);默认只显示**枢纽标签**、放大后全量;大图关闭节点发光提升性能。
257
- - **v0.2.9**:修复 **GitHub 源安装无法启动**——提交 `lib/client.js`(此前被忽略未入库,GitHub 拉取的包缺客户端 bundle,导致 DSH 启动失败)。
258
- - **v0.2.8**:安装说明去掉 `npx` 前缀,只保留跨平台 `dsh plugin --profile web add`。
259
- - **v0.2.7**:安装说明跨平台化——主推 `dsh plugin --profile web add`(无需 cd/平台路径),注明官方 registry(避免 npmmirror 滞后)与 pnpm 备选。
260
- - **v0.2.6**:安装/更新说明补充——DSH profile 为 pnpm workspace,用 `pnpm add dsh-memory-eternal@latest`(勿用 `npm install`,会因 `link:` 报 `EUNSUPPORTEDPROTOCOL`)。
261
- - **v0.2.5**:安装方式统一为 `dsh plugin --profile web add`(README 标注 npx 备选写法)。
262
- - **v0.2.4**:知识库分类扩展——新增「工具 Tools」(07) 与「教训 Mistakes」(08) 两类(错误/反模式/踩坑/debug 教训),前端筛选、图谱着色、自动捕获提示词同步更新。
263
- - **v0.2.3**:放宽兼容性——`@deepseek-ai/dsh-tools` / `dsh-llm` peer 依赖改为 `>=0.1.0-rc.2 <0.2.0`,跨整个 `0.1.x` 系列均兼容(大多数 DSH 版本可直接安装)。
264
- - **v0.2.2**:放宽 `@deepseek-ai/dsh-tools` / `dsh-llm` peer 依赖至 `^0.1.0-rc.7`,同时兼容旧版与新版 DSH。
265
- - **v0.2.1**:更新 `@deepseek-ai/dsh-tools` / `dsh-llm` peer 依赖至 `^0.1.1-rc.2`(对齐当前 DSH 运行时)。
266
- - **v0.2.0**:新增侧边栏「记忆」入口 + 完整记忆库弹窗;增强知识图谱(力导向/径向布局、滚轮缩放、左键拖拽平移、左键凸显关联/右键打开卡片、大规模抗压:节点/连线上限 + 标签节流 + O(n) 径向布局);README 头部动态演示 GIF。
267
- - **v0.1.0**:首个可用版本。对话自动沉淀(turn-stopping 钩子 + LLM 压缩 + 双层去重)、`memory_recall` 工具、system prompt 召回段、Markdown Vault(02-08 分类 + frontmatter)、CJK 检索、自研知识图谱、设置页图形化知识库。
268
-
269
- ## 📄 License
270
-
271
- MIT
272
-
273
- ---
274
-
275
- > 让 DSH 拥有第二大脑:**对话自动沉淀,知识随手可查。** 🧠
1
+ # 🧠 最强记忆大脑 dsh-memory-eternal 给 DeepSeek Harness 装上「第二大脑」
2
+
3
+ <p align="center">
4
+ <img src="https://img.shields.io/badge/DeepSeek%20Harness-plugin-3B82F6" alt="DSH plugin" />
5
+ <img src="https://img.shields.io/npm/v/dsh-memory-eternal" alt="npm version" />
6
+ <img src="https://img.shields.io/github/stars/EternalNight996/dsh-memory-eternal?style=flat" alt="GitHub stars" />
7
+ <img src="https://img.shields.io/github/license/EternalNight996/dsh-memory-eternal" alt="license" />
8
+ <img src="https://img.shields.io/badge/自研内核-CJK检索-10B981" alt="self-built" />
9
+ </p>
10
+
11
+ > **对话结束自动沉淀,跨会话真实不失忆;召回只取相关小块,省 token、少噪音。**
12
+ > 全自研、零第三方记忆框架、不改 DSH 源码、一条命令安装——为你装一个「自己说了算」的、可 git 管理的本地记忆库。
13
+
14
+ <p align="center"><strong>⭐ 觉得好用就点个 Star</strong>,让更多被「AI 总忘事」困扰的人用上它。<br/><sub>一条命令:<code>dsh plugin --profile web add dsh-memory-eternal</code></sub></p>
15
+
16
+ ---
17
+
18
+ <p align="center">
19
+ <img src="assets/screen/dsh-memory-eternal.gif" width="880" alt="记忆核心 · 对话自动沉淀 + 图形化知识库 + 增强知识图谱(动态演示)" />
20
+ <br/><em>对话自动沉淀 · 图形化知识库 · 交互式知识图谱(动态演示)</em>
21
+ </p>
22
+
23
+ ---
24
+
25
+ ## 🔥 先看痛点:AI 助手为什么「总让人失望」?
26
+
27
+ | # | 痛点(每个用 AGENT 的人都遇到过) | 没有记忆的后果 |
28
+ |---|---|---|
29
+ | 1 | **长对话就忘事** | 聊到第 10 轮,前面定的方案、改的口径全忘了,反复重来 |
30
+ | 2 | **跨会话失忆** | 换新会话 = 白纸一张,项目背景要重新讲一遍 |
31
+ | 3 | **长会话 token 暴涨** | 上下文越来越长,越聊越贵、越聊越慢,甚至爆窗 |
32
+ | 4 | **存了却找不到** | 就算有笔记,也搜不到、管不好,等于没存 |
33
+ | 5 | **数据被锁死** | 记忆存在私有库里,改不了、导不出、不敢用 |
34
+
35
+ > 这不是你的错觉,是**绝大多数 AI 产品共通的硬伤**。而解法,正是 Agent 记忆——**总结 → 存储 → 按需召回**。
36
+
37
+ ---
38
+
39
+ ## 🚀 装上它之后:五道痛点逐一被解决
40
+
41
+ | 痛点 | 装上记忆核心后 | 靠什么实现 |
42
+ |---|---|---|
43
+ | ① 长对话忘事 | 保留「最近原文」,旧轮次自动总结成卡 | 自动沉淀 + 语义去重 |
44
+ | 跨会话失忆 | 新会话也能召回旧决策/方案 | `memory_recall` 按需召回 |
45
+ | ③ 长会话 token | **只召回相关小块**,不每轮全量塞 | 相关性阈值 + 片段截断 + 沉淀预筛 |
46
+ | ④ 存了找不到 | 中文整词+bigram 检索 + 可视化图谱 | CJK 感知检索 + 知识图谱 |
47
+ | ⑤ 数据被锁死 | 纯 Markdown,可读可改可 git 托管 | 本地 Markdown Vault |
48
+
49
+ ```mermaid
50
+ flowchart LR
51
+ A["对话每一轮"] --> B{"turn-stopping<br/>有没有可复用信号?"}
52
+ B -- "无 → 跳过(省LLM)" --> X["❌ 不调模型"]
53
+ B -- "" --> C["LLM 压缩成知识卡"]
54
+ C --> D{"去重核对"}
55
+ D -- "同一主题" --> E["追加更新到已有卡"]
56
+ D -- "新主题" --> F["写入 02-08 分类目录"]
57
+ F & E --> G["本地 Markdown Vault"]
58
+ G --> H["memory_recall 按需召回"]
59
+ H --> I["AI 下次不再失忆"]
60
+ ```
61
+
62
+ ---
63
+
64
+ ## 🧬 核心内核设计:为什么要「全自研」
65
+
66
+ 市面上记忆方案很多,但大多**依赖第三方框架 / 动不动就起一个 MCP 服务 / 把记忆锁进私有库**。本插件把「记忆骨架」整个自己搭出来,**零第三方运行时依赖**,逻辑能逐行看懂:
67
+
68
+ | 模块 | 自研实现 | 替代什么 |
69
+ |---|---|---|
70
+ | **去重** | 词法 Jaccard bigram(0.62)+ **语义去重**(把已有卡索引喂模型,让它决定「新建 vs 追加」) | 防重复、防 LLM 改写导致的垃圾卡 |
71
+ | **检索** | CJK 感知:中文整词 + 字符 bigram 命中 | 无需全文搜索引擎,短查询也能命中长文 |
72
+ | **图谱** | 力导向/径向布局 + `[[wikilink]]`/共享标签/相似度连边 | 让「知识之间怎么关联」一眼看清 |
73
+ | **存储** | frontmatter 的普通 `.md`,落盘 `~/.dsh/memory-vault` | 不锁库、可读、可 git、可被任意工具读 |
74
+
75
+ > **与其他热门记忆项目的思路同向(总结→存储→按需召回),但定位不同。** 参考同类热门思路,本插件主打「**本地、自研、零依赖、可读可控**」:
76
+
77
+ | 热门项目 | 定位 | 本插件的差异 |
78
+ |---|---|---|
79
+ | [agentmemory](https://github.com/rohitg00/agentmemory) | 通用 Agent 记忆服务 | 本插件是 **DSH 原生插件**,不另起服务,直接进设置/侧边栏 |
80
+ | [mem0](https://github.com/mem0ai/mem0) | 记忆层(常配 MCP) | 本插件**无 MCP 依赖**,离线可跑,走本地 `memory_recall` 工具 |
81
+ | [MemGPT / Letta](https://github.com/letta-ai/letta) | 上下文内存管理 | 本插件承载「**持久记忆**」这一层;会话内实时压缩属 harness 层 |
82
+ | **[Zep](https://github.com/getzep/zep)** | 时间/图记忆 | 本插件零外部服务,图谱/检索纯本地 |
83
+
84
+ > 如果你已在用其中某一层,也完全能把本插件当「**本地持久记忆底座**」叠加使用——它只消费你已配置好的 provider,不抢模型、不改你的习惯。
85
+
86
+ ---
87
+
88
+ ## 功能总览(交互全在这里)
89
+
90
+ <details>
91
+ <summary><b>📇 知识卡:存得进、找得到、管得了</b></summary>
92
+
93
+ - **全自动沉淀**:每轮结束自动判定+压缩成 300-800 字知识卡,无需手动保存
94
+ - **双层去重**:Jaccard 词法 + 语义追加,不堆重复卡
95
+ - **`.md` 全文**:frontmatter 里带 `kind/title/tags/created/updated`,就是普通 Markdown
96
+ - **Markdown 渲染**、**长卡折叠**、**新卡「新」角标**、**搜索命中高亮**、**卡片行删除**
97
+ - **无限滚动懒加载**、自定义**滚动条**配色
98
+ - 排序:**最近 / 标题 / 热点(按连接数)**
99
+
100
+ </details>
101
+
102
+ <details>
103
+ <summary><b>🕸 知识图谱:秒级看懂「知识之间的关系」</b></summary>
104
+
105
+ - **力导向/径向混合布局**:按 kind 分扇区、枢纽在内圈、环间距不重叠,数量大也不乱;**几千节点不卡**
106
+ - 连线 = `[[wikilink]]` / 共享标签 / **相似度关联**;只保留**高影响力主干边**避免毛球
107
+ - 交互:**左键凸显关联**、**右键菜单**(打开/凸显/复制/删除)、**Shift 框选多选**(导出/删除/合并)、**图例过滤**、**时间维着色**(3天内绿/2周琥珀/1月橙/更早灰蓝)、**滚轮缩放**、**导图 PNG**(下载/另存为/复制/全屏)
108
+
109
+ </details>
110
+
111
+ <details>
112
+ <summary><b>💾 备份与控制:数据永远是你的</b></summary>
113
+
114
+ - 记忆库**导出 MD / JSON**、**导入**(自动去重)
115
+ - 导出可选**默认下载**或**自选位置**另存为
116
+ - 所有操作**顶部醒目 toast**:✓ 绿 = 成功、✕ 红 = 失败,并显示导出位置/文件名
117
+ - 可指向任意目录(如 Obsidian Vault)
118
+ </details>
119
+
120
+ <p align="center">
121
+ <img src="assets/screen/memory-settings.png" width="840" alt="设置 记忆:统计 + 检索 + 卡片/图谱 Tab" />
122
+ <br/><em>设置 → 记忆:统计概览、CJK 检索、知识卡网格、按 kind 分类</em>
123
+ </p>
124
+ <p align="center">
125
+ <img src="assets/screen/memory-popup.png" width="840" alt="侧边栏「记忆」一键打开完整记忆库弹窗" />
126
+ <br/><em>侧边栏底部「记忆」按钮 → 一键打开完整记忆库弹窗(含增强知识图谱)</em>
127
+ </p>
128
+
129
+ ---
130
+
131
+ ## 🚀 安装(一条命令)
132
+
133
+ ```bash
134
+ # 已发布后(npm)—— 跨平台通用
135
+ dsh plugin --profile web add dsh-memory-eternal
136
+
137
+ # 从 GitHub
138
+ dsh plugin --profile web add github:EternalNight996/dsh-memory-eternal
139
+
140
+ # 本地联调(改代码即时生效)
141
+ dsh plugin --profile web add F:/MyApp/eternal/dsh-memory-eternal
142
+ ```
143
+
144
+ > 提示:
145
+ > - `dsh` 为 DSH CLI(`npm i -g @deepseek-ai/dsh`)。
146
+ > - 要**官方最新**(避免 npmmirror 滞后):`npm_config_registry=https://registry.npmjs.org/ dsh plugin --profile web add dsh-memory-eternal`。
147
+ > - **profile 是 pnpm workspace**:`cd ~/.dsh/profiles/web && pnpm add dsh-memory-eternal@latest`(`npm install` 会报 `EUNSUPPORTEDPROTOCOL`)。
148
+
149
+ 装完**重启 dsh web**:设置 → 记忆 出现知识库;侧边栏底部出现「记忆」按钮。此后每轮对话自动沉淀。
150
+
151
+ ### 🧭 插件发现 / 收录标准
152
+
153
+ 仓库已打 **GitHub `dsh-plugin` topic** 并符合社区标准结构,**自动被以下机制发现**(无需手动 PR):
154
+
155
+ | 机制 | 收录方式 | 状态 |
156
+ |---|---|---|
157
+ | **dsh-marketplace**(ouyangyipeng) | 实时读 `topic:dsh-plugin` | ✅ topic 已打 |
158
+ | **dsh-find-plugin**(awesome-dsh-plugin) | 会话内按 topic+星数搜索 | ✅ |
159
+ | **dsh-plugin-marketplace**(YELEBAI) | 2h 自动扫描 + 静态验证进入 Registry | ✅ 已声明 `dsh.marketplace` 元数据 |
160
+ | **dsh-market**(2BingLing) | 提交 Issue | 已提 |
161
+ | **awesome-dsh-plugin** | 提交 PR | ✅ 已提 |
162
+
163
+ ---
164
+
165
+ ## 🔧 设置
166
+
167
+ | 设置项 | 默认 | 说明 |
168
+ |---|---|---|
169
+ | 启用 | | 总开关 |
170
+ | 自动沉淀 | 开 | 每轮对话结束自动捕获 |
171
+ | 自动召回 | 开 | 注入 system prompt + `memory_recall` 工具 |
172
+ | 记忆库目录 | 空 | 留空 = `~/.dsh/memory-vault`;可指向任意目录(如 Obsidian Vault) |
173
+ | 去重阈值 | 0.62 | 词法去重 Jaccard 相似度阈值 |
174
+ | 捕获最小长度 | 200 | 少于该字符数不触发捕获 |
175
+ | 日配额 | 60 | 每天最多自动写卡数,防止烧 token |
176
+
177
+ ---
178
+
179
+ ## 🛠 开发 / 构建 / 测试
180
+
181
+ ```bash
182
+ npm i
183
+ npm test # 单元测试:vault 去重/检索/图谱 + capture 管线 + API 形状
184
+ npm run build # 打包 client(改 src/client 后需要;改 index.js / lib 无需构建)
185
+ ```
186
+
187
+ ```
188
+ dsh-memory-eternal/
189
+ ├── index.js # host:设置 + turn-stopping 钩子 + 工具 + JSON API
190
+ ├── lib/
191
+ │ ├── vault.js # 存储层(frontmatter/去重/检索/图谱)
192
+ │ └── capture.js # 自动沉淀管线(LLM 压缩 + 语义去重 + 预筛)
193
+ ├── src/client/index.tsx # client:设置页 + 侧边栏「记忆」+ 完整记忆库弹窗
194
+ ├── build.mjs # esbuild 打包 client lib/client.js
195
+ ├── cordis.patch.yml # bundle 补丁层(自动挂载 host 行)
196
+ └── tests/ # node:test 单测
197
+ ```
198
+
199
+ ---
200
+
201
+ ## 🗺 Roadmap / 待办(记忆侧核心优化)
202
+
203
+ **已实现**:
204
+ - 自动沉淀 + 双层去重 + 日配额
205
+ - CJK 检索 + 图谱 + 召回工具
206
+ - 交互图谱(图例/时间维/框选/删除合并/导出)
207
+ - 知识卡管理(MD渲染/折叠/新角标/搜索高亮/无限滚动)
208
+ - 备份导入导出(MD/JSON)
209
+ - **沉淀预筛**(skip LLM)+ **召回相关性/截断**(v0.4.3)
210
+ - **注入体积可配置**(召回条数/摘要长度/是否带正文,设置项)
211
+ - **多 Vault / 多 Profile**(命名分库,当前激活一个)
212
+ - **每日回顾 + 用量统计**(今日新增/近7天/分类/今日沉淀清单)
213
+ - **整理建议**(相似卡对合并/陈旧卡,非破坏预览)
214
+ - **召回用户反馈**(`/feedback`,为相关性调优留数据)
215
+ - **会话预算**(`sessionBudgetChars` + `/budget`)
216
+
217
+ **待办(核心功能优化)**:
218
+ - [x] ~~多 Vault / 多 Profile~~、~~注入体积可配置~~、~~每日回顾+用量~~、~~自动归档整理~~、~~召回用户反馈~~、~~会话级 token 预算(接口+配置)~~(v0.4.5)
219
+ - [ ] **上下文压缩真正落地**:记忆侧已提供 `/compress` 压缩产物接口,待 harness 在会话内接「召回替代 + 压缩旧轮」(需 harness 配合)
220
+ - [ ] **语义召回升级**:`recallEmbedding` 接口已预留,接入 embedding provider 后召回更精
221
+ - [ ] 自动归档的**批量智能合并**(按相似度预分组后一键合并多张)
222
+ - [ ] Vault 的**图谱/检索跨库聚合**
223
+ - [ ] 每日回顾的**定时推送**(主动提醒而非手动查看)
224
+
225
+ ---
226
+
227
+ ## 📦 发布记录
228
+
229
+ - **v0.4.5**:记忆侧**核心功能优化**——**注入体积可配置**(召回条数/摘要长度/是否带正文)、**多 Vault/多 Profile**(命名分库)、**每日回顾 + 用量统计**(管理面板:用量/今日/整理建议)、**自动归档整理建议**(相似卡对合并/陈旧卡,非破坏预览)、**召回用户反馈**、**会话级 token 预算**(`sessionBudgetChars` + `/budget`)、**上下文压缩接口**(`/compress`,供 harness 会话内压缩)。
230
+ - **v0.4.3**:记忆侧**省 token/提质量**——沉淀预筛(无可复用信号直接跳过、不触发 LLM);召回按 `minScore` 过滤弱命中、注入片段截断到 130 字。
231
+ - **v0.4.2**:修复 GitHub/npm 顶部演示 GIF 无法显示(压缩至 README 10MB 阈值以下,8MB)。
232
+ - **v0.4.1**:Marketplace 收录标准落地(`dsh.marketplace` 元数据 + 自动扫描规则说明)。
233
+ - **v0.4.0**:图谱交互全面增强(图例过滤/时间维/右键菜单/框选/删除合并/导出PNG另存自选位置)+ 知识卡增强(MD渲染/长卡折叠/新角标/搜索高亮/删除/无限滚动)+ 相似度关联 + 导入导出 + 顶部绿红 toast。
234
+ - **v0.3.0**:知识图谱科学布局(多环径向/主干边/默认仅枢纽标签/大图关发光)。
235
+ - **v0.2.x**:GitHub 源安装修复、安装说明跨平台化/去 npx、分类扩展(工具/教训)、peer 依赖放宽。
236
+ - **v0.1.0**:首个可用版本(自动沉淀 + 召回工具 + Markdown Vault + CJK 检索 + 图谱 + 设置页)。
237
+
238
+ ---
239
+
240
+ ## 📄 License
241
+
242
+ MIT
243
+
244
+ ---
245
+
246
+ > **让 DSH 真正记住你:对话自动沉淀,知识随手可查。** ⭐ 觉得有用就点个 Star,Let's make AI not forget.
package/index.js CHANGED
@@ -19,7 +19,8 @@ import path from 'node:path'
19
19
  import os from 'node:os'
20
20
  import z from '@deepseek-ai/schemastery'
21
21
  import { defineTool } from '@deepseek-ai/dsh-tools'
22
- import { ensureVault, listCards, readCard, search, graph, overview, exportCards, deleteCard, writeCard, parseCard } from './lib/vault.js'
22
+ import { ensureVault, listCards, readCard, search, graph, overview, exportCards, deleteCard, writeCard, parseCard, stats, optimizeCandidates, readFeedback, addFeedback } from './lib/vault.js'
23
+ import { compressExcerpt } from './lib/capture.js'
23
24
  import { summarizeTurn, extractLastTurn, sliceNewEvents, resolveRoute, captureCard, captureUpdate, pickNeighbors } from './lib/capture.js'
24
25
 
25
26
  export const name = 'memory-eternal'
@@ -34,6 +35,17 @@ export const Config = z.object({
34
35
  captureMinChars: z.number().default(200),
35
36
  captureCooldownMs: z.number().default(5 * 60 * 1000),
36
37
  maxCardsPerDay: z.number().default(60),
38
+ // 注入体积可配置(召回)
39
+ recallLimit: z.number().min(1).max(20).default(5),
40
+ recallSummaryLen: z.number().min(40).max(400).default(130),
41
+ recallIncludeBody: z.boolean().default(false),
42
+ // 多 Vault / 多 Profile:命名分库,当前激活一个
43
+ vaultProfiles: z.array(z.object({ name: z.string(), path: z.string() })).default([]),
44
+ activeVault: z.string().default(''),
45
+ // 语义召回(可选 embedding provider,默认空=零依赖 bigram + LLM 判定兜底)
46
+ recallEmbedding: z.string().default(''),
47
+ // 会话级 token 预算(字符),供 harness 触发压缩/轮换;记忆侧提供估算与阈值
48
+ sessionBudgetChars: z.number().default(80000),
37
49
  })
38
50
 
39
51
  const API_PREFIX = '/memory-eternal/api'
@@ -43,6 +55,11 @@ export function apply(ctx, config) {
43
55
 
44
56
  const vaultDir = () => {
45
57
  const cfg = settings.get() ?? {}
58
+ // 多 Vault:若配了 vaultProfiles 且选中了 activeVault,则用该 profile 的目录。
59
+ const profiles = Array.isArray(cfg.vaultProfiles) ? cfg.vaultProfiles : []
60
+ const active = String(cfg.activeVault || '').trim()
61
+ const hit = active && profiles.find((p) => p.name === active)
62
+ if (hit && hit.path && hit.path.trim()) return path.resolve(hit.path.trim())
46
63
  if (cfg.vaultDir && cfg.vaultDir.trim()) return path.resolve(cfg.vaultDir.trim())
47
64
  const home = process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
48
65
  return path.join(home, 'memory-vault')
@@ -187,13 +204,19 @@ export function apply(ctx, config) {
187
204
  if (!cfg.enabled) return '(记忆核心已禁用)'
188
205
  const query = String(args.query || '').trim()
189
206
  if (!query) return '(未提供检索词)'
190
- const limit = Math.min(Math.max(Number(args.limit) || 5, 1), 20)
207
+ const cfg2 = settings.get() ?? {}
208
+ const defLimit = Number(cfg2.recallLimit) || 5
209
+ const defLen = Number(cfg2.recallSummaryLen) || 130
210
+ const includeBody = cfg2.recallIncludeBody === true
211
+ const limit = Math.min(Math.max(Number(args.limit) || defLimit, 1), 20)
191
212
  const hits = await search(vaultDir(), query, { limit, minScore: 2 })
192
213
  if (hits.length === 0) return `记忆库中没有与「${query}」相关的内容。`
193
214
  const lines = hits.map((h, i) => {
194
215
  const tags = h.tags.length ? ` [${h.tags.join(', ')}]` : ''
195
- const snippet = String(h.summary || '').replace(/\s+/g, ' ').trim().slice(0, 130)
196
- return `### ${i + 1}. ${h.title}${tags}\n路径:${h.path}\n${snippet}`
216
+ const snippet = String(includeBody ? h.summary : h.summary || '')
217
+ .replace(/\s+/g, ' ').trim().slice(0, defLen)
218
+ const body = includeBody ? `\n${String(h.excerpt || '')}` : ''
219
+ return `### ${i + 1}. ${h.title}${tags}\n路径:${h.path}\n${snippet}${body}`
197
220
  })
198
221
  return `从记忆核心检索到 ${hits.length} 条相关卡片:\n\n${lines.join('\n\n')}`
199
222
  },
@@ -320,6 +343,42 @@ async function handleApi(req, res, vaultRoot) {
320
343
  json(res, 200, { ok: true })
321
344
  return
322
345
  }
346
+ case '/stats': {
347
+ await ensureVault(vaultRoot)
348
+ json(res, 200, { ok: true, ...(await stats(vaultRoot)) })
349
+ return
350
+ }
351
+ case '/optimize': {
352
+ // 非破坏性:只返回「整理建议」(相似卡对 + 陈旧卡),不自动删改。
353
+ json(res, 200, { ok: true, ...(await optimizeCandidates(vaultRoot)) })
354
+ return
355
+ }
356
+ case '/feedback': {
357
+ let raw = ''
358
+ for await (const chunk of req) raw += chunk
359
+ let rec
360
+ try { rec = JSON.parse(raw || '{}') } catch { return json(res, 400, { ok: false, error: 'JSON 解析失败' }) }
361
+ if (!rec || !rec.path) return json(res, 400, { ok: false, error: '缺少 path' })
362
+ await addFeedback(vaultRoot, { query: String(rec.query || ''), path: String(rec.path || ''), useful: rec.useful === true })
363
+ json(res, 200, { ok: true })
364
+ return
365
+ }
366
+ case '/budget': {
367
+ const cfg = settings.get() ?? {}
368
+ json(res, 200, { ok: true, budgetChars: cfg.sessionBudgetChars ?? 80000, recallLimit: cfg.recallLimit ?? 5, embedding: cfg.recallEmbedding || '' })
369
+ return
370
+ }
371
+ case '/compress': {
372
+ // 记忆侧「压缩产物」接口:供 harness 在会话内压缩旧轮次时调用,返回一段可注入的摘要。
373
+ const cfg = settings.get() ?? {}
374
+ const body = new URLSearchParams(query)
375
+ const text = body.get('text') || ''
376
+ const maxChars = Math.min(Number(body.get('max')) || 2400, 6000)
377
+ if (!text.trim()) return json(res, 400, { ok: false, error: '缺少 text' })
378
+ const compressed = await compressExcerpt(text, maxChars)
379
+ json(res, 200, { ok: true, compressed, budgetChars: cfg.sessionBudgetChars ?? 80000 })
380
+ return
381
+ }
323
382
  default:
324
383
  json(res, 404, { ok: false, error: '未知接口' })
325
384
  }
package/lib/capture.js CHANGED
@@ -221,6 +221,17 @@ export function sliceNewEvents(events, lastSeq) {
221
221
  return out
222
222
  }
223
223
 
224
+ /** 解析当前模型路由:取第一个 provider 的旗舰模型(与驯兽师一致)。 */
225
+ /** 记忆侧「压缩产物」接口:确定性(无 LLM)从一段文本抽出结构化关键内容,供 harness 会话内压缩旧轮次时注入。 */
226
+ export function compressExcerpt(text, maxChars = 2400) {
227
+ const lines = String(text || '').split(/\r?\n/).map((L) => L.trim()).filter(Boolean)
228
+ const structured = lines.filter((L) => /^(#{1,6}\s|[-*]\s|\d+[.、)]\s|>|`|\[x\]|\[ \])/.test(L))
229
+ const src = structured.length ? structured : lines
230
+ let out = src.join('\n')
231
+ if (out.length > maxChars) out = out.slice(0, maxChars).replace(/\n+$/, '') + '\n…(已压缩,细节见记忆库)'
232
+ return out
233
+ }
234
+
224
235
  /** 解析当前模型路由:取第一个 provider 的旗舰模型(与驯兽师一致)。 */
225
236
  export async function resolveRoute(llm) {
226
237
  try {
package/lib/client.js CHANGED
@@ -3,7 +3,7 @@ window.__ModuleLoader__.load({
3
3
  factory: (require) => {
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
- var it=Object.create;var je=Object.defineProperty;var st=Object.getOwnPropertyDescriptor;var lt=Object.getOwnPropertyNames;var ct=Object.getPrototypeOf,dt=Object.prototype.hasOwnProperty;var pt=(n,l)=>{for(var m in l)je(n,m,{get:l[m],enumerable:!0})},et=(n,l,m,h)=>{if(l&&typeof l=="object"||typeof l=="function")for(let i of lt(l))!dt.call(n,i)&&i!==m&&je(n,i,{get:()=>l[i],enumerable:!(h=st(l,i))||h.enumerable});return n};var mt=(n,l,m)=>(m=n!=null?it(ct(n)):{},et(l||!n||!n.__esModule?je(m,"default",{value:n,enumerable:!0}):m,n)),ut=n=>et(je({},"__esModule",{value:!0}),n);var Et={};pt(Et,{apply:()=>xt,inject:()=>gt});module.exports=ut(Et);var c=mt(require("react"),1),o=require("react/jsx-runtime"),ke="memory-eternal",ee="/memory-eternal/api",gt=["settingsScope","slots","locale","connection","remote"],ft={nav:"\u8BB0\u5FC6",loading:"\u52A0\u8F7D\u4E2D\u2026",refresh:"\u5237\u65B0",overview:"\u8BB0\u5FC6\u5E93\u6982\u89C8",total:"\u77E5\u8BC6\u5361",recent:"\u8FD1 7 \u5929\u65B0\u589E",tags:"\u6807\u7B7E",searchPlaceholder:"\u641C\u7D22\u8BB0\u5FC6\uFF08\u652F\u6301\u4E2D\u6587\u7247\u6BB5\uFF09\u2026",search:"\u641C\u7D22",all:"\u5168\u90E8",kindProject:"\u9879\u76EE",kindKnowledge:"\u77E5\u8BC6",kindContent:"\u5185\u5BB9",kindPrompt:"\u63D0\u793A\u8BCD",kindBusiness:"\u4E1A\u52A1",kindTool:"\u5DE5\u5177",kindMistake:"\u6559\u8BAD",cardsTab:"\u77E5\u8BC6\u5361",graphTab:"\u77E5\u8BC6\u56FE\u8C31",graph:"\u77E5\u8BC6\u56FE\u8C31",graphHint:"\u8282\u70B9=\u77E5\u8BC6\u5361\uFF1B\u8FDE\u7EBF=[[\u94FE\u63A5]] \u6216\u5171\u4EAB\u6807\u7B7E\u3002\u70B9\u51FB\u8282\u70B9\u9605\u8BFB\u5361\u7247\u3002",graphTip:"\u5DE6\u952E\u51F8\u663E \xB7 Shift+\u62D6\u62FD\u6846\u9009 \xB7 \u53F3\u952E\u83DC\u5355 \xB7 \u6EDA\u8F6E\u7F29\u653E \xB7 \u62D6\u62FD\u5E73\u79FB",rebuild:"\u91CD\u5EFA",reset:"\u91CD\u7F6E",expandAll:"\u5168\u90E8\u5C55\u5F00",capped:"\u5DF2\u5C55\u793A\u6700\u6838\u5FC3\u7684",clearFocus:"\u53D6\u6D88\u9AD8\u4EAE",empty:"\u8BB0\u5FC6\u5E93\u8FD8\u662F\u7A7A\u7684\u3002\u591A\u804A\u51E0\u8F6E\u540E\uFF0C\u503C\u5F97\u4FDD\u5B58\u7684\u5185\u5BB9\u4F1A\u81EA\u52A8\u6C89\u6DC0\u6210\u77E5\u8BC6\u5361\u3002",emptyGraph:"\u56FE\u8C31\u6682\u65E0\u6570\u636E\u3002",open:"\u9605\u8BFB",updated:"\u66F4\u65B0\u4E8E",created:"\u521B\u5EFA\u4E8E",source:"\u6765\u6E90",close:"\u5173\u95ED",vaultDir:"\u8BB0\u5FC6\u5E93\u76EE\u5F55",capture:"\u81EA\u52A8\u6C89\u6DC0",recall:"\u81EA\u52A8\u53EC\u56DE",enabled:"\u5DF2\u542F\u7528",disabled:"\u5DF2\u7981\u7528",error:"\u52A0\u8F7D\u5931\u8D25",back:"\u8FD4\u56DE\u5217\u8868",cardCount:"\u5F20\u5361",nodes:"\u8282\u70B9",edges:"\u8FDE\u7EBF",memoryTitle:"\u8BB0\u5FC6\u5E93",memoryHint:"\u4F60\u7684\u672C\u5730\u7B2C\u4E8C\u5927\u8111",zoomIn:"\u653E\u5927",zoomOut:"\u7F29\u5C0F",fit:"\u9002\u5E94",exportGraph:"\u5BFC\u51FA",openCard:"\u6253\u5F00\u5361\u7247",focusNeighbors:"\u51F8\u663E\u5173\u8054",copyName:"\u590D\u5236\u540D\u79F0",noMatch:"\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5FC6",clearFilter:"\u6E05\u9664\u8FC7\u6EE4",sortRecent:"\u6700\u8FD1",sortTitle:"\u6807\u9898",sortHot:"\u70ED\u70B9",exportSel:"\u5BFC\u51FA\u9009\u4E2D",clearSelection:"\u6E05\u7A7A\u9009\u62E9",download:"\u4E0B\u8F7D",openNewTab:"\u65B0\u6807\u7B7E\u6253\u5F00",copyImage:"\u590D\u5236\u56FE\u7247",done:"\u5B8C\u6210",downloaded:"\u5DF2\u5F00\u59CB\u4E0B\u8F7D",openedTab:"\u5DF2\u5728\u65B0\u6807\u7B7E\u6253\u5F00",copied:"\u5DF2\u590D\u5236",exportedSel:"\u4E2A\u8282\u70B9\u5DF2\u5BFC\u51FA",fullscreen:"\u5168\u5C4F",exitFull:"\u9000\u51FA\u5168\u5C4F",copyFail:"\u590D\u5236\u5931\u8D25",timeDim:"\u65F6\u95F4\u7EF4",timeNew:"3 \u5929\u5185",timeRecent:"2 \u5468\u5185",timeMonth:"1 \u6708\u5185",timeOld:"\u66F4\u65E9",newBadge:"\u65B0",expand:"\u5C55\u5F00\u5168\u90E8",collapse:"\u6536\u8D77",exportVault:"\u5BFC\u51FAMD",exportJson:"\u5BFC\u51FAJSON",exporting:"\u5BFC\u51FA\u4E2D\u2026",exportedVault:"\u8BB0\u5FC6\u5E93\u5DF2\u5BFC\u51FA",exportFail:"\u5BFC\u51FA\u5931\u8D25",importVault:"\u5BFC\u5165",importedVault:"\u5BFC\u5165\u5B8C\u6210",importedSkipped:"\uFF08\u8DF3\u8FC7\u91CD\u590D ",importFail:"\u5BFC\u5165\u5931\u8D25",location:"\u81EA\u9009\u4F4D\u7F6E",saveAs:"\u53E6\u5B58\u4E3A",saveAsUnsupported:"\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u9009\u62E9\u4FDD\u5B58\u4F4D\u7F6E",exportedTo:"\u5DF2\u5BFC\u51FA\u5230",defaultDownloads:"\u9ED8\u8BA4\u4E0B\u8F7D\u6587\u4EF6\u5939",exportCancel:"\u5DF2\u53D6\u6D88\u5BFC\u51FA",delete:"\u5220\u9664",deleteConfirm:"\u786E\u5B9A\u5220\u9664\u8BE5\u8BB0\u5FC6\u5361\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002",deleted:"\u5DF2\u5220\u9664",deleteFail:"\u5220\u9664\u5931\u8D25",deleteSelected:"\u5220\u9664\u9009\u4E2D",deletedSelected:"\u5F20\u5361\u5DF2\u5220\u9664",merge:"\u5408\u5E76",mergeNeed:"\u81F3\u5C11\u9009 2 \u5F20\u5361",mergeConfirm:"\u5C06\u6240\u9009\u5361\u7247\u5408\u5E76\u4E3A\u4E00\u5F20\u5E76\u5220\u9664\u539F\u5361\uFF1F",merged:"\u5DF2\u5408\u5E76",mergeFail:"\u5408\u5E76\u5931\u8D25"},ht={nav:"Memory",loading:"Loading\u2026",refresh:"Refresh",overview:"Memory Overview",total:"Cards",recent:"Added (7d)",tags:"Tags",searchPlaceholder:"Search memory\u2026",search:"Search",all:"All",kindProject:"Projects",kindKnowledge:"Knowledge",kindContent:"Content",kindPrompt:"Prompts",kindBusiness:"Business",kindTool:"Tools",kindMistake:"Mistakes",cardsTab:"Cards",graphTab:"Graph",graph:"Knowledge Graph",graphHint:"Nodes = cards; edges = [[links]] or shared tags. Click a node to read.",graphTip:"Left-click highlights \xB7 Shift+drag box-select \xB7 right-click menu \xB7 scroll zoom \xB7 drag to pan",rebuild:"Rebuild",reset:"Reset",expandAll:"Expand all",capped:"Showing the",clearFocus:"Clear highlight",empty:"Memory is empty. After a few conversations, valuable content is auto-captured.",emptyGraph:"No graph data yet.",open:"Read",updated:"Updated",created:"Created",source:"Source",close:"Close",vaultDir:"Vault directory",capture:"Auto capture",recall:"Auto recall",enabled:"on",disabled:"off",error:"Load failed",back:"Back to list",cardCount:"cards",nodes:"nodes",edges:"edges",memoryTitle:"Memory Library",memoryHint:"Your local second brain",zoomIn:"Zoom in",zoomOut:"Zoom out",fit:"Fit",exportGraph:"Export",openCard:"Open card",focusNeighbors:"Highlight links",copyName:"Copy name",noMatch:"No matching memory",clearFilter:"Clear filter",sortRecent:"Recent",sortTitle:"Title",sortHot:"Hot",exportSel:"Export selected",clearSelection:"Clear selection",download:"Download",openNewTab:"Open in new tab",copyImage:"Copy image",done:"Done",downloaded:"Download started",openedTab:"Opened in new tab",copied:"Copied",exportedSel:" nodes exported",fullscreen:"Fullscreen",exitFull:"Exit fullscreen",copyFail:"Copy failed",timeDim:"Time",timeNew:"\u22643 days",timeRecent:"\u22642 weeks",timeMonth:"\u22641 month",timeOld:"Older",newBadge:"NEW",expand:"Expand",collapse:"Collapse",exportVault:"Export MD",exportJson:"Export JSON",exporting:"Exporting\u2026",exportedVault:"Vault exported",exportFail:"Export failed",importVault:"Import",importedVault:"Import complete",importedSkipped:" (skipped dup ",importFail:"Import failed",location:"Choose location",saveAs:"Save as",saveAsUnsupported:"Choose-save-location not supported here",exportedTo:"Exported to",defaultDownloads:"default Downloads folder",exportCancel:"Export cancelled",delete:"Delete",deleteConfirm:"Delete this memory card? This cannot be undone.",deleted:"Deleted",deleteFail:"Delete failed",deleteSelected:"Delete selected",deletedSelected:" cards deleted",merge:"Merge",mergeNeed:"Select at least 2 cards",mergeConfirm:"Merge the selected cards into one and delete the originals?",merged:"Merged",mergeFail:"Merge failed"},bt=["all","project","knowledge","content","prompt","business","tool","mistake"],Pe={project:"#3B82F6",knowledge:"#10B981",content:"#F59E0B",prompt:"#A855F7",business:"#EC4899",tool:"#06B6D4",mistake:"#EF4444",other:"#6B7280"},Ce={project:"kindProject",knowledge:"kindKnowledge",content:"kindContent",prompt:"kindPrompt",business:"kindBusiness",tool:"kindTool",mistake:"kindMistake",other:"kindKnowledge"},Ne=`
6
+ var st=Object.create;var Oe=Object.defineProperty;var lt=Object.getOwnPropertyDescriptor;var dt=Object.getOwnPropertyNames;var ct=Object.getPrototypeOf,pt=Object.prototype.hasOwnProperty;var mt=(a,s)=>{for(var p in s)Oe(a,p,{get:s[p],enumerable:!0})},tt=(a,s,p,b)=>{if(s&&typeof s=="object"||typeof s=="function")for(let r of dt(s))!pt.call(a,r)&&r!==p&&Oe(a,r,{get:()=>s[r],enumerable:!(b=lt(s,r))||b.enumerable});return a};var ut=(a,s,p)=>(p=a!=null?st(ct(a)):{},tt(s||!a||!a.__esModule?Oe(p,"default",{value:a,enumerable:!0}):p,a)),gt=a=>tt(Oe({},"__esModule",{value:!0}),a);var Et={};mt(Et,{apply:()=>xt,inject:()=>ft});module.exports=gt(Et);var d=ut(require("react"),1),e=require("react/jsx-runtime"),Me="memory-eternal",q="/memory-eternal/api",ft=["settingsScope","slots","locale","connection","remote"],bt={nav:"\u8BB0\u5FC6",loading:"\u52A0\u8F7D\u4E2D\u2026",refresh:"\u5237\u65B0",overview:"\u8BB0\u5FC6\u5E93\u6982\u89C8",total:"\u77E5\u8BC6\u5361",recent:"\u8FD1 7 \u5929\u65B0\u589E",tags:"\u6807\u7B7E",searchPlaceholder:"\u641C\u7D22\u8BB0\u5FC6\uFF08\u652F\u6301\u4E2D\u6587\u7247\u6BB5\uFF09\u2026",search:"\u641C\u7D22",all:"\u5168\u90E8",kindProject:"\u9879\u76EE",kindKnowledge:"\u77E5\u8BC6",kindContent:"\u5185\u5BB9",kindPrompt:"\u63D0\u793A\u8BCD",kindBusiness:"\u4E1A\u52A1",kindTool:"\u5DE5\u5177",kindMistake:"\u6559\u8BAD",cardsTab:"\u77E5\u8BC6\u5361",graphTab:"\u77E5\u8BC6\u56FE\u8C31",graph:"\u77E5\u8BC6\u56FE\u8C31",graphHint:"\u8282\u70B9=\u77E5\u8BC6\u5361\uFF1B\u8FDE\u7EBF=[[\u94FE\u63A5]] \u6216\u5171\u4EAB\u6807\u7B7E\u3002\u70B9\u51FB\u8282\u70B9\u9605\u8BFB\u5361\u7247\u3002",graphTip:"\u5DE6\u952E\u51F8\u663E \xB7 Shift+\u62D6\u62FD\u6846\u9009 \xB7 \u53F3\u952E\u83DC\u5355 \xB7 \u6EDA\u8F6E\u7F29\u653E \xB7 \u62D6\u62FD\u5E73\u79FB",rebuild:"\u91CD\u5EFA",reset:"\u91CD\u7F6E",expandAll:"\u5168\u90E8\u5C55\u5F00",capped:"\u5DF2\u5C55\u793A\u6700\u6838\u5FC3\u7684",clearFocus:"\u53D6\u6D88\u9AD8\u4EAE",empty:"\u8BB0\u5FC6\u5E93\u8FD8\u662F\u7A7A\u7684\u3002\u591A\u804A\u51E0\u8F6E\u540E\uFF0C\u503C\u5F97\u4FDD\u5B58\u7684\u5185\u5BB9\u4F1A\u81EA\u52A8\u6C89\u6DC0\u6210\u77E5\u8BC6\u5361\u3002",emptyGraph:"\u56FE\u8C31\u6682\u65E0\u6570\u636E\u3002",open:"\u9605\u8BFB",updated:"\u66F4\u65B0\u4E8E",created:"\u521B\u5EFA\u4E8E",source:"\u6765\u6E90",close:"\u5173\u95ED",vaultDir:"\u8BB0\u5FC6\u5E93\u76EE\u5F55",capture:"\u81EA\u52A8\u6C89\u6DC0",recall:"\u81EA\u52A8\u53EC\u56DE",enabled:"\u5DF2\u542F\u7528",disabled:"\u5DF2\u7981\u7528",error:"\u52A0\u8F7D\u5931\u8D25",back:"\u8FD4\u56DE\u5217\u8868",cardCount:"\u5F20\u5361",nodes:"\u8282\u70B9",edges:"\u8FDE\u7EBF",memoryTitle:"\u8BB0\u5FC6\u5E93",memoryHint:"\u4F60\u7684\u672C\u5730\u7B2C\u4E8C\u5927\u8111",zoomIn:"\u653E\u5927",zoomOut:"\u7F29\u5C0F",fit:"\u9002\u5E94",exportGraph:"\u5BFC\u51FA",openCard:"\u6253\u5F00\u5361\u7247",focusNeighbors:"\u51F8\u663E\u5173\u8054",copyName:"\u590D\u5236\u540D\u79F0",noMatch:"\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5FC6",clearFilter:"\u6E05\u9664\u8FC7\u6EE4",sortRecent:"\u6700\u8FD1",sortTitle:"\u6807\u9898",sortHot:"\u70ED\u70B9",exportSel:"\u5BFC\u51FA\u9009\u4E2D",clearSelection:"\u6E05\u7A7A\u9009\u62E9",download:"\u4E0B\u8F7D",openNewTab:"\u65B0\u6807\u7B7E\u6253\u5F00",copyImage:"\u590D\u5236\u56FE\u7247",done:"\u5B8C\u6210",downloaded:"\u5DF2\u5F00\u59CB\u4E0B\u8F7D",openedTab:"\u5DF2\u5728\u65B0\u6807\u7B7E\u6253\u5F00",copied:"\u5DF2\u590D\u5236",exportedSel:"\u4E2A\u8282\u70B9\u5DF2\u5BFC\u51FA",fullscreen:"\u5168\u5C4F",exitFull:"\u9000\u51FA\u5168\u5C4F",copyFail:"\u590D\u5236\u5931\u8D25",timeDim:"\u65F6\u95F4\u7EF4",timeNew:"3 \u5929\u5185",timeRecent:"2 \u5468\u5185",timeMonth:"1 \u6708\u5185",timeOld:"\u66F4\u65E9",newBadge:"\u65B0",expand:"\u5C55\u5F00\u5168\u90E8",collapse:"\u6536\u8D77",exportVault:"\u5BFC\u51FAMD",exportJson:"\u5BFC\u51FAJSON",exporting:"\u5BFC\u51FA\u4E2D\u2026",exportedVault:"\u8BB0\u5FC6\u5E93\u5DF2\u5BFC\u51FA",exportFail:"\u5BFC\u51FA\u5931\u8D25",importVault:"\u5BFC\u5165",importedVault:"\u5BFC\u5165\u5B8C\u6210",importedSkipped:"\uFF08\u8DF3\u8FC7\u91CD\u590D ",importFail:"\u5BFC\u5165\u5931\u8D25",location:"\u81EA\u9009\u4F4D\u7F6E",saveAs:"\u53E6\u5B58\u4E3A",saveAsUnsupported:"\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u9009\u62E9\u4FDD\u5B58\u4F4D\u7F6E",exportedTo:"\u5DF2\u5BFC\u51FA\u5230",defaultDownloads:"\u9ED8\u8BA4\u4E0B\u8F7D\u6587\u4EF6\u5939",exportCancel:"\u5DF2\u53D6\u6D88\u5BFC\u51FA",manage:"\u7BA1\u7406",tabUsage:"\u7528\u91CF/\u4ECA\u65E5",tabOptimize:"\u6574\u7406\u5EFA\u8BAE",todayAdd:"\u4ECA\u65E5\u65B0\u589E",weekAdd:"\u8FD1 7 \u5929",byKind:"\u5206\u7C7B\u7EDF\u8BA1",todayList:"\u4ECA\u65E5\u6C89\u6DC0",budgetLabel:"\u4F1A\u8BDD\u9884\u7B97",budgetChars:"\u9884\u7B97\u5B57\u7B26",recallLimitLabel:"\u53EC\u56DE\u6761\u6570",embeddingLabel:"\u8BED\u4E49\u53EC\u56DE",mergePairs:"\u76F8\u4F3C\u5361\u5BF9\uFF08\u53EF\u5408\u5E76\uFF09",staleCards:"\u9648\u65E7\u5361\uFF08>90 \u5929\u672A\u66F4\u65B0\uFF09",noOptimize:"\u6682\u65E0\u53EF\u6574\u7406\u9879\uFF0C\u5F88\u5065\u5EB7 \u{1F389}",mergeNow:"\u5408\u5E76",delete:"\u5220\u9664",deleteConfirm:"\u786E\u5B9A\u5220\u9664\u8BE5\u8BB0\u5FC6\u5361\uFF1F\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002",deleted:"\u5DF2\u5220\u9664",deleteFail:"\u5220\u9664\u5931\u8D25",deleteSelected:"\u5220\u9664\u9009\u4E2D",deletedSelected:"\u5F20\u5361\u5DF2\u5220\u9664",merge:"\u5408\u5E76",mergeNeed:"\u81F3\u5C11\u9009 2 \u5F20\u5361",mergeConfirm:"\u5C06\u6240\u9009\u5361\u7247\u5408\u5E76\u4E3A\u4E00\u5F20\u5E76\u5220\u9664\u539F\u5361\uFF1F",merged:"\u5DF2\u5408\u5E76",mergeFail:"\u5408\u5E76\u5931\u8D25"},ht={nav:"Memory",loading:"Loading\u2026",refresh:"Refresh",overview:"Memory Overview",total:"Cards",recent:"Added (7d)",tags:"Tags",searchPlaceholder:"Search memory\u2026",search:"Search",all:"All",kindProject:"Projects",kindKnowledge:"Knowledge",kindContent:"Content",kindPrompt:"Prompts",kindBusiness:"Business",kindTool:"Tools",kindMistake:"Mistakes",cardsTab:"Cards",graphTab:"Graph",graph:"Knowledge Graph",graphHint:"Nodes = cards; edges = [[links]] or shared tags. Click a node to read.",graphTip:"Left-click highlights \xB7 Shift+drag box-select \xB7 right-click menu \xB7 scroll zoom \xB7 drag to pan",rebuild:"Rebuild",reset:"Reset",expandAll:"Expand all",capped:"Showing the",clearFocus:"Clear highlight",empty:"Memory is empty. After a few conversations, valuable content is auto-captured.",emptyGraph:"No graph data yet.",open:"Read",updated:"Updated",created:"Created",source:"Source",close:"Close",vaultDir:"Vault directory",capture:"Auto capture",recall:"Auto recall",enabled:"on",disabled:"off",error:"Load failed",back:"Back to list",cardCount:"cards",nodes:"nodes",edges:"edges",memoryTitle:"Memory Library",memoryHint:"Your local second brain",zoomIn:"Zoom in",zoomOut:"Zoom out",fit:"Fit",exportGraph:"Export",openCard:"Open card",focusNeighbors:"Highlight links",copyName:"Copy name",noMatch:"No matching memory",clearFilter:"Clear filter",sortRecent:"Recent",sortTitle:"Title",sortHot:"Hot",exportSel:"Export selected",clearSelection:"Clear selection",download:"Download",openNewTab:"Open in new tab",copyImage:"Copy image",done:"Done",downloaded:"Download started",openedTab:"Opened in new tab",copied:"Copied",exportedSel:" nodes exported",fullscreen:"Fullscreen",exitFull:"Exit fullscreen",copyFail:"Copy failed",timeDim:"Time",timeNew:"\u22643 days",timeRecent:"\u22642 weeks",timeMonth:"\u22641 month",timeOld:"Older",newBadge:"NEW",expand:"Expand",collapse:"Collapse",exportVault:"Export MD",exportJson:"Export JSON",exporting:"Exporting\u2026",exportedVault:"Vault exported",exportFail:"Export failed",importVault:"Import",importedVault:"Import complete",importedSkipped:" (skipped dup ",importFail:"Import failed",location:"Choose location",saveAs:"Save as",saveAsUnsupported:"Choose-save-location not supported here",exportedTo:"Exported to",defaultDownloads:"default Downloads folder",exportCancel:"Export cancelled",manage:"Manage",tabUsage:"Usage / Today",tabOptimize:"Optimize",todayAdd:"Added today",weekAdd:"Last 7d",byKind:"By kind",todayList:"Captured today",budgetLabel:"Session budget",budgetChars:"Budget chars",recallLimitLabel:"Recall limit",embeddingLabel:"Semantic recall",mergePairs:"Similar pairs (mergeable)",staleCards:"Stale (>90d)",noOptimize:"Nothing to organize, healthy \u{1F389}",mergeNow:"Merge",delete:"Delete",deleteConfirm:"Delete this memory card? This cannot be undone.",deleted:"Deleted",deleteFail:"Delete failed",deleteSelected:"Delete selected",deletedSelected:" cards deleted",merge:"Merge",mergeNeed:"Select at least 2 cards",mergeConfirm:"Merge the selected cards into one and delete the originals?",merged:"Merged",mergeFail:"Merge failed"},yt=["all","project","knowledge","content","prompt","business","tool","mistake"],We={project:"#3B82F6",knowledge:"#10B981",content:"#F59E0B",prompt:"#A855F7",business:"#EC4899",tool:"#06B6D4",mistake:"#EF4444",other:"#6B7280"},ve={project:"kindProject",knowledge:"kindKnowledge",content:"kindContent",prompt:"kindPrompt",business:"kindBusiness",tool:"kindTool",mistake:"kindMistake",other:"kindKnowledge"},we=`
7
7
  .memory-eternal-root { font-family: inherit; color: var(--dsw-alias-label-primary, #1f2937); }
8
8
  .mc-card { background: var(--dsw-alias-bg-layer-1, #fff); border: 1px solid var(--dsw-alias-border-l1, #e5e7eb); border-radius: 12px; padding: 14px 16px; }
9
9
  .mc-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; margin-bottom: 14px; }
@@ -105,12 +105,12 @@ var it=Object.create;var je=Object.defineProperty;var st=Object.getOwnPropertyDe
105
105
  .me-graph-ctxmenu button { display: block; width: 100%; text-align: left; padding: 7px 10px; border: none; border-radius: 7px; background: transparent; color: inherit; font: inherit; cursor: pointer; }
106
106
  .me-graph-ctxmenu button:hover { background: rgba(255,255,255,0.1); }
107
107
  .me-graph-hint { font-size: 11px; opacity: 0.6; margin-left: auto; }
108
- `;function xt(n){n.effect(()=>n.locale.register(ke,{zh:ft,en:ht}),"memory-eternal: locale");let l=n.locale.bind(ke);n.effect(()=>n.slots.inject("settings.section",()=>n.slots.register({name:"settings.section",id:ke,order:25,label:()=>l("nav"),locale:ke,inject:()=>({})},()=>c.default.createElement(nt,{t:l,inModal:!1}))),"memory-eternal: settings section"),n.effect(()=>n.slots.inject("sidebar.footer.action",()=>n.slots.register({name:"sidebar.footer.action",id:`${ke}:footer`,order:100,label:()=>l("nav"),locale:ke,inject:()=>({})},m=>c.default.createElement(yt,{t:l,wide:!(m&&m.wide===!1)}))),"memory-eternal: sidebar footer action")}function yt({t:n,wide:l}){let[m,h]=(0,c.useState)(!1);return(0,o.jsxs)("div",{className:`me-footer${l?"":" rail"}`,children:[(0,o.jsx)("style",{children:Ne}),(0,o.jsxs)("button",{type:"button",className:"me-footer-btn",onClick:()=>h(!0),"aria-label":n("nav"),title:n("nav"),children:[(0,o.jsx)("span",{className:"me-footer-ico","aria-hidden":"true",children:(0,o.jsx)(ot,{})}),(0,o.jsx)("span",{className:"me-footer-label",children:n("nav")})]}),m&&(0,o.jsx)(wt,{t:n,onClose:()=>h(!1)})]})}function ot(){return(0,o.jsxs)("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:[(0,o.jsx)("defs",{children:(0,o.jsxs)("linearGradient",{id:"mg-brain",x1:"0",y1:"0",x2:"1",y2:"1",children:[(0,o.jsx)("stop",{offset:"0%",stopColor:"#FFC46B"}),(0,o.jsx)("stop",{offset:"50%",stopColor:"#F97316"}),(0,o.jsx)("stop",{offset:"100%",stopColor:"#EA580C"})]})}),(0,o.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 0-6.4.1 4.1 4.1 0 0 0-2.7 5.9 4.1 4.1 0 0 0 .6 6.7A4.2 4.2 0 0 0 12 18.2Z"}),(0,o.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 1 6.4.1 4.1 4.1 0 0 1 2.7 5.9 4.1 4.1 0 0 1-.6 6.7A4.2 4.2 0 0 1 12 18.2Z"}),(0,o.jsx)("path",{d:"M12 9.2c-.8-1.4-1.7-2-3.1-2.2M12 9.2c.8-1.4 1.7-2 3.1-2.2M12 12.4c-1.2 1-3.2 1.6-5 1.4M12 12.4c1.2 1 3.2 1.6 5 1.4M12 13.2v4",fill:"none",stroke:"rgba(255,255,255,0.62)",strokeWidth:"1",strokeLinecap:"round"}),(0,o.jsx)("circle",{cx:"12",cy:"8.6",r:"1.1",fill:"rgba(255,255,255,0.85)"})]})}function wt({t:n,onClose:l}){return(0,c.useEffect)(()=>{let m=h=>{h.key==="Escape"&&l()};return window.addEventListener("keydown",m),()=>window.removeEventListener("keydown",m)},[l]),(0,o.jsxs)("div",{className:"me-overlay-top",onClick:l,children:[(0,o.jsx)("style",{children:Ne}),(0,o.jsx)("div",{className:"me-modal",onClick:m=>m.stopPropagation(),children:(0,o.jsx)(nt,{t:n,inModal:!0,onClose:l})})]})}function nt({t:n,inModal:l,onClose:m}){let[h,i]=(0,c.useState)(null),[b,T]=(0,c.useState)([]),[W,C]=(0,c.useState)(!0),[E,G]=(0,c.useState)(""),[ie,pe]=(0,c.useState)("all"),[q,Oe]=(0,c.useState)(""),[B,se]=(0,c.useState)("cards"),[me,ue]=(0,c.useState)("recent"),[oe,z]=(0,c.useState)(null),j=(0,c.useRef)(null),Me=(0,c.useRef)(null),[ge,O]=(0,c.useState)(!1),[V,Ye]=(0,c.useState)(!1),[_,J]=(0,c.useState)(null),X=(0,c.useRef)(null),[A,fe]=(0,c.useState)(24),U=(0,c.useRef)(null);(0,c.useEffect)(()=>{if(B!=="cards")return;let d=U.current;if(!d)return;let a=new IntersectionObserver(t=>{t[0]&&t[0].isIntersecting&&fe(y=>y+24)},{rootMargin:"400px"});return a.observe(d),()=>a.disconnect()},[B,b,ie,q]);let he=(0,c.useCallback)(async(d=ie,a=q)=>{try{C(!0),G("");let t=new URLSearchParams;d&&d!=="all"&&t.set("kind",d),a.trim()&&t.set("q",a.trim());let N=await(await fetch(`${ee}/cards?${t.toString()}`)).json();N.ok&&(T(N.cards||[]),fe(24))}catch(t){G(String(t&&t.message?t.message:t))}finally{C(!1)}},[ie,q]),Z=(0,c.useCallback)(async()=>{try{let[d,a]=await Promise.all([fetch(`${ee}/overview`).then(t=>t.json()),fetch(`${ee}/cards`).then(t=>t.json())]);d.ok&&i(d),a.ok&&(T(a.cards||[]),fe(24))}catch(d){G(String(d&&d.message?d.message:d))}finally{C(!1)}},[]),Ie=async d=>{try{let t=await(await fetch(`${ee}/delete?path=${encodeURIComponent(d)}`)).json();t.ok?(z({ok:!0,msg:n("deleted")}),await Z()):z({ok:!1,msg:t.error||n("deleteFail")})}catch{z({ok:!1,msg:n("deleteFail")})}j.current&&clearTimeout(j.current),j.current=setTimeout(()=>z(null),2e3)};(0,c.useEffect)(()=>(Z(),()=>{X.current&&clearTimeout(X.current)}),[Z]);let xe=d=>{Oe(d),X.current&&clearTimeout(X.current),X.current=setTimeout(()=>he(ie,d),280)},Ee=d=>{pe(d),he(d,q)},le=async d=>{try{let t=await(await fetch(`${ee}/card?path=${encodeURIComponent(d.path||d.id)}`)).json();t.ok&&J({path:t.path,title:d.title,text:t.text})}catch(a){G(String(a&&a.message?a.message:a))}},ce=async(d,a)=>{O(!0);try{let t=await fetch(`${ee}/export`);if(!t.ok)throw new Error("HTTP "+t.status);let y=await t.json();if(!y.ok)throw new Error(y.error||"export failed");let N,R,D;d==="json"?(R="memory-vault.json",D="application/json",N=JSON.stringify(y.cards.map(ae=>({path:ae.path,title:ae.title,kind:ae.kind,text:ae.text})),null,2)):(R="memory-vault.md",D="text/markdown;charset=utf-8",N=y.cards.map(ae=>ae.text.trim()).filter(Boolean).join(`
108
+ `;function xt(a){a.effect(()=>a.locale.register(Me,{zh:bt,en:ht}),"memory-eternal: locale");let s=a.locale.bind(Me);a.effect(()=>a.slots.inject("settings.section",()=>a.slots.register({name:"settings.section",id:Me,order:25,label:()=>s("nav"),locale:Me,inject:()=>({})},()=>d.default.createElement(nt,{t:s,inModal:!1}))),"memory-eternal: settings section"),a.effect(()=>a.slots.inject("sidebar.footer.action",()=>a.slots.register({name:"sidebar.footer.action",id:`${Me}:footer`,order:100,label:()=>s("nav"),locale:Me,inject:()=>({})},p=>d.default.createElement(vt,{t:s,wide:!(p&&p.wide===!1)}))),"memory-eternal: sidebar footer action")}function vt({t:a,wide:s}){let[p,b]=(0,d.useState)(!1);return(0,e.jsxs)("div",{className:`me-footer${s?"":" rail"}`,children:[(0,e.jsx)("style",{children:we}),(0,e.jsxs)("button",{type:"button",className:"me-footer-btn",onClick:()=>b(!0),"aria-label":a("nav"),title:a("nav"),children:[(0,e.jsx)("span",{className:"me-footer-ico","aria-hidden":"true",children:(0,e.jsx)(at,{})}),(0,e.jsx)("span",{className:"me-footer-label",children:a("nav")})]}),p&&(0,e.jsx)(wt,{t:a,onClose:()=>b(!1)})]})}function at(){return(0,e.jsxs)("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:[(0,e.jsx)("defs",{children:(0,e.jsxs)("linearGradient",{id:"mg-brain",x1:"0",y1:"0",x2:"1",y2:"1",children:[(0,e.jsx)("stop",{offset:"0%",stopColor:"#FFC46B"}),(0,e.jsx)("stop",{offset:"50%",stopColor:"#F97316"}),(0,e.jsx)("stop",{offset:"100%",stopColor:"#EA580C"})]})}),(0,e.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 0-6.4.1 4.1 4.1 0 0 0-2.7 5.9 4.1 4.1 0 0 0 .6 6.7A4.2 4.2 0 0 0 12 18.2Z"}),(0,e.jsx)("path",{fill:"url(#mg-brain)",d:"M12 4.5a3.2 3.2 0 0 1 6.4.1 4.1 4.1 0 0 1 2.7 5.9 4.1 4.1 0 0 1-.6 6.7A4.2 4.2 0 0 1 12 18.2Z"}),(0,e.jsx)("path",{d:"M12 9.2c-.8-1.4-1.7-2-3.1-2.2M12 9.2c.8-1.4 1.7-2 3.1-2.2M12 12.4c-1.2 1-3.2 1.6-5 1.4M12 12.4c1.2 1 3.2 1.6 5 1.4M12 13.2v4",fill:"none",stroke:"rgba(255,255,255,0.62)",strokeWidth:"1",strokeLinecap:"round"}),(0,e.jsx)("circle",{cx:"12",cy:"8.6",r:"1.1",fill:"rgba(255,255,255,0.85)"})]})}function wt({t:a,onClose:s}){return(0,d.useEffect)(()=>{let p=b=>{b.key==="Escape"&&s()};return window.addEventListener("keydown",p),()=>window.removeEventListener("keydown",p)},[s]),(0,e.jsxs)("div",{className:"me-overlay-top",onClick:s,children:[(0,e.jsx)("style",{children:we}),(0,e.jsx)("div",{className:"me-modal",onClick:p=>p.stopPropagation(),children:(0,e.jsx)(nt,{t:a,inModal:!0,onClose:s})})]})}function nt({t:a,inModal:s,onClose:p}){let[b,r]=(0,d.useState)(null),[u,S]=(0,d.useState)([]),[j,C]=(0,d.useState)(!0),[M,H]=(0,d.useState)(""),[G,ie]=(0,d.useState)("all"),[X,Ie]=(0,d.useState)(""),[R,x]=(0,d.useState)("cards"),[D,O]=(0,d.useState)("recent"),[$,F]=(0,d.useState)(null),A=(0,d.useRef)(null),ze=(0,d.useRef)(null),[fe,U]=(0,d.useState)(!1),[ee,Ye]=(0,d.useState)(!1),[te,oe]=(0,d.useState)(null),[se,V]=(0,d.useState)(null),le=(0,d.useRef)(null),[J,ce]=(0,d.useState)(24),pe=(0,d.useRef)(null);(0,d.useEffect)(()=>{if(R!=="cards")return;let o=pe.current;if(!o)return;let m=new IntersectionObserver(g=>{g[0]&&g[0].isIntersecting&&ce(T=>T+24)},{rootMargin:"400px"});return m.observe(o),()=>m.disconnect()},[R,u,G,X]);let Te=(0,d.useCallback)(async(o=G,m=X)=>{try{C(!0),H("");let g=new URLSearchParams;o&&o!=="all"&&g.set("kind",o),m.trim()&&g.set("q",m.trim());let L=await(await fetch(`${q}/cards?${g.toString()}`)).json();L.ok&&(S(L.cards||[]),ce(24))}catch(g){H(String(g&&g.message?g.message:g))}finally{C(!1)}},[G,X]),ae=(0,d.useCallback)(async()=>{try{let[o,m]=await Promise.all([fetch(`${q}/overview`).then(g=>g.json()),fetch(`${q}/cards`).then(g=>g.json())]);o.ok&&r(o),m.ok&&(S(m.cards||[]),ce(24))}catch(o){H(String(o&&o.message?o.message:o))}finally{C(!1)}},[]),Le=async o=>{try{let g=await(await fetch(`${q}/delete?path=${encodeURIComponent(o)}`)).json();g.ok?(F({ok:!0,msg:a("deleted")}),await ae()):F({ok:!1,msg:g.error||a("deleteFail")})}catch{F({ok:!1,msg:a("deleteFail")})}A.current&&clearTimeout(A.current),A.current=setTimeout(()=>F(null),2e3)};(0,d.useEffect)(()=>(ae(),()=>{le.current&&clearTimeout(le.current)}),[ae]);let be=o=>{Ie(o),le.current&&clearTimeout(le.current),le.current=setTimeout(()=>Te(G,o),280)},he=o=>{ie(o),Te(o,X)},B=async o=>{try{let g=await(await fetch(`${q}/card?path=${encodeURIComponent(o.path||o.id)}`)).json();g.ok&&oe({path:g.path,title:o.title,text:g.text})}catch(m){H(String(m&&m.message?m.message:m))}},ye=async(o,m)=>{U(!0);try{let g=await fetch(`${q}/export`);if(!g.ok)throw new Error("HTTP "+g.status);let T=await g.json();if(!T.ok)throw new Error(T.error||"export failed");let L,t,xe;o==="json"?(t="memory-vault.json",xe="application/json",L=JSON.stringify(T.cards.map(ue=>({path:ue.path,title:ue.title,kind:ue.kind,text:ue.text})),null,2)):(t="memory-vault.md",xe="text/markdown;charset=utf-8",L=T.cards.map(ue=>ue.text.trim()).filter(Boolean).join(`
109
109
 
110
110
  ---
111
111
 
112
- `));let e=new Blob([N],{type:D}),ne=await at(e,R,a);if(!ne.ok){z({ok:!1,msg:ne.aborted?n("exportCancel"):n("exportFail")}),O(!1);return}z({ok:!0,msg:ne.picked?n("exportedTo")+"\uFF1A"+ne.name:n("exportedTo")+"\uFF1A"+n("defaultDownloads")+" \xB7 "+ne.name})}catch(t){z({ok:!1,msg:n("exportFail")+" \xB7 "+(t.message||"")})}finally{O(!1)}j.current&&clearTimeout(j.current),j.current=setTimeout(()=>z(null),2600)},F=async d=>{let a=d.target.files&&d.target.files[0];if(d.target.value="",!!a){try{let t=await a.text(),N=await(await fetch(`${ee}/import`,{method:"POST",headers:{"Content-Type":"application/json"},body:t})).json();N.ok?(await Z(),z({ok:!0,msg:n("importedVault")+"\uFF1A"+(N.imported||0)+(N.skipped?n("importedSkipped")+N.skipped:"")})):z({ok:!1,msg:N.error||n("importFail")})}catch{z({ok:!1,msg:n("importFail")})}j.current&&clearTimeout(j.current),j.current=setTimeout(()=>z(null),2400)}},be=(0,c.useMemo)(()=>{let d=b.slice();return me==="title"?d.sort((a,t)=>String(a.title||"").localeCompare(String(t.title||""))):me==="hot"?d.sort((a,t)=>(t.weight||t.links||0)-(a.weight||a.links||0)):d.sort((a,t)=>new Date(t.updated||0).getTime()-new Date(a.updated||0).getTime()),d},[b,me]);return(0,o.jsxs)("div",{className:"memory-eternal-root",style:{height:"100%",display:"flex",flexDirection:"column"},children:[(0,o.jsx)("style",{children:Ne}),oe&&(0,o.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,position:"fixed",left:"50%",transform:"translateX(-50%)",top:22,zIndex:70,background:"rgba(20,22,26,0.97)",backdropFilter:"blur(12px)",WebkitBackdropFilter:"blur(12px)",color:"#f5f5f5",borderRadius:12,padding:"10px 20px",fontSize:13.5,fontWeight:600,boxShadow:"0 14px 44px rgba(0,0,0,.5)",pointerEvents:"none",animation:"me-pop .22s ease",borderLeft:"4px solid "+(oe.ok?"#22c55e":"#ef4444"),maxWidth:"90vw"},children:[(0,o.jsx)("span",{style:{color:oe.ok?"#34d399":"#f87171",fontWeight:800,fontSize:15},children:oe.ok?"\u2713":"\u2715"}),(0,o.jsx)("span",{children:oe.msg})]}),l&&(0,o.jsxs)("div",{className:"me-modal-head",children:[(0,o.jsxs)("div",{className:"me-modal-title",children:[(0,o.jsx)("span",{className:"me-wicon",children:(0,o.jsx)(ot,{})}),(0,o.jsx)("h2",{children:n("memoryTitle")}),(0,o.jsx)("span",{children:n("memoryHint")})]}),(0,o.jsx)("div",{className:"spacer"}),(0,o.jsx)("button",{type:"button",className:"me-modal-close",onClick:m,"aria-label":n("close"),children:"\u2715"})]}),(0,o.jsxs)("div",{className:"me-modal-body",children:[(0,o.jsxs)("div",{className:"mc-stats",children:[(0,o.jsx)(De,{label:n("total"),value:h?h.total:"\u2014"}),(0,o.jsx)(De,{label:n("recent"),value:h?h.recent:"\u2014"}),(0,o.jsx)(De,{label:n("tags"),value:h?h.tags:"\u2014"}),(0,o.jsx)(De,{label:n("cardCount"),value:h?h.byKind?.knowledge??0:"\u2014"})]}),(0,o.jsxs)("div",{className:"mc-toolbar",children:[(0,o.jsx)("input",{type:"text",placeholder:n("searchPlaceholder"),value:q,onChange:d=>xe(d.target.value)}),(0,o.jsxs)("div",{className:"mc-tabs",children:[(0,o.jsx)("button",{type:"button",className:`mc-tab${B==="cards"?" active":""}`,onClick:()=>se("cards"),children:n("cardsTab")}),(0,o.jsx)("button",{type:"button",className:`mc-tab${B==="graph"?" active":""}`,onClick:()=>se("graph"),children:n("graphTab")})]}),(0,o.jsx)("input",{ref:Me,type:"file",accept:".json,application/json",style:{display:"none"},onChange:F}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>Me.current&&Me.current.click(),children:n("importVault")}),(0,o.jsxs)("label",{className:"mc-btn",style:{display:"inline-flex",alignItems:"center",gap:5,cursor:"pointer"},title:n("location"),children:[(0,o.jsx)("input",{type:"checkbox",checked:V,onChange:d=>Ye(d.target.checked)}),n("location")]}),(0,o.jsx)("button",{type:"button",className:"mc-btn",disabled:ge,onClick:()=>ce("md",V),children:n(ge?"exporting":"exportVault")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",disabled:ge,onClick:()=>ce("json",V),children:n(ge?"exporting":"exportJson")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>Z(),children:n("refresh")})]}),B==="cards"&&(0,o.jsxs)("div",{className:"mc-chips",children:[bt.map(d=>(0,o.jsx)("button",{type:"button",className:`mc-chip${ie===d?" active":""}`,onClick:()=>Ee(d),children:n(d==="all"?"all":Ce[d])},d)),(0,o.jsx)("span",{className:"spacer",style:{flex:1}}),[{key:"recent",label:n("sortRecent")},{key:"title",label:n("sortTitle")},{key:"hot",label:n("sortHot")}].map(d=>(0,o.jsx)("button",{type:"button",className:`mc-chip${me===d.key?" active":""}`,onClick:()=>ue(d.key),children:d.label},d.key))]}),E&&(0,o.jsxs)("div",{className:"mc-empty",children:[n("error"),"\uFF1A",E]}),B==="cards"?W&&!b.length?(0,o.jsx)("div",{className:"mc-empty",children:n("loading")}):b.length===0?(0,o.jsx)("div",{className:"mc-empty",children:n("empty")}):(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)("div",{className:"mc-grid",children:be.slice(0,A).map(d=>(0,o.jsx)(vt,{card:d,t:n,query:q.trim(),onOpen:le,onDelete:Ie},d.path))}),be.length>A&&(0,o.jsx)("div",{ref:U,style:{height:1}})]}):(0,o.jsx)(Ct,{t:n,onOpen:le}),_&&(0,o.jsx)(kt,{t:n,card:_,onClose:()=>J(null),onDelete:d=>{J(null),Ie(d)}})]})]})}var De=({label:n,value:l})=>(0,o.jsxs)("div",{className:"mc-stat mc-card",children:[(0,o.jsx)("b",{children:l}),(0,o.jsx)("span",{children:n})]}),vt=({card:n,t:l,onOpen:m,query:h,onDelete:i})=>(0,o.jsxs)("article",{className:"mc-cardrow mc-card",onClick:()=>m(n),children:[(0,o.jsxs)("h4",{children:[(0,o.jsx)("span",{className:"mc-kind",style:{background:Pe[n.kind]||Pe.other}}),h?tt(n.title,h):n.title,St(n.updated)&&(0,o.jsx)("span",{className:"mc-new",children:l("newBadge")})]}),(0,o.jsx)("p",{children:h?tt(n.summary||"",h):n.summary||""}),n.tags.length>0&&(0,o.jsx)("div",{className:"mc-tags",children:n.tags.slice(0,4).map(b=>(0,o.jsx)("span",{className:"mc-tag",children:b},b))}),(0,o.jsxs)("footer",{children:[(0,o.jsx)("span",{children:l(Ce[n.kind])}),(0,o.jsx)("span",{children:zt(n.updated)}),(0,o.jsx)("span",{className:"spacer",style:{flex:1}}),(0,o.jsx)("button",{type:"button",className:"mc-card-del",title:l("delete"),onClick:b=>{b.stopPropagation(),window.confirm(l("deleteConfirm"))&&i&&i(n.path)},children:"\u2715"})]})]});function kt({t:n,card:l,onClose:m,onDelete:h}){(0,c.useEffect)(()=>{let W=C=>{C.key==="Escape"&&m()};return window.addEventListener("keydown",W),()=>window.removeEventListener("keydown",W)},[m]);let[i,b]=(0,c.useState)(!1),T=(l.text||"").length>460;return(0,o.jsxs)("div",{className:"me-overlay",onClick:m,children:[(0,o.jsx)("style",{children:Ne}),(0,o.jsxs)("div",{className:"me-dialog",onClick:W=>W.stopPropagation(),children:[(0,o.jsxs)("div",{className:"me-dialog-head",children:[(0,o.jsx)("h3",{children:l.title}),(0,o.jsxs)("div",{style:{display:"flex",gap:8},children:[T&&(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>b(W=>!W),children:n(i?"collapse":"expand")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",style:{color:"#f87171"},onClick:()=>{window.confirm(n("deleteConfirm"))&&h&&h(l.path)},children:n("delete")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:m,children:n("close")})]})]}),(0,o.jsx)("div",{className:"me-dialog-body",style:i?{}:{maxHeight:300,overflow:"hidden"},dangerouslySetInnerHTML:{__html:Tt(l.text)}})]})]})}function Ct({t:n,onOpen:l}){let[m,h]=(0,c.useState)(null),[i,b]=(0,c.useState)(""),T=(0,c.useCallback)(async()=>{try{let E=await(await fetch(`${ee}/graph`)).json();h(E.ok?E:null),b("")}catch(C){b(String(C&&C.message?C.message:C))}},[]);(0,c.useEffect)(()=>{let C=!0;return fetch(`${ee}/graph`).then(E=>E.json()).then(E=>{C&&h(E.ok?E:null)}).catch(E=>{C&&b(String(E&&E.message?E.message:E))}),()=>{C=!1}},[]);let W=(0,c.useCallback)(async C=>{try{let G=await(await fetch(`${ee}/delete?path=${encodeURIComponent(C)}`)).json();return G&&G.ok?(await T(),!0):!1}catch{return!1}},[T]);return(0,o.jsxs)("div",{className:"me-graph",children:[(0,o.jsx)("style",{children:Ne}),i&&(0,o.jsxs)("div",{className:"mc-empty",children:[n("error"),"\uFF1A",i]}),!m&&!i&&(0,o.jsx)("div",{className:"mc-empty",children:n("loading")}),m&&m.nodes.length===0&&(0,o.jsx)("div",{className:"mc-empty",children:n("emptyGraph")}),m&&m.nodes.length>0&&(0,o.jsx)(Mt,{nodes:m.nodes,edges:m.edges,onOpen:l,onDelete:W,t:n,countLabel:`${m.nodes.length} ${n("nodes")} \xB7 ${Nt(m.edges)} ${n("edges")}`})]})}function Nt(n){let l=new Set;for(let m of n||[])l.add([m.source,m.target].sort().join("|"));return l.size}var $e={colors:{project:"#3B82F6",knowledge:"#10B981",content:"#F59E0B",prompt:"#A855F7",business:"#EC4899",tool:"#06B6D4",mistake:"#EF4444",other:"#6B7280"},shapes:{project:"circle",knowledge:"circle",content:"rect",prompt:"diamond",business:"hexagon",tool:"circle",mistake:"diamond",other:"circle"}};function Mt({nodes:n,edges:l,onOpen:m,onDelete:h,t:i,countLabel:b}){let T=(0,c.useRef)(null),W=(0,c.useRef)(null),C=(0,c.useRef)(null),E=(0,c.useRef)(null),G=(0,c.useRef)(null),ie=(0,c.useRef)(m);(0,c.useEffect)(()=>{ie.current=m},[m]);let pe=(0,c.useRef)(h);(0,c.useEffect)(()=>{pe.current=h},[h]);let[q,Oe]=(0,c.useState)(""),[B,se]=(0,c.useState)(null),me=(0,c.useRef)("");(0,c.useEffect)(()=>{me.current=q.trim().toLowerCase()},[q]);let[ue,oe]=(0,c.useState)("all"),z=(0,c.useRef)("all"),[j,Me]=(0,c.useState)(!1),ge=(0,c.useRef)(!1),[O,V]=(0,c.useState)(null);(0,c.useEffect)(()=>{z.current=ue},[ue]),(0,c.useEffect)(()=>{ge.current=j},[j]);let Ye=(()=>{let a=q.trim().toLowerCase();return!!a&&!n.some(t=>(t.title||t.name||"").toLowerCase().includes(a)||String(t.kind||"").toLowerCase().includes(a)||String(Ce[t.kind]||"").toLowerCase().includes(a))})();(0,c.useEffect)(()=>{let a=C.current;a&&a.wake&&a.wake()},[q]),(0,c.useEffect)(()=>{let a=C.current;a&&a.render&&a.render()},[ue,j]);let _=(0,c.useRef)(null),[J,X]=(0,c.useState)([]),[A,fe]=(0,c.useState)(null),[U,he]=(0,c.useState)(!1),[Z,Ie]=(0,c.useState)(null),[xe,Ee]=(0,c.useState)(""),le=(0,c.useRef)(null),ce=(0,c.useRef)(null),F=(a,t=!0)=>{le.current&&clearTimeout(le.current),Ie({msg:a,ok:t}),le.current=setTimeout(()=>Ie(null),1900)},be=a=>{Ee(a),ce.current&&clearTimeout(ce.current),ce.current=setTimeout(()=>Ee(""),1600)};(0,c.useEffect)(()=>()=>{le.current&&clearTimeout(le.current),ce.current&&clearTimeout(ce.current)},[]);let d=(0,c.useMemo)(()=>n.reduce((a,t)=>(a[t.id]=t,a),{}),[n]);return(0,c.useEffect)(()=>{let a=T.current;if(!a||n.length===0)return;let t=a.getContext("2d");if(!t)return;let y=Math.max(1,window.devicePixelRatio||1),N=()=>document.documentElement.getAttribute("data-theme")==="dark",R={};l.forEach(r=>{R[r.source]=(R[r.source]||0)+1,R[r.target]=(R[r.target]||0)+1});let D=Math.max(1,...Object.values(R)),e={nodes:n.map(r=>({id:r.id,name:r.title||"",type:r.kind||"other",r:9+Math.min(10,(R[r.id]||0)/D*8),x:0,y:0,vx:0,vy:0})),edges:l.map(r=>({sourceNodeId:r.source,targetNodeId:r.target,weight:1})),panX:0,panY:0,zoom:1,running:!0,raf:0,tickCount:0,quietTicks:0,dragNode:null,selectedId:null,mouseX:0,mouseY:0,w:0,h:0,dpr:y,ctx:t,multi:[],marquee:null};e.domain=n,e.domainById=n.reduce((r,u)=>(r[u.id]=u,r),{}),e.nodes.forEach((r,u)=>{let p=u/e.nodes.length*Math.PI*2-Math.PI/2;r.x=Math.cos(p)*60,r.y=Math.sin(p)*60});let ne=[...new Set(e.nodes.map(r=>r.type))];e.clustered=ne.length>1,e.typeCenters={},ne.forEach((r,u)=>{let p=u/ne.length*Math.PI*2-Math.PI/2;e.typeCenters[r]={x:Math.cos(p)*160,y:Math.sin(p)*160}}),C.current=e;let ae=()=>{let r=a.clientWidth,u=a.clientHeight;e.w=r,e.h=u,a.width=r*y,a.height=u*y,t.setTransform(y,0,0,y,0,0)};ae();let We=new ResizeObserver(ae);We.observe(a.parentElement);let rt=(r,u)=>r&&r.length>u?r.slice(0,u)+"\u2026":r,Se=(r,u,p,g)=>{if(t.beginPath(),g==="rect")t.rect(r-p,u-p*.75,p*2,p*1.5);else if(g==="diamond")t.moveTo(r,u-p),t.lineTo(r+p,u),t.lineTo(r,u+p),t.lineTo(r-p,u),t.closePath();else if(g==="hexagon"){for(let f=0;f<6;f++){let v=Math.PI/3*f-Math.PI/2,M=r+p*Math.cos(v),s=u+p*Math.sin(v);f===0?t.moveTo(M,s):t.lineTo(M,s)}t.closePath()}else t.arc(r,u,p,0,Math.PI*2)},Fe=()=>{if(!e.nodes.length)return;let r=1/0,u=-1/0,p=1/0,g=-1/0;e.nodes.forEach(k=>{r=Math.min(r,k.x),u=Math.max(u,k.x),p=Math.min(p,k.y),g=Math.max(g,k.y)});let f=36,v=u-r+f*2,M=g-p+f*2,s=Math.min(e.w/v,e.h/M);e.zoom=Math.max(.45,Math.min(2.4,s*1.22)),e.panX=e.w/2-(r+u)/2*e.zoom,e.panY=e.h/2-(p+g)/2*e.zoom};E.current=Fe;let qe=()=>{t.clearRect(0,0,e.w,e.h),e.searchTerm=me.current,e.kindFilter=z.current,e.timeMode=ge.current;let r=e.kindFilter,u=s=>String(s||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");t.save(),t.strokeStyle=N()?"rgba(255,255,255,0.04)":"rgba(0,0,0,0.04)",t.lineWidth=.5;for(let s=0;s<e.w;s+=24)t.beginPath(),t.moveTo(s,0),t.lineTo(s,e.h),t.stroke();for(let s=0;s<e.h;s+=24)t.beginPath(),t.moveTo(0,s),t.lineTo(e.w,s),t.stroke();t.restore(),t.save(),t.translate(e.panX,e.panY),t.scale(e.zoom,e.zoom);let p={};e.nodes.forEach(s=>p[s.id]=s);let g=e.nodes.length>40,f=g?1.5:.55,v=e.selectedId||e.hoverId;e.edges.forEach(s=>{let k=p[s.sourceNodeId],I=p[s.targetNodeId];if(!k||!I)return;if(r!=="all"){let Y=e.domainById[s.sourceNodeId]&&e.domainById[s.sourceNodeId].kind,Ze=e.domainById[s.targetNodeId]&&e.domainById[s.targetNodeId].kind;if(Y!==r&&Ze!==r)return}let x=I.x-k.x,w=I.y-k.y,S=Math.sqrt(x*x+w*w)||1,L=g?12:18,$=-w/S*L,K=x/S*L,P=(k.x+I.x)/2+$,te=(k.y+I.y)/2+K,H=$e.colors[e.domainById[k.id]&&e.domainById[k.id].kind||"other"],re=v&&(s.sourceNodeId===v||s.targetNodeId===v),ye=v?re?.6:.08:g?.14:.24,ze=parseInt(H.slice(1,3),16),Le=parseInt(H.slice(3,5),16),Je=parseInt(H.slice(5,7),16);t.strokeStyle="rgba("+ze+","+Le+","+Je+","+ye+")",t.lineWidth=re?1.6+(s.weight||1):.8+(s.weight||1)*.5,t.beginPath(),t.moveTo(k.x,k.y),t.quadraticCurveTo(P,te,I.x,I.y),t.stroke()});let M=[];if(e.nodes.forEach(s=>{let k=e.domainById[s.id]||{},I=k.kind||"other",x=e.timeMode?It(k.updated):$e.colors[I],w=$e.shapes[I],S=e.selectedId===s.id,L=e.hoverId===s.id,$=e.searchTerm||"",K=!$||(s.name||"").toLowerCase().includes($)||String(Ce[I]||"").toLowerCase().includes($)||String(I).toLowerCase().includes($),P=r==="all"||I===r,te=v&&s.id!==v&&!e.edges.some(Y=>Y.sourceNodeId===v&&Y.targetNodeId===s.id||Y.targetNodeId===v&&Y.sourceNodeId===s.id)||$&&!K||!P;t.save(),t.globalAlpha=te?.08:1,(S||L)&&(t.shadowColor=x,t.shadowBlur=S?20:14),Se(s.x,s.y,s.r,w);let H=parseInt(x.slice(1,3),16),re=parseInt(x.slice(3,5),16),ye=parseInt(x.slice(5,7),16),ze=t.createRadialGradient(s.x-s.r*.3,s.y-s.r*.3,0,s.x,s.y,s.r*1.2);ze.addColorStop(0,"rgba("+Math.min(255,H+70)+","+Math.min(255,re+70)+","+Math.min(255,ye+70)+",0.95)"),ze.addColorStop(1,x),t.fillStyle=ze,t.fill(),t.restore(),S?(t.save(),Se(s.x,s.y,s.r+3,w),t.strokeStyle=x,t.lineWidth=3,t.shadowColor=x,t.shadowBlur=12,t.stroke(),t.restore()):L?(t.save(),Se(s.x,s.y,s.r+2,w),t.strokeStyle=x,t.lineWidth=2,t.stroke(),t.restore()):$&&K?(t.save(),Se(s.x,s.y,s.r+2,w),t.strokeStyle="#e11d48",t.lineWidth=2,t.stroke(),t.restore()):e.multi.indexOf(s.id)>=0&&(t.save(),Se(s.x,s.y,s.r+2,w),t.strokeStyle="#60a5fa",t.lineWidth=2,t.shadowColor="#60a5fa",t.shadowBlur=8,t.stroke(),t.restore());let Le=rt(s.name,16);if((S||L||e.zoom>f||!g&&e.zoom>.6)&&P){let Y=1/e.zoom;t.font="500 "+(12*Y).toFixed(1)+"px -apple-system,Segoe UI,sans-serif";let de=t.measureText(Le).width+16*Y,Be=18*Y,we=s.y+s.r+8*Y,Qe=!0;for(let ve of M)if(s.x-de/2<ve.x+ve.w&&s.x+de/2>ve.x&&we<ve.y+ve.h&&we+Be>ve.y){Qe=!1;break}if(!Qe&&!S&&!L)return;M.push({x:s.x-de/2,y:we,w:de,h:Be}),t.fillStyle=N()?"rgba(30,30,35,0.92)":"rgba(255,255,255,0.92)",t.beginPath(),t.roundRect?t.roundRect(s.x-de/2,we,de,Be,4*Y):t.rect(s.x-de/2,we,de,Be),t.fill(),t.strokeStyle=N()?"rgba(255,255,255,0.08)":"rgba(0,0,0,0.08)",t.lineWidth=1*Y,t.stroke(),t.fillStyle=N()?S||L?"#eee":"#bbb":S||L?"#111":"#444",t.textAlign="center",t.fillText(Le,s.x,we+13*Y)}}),t.restore(),e.marquee){let s=e.marquee,k=Math.min(s.sx,s.ex),I=Math.min(s.sy,s.ey),x=Math.abs(s.ex-s.sx),w=Math.abs(s.ey-s.sy);t.save(),t.setLineDash([4,4]),t.strokeStyle="rgba(96,165,250,0.9)",t.lineWidth=1,t.fillStyle="rgba(96,165,250,0.12)",t.fillRect(k,I,x,w),t.strokeRect(k,I,x,w),t.restore()}if(e.hoverId&&_.current){let s=e.domainById[e.hoverId],k=e.nodes.find(function(I){return I.id===e.hoverId});if(s&&k){let I=e.edges.filter(function(S){return S.sourceNodeId===s.id||S.targetNodeId===s.id}).length,x=k.x*e.zoom+e.panX,w=k.y*e.zoom+e.panY;_.current.style.display="block",_.current.style.left=x+14+"px",_.current.style.top=w-6+"px",_.current.innerHTML="<b>"+u(s.title||s.name||"")+'</b><br/><span style="opacity:.8">'+u(s.kind||"")+" \xB7 "+I+" \u8FDE\u63A5</span>"}}else _.current&&(_.current.style.display="none")},Q=()=>{e.running=!0,e.raf||(e.raf=requestAnimationFrame(Ae))};e.wake=Q,e.render=qe;let Xe=()=>{let r=e.nodes.length;e.tickCount++;let p=.9-Math.min(.4,e.tickCount/1500),g=r>1e3?3e3:r>100?2e3:r>50?1200:800,f=r>100?.002:.005,v=r>1e3?.012:r>100?.005:.01,M=r>1e3?6:r>200?12:24,s={};e.nodes.forEach(x=>s[x.id]=x);for(let x=0;x<r;x++){if(e.dragNode===e.nodes[x])continue;let w=e.nodes[x],S=0,L=0;for(let P=0;P<r;P++){if(x===P)continue;let te=w.x-e.nodes[P].x,H=w.y-e.nodes[P].y,re=Math.sqrt(te*te+H*H)||1,ye=g/(re*re);S+=te/re*ye,L+=H/re*ye}if(e.clustered&&e.typeCenters[w.type]){let P=e.typeCenters[w.type];S+=(P.x-w.x)*.006,L+=(P.y-w.y)*.006}else S-=w.x*v,L-=w.y*v;let $=(w.vx+S)*p,K=(w.vy+L)*p;$=Math.max(-M,Math.min(M,$)),K=Math.max(-M,Math.min(M,K)),w.vx=$,w.vy=K}e.edges.forEach(x=>{let w=s[x.sourceNodeId],S=s[x.targetNodeId];if(!w||!S)return;let L=S.x-w.x,$=S.y-w.y,K=Math.sqrt(L*L+$*$)||1,P=(K-100)*f,te=L/K*P,H=$/K*P;e.dragNode!==w&&(w.vx+=te,w.vy+=H),e.dragNode!==S&&(S.vx-=te,S.vy-=H)});let k=0;e.nodes.forEach(x=>{e.dragNode!==x&&(x.x+=x.vx,x.y+=x.vy,k+=x.vx*x.vx+x.vy*x.vy)}),e.autoFitPending&&e.tickCount>45&&(e.autoFitPending=!1,Fe()),(r?Math.sqrt(k/r):0)<.05&&e.tickCount>60&&!e.dragNode?e.quietTicks=(e.quietTicks|0)+1:e.quietTicks=0},Ae=()=>{if(e.running){try{Xe(),qe()}catch{}if(e.quietTicks>30){e.raf=0;return}e.raf=requestAnimationFrame(Ae)}},Te=(r,u)=>{let p=a.getBoundingClientRect();return{x:(r-p.left-e.panX)/e.zoom,y:(u-p.top-e.panY)/e.zoom}},Re=(r,u,p)=>Math.max(u,Math.min(p,r)),Ue=r=>{e.mouseX=r.clientX,e.mouseY=r.clientY;let u=Te(r.clientX,r.clientY),p=null;for(let g=e.nodes.length-1;g>=0;g--){let f=e.nodes[g];if(z.current!=="all"&&(e.domainById[f.id]&&e.domainById[f.id].kind)!==z.current)continue;let v=f.x-u.x,M=f.y-u.y;if(v*v+M*M<f.r*f.r+36){p=f.id;break}}p!==e.hoverId&&(e.hoverId=p,Q())},Ke=r=>{if(r.button!==0)return;let u=Te(r.clientX,r.clientY),p=null;for(let g=e.nodes.length-1;g>=0;g--){let f=e.nodes[g];if(z.current!=="all"&&(e.domainById[f.id]&&e.domainById[f.id].kind)!==z.current)continue;let v=f.x-u.x,M=f.y-u.y;if(v*v+M*M<f.r*f.r+25){p=f;break}}if(r.shiftKey){e.marquee={sx:r.clientX,sy:r.clientY,ex:r.clientX,ey:r.clientY};return}e.dragStart={x:r.clientX,y:r.clientY,px:e.panX,py:e.panY},e.dragging=!1,e.dragNode=p||null,p&&(e.dragOffset={dx:p.x-u.x,dy:p.y-u.y})},He=r=>{if(e.marquee){e.marquee.ex=r.clientX,e.marquee.ey=r.clientY,Q();return}if(!e.dragStart)return;let u=r.clientX-e.dragStart.x,p=r.clientY-e.dragStart.y;if(Math.abs(u)+Math.abs(p)>3&&(e.dragging=!0),e.dragNode){let g=Te(r.clientX,r.clientY);e.dragNode.x=g.x+e.dragOffset.dx,e.dragNode.y=g.y+e.dragOffset.dy,Q()}else e.dragging&&(e.panX=Re(e.dragStart.px+u,-1e5,1e5),e.panY=Re(e.dragStart.py+p,-1e5,1e5),Q())},Ge=r=>{if(e.marquee){let p=e.marquee;e.marquee=null;let g=a.getBoundingClientRect(),f=Math.min(p.sx,p.ex)-g.left,v=Math.max(p.sx,p.ex)-g.left,M=Math.min(p.sy,p.ey)-g.top,s=Math.max(p.sy,p.ey)-g.top,k=e.nodes.filter(I=>{let x=I.x*e.zoom+e.panX,w=I.y*e.zoom+e.panY;return x>=f&&x<=v&&w>=M&&w<=s}).map(I=>I.id);e.multi=k,X(k),Q();return}if(e.dragStart&&!e.dragging){let p=Te(r.clientX,r.clientY),g=null;for(let f=e.nodes.length-1;f>=0;f--){let v=e.nodes[f];if(z.current!=="all"&&(e.domainById[v.id]&&e.domainById[v.id].kind)!==z.current)continue;let M=v.x-p.x,s=v.y-p.y;if(M*M+s*s<v.r*v.r+20){g=v;break}}g?(e.selectedId=g.id,se(g.id),Q()):(e.selectedId=null,e.hoverId=null,r.shiftKey||(e.multi=[],X([])),se(null),Q())}e.dragStart=null,e.dragging=!1,e.dragNode=null},Ve=r=>{r.preventDefault();let u=Te(r.clientX,r.clientY),p=null;for(let g=e.nodes.length-1;g>=0;g--){let f=e.nodes[g];if(z.current!=="all"&&(e.domainById[f.id]&&e.domainById[f.id].kind)!==z.current)continue;let v=f.x-u.x,M=f.y-u.y;if(v*v+M*M<f.r*f.r+25){p=f;break}}if(p){let g=e.domainById[p.id];g&&V({x:r.clientX,y:r.clientY,id:p.id,path:g.id,title:g.title})}else V(null)},_e=r=>{r.preventDefault();let u=a.getBoundingClientRect(),p=(r.clientX-u.left-e.panX)/e.zoom,g=(r.clientY-u.top-e.panY)/e.zoom,f=Math.max(.3,Math.min(3,e.zoom*(r.deltaY<0?1.1:1/1.1)));e.panX=Re(e.panX+p*(e.zoom-f),-1e5,1e5),e.panY=Re(e.panY+g*(e.zoom-f),-1e5,1e5),e.zoom=f,Q()};a.addEventListener("mousemove",Ue),a.addEventListener("mousedown",Ke),window.addEventListener("mousemove",He),window.addEventListener("mouseup",Ge),a.addEventListener("wheel",_e,{passive:!1}),a.addEventListener("contextmenu",Ve),G.current=()=>{e.selectedId=null,e.hoverId=null,e.multi=[],X([]),Fe(),Q()};for(let r=0;r<200;r++)Xe();return Fe(),Q(),()=>{e.running=!1,e.raf&&cancelAnimationFrame(e.raf),We.disconnect(),a.removeEventListener("mousemove",Ue),a.removeEventListener("mousedown",Ke),window.removeEventListener("mousemove",He),window.removeEventListener("mouseup",Ge),a.removeEventListener("wheel",_e),a.removeEventListener("contextmenu",Ve)}},[n,l]),(0,o.jsxs)("div",{className:"me-graph",ref:W,children:[(0,o.jsxs)("div",{className:"me-graph-toolbar",children:[(0,o.jsxs)("span",{className:"me-graph-count",children:[n.length," ",i("nodes")," \xB7 ",l.length," ",i("edges")]}),(0,o.jsx)("span",{className:"spacer",style:{flex:1}}),(0,o.jsx)("input",{type:"text",className:"mc-btn",style:{padding:"6px 10px",minWidth:140},placeholder:i("search"),value:q,onChange:a=>Oe(a.target.value)}),(0,o.jsx)("button",{type:"button",className:`mc-btn${j?" me-on":""}`,onClick:()=>Me(a=>!a),children:i("timeDim")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{let a=T.current;if(a)try{a.toBlob(t=>{t&&fe({url:URL.createObjectURL(t),blob:t})},"image/png")}catch{}},children:i("exportGraph")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>E.current&&E.current(),children:i("fit")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{se(null),oe("all"),G.current&&G.current()},children:i("reset")})]}),(0,o.jsxs)("div",{className:"me-graph-canvas",style:{position:"relative",overflow:"hidden"},children:[(0,o.jsx)("canvas",{ref:T,style:{width:"100%",height:"100%",display:"block",cursor:"grab"}}),(0,o.jsx)("div",{className:"me-graph-tooltip",ref:_,style:{display:"none",position:"absolute",zIndex:4,pointerEvents:"none",background:"rgba(28,28,32,0.88)",backdropFilter:"blur(10px)",WebkitBackdropFilter:"blur(10px)",color:"#eee",borderRadius:8,padding:"7px 10px",fontSize:12,maxWidth:220,boxShadow:"0 8px 24px rgba(0,0,0,.2)"}}),(0,o.jsxs)("div",{className:"me-graph-legend",style:{position:"absolute",left:12,bottom:8,zIndex:2},children:[j?[{c:"#10b981",l:i("timeNew")},{c:"#f59e0b",l:i("timeRecent")},{c:"#f97316",l:i("timeMonth")},{c:"#94a3b8",l:i("timeOld")}].map(a=>(0,o.jsxs)("span",{className:"lg",children:[(0,o.jsx)("span",{className:"mc-kind",style:{background:a.c}}),a.l]},a.c)):Object.keys(Pe).map(a=>(0,o.jsxs)("button",{type:"button",className:`lg${ue===a?" active":""}`,onClick:()=>oe(t=>t===a?"all":a),children:[(0,o.jsx)("span",{className:"mc-kind",style:{background:$e.colors[a]||Pe[a]}}),i(Ce[a])]},a)),!j&&ue!=="all"&&(0,o.jsxs)("button",{type:"button",className:"lg lg-clear",onClick:()=>oe("all"),children:[i("clearFilter")," \u2715"]})]}),Ye&&(0,o.jsx)("div",{className:"mc-empty",style:{position:"absolute",inset:0,zIndex:2,display:"flex",alignItems:"center",justifyContent:"center",backdropFilter:"blur(2px)",WebkitBackdropFilter:"blur(2px)"},children:i("noMatch")}),O&&(0,o.jsx)("div",{className:"me-graph-ctx-back",style:{position:"fixed",inset:0,zIndex:30},onMouseDown:()=>V(null)}),O&&(0,o.jsxs)("div",{className:"me-graph-ctxmenu",style:{position:"fixed",left:O.x,top:O.y,zIndex:31},onMouseDown:a=>a.stopPropagation(),children:[(0,o.jsx)("button",{type:"button",onClick:()=>{ie.current({path:O.path,title:O.title}),V(null)},children:i("openCard")}),(0,o.jsx)("button",{type:"button",onClick:()=>{O.id&&(C.current.selectedId=O.id,se(O.id)),V(null);let a=C.current;a&&a.wake&&a.wake()},children:i("focusNeighbors")}),(0,o.jsx)("button",{type:"button",onClick:()=>{try{navigator.clipboard&&navigator.clipboard.writeText(O.title||"")}catch{}V(null),F(i("copied"))},children:i("copyName")}),(0,o.jsx)("button",{type:"button",style:{color:"#f87171"},onClick:()=>{let a=O.path;V(null),window.confirm(i("deleteConfirm"))&&(async()=>{try{(pe.current?await pe.current(a):!1)?F(i("deleted")):F(i("deleteFail"),!1)}catch{F(i("deleteFail"),!1)}})()},children:i("delete")})]}),J.length>0&&(0,o.jsxs)("div",{style:{position:"absolute",left:10,top:10,zIndex:3,display:"flex",gap:8,alignItems:"center",background:"rgba(28,28,32,0.92)",backdropFilter:"blur(10px)",WebkitBackdropFilter:"blur(10px)",color:"#eee",borderRadius:10,padding:"6px 10px",fontSize:12,boxShadow:"0 10px 30px rgba(0,0,0,.25)"},children:[(0,o.jsxs)("span",{style:{fontWeight:700},children:[J.length," ",i("nodes")]}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{let t=J.map(D=>d[D]).filter(Boolean).map(D=>D.title||D.name).join(`
113
- `),y=new Blob([t],{type:"text/plain;charset=utf-8"}),N=URL.createObjectURL(y),R=document.createElement("a");R.href=N,R.download="memory-selected.txt",document.body.appendChild(R),R.click(),document.body.removeChild(R),setTimeout(()=>URL.revokeObjectURL(N),1e3),F(J.length+i("exportedSel"))},children:i("exportSel")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{if(J.length<2){F(i("mergeNeed"),!1);return}let a=J.map(y=>d[y]?d[y].id:y);if(!window.confirm(i("mergeConfirm")))return;X([]);let t=C.current;t&&(t.multi=[]),fetch(`${ee}/merge?paths=${encodeURIComponent(a.join(","))}`).then(y=>y.json()).then(y=>{y&&y.ok?F(i("merged")):F(i("mergeFail"),!1)}).catch(()=>F(i("mergeFail"),!1))},children:i("merge")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",style:{color:"#f87171"},onClick:()=>{if(!window.confirm(i("deleteConfirm")))return;let a=J.map(y=>d[y]?d[y].id:y);X([]);let t=C.current;t&&(t.multi=[]),(async()=>{let y=0;for(let N of a)try{(pe.current?await pe.current(N):!1)&&y++}catch{}F(y+i("deletedSelected"),y>0)})()},children:i("deleteSelected")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{let a=C.current;a&&(a.multi=[]),X([]);let t=C.current;t&&t.render&&t.render()},children:i("clearSelection")})]}),B&&(0,o.jsxs)("div",{className:"me-graph-sidebar",style:{position:"absolute",right:10,top:10,zIndex:3,maxWidth:240,background:"rgba(28,28,32,0.92)",backdropFilter:"blur(10px)",WebkitBackdropFilter:"blur(10px)",color:"#eee",borderRadius:10,padding:12,fontSize:12,boxShadow:"0 10px 30px rgba(0,0,0,.25)"},children:[(0,o.jsx)("div",{style:{fontWeight:700,fontSize:13,marginBottom:4},children:d[B]?d[B].title||d[B].name:B}),(0,o.jsx)("div",{style:{opacity:.8,marginBottom:8},children:d[B]?i(Ce[d[B].kind]||"kindKnowledge"):"\u2014"}),(0,o.jsx)("div",{style:{fontSize:11,opacity:.65,letterSpacing:".04em",marginBottom:6},children:i("graphHint")}),(l.filter(a=>a.source===B||a.target===B)||[]).slice(0,8).map((a,t)=>{let y=a.source===B?a.target:a.source,N=d[y];return(0,o.jsx)("div",{style:{padding:"4px 6px",cursor:"pointer",borderRadius:6},onMouseDown:R=>{R.preventDefault();let D=C.current;D&&(D.selectedId=y,D.hoverId=y,D.wake&&D.wake()),se(y)},children:N?(N.title||N.name).slice(0,28):y},t)})]})]}),(0,o.jsx)("div",{className:"me-graph-tip",children:i("graphTip")}),Z&&(0,o.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,position:"fixed",left:"50%",transform:"translateX(-50%)",top:22,zIndex:70,background:"rgba(20,22,26,0.97)",backdropFilter:"blur(12px)",WebkitBackdropFilter:"blur(12px)",color:"#f5f5f5",borderRadius:12,padding:"10px 20px",fontSize:13.5,fontWeight:600,boxShadow:"0 14px 44px rgba(0,0,0,.5)",pointerEvents:"none",animation:"me-pop .22s ease",borderLeft:"4px solid "+(Z.ok?"#22c55e":"#ef4444"),maxWidth:"90vw"},children:[(0,o.jsx)("span",{style:{color:Z.ok?"#34d399":"#f87171",fontWeight:800,fontSize:15},children:Z.ok?"\u2713":"\u2715"}),(0,o.jsx)("span",{style:{wordBreak:"break-all"},children:Z.msg})]}),A&&(0,o.jsxs)("div",{className:"me-overlay",onClick:()=>{A.url&&URL.revokeObjectURL(A.url),fe(null),he(!1)},children:[(0,o.jsx)("style",{children:Ne}),(0,o.jsxs)("div",{className:"me-dialog",style:{maxWidth:U?"100vw":900,width:U?"100vw":"92vw",maxHeight:U?"100vh":"90vh",height:U?"100vh":void 0,borderRadius:U?0:14},onClick:a=>a.stopPropagation(),children:[(0,o.jsxs)("div",{className:"me-dialog-head",children:[(0,o.jsx)("h3",{children:i("exportGraph")}),(0,o.jsxs)("div",{style:{display:"flex",gap:8},children:[(0,o.jsx)("a",{className:"mc-btn",href:A.url,download:"memory-graph.png",onClick:()=>{be("download"),F(i("downloaded")+" \xB7 "+i("defaultDownloads"))},style:{textDecoration:"none",display:"inline-flex",alignItems:"center"},children:i(xe==="download"?"done":"download")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{be("save");let a=A.blob;a&&typeof window.showSaveFilePicker=="function"?at(a,"memory-graph.png",!0).then(t=>{t.ok?F(i("exportedTo")+"\uFF1A"+t.name):t.aborted?F(i("exportCancel")):F(i("exportFail"),!1)}):F(i("saveAsUnsupported"),!1)},children:i(xe==="save"?"done":"saveAs")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{be("full"),he(a=>!a),F(i("fullscreen"))},children:i(xe==="full"?"done":U?"exitFull":"fullscreen")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{let a=A.blob;a&&window.ClipboardItem&&navigator.clipboard?navigator.clipboard.write([new window.ClipboardItem({"image/png":a})]).then(()=>{be("copy"),F(i("copied"))}).catch(()=>{F(i("copyFail"),!1)}):F(i("copyFail"),!1)},children:i(xe==="copy"?"done":"copyImage")}),(0,o.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{A.url&&URL.revokeObjectURL(A.url),fe(null),he(!1)},children:i("close")})]})]}),(0,o.jsx)("div",{className:"me-dialog-body",style:{display:"flex",justifyContent:"center",background:U?"var(--dsw-alias-bg-base, #0b0d10)":"var(--dsw-alias-bg-base, #f3f4f6)"},children:(0,o.jsx)("img",{src:A.url,alt:i("exportGraph"),style:{maxWidth:"100%",maxHeight:U?"calc(100vh - 64px)":"70vh",borderRadius:U?0:8,cursor:"zoom-in"},onClick:()=>he(a=>!a)})})]})]})]})}function It(n){let l=new Date(n||0).getTime();if(!l)return"#94a3b8";let m=(Date.now()-l)/864e5;return m<3?"#10b981":m<14?"#f59e0b":m<30?"#f97316":"#94a3b8"}function St(n){let l=new Date(n||0).getTime();return!!l&&(Date.now()-l)/864e5<3}async function at(n,l,m){if(m&&typeof window.showSaveFilePicker=="function")try{let b=await window.showSaveFilePicker({suggestedName:l}),T=await b.createWritable();return await T.write(n),await T.close(),{ok:!0,picked:!0,name:b.name}}catch(b){return b&&b.name==="AbortError"?{ok:!1,aborted:!0}:{ok:!1,err:String(b.message||b)}}let h=URL.createObjectURL(n),i=document.createElement("a");return i.href=h,i.download=l,document.body.appendChild(i),i.click(),document.body.removeChild(i),setTimeout(()=>URL.revokeObjectURL(h),1e3),{ok:!0,picked:!1,name:l}}function tt(n,l){if(!l||!n)return n;let m=n.toLowerCase(),h=l.toLowerCase(),i=[],b=0;for(;b<n.length;){let T=m.indexOf(h,b);if(T<0){i.push(n.slice(b));break}T>b&&i.push(n.slice(b,T)),i.push((0,o.jsx)("mark",{className:"mc-hl",children:n.slice(T,T+h.length)},T)),b=T+h.length}return i}function Tt(n){if(!n)return"";let l=String(n).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),m=[];return l=l.replace(/```([\w-]*)\n([\s\S]*?)```/g,function(h,i,b){return m.push("<pre><code>"+b+"</code></pre>"),"\0"+(m.length-1)+"\0"}),l=l.replace(/^(#{1,3})\s+(.+)$/gm,function(h,i,b){return"<h"+i.length+">"+b+"</h"+i.length+">"}),l=l.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),l=l.replace(/`([^`\n]+)`/g,"<code>$1</code>"),l=l.replace(/(^|[^*])\*([^*]+)\*/g,"$1<em>$2</em>"),l=l.replace(/\[([^\]]+)\]\(([^)]+)\)/g,function(h,i,b){return/^(https?:|\/)/.test(b)?'<a href="'+b+'" target="_blank" rel="noopener noreferrer">'+i+"</a>":i}),l=l.replace(/^(-)\s+(.+)$/gm,"\u2022 $2"),l=l.replace(/^\s*(&gt;)\s*(.+)$/gm,"<blockquote>$2</blockquote>"),l=l.replace(/\n/g,"<br/>"),l=l.replace(/\u0000(\d+)\u0000/g,function(h,i){return m[i]}),l}function zt(n){if(!n)return"";let l=new Date(n);if(Number.isNaN(l.getTime()))return"";let m=new Date,h=l.toDateString()===m.toDateString(),i=T=>String(T).padStart(2,"0"),b=`${i(l.getHours())}:${i(l.getMinutes())}`;return h?b:`${l.getMonth()+1}-${l.getDate()} ${b}`}
112
+ `));let Re=new Blob([L],{type:xe}),me=await rt(Re,t,m);if(!me.ok){F({ok:!1,msg:me.aborted?a("exportCancel"):a("exportFail")}),U(!1);return}F({ok:!0,msg:me.picked?a("exportedTo")+"\uFF1A"+me.name:a("exportedTo")+"\uFF1A"+a("defaultDownloads")+" \xB7 "+me.name})}catch(g){F({ok:!1,msg:a("exportFail")+" \xB7 "+(g.message||"")})}finally{U(!1)}A.current&&clearTimeout(A.current),A.current=setTimeout(()=>F(null),2600)},_=async o=>{let m=o.target.files&&o.target.files[0];if(o.target.value="",!!m){try{let g=await m.text(),L=await(await fetch(`${q}/import`,{method:"POST",headers:{"Content-Type":"application/json"},body:g})).json();L.ok?(await ae(),F({ok:!0,msg:a("importedVault")+"\uFF1A"+(L.imported||0)+(L.skipped?a("importedSkipped")+L.skipped:"")})):F({ok:!1,msg:L.error||a("importFail")})}catch{F({ok:!1,msg:a("importFail")})}A.current&&clearTimeout(A.current),A.current=setTimeout(()=>F(null),2400)}},i=(0,d.useMemo)(()=>{let o=u.slice();return D==="title"?o.sort((m,g)=>String(m.title||"").localeCompare(String(g.title||""))):D==="hot"?o.sort((m,g)=>(g.weight||g.links||0)-(m.weight||m.links||0)):o.sort((m,g)=>new Date(g.updated||0).getTime()-new Date(m.updated||0).getTime()),o},[u,D]);return(0,e.jsxs)("div",{className:"memory-eternal-root",style:{height:"100%",display:"flex",flexDirection:"column"},children:[(0,e.jsx)("style",{children:we}),$&&(0,e.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,position:"fixed",left:"50%",transform:"translateX(-50%)",top:22,zIndex:70,background:"rgba(20,22,26,0.97)",backdropFilter:"blur(12px)",WebkitBackdropFilter:"blur(12px)",color:"#f5f5f5",borderRadius:12,padding:"10px 20px",fontSize:13.5,fontWeight:600,boxShadow:"0 14px 44px rgba(0,0,0,.5)",pointerEvents:"none",animation:"me-pop .22s ease",borderLeft:"4px solid "+($.ok?"#22c55e":"#ef4444"),maxWidth:"90vw"},children:[(0,e.jsx)("span",{style:{color:$.ok?"#34d399":"#f87171",fontWeight:800,fontSize:15},children:$.ok?"\u2713":"\u2715"}),(0,e.jsx)("span",{children:$.msg})]}),s&&(0,e.jsxs)("div",{className:"me-modal-head",children:[(0,e.jsxs)("div",{className:"me-modal-title",children:[(0,e.jsx)("span",{className:"me-wicon",children:(0,e.jsx)(at,{})}),(0,e.jsx)("h2",{children:a("memoryTitle")}),(0,e.jsx)("span",{children:a("memoryHint")})]}),(0,e.jsx)("div",{className:"spacer"}),(0,e.jsx)("button",{type:"button",className:"me-modal-close",onClick:p,"aria-label":a("close"),children:"\u2715"})]}),(0,e.jsxs)("div",{className:"me-modal-body",children:[(0,e.jsxs)("div",{className:"mc-stats",children:[(0,e.jsx)(Ae,{label:a("total"),value:b?b.total:"\u2014"}),(0,e.jsx)(Ae,{label:a("recent"),value:b?b.recent:"\u2014"}),(0,e.jsx)(Ae,{label:a("tags"),value:b?b.tags:"\u2014"}),(0,e.jsx)(Ae,{label:a("cardCount"),value:b?b.byKind?.knowledge??0:"\u2014"})]}),(0,e.jsxs)("div",{className:"mc-toolbar",children:[(0,e.jsx)("input",{type:"text",placeholder:a("searchPlaceholder"),value:X,onChange:o=>be(o.target.value)}),(0,e.jsxs)("div",{className:"mc-tabs",children:[(0,e.jsx)("button",{type:"button",className:`mc-tab${R==="cards"?" active":""}`,onClick:()=>x("cards"),children:a("cardsTab")}),(0,e.jsx)("button",{type:"button",className:`mc-tab${R==="graph"?" active":""}`,onClick:()=>x("graph"),children:a("graphTab")})]}),(0,e.jsx)("input",{ref:ze,type:"file",accept:".json,application/json",style:{display:"none"},onChange:_}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>ze.current&&ze.current.click(),children:a("importVault")}),(0,e.jsxs)("label",{className:"mc-btn",style:{display:"inline-flex",alignItems:"center",gap:5,cursor:"pointer"},title:a("location"),children:[(0,e.jsx)("input",{type:"checkbox",checked:ee,onChange:o=>Ye(o.target.checked)}),a("location")]}),(0,e.jsx)("button",{type:"button",className:"mc-btn",disabled:fe,onClick:()=>ye("md",ee),children:a(fe?"exporting":"exportVault")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",disabled:fe,onClick:()=>ye("json",ee),children:a(fe?"exporting":"exportJson")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>V({tab:"stats"}),children:a("manage")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>ae(),children:a("refresh")})]}),R==="cards"&&(0,e.jsxs)("div",{className:"mc-chips",children:[yt.map(o=>(0,e.jsx)("button",{type:"button",className:`mc-chip${G===o?" active":""}`,onClick:()=>he(o),children:a(o==="all"?"all":ve[o])},o)),(0,e.jsx)("span",{className:"spacer",style:{flex:1}}),[{key:"recent",label:a("sortRecent")},{key:"title",label:a("sortTitle")},{key:"hot",label:a("sortHot")}].map(o=>(0,e.jsx)("button",{type:"button",className:`mc-chip${D===o.key?" active":""}`,onClick:()=>O(o.key),children:o.label},o.key))]}),M&&(0,e.jsxs)("div",{className:"mc-empty",children:[a("error"),"\uFF1A",M]}),R==="cards"?j&&!u.length?(0,e.jsx)("div",{className:"mc-empty",children:a("loading")}):u.length===0?(0,e.jsx)("div",{className:"mc-empty",children:a("empty")}):(0,e.jsxs)(e.Fragment,{children:[(0,e.jsx)("div",{className:"mc-grid",children:i.slice(0,J).map(o=>(0,e.jsx)(kt,{card:o,t:a,query:X.trim(),onOpen:B,onDelete:Le},o.path))}),i.length>J&&(0,e.jsx)("div",{ref:pe,style:{height:1}})]}):(0,e.jsx)(Mt,{t:a,onOpen:B}),te&&(0,e.jsx)(Nt,{t:a,card:te,onClose:()=>oe(null),onDelete:o=>{oe(null),Le(o)}}),se&&(0,e.jsx)(Ct,{t:a,tab:se.tab,onTab:o=>V({tab:o}),onClose:()=>V(null),onReload:()=>ae()})]})]})}var Ae=({label:a,value:s})=>(0,e.jsxs)("div",{className:"mc-stat mc-card",children:[(0,e.jsx)("b",{children:s}),(0,e.jsx)("span",{children:a})]}),kt=({card:a,t:s,onOpen:p,query:b,onDelete:r})=>(0,e.jsxs)("article",{className:"mc-cardrow mc-card",onClick:()=>p(a),children:[(0,e.jsxs)("h4",{children:[(0,e.jsx)("span",{className:"mc-kind",style:{background:We[a.kind]||We.other}}),b?ot(a.title,b):a.title,Tt(a.updated)&&(0,e.jsx)("span",{className:"mc-new",children:s("newBadge")})]}),(0,e.jsx)("p",{children:b?ot(a.summary||"",b):a.summary||""}),a.tags.length>0&&(0,e.jsx)("div",{className:"mc-tags",children:a.tags.slice(0,4).map(u=>(0,e.jsx)("span",{className:"mc-tag",children:u},u))}),(0,e.jsxs)("footer",{children:[(0,e.jsx)("span",{children:s(ve[a.kind])}),(0,e.jsx)("span",{children:it(a.updated)}),(0,e.jsx)("span",{className:"spacer",style:{flex:1}}),(0,e.jsx)("button",{type:"button",className:"mc-card-del",title:s("delete"),onClick:u=>{u.stopPropagation(),window.confirm(s("deleteConfirm"))&&r&&r(a.path)},children:"\u2715"})]})]});function Ct({t:a,tab:s,onTab:p,onClose:b,onReload:r}){let[u,S]=(0,d.useState)(null),[j,C]=(0,d.useState)(null),[M,H]=(0,d.useState)(null),G=(0,d.useCallback)(async()=>{try{let[x,D,O]=await Promise.all([fetch(`${q}/stats`).then($=>$.json()),fetch(`${q}/optimize`).then($=>$.json()),fetch(`${q}/budget`).then($=>$.json())]);x.ok&&S(x),D.ok&&C(D),O.ok&&H(O)}catch{}},[]);(0,d.useEffect)(()=>{G()},[G]);let ie=async(x,D)=>{if(window.confirm(a("mergeConfirm")))try{(await fetch(`${q}/merge?paths=${encodeURIComponent(x+","+D)}`).then($=>$.json())).ok&&(await G(),r&&r())}catch{}},X=async x=>{if(window.confirm(a("deleteConfirm")))try{(await fetch(`${q}/delete?path=${encodeURIComponent(x)}`).then(O=>O.json())).ok&&(await G(),r&&r())}catch{}},Ie=["project","knowledge","content","prompt","business","tool","mistake"],R=({title:x,kind:D,updated:O,path:$})=>(0,e.jsxs)("li",{style:{display:"flex",alignItems:"center",gap:8,padding:"3px 4px",borderBottom:"1px solid rgba(127,127,127,0.12)"},children:[(0,e.jsx)("span",{className:"mc-kind",style:{background:Se.colors[D]||"#666"}}),(0,e.jsx)("span",{style:{flex:1,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:x}),(0,e.jsx)("span",{style:{opacity:.55,fontSize:11},children:it(O)}),(0,e.jsxs)("button",{type:"button",className:"mc-btn",style:{padding:"2px 8px"},onClick:()=>X($),children:[a("delete")," \u2715"]})]});return(0,e.jsxs)("div",{className:"me-overlay",onClick:b,children:[(0,e.jsx)("style",{children:we}),(0,e.jsxs)("div",{className:"me-dialog",style:{maxWidth:720,width:"94vw",maxHeight:"86vh"},onClick:x=>x.stopPropagation(),children:[(0,e.jsxs)("div",{className:"me-dialog-head",children:[(0,e.jsxs)("div",{style:{display:"flex",gap:8},children:[(0,e.jsx)("button",{type:"button",className:`mc-tab${s==="stats"?" active":""}`,onClick:()=>p("stats"),children:a("tabUsage")}),(0,e.jsx)("button",{type:"button",className:`mc-tab${s==="optimize"?" active":""}`,onClick:()=>p("optimize"),children:a("tabOptimize")})]}),(0,e.jsx)("div",{className:"spacer",style:{flex:1}}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:b,children:a("close")})]}),(0,e.jsxs)("div",{className:"me-dialog-body",style:{overflow:"auto"},children:[s==="stats"&&(0,e.jsx)("div",{children:u&&(0,e.jsxs)(e.Fragment,{children:[(0,e.jsxs)("div",{className:"mc-stats",children:[(0,e.jsxs)("div",{className:"mc-stat mc-card",children:[(0,e.jsx)("b",{children:u.total}),(0,e.jsx)("span",{children:a("total")})]}),(0,e.jsxs)("div",{className:"mc-stat mc-card",children:[(0,e.jsx)("b",{children:u.today}),(0,e.jsx)("span",{children:a("todayAdd")})]}),(0,e.jsxs)("div",{className:"mc-stat mc-card",children:[(0,e.jsx)("b",{children:u.week}),(0,e.jsx)("span",{children:a("weekAdd")})]}),(0,e.jsxs)("div",{className:"mc-stat mc-card",children:[(0,e.jsx)("b",{children:u.tags}),(0,e.jsx)("span",{children:a("tags")})]})]}),(0,e.jsxs)("div",{className:"mc-card",style:{marginBottom:10},children:[(0,e.jsx)("div",{style:{fontSize:12,fontWeight:700,marginBottom:6},children:a("byKind")}),(0,e.jsx)("div",{style:{display:"flex",gap:10,flexWrap:"wrap"},children:Ie.map(x=>(0,e.jsxs)("span",{style:{fontSize:12},children:[(0,e.jsx)("span",{className:"mc-kind",style:{background:Se.colors[x]||"#666"}}),a(ve[x]),": ",u.byKind?.[x]||0]},x))})]}),M&&(M.budgetChars||M.recallLimit||M.embedding)&&(0,e.jsxs)("div",{className:"mc-card",style:{marginBottom:10,fontSize:12},children:[(0,e.jsx)("b",{children:a("budgetLabel")}),"\uFF1A",a("budgetChars")," ",(0,e.jsx)("b",{children:M.budgetChars||8e4})," \xB7 ",a("recallLimitLabel")," ",(0,e.jsx)("b",{children:M.recallLimit||5})," \xB7 ",a("embeddingLabel")," ",M.embedding?"ON":"OFF"]}),(0,e.jsxs)("div",{className:"mc-card",children:[(0,e.jsxs)("div",{style:{fontSize:12,fontWeight:700,marginBottom:6},children:[a("todayList"),"\uFF08",u.todayCards?.length||0,"\uFF09"]}),u.todayCards?.length?(0,e.jsx)("ul",{style:{margin:0,padding:0,listStyle:"none"},children:u.todayCards.map(x=>(0,e.jsx)(R,{title:x.title,kind:x.kind,updated:x.updated,path:x.path},x.path))}):(0,e.jsx)("div",{style:{opacity:.6,fontSize:12},children:a("noOptimize")})]})]})}),s==="optimize"&&(0,e.jsx)("div",{children:j&&(0,e.jsxs)(e.Fragment,{children:[(0,e.jsx)("div",{style:{fontSize:12,fontWeight:700,marginBottom:6},children:a("mergePairs")}),j.merge?.length?(0,e.jsx)("ul",{style:{margin:"0 0 12px",padding:0,listStyle:"none"},children:j.merge.map((x,D)=>(0,e.jsxs)("li",{style:{display:"flex",alignItems:"center",gap:8,padding:"4px 4px",borderBottom:"1px solid rgba(127,127,127,0.12)",fontSize:12},children:[(0,e.jsxs)("span",{style:{flex:1,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:[x.a.title," \u21C4 ",x.b.title]}),(0,e.jsxs)("span",{style:{opacity:.6},children:[Math.round(x.sim*100),"%"]}),(0,e.jsx)("button",{type:"button",className:"mc-btn",style:{padding:"2px 8px"},onClick:()=>ie(x.a.path,x.b.path),children:a("mergeNow")})]},D))}):(0,e.jsx)("div",{style:{opacity:.6,fontSize:12,marginBottom:12},children:a("noOptimize")}),(0,e.jsxs)("div",{style:{fontSize:12,fontWeight:700,marginBottom:6},children:[a("staleCards"),"\uFF08",j.stale?.length||0,"\uFF09"]}),j.stale?.length?(0,e.jsx)("ul",{style:{margin:0,padding:0,listStyle:"none"},children:j.stale.map(x=>(0,e.jsx)(R,{title:x.title,kind:"other",updated:x.updated,path:x.path},x.path))}):(0,e.jsx)("div",{style:{opacity:.6,fontSize:12},children:a("noOptimize")})]})})]})]})]})}function Nt({t:a,card:s,onClose:p,onDelete:b}){(0,d.useEffect)(()=>{let j=C=>{C.key==="Escape"&&p()};return window.addEventListener("keydown",j),()=>window.removeEventListener("keydown",j)},[p]);let[r,u]=(0,d.useState)(!1),S=(s.text||"").length>460;return(0,e.jsxs)("div",{className:"me-overlay",onClick:p,children:[(0,e.jsx)("style",{children:we}),(0,e.jsxs)("div",{className:"me-dialog",onClick:j=>j.stopPropagation(),children:[(0,e.jsxs)("div",{className:"me-dialog-head",children:[(0,e.jsx)("h3",{children:s.title}),(0,e.jsxs)("div",{style:{display:"flex",gap:8},children:[S&&(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>u(j=>!j),children:a(r?"collapse":"expand")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",style:{color:"#f87171"},onClick:()=>{window.confirm(a("deleteConfirm"))&&b&&b(s.path)},children:a("delete")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:p,children:a("close")})]})]}),(0,e.jsx)("div",{className:"me-dialog-body",style:r?{}:{maxHeight:300,overflow:"hidden"},dangerouslySetInnerHTML:{__html:Lt(s.text)}})]})]})}function Mt({t:a,onOpen:s}){let[p,b]=(0,d.useState)(null),[r,u]=(0,d.useState)(""),S=(0,d.useCallback)(async()=>{try{let M=await(await fetch(`${q}/graph`)).json();b(M.ok?M:null),u("")}catch(C){u(String(C&&C.message?C.message:C))}},[]);(0,d.useEffect)(()=>{let C=!0;return fetch(`${q}/graph`).then(M=>M.json()).then(M=>{C&&b(M.ok?M:null)}).catch(M=>{C&&u(String(M&&M.message?M.message:M))}),()=>{C=!1}},[]);let j=(0,d.useCallback)(async C=>{try{let H=await(await fetch(`${q}/delete?path=${encodeURIComponent(C)}`)).json();return H&&H.ok?(await S(),!0):!1}catch{return!1}},[S]);return(0,e.jsxs)("div",{className:"me-graph",children:[(0,e.jsx)("style",{children:we}),r&&(0,e.jsxs)("div",{className:"mc-empty",children:[a("error"),"\uFF1A",r]}),!p&&!r&&(0,e.jsx)("div",{className:"mc-empty",children:a("loading")}),p&&p.nodes.length===0&&(0,e.jsx)("div",{className:"mc-empty",children:a("emptyGraph")}),p&&p.nodes.length>0&&(0,e.jsx)(It,{nodes:p.nodes,edges:p.edges,onOpen:s,onDelete:j,t:a,countLabel:`${p.nodes.length} ${a("nodes")} \xB7 ${St(p.edges)} ${a("edges")}`})]})}function St(a){let s=new Set;for(let p of a||[])s.add([p.source,p.target].sort().join("|"));return s.size}var Se={colors:{project:"#3B82F6",knowledge:"#10B981",content:"#F59E0B",prompt:"#A855F7",business:"#EC4899",tool:"#06B6D4",mistake:"#EF4444",other:"#6B7280"},shapes:{project:"circle",knowledge:"circle",content:"rect",prompt:"diamond",business:"hexagon",tool:"circle",mistake:"diamond",other:"circle"}};function It({nodes:a,edges:s,onOpen:p,onDelete:b,t:r,countLabel:u}){let S=(0,d.useRef)(null),j=(0,d.useRef)(null),C=(0,d.useRef)(null),M=(0,d.useRef)(null),H=(0,d.useRef)(null),G=(0,d.useRef)(p);(0,d.useEffect)(()=>{G.current=p},[p]);let ie=(0,d.useRef)(b);(0,d.useEffect)(()=>{ie.current=b},[b]);let[X,Ie]=(0,d.useState)(""),[R,x]=(0,d.useState)(null),D=(0,d.useRef)("");(0,d.useEffect)(()=>{D.current=X.trim().toLowerCase()},[X]);let[O,$]=(0,d.useState)("all"),F=(0,d.useRef)("all"),[A,ze]=(0,d.useState)(!1),fe=(0,d.useRef)(!1),[U,ee]=(0,d.useState)(null);(0,d.useEffect)(()=>{F.current=O},[O]),(0,d.useEffect)(()=>{fe.current=A},[A]);let Ye=(()=>{let i=X.trim().toLowerCase();return!!i&&!a.some(o=>(o.title||o.name||"").toLowerCase().includes(i)||String(o.kind||"").toLowerCase().includes(i)||String(ve[o.kind]||"").toLowerCase().includes(i))})();(0,d.useEffect)(()=>{let i=C.current;i&&i.wake&&i.wake()},[X]),(0,d.useEffect)(()=>{let i=C.current;i&&i.render&&i.render()},[O,A]);let te=(0,d.useRef)(null),[oe,se]=(0,d.useState)([]),[V,le]=(0,d.useState)(null),[J,ce]=(0,d.useState)(!1),[pe,Te]=(0,d.useState)(null),[ae,Le]=(0,d.useState)(""),be=(0,d.useRef)(null),he=(0,d.useRef)(null),B=(i,o=!0)=>{be.current&&clearTimeout(be.current),Te({msg:i,ok:o}),be.current=setTimeout(()=>Te(null),1900)},ye=i=>{Le(i),he.current&&clearTimeout(he.current),he.current=setTimeout(()=>Le(""),1600)};(0,d.useEffect)(()=>()=>{be.current&&clearTimeout(be.current),he.current&&clearTimeout(he.current)},[]);let _=(0,d.useMemo)(()=>a.reduce((i,o)=>(i[o.id]=o,i),{}),[a]);return(0,d.useEffect)(()=>{let i=S.current;if(!i||a.length===0)return;let o=i.getContext("2d");if(!o)return;let m=Math.max(1,window.devicePixelRatio||1),g=()=>document.documentElement.getAttribute("data-theme")==="dark",T={};s.forEach(n=>{T[n.source]=(T[n.source]||0)+1,T[n.target]=(T[n.target]||0)+1});let L=Math.max(1,...Object.values(T)),t={nodes:a.map(n=>({id:n.id,name:n.title||"",type:n.kind||"other",r:9+Math.min(10,(T[n.id]||0)/L*8),x:0,y:0,vx:0,vy:0})),edges:s.map(n=>({sourceNodeId:n.source,targetNodeId:n.target,weight:1})),panX:0,panY:0,zoom:1,running:!0,raf:0,tickCount:0,quietTicks:0,dragNode:null,selectedId:null,mouseX:0,mouseY:0,w:0,h:0,dpr:m,ctx:o,multi:[],marquee:null};t.domain=a,t.domainById=a.reduce((n,f)=>(n[f.id]=f,n),{}),t.nodes.forEach((n,f)=>{let c=f/t.nodes.length*Math.PI*2-Math.PI/2;n.x=Math.cos(c)*60,n.y=Math.sin(c)*60});let xe=[...new Set(t.nodes.map(n=>n.type))];t.clustered=xe.length>1,t.typeCenters={},xe.forEach((n,f)=>{let c=f/xe.length*Math.PI*2-Math.PI/2;t.typeCenters[n]={x:Math.cos(c)*160,y:Math.sin(c)*160}}),C.current=t;let Re=()=>{let n=i.clientWidth,f=i.clientHeight;t.w=n,t.h=f,i.width=n*m,i.height=f*m,o.setTransform(m,0,0,m,0,0)};Re();let me=new ResizeObserver(Re);me.observe(i.parentElement);let ue=(n,f)=>n&&n.length>f?n.slice(0,f)+"\u2026":n,Ee=(n,f,c,h)=>{if(o.beginPath(),h==="rect")o.rect(n-c,f-c*.75,c*2,c*1.5);else if(h==="diamond")o.moveTo(n,f-c),o.lineTo(n+c,f),o.lineTo(n,f+c),o.lineTo(n-c,f),o.closePath();else if(h==="hexagon"){for(let y=0;y<6;y++){let k=Math.PI/3*y-Math.PI/2,I=n+c*Math.cos(k),l=f+c*Math.sin(k);y===0?o.moveTo(I,l):o.lineTo(I,l)}o.closePath()}else o.arc(n,f,c,0,Math.PI*2)},je=()=>{if(!t.nodes.length)return;let n=1/0,f=-1/0,c=1/0,h=-1/0;t.nodes.forEach(N=>{n=Math.min(n,N.x),f=Math.max(f,N.x),c=Math.min(c,N.y),h=Math.max(h,N.y)});let y=36,k=f-n+y*2,I=h-c+y*2,l=Math.min(t.w/k,t.h/I);t.zoom=Math.max(.45,Math.min(2.4,l*1.22)),t.panX=t.w/2-(n+f)/2*t.zoom,t.panY=t.h/2-(c+h)/2*t.zoom};M.current=je;let qe=()=>{o.clearRect(0,0,t.w,t.h),t.searchTerm=D.current,t.kindFilter=F.current,t.timeMode=fe.current;let n=t.kindFilter,f=l=>String(l||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");o.save(),o.strokeStyle=g()?"rgba(255,255,255,0.04)":"rgba(0,0,0,0.04)",o.lineWidth=.5;for(let l=0;l<t.w;l+=24)o.beginPath(),o.moveTo(l,0),o.lineTo(l,t.h),o.stroke();for(let l=0;l<t.h;l+=24)o.beginPath(),o.moveTo(0,l),o.lineTo(t.w,l),o.stroke();o.restore(),o.save(),o.translate(t.panX,t.panY),o.scale(t.zoom,t.zoom);let c={};t.nodes.forEach(l=>c[l.id]=l);let h=t.nodes.length>40,y=h?1.5:.55,k=t.selectedId||t.hoverId;t.edges.forEach(l=>{let N=c[l.sourceNodeId],z=c[l.targetNodeId];if(!N||!z)return;if(n!=="all"){let K=t.domainById[l.sourceNodeId]&&t.domainById[l.sourceNodeId].kind,Qe=t.domainById[l.targetNodeId]&&t.domainById[l.targetNodeId].kind;if(K!==n&&Qe!==n)return}let v=z.x-N.x,w=z.y-N.y,E=Math.sqrt(v*v+w*w)||1,P=h?12:18,W=-w/E*P,Z=v/E*P,Y=(N.x+z.x)/2+W,re=(N.y+z.y)/2+Z,Q=Se.colors[t.domainById[N.id]&&t.domainById[N.id].kind||"other"],de=k&&(l.sourceNodeId===k||l.targetNodeId===k),ke=k?de?.6:.08:h?.14:.24,Be=parseInt(Q.slice(1,3),16),De=parseInt(Q.slice(3,5),16),Ze=parseInt(Q.slice(5,7),16);o.strokeStyle="rgba("+Be+","+De+","+Ze+","+ke+")",o.lineWidth=de?1.6+(l.weight||1):.8+(l.weight||1)*.5,o.beginPath(),o.moveTo(N.x,N.y),o.quadraticCurveTo(Y,re,z.x,z.y),o.stroke()});let I=[];if(t.nodes.forEach(l=>{let N=t.domainById[l.id]||{},z=N.kind||"other",v=t.timeMode?zt(N.updated):Se.colors[z],w=Se.shapes[z],E=t.selectedId===l.id,P=t.hoverId===l.id,W=t.searchTerm||"",Z=!W||(l.name||"").toLowerCase().includes(W)||String(ve[z]||"").toLowerCase().includes(W)||String(z).toLowerCase().includes(W),Y=n==="all"||z===n,re=k&&l.id!==k&&!t.edges.some(K=>K.sourceNodeId===k&&K.targetNodeId===l.id||K.targetNodeId===k&&K.sourceNodeId===l.id)||W&&!Z||!Y;o.save(),o.globalAlpha=re?.08:1,(E||P)&&(o.shadowColor=v,o.shadowBlur=E?20:14),Ee(l.x,l.y,l.r,w);let Q=parseInt(v.slice(1,3),16),de=parseInt(v.slice(3,5),16),ke=parseInt(v.slice(5,7),16),Be=o.createRadialGradient(l.x-l.r*.3,l.y-l.r*.3,0,l.x,l.y,l.r*1.2);Be.addColorStop(0,"rgba("+Math.min(255,Q+70)+","+Math.min(255,de+70)+","+Math.min(255,ke+70)+",0.95)"),Be.addColorStop(1,v),o.fillStyle=Be,o.fill(),o.restore(),E?(o.save(),Ee(l.x,l.y,l.r+3,w),o.strokeStyle=v,o.lineWidth=3,o.shadowColor=v,o.shadowBlur=12,o.stroke(),o.restore()):P?(o.save(),Ee(l.x,l.y,l.r+2,w),o.strokeStyle=v,o.lineWidth=2,o.stroke(),o.restore()):W&&Z?(o.save(),Ee(l.x,l.y,l.r+2,w),o.strokeStyle="#e11d48",o.lineWidth=2,o.stroke(),o.restore()):t.multi.indexOf(l.id)>=0&&(o.save(),Ee(l.x,l.y,l.r+2,w),o.strokeStyle="#60a5fa",o.lineWidth=2,o.shadowColor="#60a5fa",o.shadowBlur=8,o.stroke(),o.restore());let De=ue(l.name,16);if((E||P||t.zoom>y||!h&&t.zoom>.6)&&Y){let K=1/t.zoom;o.font="500 "+(12*K).toFixed(1)+"px -apple-system,Segoe UI,sans-serif";let ge=o.measureText(De).width+16*K,Pe=18*K,Ce=l.y+l.r+8*K,et=!0;for(let Ne of I)if(l.x-ge/2<Ne.x+Ne.w&&l.x+ge/2>Ne.x&&Ce<Ne.y+Ne.h&&Ce+Pe>Ne.y){et=!1;break}if(!et&&!E&&!P)return;I.push({x:l.x-ge/2,y:Ce,w:ge,h:Pe}),o.fillStyle=g()?"rgba(30,30,35,0.92)":"rgba(255,255,255,0.92)",o.beginPath(),o.roundRect?o.roundRect(l.x-ge/2,Ce,ge,Pe,4*K):o.rect(l.x-ge/2,Ce,ge,Pe),o.fill(),o.strokeStyle=g()?"rgba(255,255,255,0.08)":"rgba(0,0,0,0.08)",o.lineWidth=1*K,o.stroke(),o.fillStyle=g()?E||P?"#eee":"#bbb":E||P?"#111":"#444",o.textAlign="center",o.fillText(De,l.x,Ce+13*K)}}),o.restore(),t.marquee){let l=t.marquee,N=Math.min(l.sx,l.ex),z=Math.min(l.sy,l.ey),v=Math.abs(l.ex-l.sx),w=Math.abs(l.ey-l.sy);o.save(),o.setLineDash([4,4]),o.strokeStyle="rgba(96,165,250,0.9)",o.lineWidth=1,o.fillStyle="rgba(96,165,250,0.12)",o.fillRect(N,z,v,w),o.strokeRect(N,z,v,w),o.restore()}if(t.hoverId&&te.current){let l=t.domainById[t.hoverId],N=t.nodes.find(function(z){return z.id===t.hoverId});if(l&&N){let z=t.edges.filter(function(E){return E.sourceNodeId===l.id||E.targetNodeId===l.id}).length,v=N.x*t.zoom+t.panX,w=N.y*t.zoom+t.panY;te.current.style.display="block",te.current.style.left=v+14+"px",te.current.style.top=w-6+"px",te.current.innerHTML="<b>"+f(l.title||l.name||"")+'</b><br/><span style="opacity:.8">'+f(l.kind||"")+" \xB7 "+z+" \u8FDE\u63A5</span>"}}else te.current&&(te.current.style.display="none")},ne=()=>{t.running=!0,t.raf||(t.raf=requestAnimationFrame(Xe))};t.wake=ne,t.render=qe;let Ue=()=>{let n=t.nodes.length;t.tickCount++;let c=.9-Math.min(.4,t.tickCount/1500),h=n>1e3?3e3:n>100?2e3:n>50?1200:800,y=n>100?.002:.005,k=n>1e3?.012:n>100?.005:.01,I=n>1e3?6:n>200?12:24,l={};t.nodes.forEach(v=>l[v.id]=v);for(let v=0;v<n;v++){if(t.dragNode===t.nodes[v])continue;let w=t.nodes[v],E=0,P=0;for(let Y=0;Y<n;Y++){if(v===Y)continue;let re=w.x-t.nodes[Y].x,Q=w.y-t.nodes[Y].y,de=Math.sqrt(re*re+Q*Q)||1,ke=h/(de*de);E+=re/de*ke,P+=Q/de*ke}if(t.clustered&&t.typeCenters[w.type]){let Y=t.typeCenters[w.type];E+=(Y.x-w.x)*.006,P+=(Y.y-w.y)*.006}else E-=w.x*k,P-=w.y*k;let W=(w.vx+E)*c,Z=(w.vy+P)*c;W=Math.max(-I,Math.min(I,W)),Z=Math.max(-I,Math.min(I,Z)),w.vx=W,w.vy=Z}t.edges.forEach(v=>{let w=l[v.sourceNodeId],E=l[v.targetNodeId];if(!w||!E)return;let P=E.x-w.x,W=E.y-w.y,Z=Math.sqrt(P*P+W*W)||1,Y=(Z-100)*y,re=P/Z*Y,Q=W/Z*Y;t.dragNode!==w&&(w.vx+=re,w.vy+=Q),t.dragNode!==E&&(E.vx-=re,E.vy-=Q)});let N=0;t.nodes.forEach(v=>{t.dragNode!==v&&(v.x+=v.vx,v.y+=v.vy,N+=v.vx*v.vx+v.vy*v.vy)}),t.autoFitPending&&t.tickCount>45&&(t.autoFitPending=!1,je()),(n?Math.sqrt(N/n):0)<.05&&t.tickCount>60&&!t.dragNode?t.quietTicks=(t.quietTicks|0)+1:t.quietTicks=0},Xe=()=>{if(t.running){try{Ue(),qe()}catch{}if(t.quietTicks>30){t.raf=0;return}t.raf=requestAnimationFrame(Xe)}},Fe=(n,f)=>{let c=i.getBoundingClientRect();return{x:(n-c.left-t.panX)/t.zoom,y:(f-c.top-t.panY)/t.zoom}},$e=(n,f,c)=>Math.max(f,Math.min(c,n)),Ke=n=>{t.mouseX=n.clientX,t.mouseY=n.clientY;let f=Fe(n.clientX,n.clientY),c=null;for(let h=t.nodes.length-1;h>=0;h--){let y=t.nodes[h];if(F.current!=="all"&&(t.domainById[y.id]&&t.domainById[y.id].kind)!==F.current)continue;let k=y.x-f.x,I=y.y-f.y;if(k*k+I*I<y.r*y.r+36){c=y.id;break}}c!==t.hoverId&&(t.hoverId=c,ne())},He=n=>{if(n.button!==0)return;let f=Fe(n.clientX,n.clientY),c=null;for(let h=t.nodes.length-1;h>=0;h--){let y=t.nodes[h];if(F.current!=="all"&&(t.domainById[y.id]&&t.domainById[y.id].kind)!==F.current)continue;let k=y.x-f.x,I=y.y-f.y;if(k*k+I*I<y.r*y.r+25){c=y;break}}if(n.shiftKey){t.marquee={sx:n.clientX,sy:n.clientY,ex:n.clientX,ey:n.clientY};return}t.dragStart={x:n.clientX,y:n.clientY,px:t.panX,py:t.panY},t.dragging=!1,t.dragNode=c||null,c&&(t.dragOffset={dx:c.x-f.x,dy:c.y-f.y})},Ge=n=>{if(t.marquee){t.marquee.ex=n.clientX,t.marquee.ey=n.clientY,ne();return}if(!t.dragStart)return;let f=n.clientX-t.dragStart.x,c=n.clientY-t.dragStart.y;if(Math.abs(f)+Math.abs(c)>3&&(t.dragging=!0),t.dragNode){let h=Fe(n.clientX,n.clientY);t.dragNode.x=h.x+t.dragOffset.dx,t.dragNode.y=h.y+t.dragOffset.dy,ne()}else t.dragging&&(t.panX=$e(t.dragStart.px+f,-1e5,1e5),t.panY=$e(t.dragStart.py+c,-1e5,1e5),ne())},Ve=n=>{if(t.marquee){let c=t.marquee;t.marquee=null;let h=i.getBoundingClientRect(),y=Math.min(c.sx,c.ex)-h.left,k=Math.max(c.sx,c.ex)-h.left,I=Math.min(c.sy,c.ey)-h.top,l=Math.max(c.sy,c.ey)-h.top,N=t.nodes.filter(z=>{let v=z.x*t.zoom+t.panX,w=z.y*t.zoom+t.panY;return v>=y&&v<=k&&w>=I&&w<=l}).map(z=>z.id);t.multi=N,se(N),ne();return}if(t.dragStart&&!t.dragging){let c=Fe(n.clientX,n.clientY),h=null;for(let y=t.nodes.length-1;y>=0;y--){let k=t.nodes[y];if(F.current!=="all"&&(t.domainById[k.id]&&t.domainById[k.id].kind)!==F.current)continue;let I=k.x-c.x,l=k.y-c.y;if(I*I+l*l<k.r*k.r+20){h=k;break}}h?(t.selectedId=h.id,x(h.id),ne()):(t.selectedId=null,t.hoverId=null,n.shiftKey||(t.multi=[],se([])),x(null),ne())}t.dragStart=null,t.dragging=!1,t.dragNode=null},_e=n=>{n.preventDefault();let f=Fe(n.clientX,n.clientY),c=null;for(let h=t.nodes.length-1;h>=0;h--){let y=t.nodes[h];if(F.current!=="all"&&(t.domainById[y.id]&&t.domainById[y.id].kind)!==F.current)continue;let k=y.x-f.x,I=y.y-f.y;if(k*k+I*I<y.r*y.r+25){c=y;break}}if(c){let h=t.domainById[c.id];h&&ee({x:n.clientX,y:n.clientY,id:c.id,path:h.id,title:h.title})}else ee(null)},Je=n=>{n.preventDefault();let f=i.getBoundingClientRect(),c=(n.clientX-f.left-t.panX)/t.zoom,h=(n.clientY-f.top-t.panY)/t.zoom,y=Math.max(.3,Math.min(3,t.zoom*(n.deltaY<0?1.1:1/1.1)));t.panX=$e(t.panX+c*(t.zoom-y),-1e5,1e5),t.panY=$e(t.panY+h*(t.zoom-y),-1e5,1e5),t.zoom=y,ne()};i.addEventListener("mousemove",Ke),i.addEventListener("mousedown",He),window.addEventListener("mousemove",Ge),window.addEventListener("mouseup",Ve),i.addEventListener("wheel",Je,{passive:!1}),i.addEventListener("contextmenu",_e),H.current=()=>{t.selectedId=null,t.hoverId=null,t.multi=[],se([]),je(),ne()};for(let n=0;n<200;n++)Ue();return je(),ne(),()=>{t.running=!1,t.raf&&cancelAnimationFrame(t.raf),me.disconnect(),i.removeEventListener("mousemove",Ke),i.removeEventListener("mousedown",He),window.removeEventListener("mousemove",Ge),window.removeEventListener("mouseup",Ve),i.removeEventListener("wheel",Je),i.removeEventListener("contextmenu",_e)}},[a,s]),(0,e.jsxs)("div",{className:"me-graph",ref:j,children:[(0,e.jsxs)("div",{className:"me-graph-toolbar",children:[(0,e.jsxs)("span",{className:"me-graph-count",children:[a.length," ",r("nodes")," \xB7 ",s.length," ",r("edges")]}),(0,e.jsx)("span",{className:"spacer",style:{flex:1}}),(0,e.jsx)("input",{type:"text",className:"mc-btn",style:{padding:"6px 10px",minWidth:140},placeholder:r("search"),value:X,onChange:i=>Ie(i.target.value)}),(0,e.jsx)("button",{type:"button",className:`mc-btn${A?" me-on":""}`,onClick:()=>ze(i=>!i),children:r("timeDim")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{let i=S.current;if(i)try{i.toBlob(o=>{o&&le({url:URL.createObjectURL(o),blob:o})},"image/png")}catch{}},children:r("exportGraph")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>M.current&&M.current(),children:r("fit")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{x(null),$("all"),H.current&&H.current()},children:r("reset")})]}),(0,e.jsxs)("div",{className:"me-graph-canvas",style:{position:"relative",overflow:"hidden"},children:[(0,e.jsx)("canvas",{ref:S,style:{width:"100%",height:"100%",display:"block",cursor:"grab"}}),(0,e.jsx)("div",{className:"me-graph-tooltip",ref:te,style:{display:"none",position:"absolute",zIndex:4,pointerEvents:"none",background:"rgba(28,28,32,0.88)",backdropFilter:"blur(10px)",WebkitBackdropFilter:"blur(10px)",color:"#eee",borderRadius:8,padding:"7px 10px",fontSize:12,maxWidth:220,boxShadow:"0 8px 24px rgba(0,0,0,.2)"}}),(0,e.jsxs)("div",{className:"me-graph-legend",style:{position:"absolute",left:12,bottom:8,zIndex:2},children:[A?[{c:"#10b981",l:r("timeNew")},{c:"#f59e0b",l:r("timeRecent")},{c:"#f97316",l:r("timeMonth")},{c:"#94a3b8",l:r("timeOld")}].map(i=>(0,e.jsxs)("span",{className:"lg",children:[(0,e.jsx)("span",{className:"mc-kind",style:{background:i.c}}),i.l]},i.c)):Object.keys(We).map(i=>(0,e.jsxs)("button",{type:"button",className:`lg${O===i?" active":""}`,onClick:()=>$(o=>o===i?"all":i),children:[(0,e.jsx)("span",{className:"mc-kind",style:{background:Se.colors[i]||We[i]}}),r(ve[i])]},i)),!A&&O!=="all"&&(0,e.jsxs)("button",{type:"button",className:"lg lg-clear",onClick:()=>$("all"),children:[r("clearFilter")," \u2715"]})]}),Ye&&(0,e.jsx)("div",{className:"mc-empty",style:{position:"absolute",inset:0,zIndex:2,display:"flex",alignItems:"center",justifyContent:"center",backdropFilter:"blur(2px)",WebkitBackdropFilter:"blur(2px)"},children:r("noMatch")}),U&&(0,e.jsx)("div",{className:"me-graph-ctx-back",style:{position:"fixed",inset:0,zIndex:30},onMouseDown:()=>ee(null)}),U&&(0,e.jsxs)("div",{className:"me-graph-ctxmenu",style:{position:"fixed",left:U.x,top:U.y,zIndex:31},onMouseDown:i=>i.stopPropagation(),children:[(0,e.jsx)("button",{type:"button",onClick:()=>{G.current({path:U.path,title:U.title}),ee(null)},children:r("openCard")}),(0,e.jsx)("button",{type:"button",onClick:()=>{U.id&&(C.current.selectedId=U.id,x(U.id)),ee(null);let i=C.current;i&&i.wake&&i.wake()},children:r("focusNeighbors")}),(0,e.jsx)("button",{type:"button",onClick:()=>{try{navigator.clipboard&&navigator.clipboard.writeText(U.title||"")}catch{}ee(null),B(r("copied"))},children:r("copyName")}),(0,e.jsx)("button",{type:"button",style:{color:"#f87171"},onClick:()=>{let i=U.path;ee(null),window.confirm(r("deleteConfirm"))&&(async()=>{try{(ie.current?await ie.current(i):!1)?B(r("deleted")):B(r("deleteFail"),!1)}catch{B(r("deleteFail"),!1)}})()},children:r("delete")})]}),oe.length>0&&(0,e.jsxs)("div",{style:{position:"absolute",left:10,top:10,zIndex:3,display:"flex",gap:8,alignItems:"center",background:"rgba(28,28,32,0.92)",backdropFilter:"blur(10px)",WebkitBackdropFilter:"blur(10px)",color:"#eee",borderRadius:10,padding:"6px 10px",fontSize:12,boxShadow:"0 10px 30px rgba(0,0,0,.25)"},children:[(0,e.jsxs)("span",{style:{fontWeight:700},children:[oe.length," ",r("nodes")]}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{let o=oe.map(L=>_[L]).filter(Boolean).map(L=>L.title||L.name).join(`
113
+ `),m=new Blob([o],{type:"text/plain;charset=utf-8"}),g=URL.createObjectURL(m),T=document.createElement("a");T.href=g,T.download="memory-selected.txt",document.body.appendChild(T),T.click(),document.body.removeChild(T),setTimeout(()=>URL.revokeObjectURL(g),1e3),B(oe.length+r("exportedSel"))},children:r("exportSel")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{if(oe.length<2){B(r("mergeNeed"),!1);return}let i=oe.map(m=>_[m]?_[m].id:m);if(!window.confirm(r("mergeConfirm")))return;se([]);let o=C.current;o&&(o.multi=[]),fetch(`${q}/merge?paths=${encodeURIComponent(i.join(","))}`).then(m=>m.json()).then(m=>{m&&m.ok?B(r("merged")):B(r("mergeFail"),!1)}).catch(()=>B(r("mergeFail"),!1))},children:r("merge")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",style:{color:"#f87171"},onClick:()=>{if(!window.confirm(r("deleteConfirm")))return;let i=oe.map(m=>_[m]?_[m].id:m);se([]);let o=C.current;o&&(o.multi=[]),(async()=>{let m=0;for(let g of i)try{(ie.current?await ie.current(g):!1)&&m++}catch{}B(m+r("deletedSelected"),m>0)})()},children:r("deleteSelected")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{let i=C.current;i&&(i.multi=[]),se([]);let o=C.current;o&&o.render&&o.render()},children:r("clearSelection")})]}),R&&(0,e.jsxs)("div",{className:"me-graph-sidebar",style:{position:"absolute",right:10,top:10,zIndex:3,maxWidth:240,background:"rgba(28,28,32,0.92)",backdropFilter:"blur(10px)",WebkitBackdropFilter:"blur(10px)",color:"#eee",borderRadius:10,padding:12,fontSize:12,boxShadow:"0 10px 30px rgba(0,0,0,.25)"},children:[(0,e.jsx)("div",{style:{fontWeight:700,fontSize:13,marginBottom:4},children:_[R]?_[R].title||_[R].name:R}),(0,e.jsx)("div",{style:{opacity:.8,marginBottom:8},children:_[R]?r(ve[_[R].kind]||"kindKnowledge"):"\u2014"}),(0,e.jsx)("div",{style:{fontSize:11,opacity:.65,letterSpacing:".04em",marginBottom:6},children:r("graphHint")}),(s.filter(i=>i.source===R||i.target===R)||[]).slice(0,8).map((i,o)=>{let m=i.source===R?i.target:i.source,g=_[m];return(0,e.jsx)("div",{style:{padding:"4px 6px",cursor:"pointer",borderRadius:6},onMouseDown:T=>{T.preventDefault();let L=C.current;L&&(L.selectedId=m,L.hoverId=m,L.wake&&L.wake()),x(m)},children:g?(g.title||g.name).slice(0,28):m},o)})]})]}),(0,e.jsx)("div",{className:"me-graph-tip",children:r("graphTip")}),pe&&(0,e.jsxs)("div",{style:{display:"flex",alignItems:"center",gap:8,position:"fixed",left:"50%",transform:"translateX(-50%)",top:22,zIndex:70,background:"rgba(20,22,26,0.97)",backdropFilter:"blur(12px)",WebkitBackdropFilter:"blur(12px)",color:"#f5f5f5",borderRadius:12,padding:"10px 20px",fontSize:13.5,fontWeight:600,boxShadow:"0 14px 44px rgba(0,0,0,.5)",pointerEvents:"none",animation:"me-pop .22s ease",borderLeft:"4px solid "+(pe.ok?"#22c55e":"#ef4444"),maxWidth:"90vw"},children:[(0,e.jsx)("span",{style:{color:pe.ok?"#34d399":"#f87171",fontWeight:800,fontSize:15},children:pe.ok?"\u2713":"\u2715"}),(0,e.jsx)("span",{style:{wordBreak:"break-all"},children:pe.msg})]}),V&&(0,e.jsxs)("div",{className:"me-overlay",onClick:()=>{V.url&&URL.revokeObjectURL(V.url),le(null),ce(!1)},children:[(0,e.jsx)("style",{children:we}),(0,e.jsxs)("div",{className:"me-dialog",style:{maxWidth:J?"100vw":900,width:J?"100vw":"92vw",maxHeight:J?"100vh":"90vh",height:J?"100vh":void 0,borderRadius:J?0:14},onClick:i=>i.stopPropagation(),children:[(0,e.jsxs)("div",{className:"me-dialog-head",children:[(0,e.jsx)("h3",{children:r("exportGraph")}),(0,e.jsxs)("div",{style:{display:"flex",gap:8},children:[(0,e.jsx)("a",{className:"mc-btn",href:V.url,download:"memory-graph.png",onClick:()=>{ye("download"),B(r("downloaded")+" \xB7 "+r("defaultDownloads"))},style:{textDecoration:"none",display:"inline-flex",alignItems:"center"},children:r(ae==="download"?"done":"download")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{ye("save");let i=V.blob;i&&typeof window.showSaveFilePicker=="function"?rt(i,"memory-graph.png",!0).then(o=>{o.ok?B(r("exportedTo")+"\uFF1A"+o.name):o.aborted?B(r("exportCancel")):B(r("exportFail"),!1)}):B(r("saveAsUnsupported"),!1)},children:r(ae==="save"?"done":"saveAs")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{ye("full"),ce(i=>!i),B(r("fullscreen"))},children:r(ae==="full"?"done":J?"exitFull":"fullscreen")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{let i=V.blob;i&&window.ClipboardItem&&navigator.clipboard?navigator.clipboard.write([new window.ClipboardItem({"image/png":i})]).then(()=>{ye("copy"),B(r("copied"))}).catch(()=>{B(r("copyFail"),!1)}):B(r("copyFail"),!1)},children:r(ae==="copy"?"done":"copyImage")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{V.url&&URL.revokeObjectURL(V.url),le(null),ce(!1)},children:r("close")})]})]}),(0,e.jsx)("div",{className:"me-dialog-body",style:{display:"flex",justifyContent:"center",background:J?"var(--dsw-alias-bg-base, #0b0d10)":"var(--dsw-alias-bg-base, #f3f4f6)"},children:(0,e.jsx)("img",{src:V.url,alt:r("exportGraph"),style:{maxWidth:"100%",maxHeight:J?"calc(100vh - 64px)":"70vh",borderRadius:J?0:8,cursor:"zoom-in"},onClick:()=>ce(i=>!i)})})]})]})]})}function zt(a){let s=new Date(a||0).getTime();if(!s)return"#94a3b8";let p=(Date.now()-s)/864e5;return p<3?"#10b981":p<14?"#f59e0b":p<30?"#f97316":"#94a3b8"}function Tt(a){let s=new Date(a||0).getTime();return!!s&&(Date.now()-s)/864e5<3}async function rt(a,s,p){if(p&&typeof window.showSaveFilePicker=="function")try{let u=await window.showSaveFilePicker({suggestedName:s}),S=await u.createWritable();return await S.write(a),await S.close(),{ok:!0,picked:!0,name:u.name}}catch(u){return u&&u.name==="AbortError"?{ok:!1,aborted:!0}:{ok:!1,err:String(u.message||u)}}let b=URL.createObjectURL(a),r=document.createElement("a");return r.href=b,r.download=s,document.body.appendChild(r),r.click(),document.body.removeChild(r),setTimeout(()=>URL.revokeObjectURL(b),1e3),{ok:!0,picked:!1,name:s}}function ot(a,s){if(!s||!a)return a;let p=a.toLowerCase(),b=s.toLowerCase(),r=[],u=0;for(;u<a.length;){let S=p.indexOf(b,u);if(S<0){r.push(a.slice(u));break}S>u&&r.push(a.slice(u,S)),r.push((0,e.jsx)("mark",{className:"mc-hl",children:a.slice(S,S+b.length)},S)),u=S+b.length}return r}function Lt(a){if(!a)return"";let s=String(a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),p=[];return s=s.replace(/```([\w-]*)\n([\s\S]*?)```/g,function(b,r,u){return p.push("<pre><code>"+u+"</code></pre>"),"\0"+(p.length-1)+"\0"}),s=s.replace(/^(#{1,3})\s+(.+)$/gm,function(b,r,u){return"<h"+r.length+">"+u+"</h"+r.length+">"}),s=s.replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),s=s.replace(/`([^`\n]+)`/g,"<code>$1</code>"),s=s.replace(/(^|[^*])\*([^*]+)\*/g,"$1<em>$2</em>"),s=s.replace(/\[([^\]]+)\]\(([^)]+)\)/g,function(b,r,u){return/^(https?:|\/)/.test(u)?'<a href="'+u+'" target="_blank" rel="noopener noreferrer">'+r+"</a>":r}),s=s.replace(/^(-)\s+(.+)$/gm,"\u2022 $2"),s=s.replace(/^\s*(&gt;)\s*(.+)$/gm,"<blockquote>$2</blockquote>"),s=s.replace(/\n/g,"<br/>"),s=s.replace(/\u0000(\d+)\u0000/g,function(b,r){return p[r]}),s}function it(a){if(!a)return"";let s=new Date(a);if(Number.isNaN(s.getTime()))return"";let p=new Date,b=s.toDateString()===p.toDateString(),r=S=>String(S).padStart(2,"0"),u=`${r(s.getHours())}:${r(s.getMinutes())}`;return b?u:`${s.getMonth()+1}-${s.getDate()} ${u}`}
114
114
 
115
115
  return module.exports;
116
116
  },
package/lib/vault.js CHANGED
@@ -386,6 +386,58 @@ export async function graph(root) {
386
386
  return { nodes, edges }
387
387
  }
388
388
 
389
+ /** 统计(增强):overview 全量 + 今日/本周新增 + 今日新增卡片明细。 */
390
+ export async function stats(root) {
391
+ const cards = await listCards(root)
392
+ const byKind = {}
393
+ for (const c of cards) byKind[c.kind] = (byKind[c.kind] || 0) + 1
394
+ const now = Date.now()
395
+ const day = now - 86400000
396
+ const week = now - 7 * 86400000
397
+ const today = cards.filter((c) => c.mtime > day)
398
+ const tagSet = new Set()
399
+ for (const c of cards) for (const t of c.tags) tagSet.add(t)
400
+ return {
401
+ total: cards.length,
402
+ today: today.length,
403
+ week: cards.filter((c) => c.mtime > week).length,
404
+ tags: tagSet.size,
405
+ byKind,
406
+ todayCards: today.slice(0, 30).map((c) => ({ path: c.path, title: c.title, kind: c.kind, updated: c.updated, summary: c.summary.slice(0, 80) })),
407
+ }
408
+ }
409
+
410
+ /** 整理建议(非破坏性预览):高相似卡对 + 陈旧卡(>90 天未更新且无连线)。 */
411
+ export async function optimizeCandidates(root) {
412
+ const cards = await listCards(root)
413
+ const merge = []
414
+ const bodies = new Map()
415
+ for (const c of cards) {
416
+ try { bodies.set(c.path, (await fs.readFile(path.join(root, c.path), 'utf8')).slice(0, 4000)) } catch { bodies.set(c.path, '') }
417
+ }
418
+ for (let i = 0; i < cards.length; i++) for (let j = i + 1; j < cards.length; j++) {
419
+ const a = cards[i], b = cards[j]
420
+ if (a.kind !== b.kind) continue
421
+ const sim = textSimilarity(bodies.get(a.path) || '', bodies.get(b.path) || '')
422
+ if (sim >= 0.55) merge.push({ a: { path: a.path, title: a.title }, b: { path: b.path, title: b.title }, sim: Math.round(sim * 100) / 100 })
423
+ }
424
+ merge.sort((x, y) => y.sim - x.sim)
425
+ const old = Date.now() - 90 * 86400000
426
+ const stale = cards.filter((c) => c.mtime < old).slice(0, 30).map((c) => ({ path: c.path, title: c.title, updated: c.updated }))
427
+ return { merge: merge.slice(0, 40), stale }
428
+ }
429
+
430
+ /** 召回反馈记录:命中是否有用(供相关性调优)。文件存 vault 根 `.feedback.json`。 */
431
+ export async function readFeedback(root) {
432
+ try { return JSON.parse(await fs.readFile(path.join(root, '.feedback.json'), 'utf8')) } catch { return [] }
433
+ }
434
+ export async function addFeedback(root, rec) {
435
+ const list = await readFeedback(root)
436
+ list.unshift({ ts: Date.now(), ...rec })
437
+ await fs.writeFile(path.join(root, '.feedback.json'), JSON.stringify(list.slice(0, 500), null, 2), 'utf8')
438
+ return { ok: true }
439
+ }
440
+
389
441
  /** 统计:按 kind 计数 + 最近 7 天更新数 + 总标签数。 */
390
442
  export async function overview(root) {
391
443
  const cards = await listCards(root)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-memory-eternal",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "记忆核心(Memory Eternal):自研的 DeepSeek Harness 记忆插件,不移植任何既有记忆框架——对话结束后自动沉淀知识卡到本地 Markdown Vault(自研去重、自研 CJK 检索、可 git 管理),设置页提供图形化知识库(统计 / 搜索 / 知识图谱 + 侧边栏一键弹窗),Agent 通过 memory_recall 工具按需召回历史上下文。零人工干预。",
5
5
  "license": "MIT",
6
6
  "repository": {