dsh-memory-eternal 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PUBLISH.md +1 -1
- package/README.md +45 -12
- package/assets/dsh-memory-eternal.gif +0 -0
- package/assets/screen/memory-popup.html +111 -0
- package/assets/screen/memory-popup.png +0 -0
- package/assets/screen/memory-settings.html +132 -0
- package/assets/screen/memory-settings.png +0 -0
- package/assets/screen/memory-sidebar.html +54 -0
- package/assets/screen/memory-sidebar.png +0 -0
- package/docs/ARCHITECTURE.md +2 -2
- package/lib/client.js +86 -38
- package/package.json +2 -2
- package/assets/preview.html +0 -139
- package/assets/screen/memory-preview.html +0 -139
- package/assets/screen/memory-preview.png +0 -0
package/PUBLISH.md
CHANGED
|
@@ -57,7 +57,7 @@ git push -u origin main
|
|
|
57
57
|
|
|
58
58
|
**关键一步**:在 GitHub 仓库页 → ⚙️ Settings → Topics,添加 `dsh-plugin`(再加 `deepseek-harness`、`memory`、`knowledge-graph` 等)。这是市场自动收录 GitHub 源的识别标志。
|
|
59
59
|
|
|
60
|
-
> README 里的截图放 `assets/screen/` 并在 README 引用(市场会自动从 README
|
|
60
|
+
> README 里的截图放 `assets/screen/` 并在 README 引用(市场会自动从 README 提取截图);头部可放一张 `assets/dsh-memory-eternal.gif` 动态演示(本仓库已压缩至 ~3MB 并置于 README 头部)。
|
|
61
61
|
|
|
62
62
|
## 4. 上传 npm
|
|
63
63
|
|
package/README.md
CHANGED
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
# 记忆核心(dsh-memory-eternal)
|
|
2
2
|
|
|
3
|
-
> 🧠 **给 DeepSeek Harness
|
|
3
|
+
> 🧠 **给 DeepSeek Harness 装上「第二大脑」**:一款**自研独立**的 DSH 记忆插件,为任意 DeepSeek Harness 提供对话级长期记忆——对话结束后**自动**把值得长期复用的内容压缩成知识卡,写入本地 Markdown Vault(自研去重、自研 CJK 检索、可 git 管理);设置页提供**图形化知识库**:统计概览、CJK 检索、知识卡网格、自研知识图谱。Agent 通过 `memory_recall` 工具按需召回历史上下文。**一条命令安装,零人工干预,不改 dsh 源码。**
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<img src="assets/
|
|
8
|
+
<img src="assets/dsh-memory-eternal.gif" width="880" alt="记忆核心 DSH 插件效果演示(动态)" />
|
|
9
9
|
<br/>
|
|
10
|
-
<em
|
|
10
|
+
<em>记忆核心 · 对话自动沉淀 + 图形化知识库 + 增强知识图谱(动态演示)</em>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
<p align="center">
|
|
14
|
+
<img src="assets/screen/memory-settings.png" width="840" alt="设置 → 记忆:图形化知识库主界面(统计 + 检索 + 卡片/图谱 Tab + 分类)" />
|
|
15
|
+
<br/>
|
|
16
|
+
<em>设置 → 记忆:统计概览、CJK 检索、知识卡网格、按 kind 分类(「知识卡 / 知识图谱」Tab 一键切换)</em>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="assets/screen/memory-popup.png" width="840" alt="侧边栏「记忆」按钮一键打开完整记忆库弹窗(含增强知识图谱)" />
|
|
21
|
+
<br/>
|
|
22
|
+
<em>侧边栏底部「记忆」按钮 → 一键打开<strong>完整记忆库弹窗</strong>:更开阔的查看体验 + 增强版知识图谱</em>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
<p align="center">
|
|
26
|
+
<img src="assets/screen/memory-sidebar.png" width="460" alt="侧边栏底部新增「记忆」入口,位于更新配置下方" />
|
|
27
|
+
<br/>
|
|
28
|
+
<em>侧边栏底部新增「记忆」入口(位于「设置 / 更新配置」下方),一键直达知识库</em>
|
|
11
29
|
</p>
|
|
12
30
|
|
|
13
31
|
---
|
|
@@ -16,11 +34,12 @@
|
|
|
16
34
|
|
|
17
35
|
| 亮点 | 说明 |
|
|
18
36
|
| --- | --- |
|
|
37
|
+
| 🧬 **原创自研** | 去重 / CJK 检索 / 知识图谱 / 图形化界面均由本仓库**独立实现**;运行时**不依赖**任何第三方记忆框架,核心逻辑可在 `lib/` 逐行核对 |
|
|
19
38
|
| 🤖 **全自动沉淀** | 每轮对话结束自动触发(监听 `agent/turn-stopping`),模型判定「是否值得保存」并压缩成 300-800 字知识卡——**无需任何手动操作** |
|
|
20
39
|
| 📚 **本地 Markdown Vault** | 知识卡是带 frontmatter 的普通 `.md` 文件,默认落在 `~/.dsh/memory-vault`,可手动编辑、可 git 版本管理、可被任何工具读取,不锁进私有数据库 |
|
|
21
|
-
| 🚫 **智能去重** |
|
|
40
|
+
| 🚫 **智能去重** | 双层去重:**自研词法 Jaccard bigram**(0.62 阈值)+ **语义去重**(把已有卡片索引喂给模型,由模型决定「新建 vs 追加到已有卡」,对 LLM 改写免疫) |
|
|
22
41
|
| 🔍 **CJK 感知检索** | 中文整词 + 字符 bigram 命中,短查询「强化学习」「蒸馏」也能在长文里命中,无需全文搜索引擎 |
|
|
23
|
-
| 🕸 **知识图谱** | 卡片间的 `[[wikilink]]` 与共享标签自动连线,SVG
|
|
42
|
+
| 🕸 **知识图谱** | 卡片间的 `[[wikilink]]` 与共享标签自动连线,SVG 图谱左键凸显关联、右键打开卡片阅读 |
|
|
24
43
|
| 🧠 **自动召回** | 注入 system prompt 告知 Agent 它拥有记忆核心,并注册 `memory_recall` 工具——需要项目背景/历史决策/领域知识时自动检索 |
|
|
25
44
|
| 🔒 **本地优先** | 所有内容留在你的本机 Vault;不设遥测、不传云端。模型请求仍走你已配置好的 provider |
|
|
26
45
|
| ⚙️ **一键安装** | 与 `dsh-ui-three-body` 同款插件机制,`npx @deepseek-ai/dsh plugin add dsh-memory-eternal` 即可 |
|
|
@@ -42,7 +61,7 @@ npx @deepseek-ai/dsh plugin --profile web add github:EternalNight996/dsh-memory-
|
|
|
42
61
|
npx @deepseek-ai/dsh plugin --profile web add F:/MyApp/eternal/dsh-memory-eternal
|
|
43
62
|
```
|
|
44
63
|
|
|
45
|
-
装完**重启 dsh web**:设置 → 记忆
|
|
64
|
+
装完**重启 dsh web**:设置 → 记忆 出现知识库页面;侧边栏底部也会出现「记忆」按钮,一键打开完整记忆库弹窗。此后每轮对话自动沉淀知识卡。
|
|
46
65
|
|
|
47
66
|
---
|
|
48
67
|
|
|
@@ -82,7 +101,7 @@ source: session:session-xxxx
|
|
|
82
101
|
- 索引是数据库为加速查找而额外维护的数据结构...
|
|
83
102
|
```
|
|
84
103
|
|
|
85
|
-
Vault
|
|
104
|
+
Vault 目录结构:
|
|
86
105
|
|
|
87
106
|
```
|
|
88
107
|
~/.dsh/memory-vault/
|
|
@@ -94,12 +113,13 @@ Vault 目录结构(与 boujoy 一致):
|
|
|
94
113
|
└── 06-Business/ # 业务 / 商业
|
|
95
114
|
```
|
|
96
115
|
|
|
97
|
-
## 🕸 图形化知识库(设置 →
|
|
116
|
+
## 🕸 图形化知识库(设置 → 记忆 + 侧边栏「记忆」入口)
|
|
98
117
|
|
|
118
|
+
- **双入口**:设置 → 记忆 页 + 侧边栏底部「记忆」按钮(一键打开完整记忆库弹窗)
|
|
99
119
|
- **统计概览**:总卡数、近 7 天新增、标签数、知识卡数
|
|
100
120
|
- **检索**:中文片段即时搜索(280ms 防抖)
|
|
101
121
|
- **知识卡网格**:kind 筛选(全部/项目/知识/内容/提示词/业务),点击卡片阅读全文(frontmatter 一并展示)
|
|
102
|
-
-
|
|
122
|
+
- **知识图谱(增强版)**:力导向有机布局(大规模自动切**径向布局** + 节点/连线上限 + 标签节流,**几千节点不卡**);节点 = 知识卡(按 kind 渐变+发光着色、按连接数自动放大);连线 = `[[wikilink]]` 或共享标签(曲线渐变 + 流动动画);**滚轮缩放、按住左键拖动画布**、**左键凸显关联节点并淡化无关节点**、**右键打开卡片**、悬停高亮
|
|
103
123
|
|
|
104
124
|
## 🔧 设置
|
|
105
125
|
|
|
@@ -109,7 +129,7 @@ Vault 目录结构(与 boujoy 一致):
|
|
|
109
129
|
| 自动沉淀 | 开 | 每轮对话结束自动捕获 |
|
|
110
130
|
| 自动召回 | 开 | 注入 system prompt + `memory_recall` 工具 |
|
|
111
131
|
| 记忆库目录 | 空 | 留空 = `~/.dsh/memory-vault`;可指向任意目录(如 Obsidian Vault) |
|
|
112
|
-
| 去重阈值 | 0.62 | 词法去重 Jaccard
|
|
132
|
+
| 去重阈值 | 0.62 | 词法去重 Jaccard 相似度阈值(自研默认值) |
|
|
113
133
|
| 捕获最小长度 | 200 | 少于该字符数的对话不触发捕获 |
|
|
114
134
|
| 日配额 | 60 | 每天最多自动写卡数,防止烧 token |
|
|
115
135
|
|
|
@@ -129,15 +149,28 @@ dsh-memory-eternal/
|
|
|
129
149
|
├── lib/
|
|
130
150
|
│ ├── vault.js # Markdown Vault 存储层(frontmatter/去重/检索/图谱,纯 Node 可单测)
|
|
131
151
|
│ └── capture.js # 自动沉淀管线(LLM 压缩 + 语义去重)
|
|
132
|
-
├── src/client/index.tsx # client
|
|
152
|
+
├── src/client/index.tsx # client 半边:设置页 + 侧边栏「记忆」按钮 + 完整记忆库弹窗(统计/搜索/卡片/图谱)
|
|
133
153
|
├── build.mjs # esbuild 打包 client → lib/client.js
|
|
134
154
|
├── cordis.patch.yml # bundle 补丁层(自动挂载 host 行)
|
|
135
155
|
└── tests/ # node:test 单测
|
|
136
156
|
```
|
|
137
157
|
|
|
158
|
+
## 🗺 Roadmap / 待办
|
|
159
|
+
|
|
160
|
+
- [x] 对话自动沉淀(turn-stopping 钩子 + LLM 压缩 + 双层去重)
|
|
161
|
+
- [x] CJK 感知检索 + 本地 Markdown Vault(02-06 分类 + frontmatter)
|
|
162
|
+
- [x] `memory_recall` 自动召回工具 + system prompt 分段
|
|
163
|
+
- [x] 图形化知识库:统计 / 检索 / 知识卡网格
|
|
164
|
+
- [x] 侧边栏「记忆」按钮 + 完整记忆库弹窗
|
|
165
|
+
- [x] 增强知识图谱:力导向/径向布局、滚轮缩放、左键拖拽、右键凸显关联、大规模抗压
|
|
166
|
+
- [ ] 每日记忆回顾 + 用量统计
|
|
167
|
+
- [ ] 多语言模板与自定义去重规则
|
|
168
|
+
- [ ] 卡片批量导入 / 导出 / 拖拽归类
|
|
169
|
+
|
|
138
170
|
## 📦 发布记录
|
|
139
171
|
|
|
140
|
-
- **v0.
|
|
172
|
+
- **v0.2.0**:新增侧边栏「记忆」入口 + 完整记忆库弹窗;增强知识图谱(力导向/径向布局、滚轮缩放、左键拖拽平移、左键凸显关联/右键打开卡片、大规模抗压:节点/连线上限 + 标签节流 + O(n) 径向布局);README 头部动态演示 GIF。
|
|
173
|
+
- **v0.1.0**:首个可用版本。对话自动沉淀(turn-stopping 钩子 + LLM 压缩 + 双层去重)、`memory_recall` 工具、system prompt 召回段、Markdown Vault(02-06 分类 + frontmatter)、CJK 检索、自研知识图谱、设置页图形化知识库。
|
|
141
174
|
|
|
142
175
|
## 📄 License
|
|
143
176
|
|
|
Binary file
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>记忆核心 · 完整记忆库弹窗</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing: border-box; margin:0; padding:0; }
|
|
9
|
+
body { font-family:-apple-system,"Segoe UI","Microsoft YaHei",sans-serif; background:#e9ebee; color:#1f2937; }
|
|
10
|
+
.backdrop { position:fixed; inset:0; background:rgba(10,15,30,.55); backdrop-filter:blur(6px); display:flex; align-items:center; justify-content:center; padding:36px; }
|
|
11
|
+
.modal { width:min(1080px,96vw); height:min(86vh,760px); display:flex; flex-direction:column; background:#fff; border:1px solid #e5e7eb; border-radius:18px; box-shadow:0 34px 90px rgba(0,0,0,.5); overflow:hidden; }
|
|
12
|
+
.mhead { display:flex; align-items:center; gap:12px; padding:16px 20px; border-bottom:1px solid #eef0f3; }
|
|
13
|
+
.mhead .logo { width:26px;height:26px;border-radius:8px;background:#6366f1;color:#fff;display:flex;align-items:center;justify-content:center;font-size:14px; }
|
|
14
|
+
.mhead h2 { font-size:17px; font-weight:700; }
|
|
15
|
+
.mhead .sub { font-size:12px; color:#6b7280; }
|
|
16
|
+
.mhead .sp { flex:1; }
|
|
17
|
+
.mhead .x { border:1px solid #d1d5db; background:#fff; width:30px;height:30px;border-radius:8px;cursor:pointer;font-size:15px; }
|
|
18
|
+
.body { flex:1; padding:18px 20px; overflow:auto; }
|
|
19
|
+
.stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin-bottom:14px; }
|
|
20
|
+
.stat { text-align:center; }
|
|
21
|
+
.stat b { display:block; font-size:24px; }
|
|
22
|
+
.stat span { font-size:12px; color:#6b7280; }
|
|
23
|
+
.toolbar { display:flex; gap:8px; align-items:center; margin-bottom:12px; }
|
|
24
|
+
.toolbar input { flex:1; padding:8px 12px; border-radius:8px; border:1px solid #d1d5db; font-size:13px; }
|
|
25
|
+
.tabs { display:inline-flex; gap:4px; padding:3px; background:#eef0f3; border-radius:999px; }
|
|
26
|
+
.tab { border:0; background:transparent; border-radius:999px; padding:6px 16px; font-size:12px; color:#6b7280; cursor:pointer; }
|
|
27
|
+
.tab.active { background:#fff; color:#111; font-weight:600; box-shadow:0 1px 4px rgba(0,0,0,.12); }
|
|
28
|
+
.btn { border:1px solid #d1d5db; background:#fff; border-radius:8px; padding:6px 14px; font-size:12px; cursor:pointer; }
|
|
29
|
+
.graphwrap { border:1px solid #e5e7eb; border-radius:14px; overflow:hidden; background:radial-gradient(120% 120% at 50% 35%, #f8fafc 0%, #eef2f7 100%); }
|
|
30
|
+
.graphwrap svg { width:100%; height:100%; display:block; }
|
|
31
|
+
.count { font-size:12px; color:#6b7280; }
|
|
32
|
+
.legend { display:flex; gap:12px; flex-wrap:wrap; margin-top:10px; font-size:11px; color:#6b7280; }
|
|
33
|
+
.dot { width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:5px; }
|
|
34
|
+
.edge { fill:none; stroke-width:1.6; stroke-dasharray:4 5; animation:dash 1.6s linear infinite; }
|
|
35
|
+
@keyframes dash { to { stroke-dashoffset:-16; } }
|
|
36
|
+
.node text { font-size:12px; fill:#334155; paint-order:stroke; stroke:rgba(255,255,255,.85); stroke-width:3px; pointer-events:none; }
|
|
37
|
+
</style>
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<div class="backdrop">
|
|
41
|
+
<div class="modal">
|
|
42
|
+
<div class="mhead">
|
|
43
|
+
<span class="logo">◆</span>
|
|
44
|
+
<h2>记忆库</h2>
|
|
45
|
+
<span class="sub">你的本地第二大脑 · 原创自研</span>
|
|
46
|
+
<span class="sp"></span>
|
|
47
|
+
<button class="x">✕</button>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="body">
|
|
50
|
+
<div class="stats">
|
|
51
|
+
<div class="stat"><b style="color:#3b82f6">42</b><span>知识卡</span></div>
|
|
52
|
+
<div class="stat"><b style="color:#22c55e">9</b><span>近 7 天新增</span></div>
|
|
53
|
+
<div class="stat"><b style="color:#eab308">31</b><span>标签</span></div>
|
|
54
|
+
<div class="stat"><b style="color:#8b5cf6">37</b><span>知识卡</span></div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="toolbar">
|
|
57
|
+
<input type="text" placeholder="搜索记忆(支持中文片段)…" value="B+树 索引">
|
|
58
|
+
<div class="tabs"><button class="tab">知识卡</button><button class="tab active">知识图谱</button></div>
|
|
59
|
+
<button class="btn">刷新</button>
|
|
60
|
+
</div>
|
|
61
|
+
<div class="graphwrap" id="graphwrap" style="height:380px"></div>
|
|
62
|
+
<div class="legend" id="legend"></div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<script>
|
|
68
|
+
const W=980,H=380;
|
|
69
|
+
const KIND={project:'#3b82f6',knowledge:'#22c55e',content:'#f43f5e',prompt:'#06b6d4',business:'#eab308',other:'#8b5cf6'};
|
|
70
|
+
const LBL={project:'项目',knowledge:'知识',content:'内容',prompt:'提示词',business:'业务',other:'其他'};
|
|
71
|
+
const nodes=[
|
|
72
|
+
{id:'a',title:'数据库索引原理与B+树',kind:'knowledge'},
|
|
73
|
+
{id:'b',title:'Redis缓存雪崩防护',kind:'knowledge'},
|
|
74
|
+
{id:'c',title:'项目极光 · 技术选型',kind:'project'},
|
|
75
|
+
{id:'d',title:'DSH 插件发布范式',kind:'prompt'},
|
|
76
|
+
{id:'e',title:'知识图谱可视化方案',kind:'content'},
|
|
77
|
+
{id:'f',title:'插件市场上架流程',kind:'business'},
|
|
78
|
+
{id:'g',title:'B+树磁盘IO模型',kind:'knowledge'},
|
|
79
|
+
{id:'h',title:'覆盖索引回表优化',kind:'knowledge'},
|
|
80
|
+
];
|
|
81
|
+
const edges=[{s:'a',t:'g'},{s:'a',t:'h'},{s:'a',t:'e'},{s:'b',t:'e'},{s:'c',t:'f'},{s:'c',t:'d'},{s:'f',t:'d'},{s:'e',t:'d'},{s:'g',t:'a'}];
|
|
82
|
+
function hexToRgb(h){const x=h.replace('#','');const f=x.length===3?x.split('').map(c=>c+c).join(''):x;return[parseInt(f.slice(0,2),16),parseInt(f.slice(2,4),16),parseInt(f.slice(4,6),16)];}
|
|
83
|
+
function lighten(h,a){const[r,g,b]=hexToRgb(h);return`rgb(${Math.round(r+(255-r)*a)},${Math.round(g+(255-g)*a)},${Math.round(b+(255-b)*a)})`;}
|
|
84
|
+
function darken(h,a){const[r,g,b]=hexToRgb(h);return`rgb(${Math.round(r*(1-a))},${Math.round(g*(1-a))},${Math.round(b*(1-a))})`;}
|
|
85
|
+
function layout(nodes,edges,w,h){
|
|
86
|
+
const n=nodes.length,pos=nodes.map((_,i)=>{const a=(i/n)*Math.PI*2-Math.PI/2;return{x:Math.cos(a),y:Math.sin(a)};});
|
|
87
|
+
const vel=pos.map(()=>({x:0,y:0}));const adj=nodes.map(()=>[]);
|
|
88
|
+
edges.forEach(e=>{const si=nodes.findIndex(x=>x.id===e.s),ti=nodes.findIndex(x=>x.id===e.t);if(si>=0&&ti>=0&&si!==ti){adj[si].push(ti);adj[ti].push(si);}});
|
|
89
|
+
const rep=1.35,att=0.04,cen=0.03,dam=0.84;
|
|
90
|
+
for(let it=0;it<300;it++){
|
|
91
|
+
for(let i=0;i<n;i++)for(let j=i+1;j<n;j++){let dx=pos[i].x-pos[j].x,dy=pos[i].y-pos[j].y;const d2=dx*dx+dy*dy||0.01,d=Math.sqrt(d2),f=rep/d2,fx=(dx/d)*f,fy=(dy/d)*f;vel[i].x+=fx;vel[i].y+=fy;vel[j].x-=fx;vel[j].y-=fy;}
|
|
92
|
+
for(let i=0;i<n;i++)for(const j of adj[i]){vel[i].x+=(pos[j].x-pos[i].x)*att;vel[i].y+=(pos[j].y-pos[i].y)*att;}
|
|
93
|
+
for(let i=0;i<n;i++){vel[i].x-=pos[i].x*cen;vel[i].y-=pos[i].y*cen;}
|
|
94
|
+
for(let i=0;i<n;i++){vel[i].x=Math.max(-0.5,Math.min(0.5,vel[i].x*dam));vel[i].y=Math.max(-0.5,Math.min(0.5,vel[i].y*dam));pos[i].x+=vel[i].x;pos[i].y+=vel[i].y;}
|
|
95
|
+
}
|
|
96
|
+
let mi=Infinity,ma=-Infinity,mj=Infinity,mb=-Infinity;pos.forEach(p=>{mi=Math.min(mi,p.x);ma=Math.max(ma,p.x);mj=Math.min(mj,p.y);mb=Math.max(mb,p.y);});
|
|
97
|
+
const pad=70,s=Math.min((w-2*pad)/(ma-mi||1),(h-2*pad)/(mb-mj||1)),cx=(mi+ma)/2,cy=(mj+mb)/2;
|
|
98
|
+
return pos.map(p=>({x:w/2+(p.x-cx)*s,y:h/2+(p.y-cy)*s}));
|
|
99
|
+
}
|
|
100
|
+
function path(a,b){const mx=(a.x+b.x)/2,my=(a.y+b.y)/2,dx=b.x-a.x,dy=b.y-a.y,dist=Math.sqrt(dx*dx+dy*dy)||1,bend=Math.min(38,dist*0.18),cx=mx-(dy/dist)*bend,cy=my+(dx/dist)*bend;return`M ${a.x} ${a.y} Q ${cx} ${cy} ${b.x} ${b.y}`;}
|
|
101
|
+
const l=layout(nodes,edges,W,H),deg={};edges.forEach(e=>{deg[e.s]=(deg[e.s]||0)+1;deg[e.t]=(deg[e.t]||0)+1;});
|
|
102
|
+
const idx=nodes.reduce((m,nd,i)=>(m[nd.id]=i,m),{});const seen=new Set();const shown=edges.filter(e=>{const k=[e.s,e.t].sort().join('|');if(seen.has(k))return false;seen.add(k);return true;});
|
|
103
|
+
let defs='';Object.keys(KIND).forEach(k=>{defs+=`<radialGradient id="g-${k}" cx="35%" cy="30%" r="75%"><stop offset="0%" stop-color="${lighten(KIND[k],0.55)}"/><stop offset="60%" stop-color="${KIND[k]}"/><stop offset="100%" stop-color="${darken(KIND[k],0.25)}"/></radialGradient><linearGradient id="e-${k}" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="${KIND[k]}" stop-opacity="0.4"/><stop offset="100%" stop-color="${KIND[k]}" stop-opacity="0.85"/></linearGradient>`;});
|
|
104
|
+
defs+=`<filter id="glow" x="-60%" y="-60%" width="220%" height="220%"><feGaussianBlur stdDeviation="3" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>`;
|
|
105
|
+
let es=shown.map((e,i)=>{const si=idx[e.s],ti=idx[e.t];if(si===undefined||ti===undefined)return'';const a=l[si],b=l[ti],kind=nodes[si].kind||'other';return`<path d="${path(a,b)}" class="edge" style="stroke:url(#e-${kind});animation-delay:${(i%8)*0.12}s"/>`;}).join('');
|
|
106
|
+
let ns=nodes.map((nd,i)=>{const p=l[i],kind=nd.kind||'other',r=8+Math.min(9,(deg[nd.id]||0)*1.4);return`<g transform="translate(${p.x} ${p.y})"><circle r="${r}" fill="url(#g-${kind})" stroke="${KIND[kind]}" stroke-opacity="0.9" filter="url(#glow)"/><circle r="${Math.max(2.4,r*0.2)}" fill="rgba(255,255,255,.85)"/><text x="${r+8}" y="3">${nd.title.slice(0,14)}</text></g>`;}).join('');
|
|
107
|
+
document.getElementById('graphwrap').innerHTML=`<svg viewBox="0 0 ${W} ${H}"><defs>${defs}</defs>${es}${ns}</svg>`;
|
|
108
|
+
document.getElementById('legend').innerHTML=Object.keys(KIND).map(k=>`<span><span class="dot" style="background:${KIND[k]}"></span>${LBL[k]}</span>`).join('')+`<span style="opacity:.7;margin-left:auto">节点=知识卡;连线=[[链接]]或共享标签 · 点击节点阅读卡片</span>`;
|
|
109
|
+
</script>
|
|
110
|
+
</body>
|
|
111
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>记忆核心 · 新设置主界面</title>
|
|
6
|
+
<style>
|
|
7
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
8
|
+
body { font-family: -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif; background: var(--bg,#f4f5f7); color: #1f2937; }
|
|
9
|
+
.app { display: grid; grid-template-columns: 216px 1fr; min-height: 100vh; }
|
|
10
|
+
/* sidebar */
|
|
11
|
+
.nav { background: #0f172a; color: #cbd5e1; padding: 18px 12px; }
|
|
12
|
+
.nav .brand { display:flex; align-items:center; gap:8px; font-weight:700; color:#fff; font-size:15px; padding:0 8px 16px; }
|
|
13
|
+
.nav .brand .mark { width:24px;height:24px;border-radius:7px;background:#6366f1;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:13px; }
|
|
14
|
+
.nav a { display:flex; align-items:center; gap:10px; padding:9px 10px; border-radius:8px; color:#cbd5e1; font-size:13.5px; text-decoration:none; margin-bottom:2px; }
|
|
15
|
+
.nav a .i { width:16px;height:16px;flex:none;opacity:.85; }
|
|
16
|
+
.nav a.active { background:#6366f1; color:#fff; font-weight:600; }
|
|
17
|
+
.nav .grp { font-size:11px;color:#64748b;padding:12px 10px 6px;letter-spacing:.06em; }
|
|
18
|
+
/* main */
|
|
19
|
+
.main { padding: 26px 30px 40px; }
|
|
20
|
+
.pagehead { display:flex; align-items:center; gap:12px; margin-bottom:6px; }
|
|
21
|
+
.pagehead h1 { font-size:19px; }
|
|
22
|
+
.pagehead .sub { font-size:12px;color:#6b7280; }
|
|
23
|
+
.stats { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin:16px 0 14px; }
|
|
24
|
+
.stat { text-align:center; }
|
|
25
|
+
.stat b { display:block; font-size:26px; }
|
|
26
|
+
.stat span { font-size:12px; color:#6b7280; }
|
|
27
|
+
.toolbar { display:flex; gap:8px; align-items:center; margin-bottom:12px; }
|
|
28
|
+
.toolbar input { flex:1; padding:8px 12px; border-radius:8px; border:1px solid #d1d5db; font-size:13px; }
|
|
29
|
+
.tabs { display:inline-flex; gap:4px; padding:3px; background:#eef0f3; border-radius:999px; }
|
|
30
|
+
.tab { border:0; background:transparent; border-radius:999px; padding:6px 16px; font-size:12px; color:#6b7280; cursor:pointer; }
|
|
31
|
+
.tab.active { background:#fff; color:#111; font-weight:600; box-shadow:0 1px 4px rgba(0,0,0,.12); }
|
|
32
|
+
.btn { border:1px solid #d1d5db; background:#fff; border-radius:8px; padding:6px 14px; font-size:12px; cursor:pointer; }
|
|
33
|
+
.chips { display:flex; gap:6px; margin-bottom:14px; flex-wrap:wrap; }
|
|
34
|
+
.chip { border:1px solid #d1d5db; background:#fff; border-radius:999px; padding:4px 12px; font-size:12px; color:#6b7280; }
|
|
35
|
+
.chip.active { color:#111; font-weight:600; border-color:#111; }
|
|
36
|
+
.grid { display:grid; grid-template-columns:repeat(3,1fr); gap:12px; }
|
|
37
|
+
.kcard { border:1px solid #e5e7eb; border-radius:12px; padding:14px 16px; display:flex; flex-direction:column; gap:6px; background:#fff; cursor:pointer; }
|
|
38
|
+
.kcard h4 { font-size:14px; }
|
|
39
|
+
.kcard p { font-size:12px; color:#6b7280; line-height:1.5; display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden; }
|
|
40
|
+
.kcard footer { margin-top:auto; display:flex; justify-content:space-between; font-size:11px; color:#9ca3af; }
|
|
41
|
+
.dot { width:8px; height:8px; border-radius:50%; display:inline-block; margin-right:6px; }
|
|
42
|
+
.tags { display:flex; gap:4px; }
|
|
43
|
+
.tag { font-size:10px; padding:1px 7px; border-radius:999px; background:#f3f4f6; color:#6b7280; }
|
|
44
|
+
.well { background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:14px 16px; }
|
|
45
|
+
</style>
|
|
46
|
+
</head>
|
|
47
|
+
<body>
|
|
48
|
+
<div class="app">
|
|
49
|
+
<div class="nav">
|
|
50
|
+
<div class="brand"><span class="mark">◈</span> deepseek<small style="margin-left:4px;font-weight:500;opacity:.8">HARNESS</small></div>
|
|
51
|
+
<div class="grp">设置</div>
|
|
52
|
+
<a href="#"><span class="i">⚙</span> 通用设置</a>
|
|
53
|
+
<a href="#"><span class="i">▦</span> 模型</a>
|
|
54
|
+
<a href="#"><span class="i">◧</span> 插件</a>
|
|
55
|
+
<a href="#"><span class="i">◫</span> Agent 预设</a>
|
|
56
|
+
<a href="#"><span class="i">◈</span> 三体</a>
|
|
57
|
+
<a href="#"><span class="i">▤</span> 像素办公室</a>
|
|
58
|
+
<a href="#" class="active"><span class="i">◆</span> 记忆</a>
|
|
59
|
+
<a href="#"><span class="i">◒</span> 插件市场</a>
|
|
60
|
+
<a href="#"><span class="i">▣</span> Vision Router</a>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="main">
|
|
63
|
+
<div class="pagehead">
|
|
64
|
+
<h1>🧠 记忆</h1>
|
|
65
|
+
<span class="sub">记忆核心 · 对话自动沉淀知识卡到本地 Markdown Vault · 原创自研</span>
|
|
66
|
+
</div>
|
|
67
|
+
<div class="well">
|
|
68
|
+
<div class="stats">
|
|
69
|
+
<div class="stat"><b style="color:#3b82f6">42</b><span>知识卡</span></div>
|
|
70
|
+
<div class="stat"><b style="color:#22c55e">9</b><span>近 7 天新增</span></div>
|
|
71
|
+
<div class="stat"><b style="color:#eab308">31</b><span>标签</span></div>
|
|
72
|
+
<div class="stat"><b style="color:#8b5cf6">37</b><span>知识卡</span></div>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="toolbar">
|
|
75
|
+
<input type="text" placeholder="搜索记忆(支持中文片段)…" value="B+树 索引">
|
|
76
|
+
<div class="tabs">
|
|
77
|
+
<button class="tab active">知识卡</button>
|
|
78
|
+
<button class="tab">知识图谱</button>
|
|
79
|
+
</div>
|
|
80
|
+
<button class="btn">刷新</button>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="chips">
|
|
83
|
+
<button class="chip active">全部</button>
|
|
84
|
+
<button class="chip">项目</button>
|
|
85
|
+
<button class="chip">知识</button>
|
|
86
|
+
<button class="chip">内容</button>
|
|
87
|
+
<button class="chip">提示词</button>
|
|
88
|
+
<button class="chip">业务</button>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="grid" style="margin-top:14px">
|
|
92
|
+
<div class="kcard">
|
|
93
|
+
<h4><span class="dot" style="background:#22c55e"></span>数据库索引原理与B+树加速机制</h4>
|
|
94
|
+
<p>索引是数据库为加速查找而额外维护的数据结构…复杂度从 O(n) 降到 O(log n),一次查询只需 3~4 次磁盘 I/O。</p>
|
|
95
|
+
<div class="tags"><span class="tag">数据库</span><span class="tag">索引</span><span class="tag">B+树</span></div>
|
|
96
|
+
<footer><span>知识</span><span>08-21 20:04</span></footer>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="kcard">
|
|
99
|
+
<h4><span class="dot" style="background:#22c55e"></span>Redis 缓存雪崩:定义与预防方案</h4>
|
|
100
|
+
<p>缓存热点数据 TTL 10 分钟,穿透用空值缓存,随机过期防雪崩,大 key 分段缓存降低序列化开销…</p>
|
|
101
|
+
<div class="tags"><span class="tag">Redis</span><span class="tag">缓存</span><span class="tag">高可用</span></div>
|
|
102
|
+
<footer><span>知识</span><span>08-21 20:41</span></footer>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="kcard">
|
|
105
|
+
<h4><span class="dot" style="background:#3b82f6"></span>项目极光 · 技术选型</h4>
|
|
106
|
+
<p>项目代号:极光(Aurora)。数据库最终选用 PostgreSQL;前端用 Vite 构建。迁移成本可控…</p>
|
|
107
|
+
<div class="tags"><span class="tag">项目</span><span class="tag">PostgreSQL</span></div>
|
|
108
|
+
<footer><span>项目</span><span>08-21 19:59</span></footer>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="kcard">
|
|
111
|
+
<h4><span class="dot" style="background:#06b6d4"></span>DSH 插件发布范式</h4>
|
|
112
|
+
<p>npm keywords 带 dsh-plugin + GitHub 打 topic → 市场自动收录;prepublishOnly 自动 build client…</p>
|
|
113
|
+
<div class="tags"><span class="tag">DSH</span><span class="tag">插件</span></div>
|
|
114
|
+
<footer><span>提示词</span><span>08-20 22:11</span></footer>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="kcard">
|
|
117
|
+
<h4><span class="dot" style="background:#f43f5e"></span>知识图谱可视化方案</h4>
|
|
118
|
+
<p>力导向布局按 kind 着色;wikilink 与共享标签连线;点击节点打开卡片阅读全文…</p>
|
|
119
|
+
<div class="tags"><span class="tag">可视化</span><span class="tag">图谱</span></div>
|
|
120
|
+
<footer><span>内容</span><span>08-19 15:02</span></footer>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="kcard">
|
|
123
|
+
<h4><span class="dot" style="background:#eab308"></span>插件市场选品与上架流程</h4>
|
|
124
|
+
<p>关键词覆盖 + 截图质量 + 自动化测试是拿 Star 的关键;发布前跑通 npm test 与 build…</p>
|
|
125
|
+
<div class="tags"><span class="tag">业务</span><span class="tag">上架</span></div>
|
|
126
|
+
<footer><span>业务</span><span>08-18 10:12</span></footer>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</body>
|
|
132
|
+
</html>
|
|
Binary file
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>记忆核心 · 侧边栏入口</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing:border-box; margin:0; padding:0; }
|
|
9
|
+
body { font-family:-apple-system,"Segoe UI","Microsoft YaHei",sans-serif; background:#eceef1; color:#1f2937; padding:36px; }
|
|
10
|
+
.phone { width:300px; background:#101827; border-radius:14px; overflow:hidden; box-shadow:0 24px 60px rgba(0,0,0,.35); margin:0 auto; }
|
|
11
|
+
.brand { display:flex; align-items:center; gap:8px; color:#fff; font-weight:700; font-size:14px; padding:16px 14px 10px; }
|
|
12
|
+
.brand .mk { width:24px;height:24px;border-radius:7px;background:#6366f1;display:flex;align-items:center;justify-content:center;font-size:13px; }
|
|
13
|
+
.brand small { font-weight:500; opacity:.8; }
|
|
14
|
+
.new { margin:6px 10px 12px; background:#111c2f; border:1px solid #1e293b; color:#e2e8f0; border-radius:10px; padding:9px; font-size:12.5px; display:flex; align-items:center; justify-content:center; gap:6px; }
|
|
15
|
+
.wshead { display:flex; align-items:center; justify-content:space-between; padding:6px 14px; color:#64748b; font-size:11px; }
|
|
16
|
+
.ws { padding:8px 14px; display:flex; align-items:center; gap:9px; color:#cbd5e1; font-size:13px; border-radius:8px; margin:0 8px; }
|
|
17
|
+
.ws.active { background:#1e2a3f; color:#fff; }
|
|
18
|
+
.ws .fc { width:18px;height:18px;border-radius:5px;display:inline-flex;align-items:center;justify-content:center;font-size:10px;color:#94a3b8;border:1px solid #243247; }
|
|
19
|
+
.ws .sp { flex:1; }
|
|
20
|
+
.ws .mt { font-size:11px; color:#64748b; }
|
|
21
|
+
.foot { margin-top:14px; border-top:1px solid #162033; padding:8px; }
|
|
22
|
+
.footbtn { display:flex; align-items:center; gap:10px; padding:9px 12px; border-radius:8px; color:#94a3b8; font-size:13px; }
|
|
23
|
+
.footbtn:hover { background:#1a2639; color:#fff; }
|
|
24
|
+
.footbtn.hot { color:#e2e8f0; }
|
|
25
|
+
.footbtn .ic { width:18px;height:18px;display:inline-flex;align-items:center;justify-content:center;font-size:14px; }
|
|
26
|
+
.mark { position:absolute; right:-8px; top:-8px; background:#6366f1; color:#fff; font-size:11px; padding:2px 8px; border-radius:999px; font-weight:600; box-shadow:0 6px 16px rgba(99,102,241,.5); }
|
|
27
|
+
.tag { display:inline-block; margin-top:10px; font-size:12px; color:#6b7280; text-align:center; width:100%; }
|
|
28
|
+
</style>
|
|
29
|
+
</head>
|
|
30
|
+
<body>
|
|
31
|
+
<div style="position:relative;max-width:300px;margin:0 auto">
|
|
32
|
+
<div class="phone">
|
|
33
|
+
<div class="brand"><span class="mk">◈</span> deepseek<small>HARNESS</small></div>
|
|
34
|
+
<div class="new">+ 新会话</div>
|
|
35
|
+
<div class="wshead"><span>工作区</span><span style="opacity:.7">🔍 ⚙ +</span></div>
|
|
36
|
+
<div class="ws active"><span class="fc">▤</span> dsh-memory-eternal<span class="sp"></span></div>
|
|
37
|
+
<div class="ws"><span class="fc">▤</span> shj-platform</div>
|
|
38
|
+
<div class="ws"><span class="fc">▤</span> ai-job-seeker</div>
|
|
39
|
+
<div class="ws"><span class="fc">▤</span> dsh-desktop</div>
|
|
40
|
+
<div class="ws"><span class="fc">○</span> e-test</div>
|
|
41
|
+
<div class="ws"><span class="fc">○</span> os-active</div>
|
|
42
|
+
<div class="ws"><span class="fc">▤</span> dsh-ui-three-body</div>
|
|
43
|
+
<div class="ws"><span class="fc">▤</span> dsh-ui-agents-pixe</div>
|
|
44
|
+
<div class="foot">
|
|
45
|
+
<div class="footbtn"><span class="ic">⚙</span> 设置</div>
|
|
46
|
+
<div class="footbtn"><span class="ic">↻</span> 更新配置</div>
|
|
47
|
+
<div class="footbtn hot"><span class="ic">◆</span> 记忆<span style="margin-left:auto;font-size:10px;opacity:.7">42 卡</span></div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<span class="mark">新增</span>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="tag">侧边栏底部新增「记忆」入口 · 一键打开完整记忆库</div>
|
|
53
|
+
</body>
|
|
54
|
+
</html>
|
|
Binary file
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -72,12 +72,12 @@ dsh-memory-eternal/
|
|
|
72
72
|
├── lib/
|
|
73
73
|
│ ├── vault.js # Vault 存储层(parseCard/safeSlug/textSimilarity/dedupCheck/queryTerms/listCards/search/graph/overview/writeCard/appendUpdate/ensureVault/readCard)
|
|
74
74
|
│ └── capture.js # 沉淀管线(summarizeTurn/parseCaptureJson/extractLastTurn/sliceNewEvents/pickNeighbors/captureCard/captureUpdate/makeDedupChecker)
|
|
75
|
-
├── src/client/index.tsx # client 半边(设置页
|
|
75
|
+
├── src/client/index.tsx # client 半边(设置页 + 侧边栏「记忆」按钮 + 完整记忆库弹窗)
|
|
76
76
|
├── build.mjs # esbuild → lib/client.js
|
|
77
77
|
├── cordis.patch.yml # bundle 补丁层
|
|
78
78
|
├── tests/ # node:test 单测(vault 12 + capture 12 + api 5 = 29)
|
|
79
79
|
├── README.md / PUBLISH.md / LICENSE
|
|
80
|
-
└── assets/ # README 截图(memory-
|
|
80
|
+
└── assets/ # README 截图(memory-settings / memory-popup / memory-sidebar)
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
## 依赖
|
package/lib/client.js
CHANGED
|
@@ -3,44 +3,92 @@ window.__ModuleLoader__.load({
|
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
|
-
var
|
|
7
|
-
.memory-eternal-root { font-family: inherit; color: var(--
|
|
8
|
-
.
|
|
9
|
-
.
|
|
10
|
-
.
|
|
11
|
-
.
|
|
12
|
-
.
|
|
13
|
-
.
|
|
14
|
-
.
|
|
15
|
-
.
|
|
16
|
-
.
|
|
17
|
-
.
|
|
18
|
-
.
|
|
19
|
-
.
|
|
20
|
-
.
|
|
21
|
-
.
|
|
22
|
-
.
|
|
23
|
-
.
|
|
24
|
-
.
|
|
25
|
-
.
|
|
26
|
-
.
|
|
27
|
-
.
|
|
28
|
-
.
|
|
29
|
-
.
|
|
30
|
-
.
|
|
31
|
-
.
|
|
32
|
-
.
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
.
|
|
37
|
-
.
|
|
38
|
-
.
|
|
39
|
-
.
|
|
40
|
-
.
|
|
41
|
-
.
|
|
42
|
-
.
|
|
43
|
-
|
|
6
|
+
var te=Object.create;var q=Object.defineProperty;var ae=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var ne=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var se=(t,r)=>{for(var n in r)q(t,n,{get:r[n],enumerable:!0})},V=(t,r,n,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let d of re(r))!oe.call(t,d)&&d!==n&&q(t,d,{get:()=>r[d],enumerable:!(o=ae(r,d))||o.enumerable});return t};var ie=(t,r,n)=>(n=t!=null?te(ne(t)):{},V(r||!t||!t.__esModule?q(n,"default",{value:t,enumerable:!0}):n,t)),le=t=>V(q({},"__esModule",{value:!0}),t);var ze={};se(ze,{apply:()=>ge,inject:()=>ce});module.exports=le(ze);var c=ie(require("react"),1),e=require("react/jsx-runtime"),H="memory-eternal",U="/memory-eternal/api",ce=["settingsScope","slots","locale","connection","remote"],de={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",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\u5173\u8054\u8282\u70B9 \xB7 \u53F3\u952E\u6253\u5F00\u5361\u7247 \xB7 \u6EDA\u8F6E\u7F29\u653E \xB7 \u6309\u4F4F\u5DE6\u952E\u62D6\u52A8\u753B\u5E03",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"},pe={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",cardsTab:"Cards",graphTab:"Graph",graph:"Knowledge Graph",graphHint:"Nodes = cards; edges = [[links]] or shared tags. Click a node to read.",graphTip:"Left-click focuses neighbors \xB7 right-click opens card \xB7 scroll to zoom \xB7 hold left to pan",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"},me=["all","project","knowledge","content","prompt","business"],L={project:"#3b82f6",knowledge:"#22c55e",content:"#f43f5e",prompt:"#06b6d4",business:"#eab308",other:"#8b5cf6"},_={project:"kindProject",knowledge:"kindKnowledge",content:"kindContent",prompt:"kindPrompt",business:"kindBusiness",other:"kindKnowledge"},Y=`
|
|
7
|
+
.memory-eternal-root { font-family: inherit; color: var(--dsw-alias-label-primary, #1f2937); }
|
|
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
|
+
.mc-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; margin-bottom: 14px; }
|
|
10
|
+
.mc-stat { text-align: center; }
|
|
11
|
+
.mc-stat b { display: block; font-size: 22px; line-height: 1.2; }
|
|
12
|
+
.mc-stat span { font-size: 12px; opacity: 0.65; }
|
|
13
|
+
.mc-toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
|
|
14
|
+
.mc-toolbar input[type=text] { flex: 1; min-width: 180px; padding: 7px 10px; border-radius: 8px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: var(--dsw-alias-bg-base, #f9fafb); color: inherit; font-size: 13px; }
|
|
15
|
+
.mc-btn { border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: transparent; color: inherit; border-radius: 8px; padding: 6px 12px; font-size: 12px; cursor: pointer; }
|
|
16
|
+
.mc-btn:hover { opacity: 0.85; }
|
|
17
|
+
.mc-tabs { display: inline-flex; gap: 4px; padding: 3px; background: var(--dsw-alias-bg-base, #f3f4f6); border-radius: 999px; }
|
|
18
|
+
.mc-tab { border: 0; background: transparent; color: inherit; border-radius: 999px; padding: 5px 14px; font-size: 12px; cursor: pointer; opacity: 0.7; }
|
|
19
|
+
.mc-tab.active { background: var(--dsw-alias-bg-layer-1, #fff); opacity: 1; font-weight: 600; box-shadow: 0 1px 4px rgba(0,0,0,0.12); }
|
|
20
|
+
.mc-chips { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
|
|
21
|
+
.mc-chip { border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: transparent; color: inherit; border-radius: 999px; padding: 4px 12px; font-size: 12px; cursor: pointer; opacity: 0.7; }
|
|
22
|
+
.mc-chip.active { opacity: 1; font-weight: 600; border-color: currentColor; }
|
|
23
|
+
.mc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; }
|
|
24
|
+
.mc-cardrow { cursor: pointer; transition: border-color 0.15s, transform 0.15s; display: flex; flex-direction: column; gap: 6px; min-height: 110px; }
|
|
25
|
+
.mc-cardrow:hover { border-color: var(--dsw-alias-brand-primary, #6366f1); transform: translateY(-1px); box-shadow: 0 8px 22px rgba(0,0,0,0.08); }
|
|
26
|
+
.mc-cardrow h4 { margin: 0; font-size: 14px; line-height: 1.35; }
|
|
27
|
+
.mc-cardrow p { margin: 0; font-size: 12px; opacity: 0.7; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
|
28
|
+
.mc-cardrow footer { margin-top: auto; display: flex; justify-content: space-between; align-items: center; font-size: 11px; opacity: 0.55; }
|
|
29
|
+
.mc-tags { display: flex; gap: 4px; flex-wrap: wrap; }
|
|
30
|
+
.mc-tag { font-size: 10px; padding: 1px 7px; border-radius: 999px; background: var(--dsw-alias-border-l1, #e5e7eb); opacity: 0.85; }
|
|
31
|
+
.mc-kind { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
|
|
32
|
+
.mc-empty { text-align: center; padding: 40px 10px; opacity: 0.6; font-size: 13px; }
|
|
33
|
+
.mc-flag { font-size: 11px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l2, #d1d5db); margin-left: 6px; }
|
|
34
|
+
|
|
35
|
+
/* ---- sidebar footer button ---- */
|
|
36
|
+
.me-footer { width: 100%; }
|
|
37
|
+
.me-footer-btn { display: flex; align-items: center; gap: 9px; width: 100%; padding: 7px 10px; border: none; background: transparent; color: var(--dsw-alias-label-secondary, #6b7280); font: inherit; font-size: 13.5px; line-height: 18px; border-radius: 8px; cursor: pointer; text-align: left; }
|
|
38
|
+
.me-footer-btn:hover { background: var(--dsw-alias-bg-layer-1, rgba(255,255,255,0.06)); color: var(--dsw-alias-label-primary, #111); }
|
|
39
|
+
.me-footer-btn:active { transform: translateY(0.5px); }
|
|
40
|
+
.me-footer-ico { display: inline-flex; flex: none; width: 18px; height: 18px; align-items: center; justify-content: center; }
|
|
41
|
+
.me-footer-ico svg { width: 18px; height: 18px; }
|
|
42
|
+
.me-footer-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
|
|
43
|
+
.me-footer.rail .me-footer-btn { justify-content: center; padding: 7px 0; }
|
|
44
|
+
.me-footer.rail .me-footer-label { display: none; }
|
|
45
|
+
|
|
46
|
+
/* ---- full library modal ---- */
|
|
47
|
+
.me-overlay-top { position: fixed; inset: 0; z-index: 1001; background: rgba(0,0,0,0.55); backdrop-filter: blur(6px); display: flex; align-items: center; justify-content: center; padding: 28px; }
|
|
48
|
+
.me-modal { width: min(1120px, 96vw); height: min(88vh, 940px); display: flex; flex-direction: column; background: var(--dsw-alias-bg-overlay, #fff); color: var(--dsw-alias-label-primary, #111); border: 1px solid var(--dsw-alias-border-l1, #e5e7eb); border-radius: 18px; box-shadow: 0 34px 90px rgba(0,0,0,0.5); overflow: hidden; animation: me-pop 0.22s cubic-bezier(0.2,0.8,0.2,1); }
|
|
49
|
+
@keyframes me-pop { from { opacity: 0; transform: translateY(12px) scale(0.985); } }
|
|
50
|
+
.me-modal-head { display: flex; align-items: center; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e5e7eb); }
|
|
51
|
+
.me-modal-title { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
|
|
52
|
+
.me-modal-title h2 { margin: 0; font-size: 17px; font-weight: 700; }
|
|
53
|
+
.me-modal-title span { font-size: 12px; opacity: 0.55; }
|
|
54
|
+
.me-modal-head .spacer { flex: 1; }
|
|
55
|
+
.me-modal-close { border: 1px solid var(--dsw-alias-border-l2, #d1d5db); background: transparent; color: inherit; border-radius: 8px; width: 30px; height: 30px; line-height: 1; font-size: 16px; cursor: pointer; display: flex; align-items: center; justify-content: center; }
|
|
56
|
+
.me-modal-close:hover { background: var(--dsw-alias-bg-layer-1, #f3f4f6); }
|
|
57
|
+
.me-modal-body { flex: 1; padding: 18px 20px; overflow: auto; }
|
|
58
|
+
.me-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 1002; display: flex; align-items: center; justify-content: center; padding: 24px; }
|
|
59
|
+
.me-dialog { background: var(--dsw-alias-bg-overlay, #fff); color: var(--dsw-alias-label-primary, #111); border-radius: 14px; max-width: 760px; width: 100%; max-height: 84vh; display: flex; flex-direction: column; box-shadow: 0 24px 60px rgba(0,0,0,0.3); }
|
|
60
|
+
.me-dialog-head { display: flex; justify-content: space-between; align-items: center; padding: 12px 18px; border-bottom: 1px solid var(--dsw-alias-border-l1, #e5e7eb); }
|
|
61
|
+
.me-dialog-head h3 { margin: 0; font-size: 15px; }
|
|
62
|
+
.me-dialog-body { padding: 14px 18px; overflow: auto; font-size: 13px; line-height: 1.7; white-space: pre-wrap; word-break: break-word; }
|
|
63
|
+
|
|
64
|
+
/* ---- enhanced graph ---- */
|
|
65
|
+
.me-graph { display: flex; flex-direction: column; gap: 8px; height: 100%; min-height: 320px; }
|
|
66
|
+
.me-graph-toolbar { display: flex; align-items: center; gap: 8px; }
|
|
67
|
+
.me-graph-count { font-size: 12px; opacity: 0.7; }
|
|
68
|
+
.me-graph-canvas { position: relative; flex: 1; min-height: 300px; border-radius: 14px; overflow: hidden; background: radial-gradient(120% 120% at 50% 40%, var(--dsw-alias-bg-layer-2, #f8fafc) 0%, var(--dsw-alias-bg-base, #eef2f7) 100%); border: 1px solid var(--dsw-alias-border-l1, #e5e7eb); cursor: grab; touch-action: none; user-select: none; }
|
|
69
|
+
.me-graph-canvas.dragging { cursor: grabbing; }
|
|
70
|
+
.me-graph-tip { font-size: 11px; opacity: 0.6; }
|
|
71
|
+
.me-graph-canvas svg { width: 100%; height: 100%; display: block; }
|
|
72
|
+
.me-graph-edge { fill: none; stroke: var(--dsw-alias-border-l2, #cbd5e1); stroke-width: 1.6; opacity: 0.75; stroke-dasharray: 4 5; animation: me-dash 1.6s linear infinite; }
|
|
73
|
+
@keyframes me-dash { to { stroke-dashoffset: -16; } }
|
|
74
|
+
.me-graph-node { cursor: pointer; }
|
|
75
|
+
.me-graph-node circle.inner { transition: r 0.18s; }
|
|
76
|
+
.me-graph-node:hover circle.inner { stroke: var(--dsw-alias-brand-primary, #6366f1); stroke-width: 2.5; }
|
|
77
|
+
.me-graph-node text { font-size: 11px; fill: var(--dsw-alias-label-primary, #334); pointer-events: none; }
|
|
78
|
+
.me-labels text { font-size: 11px; fill: var(--dsw-alias-label-primary, #334); pointer-events: none; transition: opacity .12s; }
|
|
79
|
+
.me-labels text.minor { opacity: 0; }
|
|
80
|
+
.me-labels.all text { opacity: 0.9; }
|
|
81
|
+
.me-labels text.dim { opacity: 0.06; }
|
|
82
|
+
.me-graph-node.dim { opacity: 0.16; transition: opacity .18s; }
|
|
83
|
+
.me-graph-node.focus circle.inner { stroke: #fff; stroke-width: 3; }
|
|
84
|
+
.me-graph-edge.dim { opacity: 0.06; }
|
|
85
|
+
.me-graph-edge.focus { opacity: 1; }
|
|
86
|
+
.me-graph-node.in { animation: me-node-in 0.5s cubic-bezier(0.2,0.8,0.2,1) backwards; }
|
|
87
|
+
@keyframes me-node-in { from { opacity: 0; transform: scale(0.4); } }
|
|
88
|
+
.me-graph-legend { display: flex; gap: 12px; flex-wrap: wrap; padding: 2px 2px 0; font-size: 11px; opacity: 0.85; }
|
|
89
|
+
.me-graph-legend .lg { display: inline-flex; align-items: center; gap: 5px; }
|
|
90
|
+
.me-graph-hint { font-size: 11px; opacity: 0.6; margin-left: auto; }
|
|
91
|
+
`;function ge(t){t.effect(()=>t.locale.register(H,{zh:de,en:pe}),"memory-eternal: locale");let r=t.locale.bind(H);t.effect(()=>t.slots.inject("settings.section",()=>t.slots.register({name:"settings.section",id:H,order:25,label:()=>r("nav"),locale:H,inject:()=>({})},()=>c.default.createElement(W,{t:r,inModal:!1}))),"memory-eternal: settings section"),t.effect(()=>t.slots.inject("sidebar.footer.action",()=>t.slots.register({name:"sidebar.footer.action",id:`${H}:footer`,order:100,label:()=>r("nav"),locale:H,inject:()=>({})},n=>c.default.createElement(fe,{t:r,wide:!(n&&n.wide===!1)}))),"memory-eternal: sidebar footer action")}function fe({t,wide:r}){let[n,o]=(0,c.useState)(!1);return(0,e.jsxs)("div",{className:`me-footer${r?"":" rail"}`,children:[(0,e.jsx)("style",{children:Y}),(0,e.jsxs)("button",{type:"button",className:"me-footer-btn",onClick:()=>o(!0),"aria-label":t("nav"),title:t("nav"),children:[(0,e.jsx)("span",{className:"me-footer-ico","aria-hidden":"true",children:(0,e.jsx)(Z,{})}),(0,e.jsx)("span",{className:"me-footer-label",children:t("nav")})]}),n&&(0,e.jsx)(ue,{t,onClose:()=>o(!1)})]})}function Z(){return(0,e.jsxs)("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[(0,e.jsx)("ellipse",{cx:"12",cy:"5.5",rx:"8",ry:"3"}),(0,e.jsx)("path",{d:"M4 5.5v6c0 1.66 3.58 3 8 3s8-1.34 8-3v-6"}),(0,e.jsx)("path",{d:"M4 11.5v6c0 1.66 3.58 3 8 3s8-1.34 8-3v-6"})]})}function ue({t,onClose:r}){return(0,c.useEffect)(()=>{let n=o=>{o.key==="Escape"&&r()};return window.addEventListener("keydown",n),()=>window.removeEventListener("keydown",n)},[r]),(0,e.jsxs)("div",{className:"me-overlay-top",onClick:r,children:[(0,e.jsx)("style",{children:Y}),(0,e.jsx)("div",{className:"me-modal",onClick:n=>n.stopPropagation(),children:(0,e.jsx)(W,{t,inModal:!0,onClose:r})})]})}function W({t,inModal:r,onClose:n}){let[o,d]=(0,c.useState)(null),[N,g]=(0,c.useState)([]),[l,z]=(0,c.useState)(!0),[D,j]=(0,c.useState)(""),[k,C]=(0,c.useState)("all"),[y,u]=(0,c.useState)(""),[M,f]=(0,c.useState)("cards"),[P,A]=(0,c.useState)(null),E=(0,c.useRef)(null),T=(0,c.useCallback)(async(p=k,$=y)=>{try{z(!0),j("");let v=new URLSearchParams;p&&p!=="all"&&v.set("kind",p),$.trim()&&v.set("q",$.trim());let a=await(await fetch(`${U}/cards?${v.toString()}`)).json();a.ok&&g(a.cards||[])}catch(v){j(String(v&&v.message?v.message:v))}finally{z(!1)}},[k,y]),h=(0,c.useCallback)(async()=>{try{let[p,$]=await Promise.all([fetch(`${U}/overview`).then(v=>v.json()),fetch(`${U}/cards`).then(v=>v.json())]);p.ok&&d(p),$.ok&&g($.cards||[])}catch(p){j(String(p&&p.message?p.message:p))}finally{z(!1)}},[]);(0,c.useEffect)(()=>(h(),()=>{E.current&&clearTimeout(E.current)}),[h]);let i=p=>{u(p),E.current&&clearTimeout(E.current),E.current=setTimeout(()=>T(k,p),280)},x=p=>{C(p),T(p,y)},I=async p=>{try{let v=await(await fetch(`${U}/card?path=${encodeURIComponent(p.path||p.id)}`)).json();v.ok&&A({path:v.path,title:p.title,text:v.text})}catch($){j(String($&&$.message?$.message:$))}};return(0,e.jsxs)("div",{className:"memory-eternal-root",style:{height:"100%",display:"flex",flexDirection:"column"},children:[(0,e.jsx)("style",{children:Y}),r&&(0,e.jsxs)("div",{className:"me-modal-head",children:[(0,e.jsxs)("div",{className:"me-modal-title",children:[(0,e.jsx)(Z,{}),(0,e.jsx)("h2",{children:t("memoryTitle")}),(0,e.jsx)("span",{children:t("memoryHint")})]}),(0,e.jsx)("div",{className:"spacer"}),(0,e.jsx)("button",{type:"button",className:"me-modal-close",onClick:n,"aria-label":t("close"),children:"\u2715"})]}),(0,e.jsxs)("div",{className:"me-modal-body",children:[(0,e.jsxs)("div",{className:"mc-stats",children:[(0,e.jsx)(X,{label:t("total"),value:o?o.total:"\u2014"}),(0,e.jsx)(X,{label:t("recent"),value:o?o.recent:"\u2014"}),(0,e.jsx)(X,{label:t("tags"),value:o?o.tags:"\u2014"}),(0,e.jsx)(X,{label:t("cardCount"),value:o?o.byKind?.knowledge??0:"\u2014"})]}),(0,e.jsxs)("div",{className:"mc-toolbar",children:[(0,e.jsx)("input",{type:"text",placeholder:t("searchPlaceholder"),value:y,onChange:p=>i(p.target.value)}),(0,e.jsxs)("div",{className:"mc-tabs",children:[(0,e.jsx)("button",{type:"button",className:`mc-tab${M==="cards"?" active":""}`,onClick:()=>f("cards"),children:t("cardsTab")}),(0,e.jsx)("button",{type:"button",className:`mc-tab${M==="graph"?" active":""}`,onClick:()=>f("graph"),children:t("graphTab")})]}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>h(),children:t("refresh")})]}),M==="cards"&&(0,e.jsx)("div",{className:"mc-chips",children:me.map(p=>(0,e.jsx)("button",{type:"button",className:`mc-chip${k===p?" active":""}`,onClick:()=>x(p),children:t(p==="all"?"all":_[p])},p))}),D&&(0,e.jsxs)("div",{className:"mc-empty",children:[t("error"),"\uFF1A",D]}),M==="cards"?l&&!N.length?(0,e.jsx)("div",{className:"mc-empty",children:t("loading")}):N.length===0?(0,e.jsx)("div",{className:"mc-empty",children:t("empty")}):(0,e.jsx)("div",{className:"mc-grid",children:N.map(p=>(0,e.jsx)(he,{card:p,t,onOpen:I},p.path))}):(0,e.jsx)(ye,{t,onOpen:I}),P&&(0,e.jsx)(be,{t,card:P,onClose:()=>A(null)})]})]})}var X=({label:t,value:r})=>(0,e.jsxs)("div",{className:"mc-stat mc-card",children:[(0,e.jsx)("b",{children:r}),(0,e.jsx)("span",{children:t})]}),he=({card:t,t:r,onOpen:n})=>(0,e.jsxs)("article",{className:"mc-cardrow mc-card",onClick:()=>n(t),children:[(0,e.jsxs)("h4",{children:[(0,e.jsx)("span",{className:"mc-kind",style:{background:L[t.kind]||L.other}}),t.title]}),(0,e.jsx)("p",{children:t.summary||""}),t.tags.length>0&&(0,e.jsx)("div",{className:"mc-tags",children:t.tags.slice(0,4).map(o=>(0,e.jsx)("span",{className:"mc-tag",children:o},o))}),(0,e.jsxs)("footer",{children:[(0,e.jsx)("span",{children:r(_[t.kind])}),(0,e.jsx)("span",{children:Ce(t.updated)})]})]});function be({t,card:r,onClose:n}){return(0,c.useEffect)(()=>{let o=d=>{d.key==="Escape"&&n()};return window.addEventListener("keydown",o),()=>window.removeEventListener("keydown",o)},[n]),(0,e.jsxs)("div",{className:"me-overlay",onClick:n,children:[(0,e.jsx)("style",{children:Y}),(0,e.jsxs)("div",{className:"me-dialog",onClick:o=>o.stopPropagation(),children:[(0,e.jsxs)("div",{className:"me-dialog-head",children:[(0,e.jsx)("h3",{children:r.title}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:n,children:t("close")})]}),(0,e.jsx)("div",{className:"me-dialog-body",children:r.text})]})]})}function ye({t,onOpen:r}){let[n,o]=(0,c.useState)(null),[d,N]=(0,c.useState)("");return(0,c.useEffect)(()=>{let g=!0;return fetch(`${U}/graph`).then(l=>l.json()).then(l=>{g&&o(l.ok?l:null)}).catch(l=>{g&&N(String(l&&l.message?l.message:l))}),()=>{g=!1}},[]),(0,e.jsxs)("div",{className:"me-graph",children:[(0,e.jsx)("style",{children:Y}),d&&(0,e.jsxs)("div",{className:"mc-empty",children:[t("error"),"\uFF1A",d]}),!n&&!d&&(0,e.jsx)("div",{className:"mc-empty",children:t("loading")}),n&&n.nodes.length===0&&(0,e.jsx)("div",{className:"mc-empty",children:t("emptyGraph")}),n&&n.nodes.length>0&&(0,e.jsx)(ve,{nodes:n.nodes,edges:n.edges,onOpen:r,t,countLabel:`${n.nodes.length} ${t("nodes")} \xB7 ${xe(n.edges)} ${t("edges")}`})]})}function xe(t){let r=new Set;for(let n of t||[])r.add([n.source,n.target].sort().join("|"));return r.size}function ve({nodes:t,edges:r,onOpen:n,t:o,countLabel:d}){let l=(0,c.useRef)(null),z=(0,c.useRef)(null),D=(0,c.useRef)(null),j=(0,c.useRef)(null),k=(0,c.useRef)({x:0,y:0,k:1}),C=(0,c.useRef)(null),y=(0,c.useRef)(!1),[u,M]=(0,c.useState)(null),f=(0,c.useMemo)(()=>{let a={};r.forEach(w=>{a[w.source]=(a[w.source]||0)+1,a[w.target]=(a[w.target]||0)+1});let s=420,m=900,b=t,S=!1;t.length>s&&(b=[...t].sort((w,K)=>(a[K.id]||0)-(a[w.id]||0)).slice(0,s),S=!0);let R=new Set(b.map(w=>w.id)),B=new Set,O=[];r.forEach(w=>{if(!R.has(w.source)||!R.has(w.target))return;let K=[w.source,w.target].sort().join("|");B.has(K)||(B.add(K),O.push(w))}),O.sort((w,K)=>(a[w.source]||0)+(a[w.target]||0)-((a[K.source]||0)+(a[K.target]||0)));let F=Math.min(b.length,28),J=b.map(w=>a[w.id]||0).sort((w,K)=>K-w),ee=F>0?J[F-1]:1/0;return{list:b,es:O.slice(0,m),capped:S,maxDeg:Math.max(1,...Object.values(a)),deg:a,hubMin:ee}},[t,r]),P=(0,c.useMemo)(()=>we(f.list,f.es,980,620),[f.list,f.es,980,620]),A=(0,c.useMemo)(()=>f.list.reduce((a,s,m)=>(a[s.id]=m,a),{}),[f.list]),E=(0,c.useMemo)(()=>{let a={};return f.list.forEach(s=>{a[s.id]=new Set}),f.es.forEach(s=>{a[s.source]&&a[s.source].add(s.target),a[s.target]&&a[s.target].add(s.source)}),a},[f.list,f.es]),T=(0,c.useCallback)(()=>{let a=D.current;if(!a)return;let{x:s,y:m,k:b}=k.current;a.setAttribute("transform",`translate(${s} ${m}) scale(${b})`),j.current&&j.current.classList.toggle("all",b>=1.4)},[]);(0,c.useEffect)(()=>{T()},[T,P]);let h=(0,c.useCallback)((a,s)=>{let m=z.current;if(!m||typeof m.getScreenCTM!="function")return{x:a,y:s};let b=m.getScreenCTM();if(!b)return{x:a,y:s};let S=new DOMPoint(a,s).matrixTransform(b.inverse());return{x:S.x,y:S.y}},[]),i=(0,c.useCallback)((a,s)=>{let m=k.current,b=Math.min(5,Math.max(.25,m.k*s));k.current={k:b,x:a.x-b/m.k*(a.x-m.x),y:a.y-b/m.k*(a.y-m.y)},T()},[T]);(0,c.useEffect)(()=>{let a=l.current;if(!a)return;let s=m=>{m.preventDefault(),i(h(m.clientX,m.clientY),m.deltaY<0?1.12:1/1.12)};return a.addEventListener("wheel",s,{passive:!1}),()=>a.removeEventListener("wheel",s)},[h,i]);let x=a=>{if(a.button!==0)return;let s=h(a.clientX,a.clientY);C.current={px:s.x,py:s.y,tx:k.current.x,ty:k.current.y,started:!1,id:a.pointerId},y.current=!1},I=a=>{let s=C.current;if(!s)return;let m=h(a.clientX,a.clientY);Math.abs(m.x-s.px)+Math.abs(m.y-s.py)>1.5&&(y.current=!0,!s.started&&l.current&&l.current.setPointerCapture&&(l.current.setPointerCapture(a.pointerId),s.started=!0),l.current&&l.current.classList.add("dragging"),k.current={...k.current,x:s.tx+(m.x-s.px),y:s.ty+(m.y-s.py)},T())},p=()=>{C.current=null,l.current&&l.current.classList.remove("dragging")},$=a=>{n({path:a.id,title:a.title})},v=a=>{y.current||M(a.id)},G=a=>7+Math.min(9,(f.deg[a.id]||0)/f.maxDeg*9);return(0,e.jsxs)("div",{className:"me-graph",children:[(0,e.jsxs)("div",{className:"me-graph-toolbar",children:[(0,e.jsx)("span",{className:"me-graph-count",children:d}),u&&(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>M(null),children:o("clearFocus")}),(0,e.jsx)("span",{className:"spacer",style:{flex:1}}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>i({x:980/2,y:620/2},1/1.25),"aria-label":o("zoomOut"),children:"\u2212"}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>{k.current={x:0,y:0,k:1},T()},"aria-label":o("fit"),children:o("fit")}),(0,e.jsx)("button",{type:"button",className:"mc-btn",onClick:()=>i({x:980/2,y:620/2},1.25),"aria-label":o("zoomIn"),children:"+"})]}),(0,e.jsxs)("div",{className:"me-graph-canvas",ref:l,onPointerDown:x,onPointerMove:I,onPointerUp:p,onPointerLeave:p,onContextMenu:a=>{a.preventDefault(),M(null)},onClick:a=>{y.current||M(null)},children:[(0,e.jsxs)("svg",{ref:z,viewBox:"0 0 980 620",role:"img",children:[(0,e.jsxs)("defs",{children:[Object.keys(L).map(a=>(0,e.jsxs)("radialGradient",{id:`me-grad-${a}`,cx:"35%",cy:"30%",r:"75%",children:[(0,e.jsx)("stop",{offset:"0%",stopColor:Me(L[a],.55)}),(0,e.jsx)("stop",{offset:"60%",stopColor:L[a]}),(0,e.jsx)("stop",{offset:"100%",stopColor:$e(L[a],.25)})]},a)),Object.keys(L).map(a=>(0,e.jsxs)("linearGradient",{id:`me-edge-${a}`,gradientUnits:"userSpaceOnUse",children:[(0,e.jsx)("stop",{offset:"0%",stopColor:L[a],stopOpacity:"0.4"}),(0,e.jsx)("stop",{offset:"100%",stopColor:L[a],stopOpacity:"0.85"})]},`e-${a}`)),(0,e.jsxs)("filter",{id:"me-glow",x:"-60%",y:"-60%",width:"220%",height:"220%",children:[(0,e.jsx)("feGaussianBlur",{stdDeviation:"3.2",result:"b"}),(0,e.jsxs)("feMerge",{children:[(0,e.jsx)("feMergeNode",{in:"b"}),(0,e.jsx)("feMergeNode",{in:"SourceGraphic"})]})]})]}),(0,e.jsxs)("g",{ref:D,transform:"translate(0 0) scale(1)",children:[f.es.map((a,s)=>{let m=A[a.source],b=A[a.target];if(m===void 0||b===void 0)return null;let S=P[m],R=P[b];if(!S||!R)return null;let B=f.list[m].kind||"other",O=u&&a.source!==u&&a.target!==u,F=u&&(a.source===u||a.target===u);return(0,e.jsx)("path",{d:Ne(S,R),className:`me-graph-edge${O?" dim":""}${F?" focus":""}`,style:{stroke:`url(#me-edge-${B})`,animationDelay:`${s%8*.12}s`}},`e${s}`)}),f.list.map((a,s)=>{let m=P[s];if(!m)return null;let b=a.kind||"other",S=G(a),R=u&&a.id!==u&&!E[a.id].has(u),B=u&&(a.id===u||E[a.id].has(u));return(0,e.jsxs)("g",{className:`me-graph-node in${B?" focus":""}${R?" dim":""}`,style:{animationDelay:`${s*.02}s`},transform:`translate(${m.x} ${m.y})`,onClick:O=>{O.stopPropagation(),v(a)},onContextMenu:O=>{O.preventDefault(),O.stopPropagation(),$(a)},children:[(0,e.jsx)("circle",{className:"inner",r:S,fill:`url(#me-grad-${b})`,stroke:L[b],strokeOpacity:"0.9",filter:"url(#me-glow)"}),(0,e.jsx)("circle",{r:Math.max(2.4,S*.2),fill:"rgba(255,255,255,0.85)"})]},a.id)}),(0,e.jsx)("g",{ref:j,className:"me-labels",children:f.list.map((a,s)=>{let m=P[s];if(!m)return null;let b=G(a),S=(f.deg[a.id]||0)>=f.hubMin,R=u&&a.id!==u&&!E[a.id].has(u);return(0,e.jsx)("text",{className:`${S?"me-nodelabel":"me-nodelabel minor"}${R?" dim":""}`,x:m.x+b+7,y:m.y+4,children:String(a.title||"").slice(0,16)},`l${a.id}`)})})]})]}),(0,e.jsxs)("div",{className:"me-graph-legend",children:[Object.keys(L).map(a=>(0,e.jsxs)("span",{className:"lg",children:[(0,e.jsx)("span",{className:"mc-kind",style:{background:L[a]}}),o(_[a])]},a)),(0,e.jsx)("span",{className:"me-graph-hint",children:o("graphHint")})]})]}),(0,e.jsxs)("div",{className:"me-graph-tip",children:[f.capped?`${o("capped")} ${f.list.length} / ${t.length} ${o("nodes")} \xB7 `:"",o("graphTip")]})]})}function we(t,r,n,o){let d=t.length;if(d===0)return[];if(d>160)return ke(t,n,o);let N=d<60?260:d<120?180:120,g=t.map((h,i)=>{let x=i/d*Math.PI*2-Math.PI/2;return{x:Math.cos(x),y:Math.sin(x)}}),l=g.map(()=>({x:0,y:0})),z=t.map(()=>[]);r.forEach(h=>{let i=t.findIndex(I=>I.id===h.source),x=t.findIndex(I=>I.id===h.target);i>=0&&x>=0&&i!==x&&(z[i].push(x),z[x].push(i))});let D=1.35,j=.035,k=.03,C=.84;for(let h=0;h<N;h++){for(let i=0;i<d;i++)for(let x=i+1;x<d;x++){let I=g[i].x-g[x].x,p=g[i].y-g[x].y,$=I*I+p*p||.01,v=Math.sqrt($),G=D/$,a=I/v*G,s=p/v*G;l[i].x+=a,l[i].y+=s,l[x].x-=a,l[x].y-=s}for(let i=0;i<d;i++)for(let x of z[i])l[i].x+=(g[x].x-g[i].x)*j,l[i].y+=(g[x].y-g[i].y)*j;for(let i=0;i<d;i++)l[i].x-=g[i].x*k,l[i].y-=g[i].y*k;for(let i=0;i<d;i++)l[i].x=Math.max(-.5,Math.min(.5,l[i].x*C)),l[i].y=Math.max(-.5,Math.min(.5,l[i].y*C)),g[i].x+=l[i].x,g[i].y+=l[i].y}let y=1/0,u=-1/0,M=1/0,f=-1/0;g.forEach(h=>{y=Math.min(y,h.x),u=Math.max(u,h.x),M=Math.min(M,h.y),f=Math.max(f,h.y)});let P=70,A=Math.min((n-2*P)/(u-y||1),(o-2*P)/(f-M||1)),E=(y+u)/2,T=(M+f)/2;return g.map(h=>({x:n/2+(h.x-E)*A,y:o/2+(h.y-T)*A}))}function ke(t,r,n){let o=t.length,d=r/2,N=n/2,g=Math.min(r,n)/2-80,l={};t.forEach((k,C)=>{let y=k.kind||"other";(l[y]=l[y]||[]).push(C)});let z=Object.keys(l),D=new Array(o),j=-Math.PI/2;for(let k of z){let C=l[k],y=C.length/o*Math.PI*2,u=j;C.forEach((M,f)=>{let P=C.length===1?u+y/2:u+y*(f+.5)/C.length;D[M]={x:d+g*Math.cos(P),y:N+g*Math.sin(P)}}),j+=y}return D}function Ne(t,r){let n=(t.x+r.x)/2,o=(t.y+r.y)/2,d=r.x-t.x,N=r.y-t.y,g=Math.sqrt(d*d+N*N)||1,l=Math.min(38,g*.18),z=n-N/g*l,D=o+d/g*l;return`M ${t.x} ${t.y} Q ${z} ${D} ${r.x} ${r.y}`}function Q(t){let r=t.replace("#",""),n=r.length===3?r.split("").map(o=>o+o).join(""):r;return[parseInt(n.slice(0,2),16),parseInt(n.slice(2,4),16),parseInt(n.slice(4,6),16)]}function Me(t,r){let[n,o,d]=Q(t);return`rgb(${Math.round(n+(255-n)*r)}, ${Math.round(o+(255-o)*r)}, ${Math.round(d+(255-d)*r)})`}function $e(t,r){let[n,o,d]=Q(t);return`rgb(${Math.round(n*(1-r))}, ${Math.round(o*(1-r))}, ${Math.round(d*(1-r))})`}function Ce(t){if(!t)return"";let r=new Date(t);if(Number.isNaN(r.getTime()))return"";let n=new Date,o=r.toDateString()===n.toDateString(),d=g=>String(g).padStart(2,"0"),N=`${d(r.getHours())}:${d(r.getMinutes())}`;return o?N:`${r.getMonth()+1}-${r.getDate()} ${N}`}
|
|
44
92
|
|
|
45
93
|
return module.exports;
|
|
46
94
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-memory-eternal",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "记忆核心(Memory Eternal
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "记忆核心(Memory Eternal):自研的 DeepSeek Harness 记忆插件,不移植任何既有记忆框架——对话结束后自动沉淀知识卡到本地 Markdown Vault(自研去重、自研 CJK 检索、可 git 管理),设置页提供图形化知识库(统计 / 搜索 / 知识图谱 + 侧边栏一键弹窗),Agent 通过 memory_recall 工具按需召回历史上下文。零人工干预。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
package/assets/preview.html
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="zh">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<title>记忆核心预览</title>
|
|
6
|
-
<style>
|
|
7
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
8
|
-
body { font-family: -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif; background: #f5f6f8; color: #1f2937; padding: 32px; }
|
|
9
|
-
.card { background: #fff; border: 1px solid #e5e7eb; border-radius: 14px; padding: 18px 20px; }
|
|
10
|
-
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 14px; }
|
|
11
|
-
.stat { text-align: center; }
|
|
12
|
-
.stat b { display: block; font-size: 26px; }
|
|
13
|
-
.stat span { font-size: 12px; color: #6b7280; }
|
|
14
|
-
.toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; }
|
|
15
|
-
.toolbar input { flex: 1; padding: 8px 12px; border-radius: 8px; border: 1px solid #d1d5db; font-size: 13px; }
|
|
16
|
-
.btn { border: 1px solid #d1d5db; background: #fff; border-radius: 8px; padding: 7px 14px; font-size: 12px; cursor: pointer; }
|
|
17
|
-
.chips { display: flex; gap: 6px; margin-bottom: 14px; }
|
|
18
|
-
.chip { border: 1px solid #d1d5db; background: #fff; border-radius: 999px; padding: 4px 12px; font-size: 12px; color: #6b7280; }
|
|
19
|
-
.chip.active { color: #111; font-weight: 600; border-color: #111; }
|
|
20
|
-
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
|
|
21
|
-
.kcard { border: 1px solid #e5e7eb; border-radius: 12px; padding: 14px 16px; display: flex; flex-direction: column; gap: 6px; background: #fff; cursor: pointer; }
|
|
22
|
-
.kcard h4 { font-size: 14px; }
|
|
23
|
-
.kcard p { font-size: 12px; color: #6b7280; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
|
24
|
-
.kcard footer { margin-top: auto; display: flex; justify-content: space-between; font-size: 11px; color: #9ca3af; }
|
|
25
|
-
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
|
|
26
|
-
.tags { display: flex; gap: 4px; }
|
|
27
|
-
.tag { font-size: 10px; padding: 1px 7px; border-radius: 999px; background: #f3f4f6; color: #6b7280; }
|
|
28
|
-
.title { font-size: 20px; font-weight: 700; margin-bottom: 4px; display: flex; align-items: center; gap: 10px; }
|
|
29
|
-
.subtitle { font-size: 12px; color: #6b7280; margin-bottom: 18px; }
|
|
30
|
-
.wrap { max-width: 980px; margin: 0 auto; }
|
|
31
|
-
.layout { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: stretch; }
|
|
32
|
-
.graph-card { background: #fff; border: 1px solid #e5e7eb; border-radius: 14px; padding: 14px; }
|
|
33
|
-
.graph-card h3 { font-size: 13px; margin-bottom: 8px; color: #374151; }
|
|
34
|
-
</style>
|
|
35
|
-
</head>
|
|
36
|
-
<body>
|
|
37
|
-
<div class="wrap">
|
|
38
|
-
<div class="title">🧠 记忆核心 <span style="font-size:13px;color:#6b7280;font-weight:400">Memory Core · 设置 → 记忆</span></div>
|
|
39
|
-
<div class="subtitle">对话自动沉淀知识卡到本地 Markdown Vault · 双层去重 · CJK 检索 · 知识图谱</div>
|
|
40
|
-
|
|
41
|
-
<div class="card" style="margin-bottom:14px">
|
|
42
|
-
<div class="stats">
|
|
43
|
-
<div class="stat"><b style="color:#3b82f6">42</b><span>知识卡</span></div>
|
|
44
|
-
<div class="stat"><b style="color:#22c55e">9</b><span>近 7 天新增</span></div>
|
|
45
|
-
<div class="stat"><b style="color:#eab308">31</b><span>标签</span></div>
|
|
46
|
-
<div class="stat"><b style="color:#8b5cf6">3</b><span>项目</span></div>
|
|
47
|
-
</div>
|
|
48
|
-
<div class="toolbar">
|
|
49
|
-
<input type="text" placeholder="搜索记忆(支持中文片段)…" value="B+树 索引">
|
|
50
|
-
<button class="btn">搜索</button>
|
|
51
|
-
<button class="btn">刷新</button>
|
|
52
|
-
<button class="btn" style="background:#111;color:#fff;border-color:#111">🕸 知识图谱</button>
|
|
53
|
-
</div>
|
|
54
|
-
<div class="chips">
|
|
55
|
-
<button class="chip active">全部</button>
|
|
56
|
-
<button class="chip">项目</button>
|
|
57
|
-
<button class="chip">知识</button>
|
|
58
|
-
<button class="chip">内容</button>
|
|
59
|
-
<button class="chip">提示词</button>
|
|
60
|
-
<button class="chip">业务</button>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<div class="layout">
|
|
65
|
-
<div>
|
|
66
|
-
<div class="grid">
|
|
67
|
-
<div class="kcard">
|
|
68
|
-
<h4><span class="dot" style="background:#22c55e"></span>数据库索引原理与B+树加速机制</h4>
|
|
69
|
-
<p>索引是数据库为加速查找而额外维护的数据结构…复杂度从 O(n) 降到 O(log n),一次查询只需 3~4 次磁盘 I/O。</p>
|
|
70
|
-
<div class="tags"><span class="tag">数据库</span><span class="tag">索引</span><span class="tag">B+树</span><span class="tag">查询优化</span></div>
|
|
71
|
-
<footer><span>知识</span><span>08-21 20:04</span></footer>
|
|
72
|
-
</div>
|
|
73
|
-
<div class="kcard">
|
|
74
|
-
<h4><span class="dot" style="background:#22c55e"></span>B+树磁盘IO模型与覆盖索引回表优化</h4>
|
|
75
|
-
<p>磁盘随机读 5-10ms,内存纳秒级,相差 5-6 个数量级;覆盖索引避免回表,范围查询顺序读相邻叶子…</p>
|
|
76
|
-
<div class="tags"><span class="tag">MySQL</span><span class="tag">B+树</span><span class="tag">覆盖索引</span></div>
|
|
77
|
-
<footer><span>知识</span><span>08-21 20:06</span></footer>
|
|
78
|
-
</div>
|
|
79
|
-
<div class="kcard">
|
|
80
|
-
<h4><span class="dot" style="background:#3b82f6"></span>项目极光 · 技术选型</h4>
|
|
81
|
-
<p>项目代号:极光(Aurora)。数据库最终选用 PostgreSQL;前端用 Vite 构建。迁移成本可控…</p>
|
|
82
|
-
<div class="tags"><span class="tag">项目</span><span class="tag">PostgreSQL</span></div>
|
|
83
|
-
<footer><span>项目</span><span>08-21 19:59</span></footer>
|
|
84
|
-
</div>
|
|
85
|
-
<div class="kcard">
|
|
86
|
-
<h4><span class="dot" style="background:#22c55e"></span>Redis 缓存策略:TTL与雪崩防护</h4>
|
|
87
|
-
<p>缓存热点数据 TTL 10 分钟,穿透用空值缓存,随机过期防雪崩,大 key 分段缓存降低序列化开销…</p>
|
|
88
|
-
<div class="tags"><span class="tag">Redis</span><span class="tag">缓存</span></div>
|
|
89
|
-
<footer><span>知识</span><span>08-21 18:30</span></footer>
|
|
90
|
-
</div>
|
|
91
|
-
<div class="kcard">
|
|
92
|
-
<h4><span class="dot" style="background:#06b6d4"></span>DSH 插件发布范式(PUBLISH 工作流)</h4>
|
|
93
|
-
<p>npm keywords 带 dsh-plugin + GitHub 打 topic → 市场自动收录;prepublishOnly 自动 build client…</p>
|
|
94
|
-
<div class="tags"><span class="tag">DSH</span><span class="tag">插件</span></div>
|
|
95
|
-
<footer><span>提示词</span><span>08-20 22:11</span></footer>
|
|
96
|
-
</div>
|
|
97
|
-
<div class="kcard">
|
|
98
|
-
<h4><span class="dot" style="background:#f43f5e"></span>知识图谱可视化方案</h4>
|
|
99
|
-
<p>SVG 圆形布局按 kind 分区着色;wikilink 与共享标签连线;点击节点打开卡片阅读全文…</p>
|
|
100
|
-
<div class="tags"><span class="tag">可视化</span><span class="tag">图谱</span></div>
|
|
101
|
-
<footer><span>内容</span><span>08-19 15:02</span></footer>
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
<div class="graph-card">
|
|
106
|
-
<h3>🕸 知识图谱 · 12 节点 · 9 连线</h3>
|
|
107
|
-
<svg viewBox="0 0 440 380" width="100%" role="img">
|
|
108
|
-
<defs>
|
|
109
|
-
<marker id="arrow" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" fill="#d1d5db"/></marker>
|
|
110
|
-
</defs>
|
|
111
|
-
<!-- edges -->
|
|
112
|
-
<line x1="220" y1="60" x2="90" y2="180" stroke="#d1d5db" stroke-width="1.2"/>
|
|
113
|
-
<line x1="220" y1="60" x2="350" y2="180" stroke="#d1d5db" stroke-width="1.2"/>
|
|
114
|
-
<line x1="90" y1="180" x2="220" y2="300" stroke="#d1d5db" stroke-width="1.2" stroke-dasharray="4 3"/>
|
|
115
|
-
<line x1="350" y1="180" x2="220" y2="300" stroke="#d1d5db" stroke-width="1.2"/>
|
|
116
|
-
<line x1="90" y1="180" x2="40" y2="300" stroke="#d1d5db" stroke-width="1.2" stroke-dasharray="4 3"/>
|
|
117
|
-
<line x1="350" y1="180" x2="400" y2="300" stroke="#d1d5db" stroke-width="1.2" stroke-dasharray="4 3"/>
|
|
118
|
-
<line x1="220" y1="300" x2="40" y2="300" stroke="#d1d5db" stroke-width="1" stroke-dasharray="2 3"/>
|
|
119
|
-
<line x1="220" y1="300" x2="400" y2="300" stroke="#d1d5db" stroke-width="1" stroke-dasharray="2 3"/>
|
|
120
|
-
<!-- nodes -->
|
|
121
|
-
<g transform="translate(220 60)"><circle r="11" fill="#22c55e"/><text x="15" y="4" font-size="11" fill="#374151">数据库索引原理</text></g>
|
|
122
|
-
<g transform="translate(90 180)"><circle r="9" fill="#22c55e"/><text x="13" y="3" font-size="10" fill="#374151">B+树IO模型</text></g>
|
|
123
|
-
<g transform="translate(350 180)"><circle r="9" fill="#22c55e"/><text x="-60" y="-8" font-size="10" fill="#374151">Redis缓存策略</text></g>
|
|
124
|
-
<g transform="translate(220 300)"><circle r="12" fill="#3b82f6"/><text x="16" y="4" font-size="11" fill="#374151">项目极光</text></g>
|
|
125
|
-
<g transform="translate(40 300)"><circle r="8" fill="#06b6d4"/><text x="12" y="3" font-size="10" fill="#374151">发布范式</text></g>
|
|
126
|
-
<g transform="translate(400 300)"><circle r="8" fill="#f43f5e"/><text x="-52" y="-10" font-size="10" fill="#374151">图谱方案</text></g>
|
|
127
|
-
</svg>
|
|
128
|
-
<div style="display:flex;gap:10px;flex-wrap:wrap;font-size:11px;color:#6b7280;margin-top:6px">
|
|
129
|
-
<span><span class="dot" style="background:#3b82f6"></span>项目</span>
|
|
130
|
-
<span><span class="dot" style="background:#22c55e"></span>知识</span>
|
|
131
|
-
<span><span class="dot" style="background:#f43f5e"></span>内容</span>
|
|
132
|
-
<span><span class="dot" style="background:#06b6d4"></span>提示词</span>
|
|
133
|
-
<span style="opacity:.7">连线=[[链接]]或共享标签 · 点击节点阅读卡片</span>
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
</body>
|
|
139
|
-
</html>
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="zh">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<title>记忆核心预览</title>
|
|
6
|
-
<style>
|
|
7
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
8
|
-
body { font-family: -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif; background: #f5f6f8; color: #1f2937; padding: 32px; }
|
|
9
|
-
.card { background: #fff; border: 1px solid #e5e7eb; border-radius: 14px; padding: 18px 20px; }
|
|
10
|
-
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 14px; }
|
|
11
|
-
.stat { text-align: center; }
|
|
12
|
-
.stat b { display: block; font-size: 26px; }
|
|
13
|
-
.stat span { font-size: 12px; color: #6b7280; }
|
|
14
|
-
.toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; }
|
|
15
|
-
.toolbar input { flex: 1; padding: 8px 12px; border-radius: 8px; border: 1px solid #d1d5db; font-size: 13px; }
|
|
16
|
-
.btn { border: 1px solid #d1d5db; background: #fff; border-radius: 8px; padding: 7px 14px; font-size: 12px; cursor: pointer; }
|
|
17
|
-
.chips { display: flex; gap: 6px; margin-bottom: 14px; }
|
|
18
|
-
.chip { border: 1px solid #d1d5db; background: #fff; border-radius: 999px; padding: 4px 12px; font-size: 12px; color: #6b7280; }
|
|
19
|
-
.chip.active { color: #111; font-weight: 600; border-color: #111; }
|
|
20
|
-
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
|
|
21
|
-
.kcard { border: 1px solid #e5e7eb; border-radius: 12px; padding: 14px 16px; display: flex; flex-direction: column; gap: 6px; background: #fff; cursor: pointer; }
|
|
22
|
-
.kcard h4 { font-size: 14px; }
|
|
23
|
-
.kcard p { font-size: 12px; color: #6b7280; line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
|
|
24
|
-
.kcard footer { margin-top: auto; display: flex; justify-content: space-between; font-size: 11px; color: #9ca3af; }
|
|
25
|
-
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 6px; }
|
|
26
|
-
.tags { display: flex; gap: 4px; }
|
|
27
|
-
.tag { font-size: 10px; padding: 1px 7px; border-radius: 999px; background: #f3f4f6; color: #6b7280; }
|
|
28
|
-
.title { font-size: 20px; font-weight: 700; margin-bottom: 4px; display: flex; align-items: center; gap: 10px; }
|
|
29
|
-
.subtitle { font-size: 12px; color: #6b7280; margin-bottom: 18px; }
|
|
30
|
-
.wrap { max-width: 980px; margin: 0 auto; }
|
|
31
|
-
.layout { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: stretch; }
|
|
32
|
-
.graph-card { background: #fff; border: 1px solid #e5e7eb; border-radius: 14px; padding: 14px; }
|
|
33
|
-
.graph-card h3 { font-size: 13px; margin-bottom: 8px; color: #374151; }
|
|
34
|
-
</style>
|
|
35
|
-
</head>
|
|
36
|
-
<body>
|
|
37
|
-
<div class="wrap">
|
|
38
|
-
<div class="title">🧠 记忆核心 <span style="font-size:13px;color:#6b7280;font-weight:400">Memory Core · 设置 → 记忆</span></div>
|
|
39
|
-
<div class="subtitle">对话自动沉淀知识卡到本地 Markdown Vault · 双层去重 · CJK 检索 · 知识图谱</div>
|
|
40
|
-
|
|
41
|
-
<div class="card" style="margin-bottom:14px">
|
|
42
|
-
<div class="stats">
|
|
43
|
-
<div class="stat"><b style="color:#3b82f6">42</b><span>知识卡</span></div>
|
|
44
|
-
<div class="stat"><b style="color:#22c55e">9</b><span>近 7 天新增</span></div>
|
|
45
|
-
<div class="stat"><b style="color:#eab308">31</b><span>标签</span></div>
|
|
46
|
-
<div class="stat"><b style="color:#8b5cf6">3</b><span>项目</span></div>
|
|
47
|
-
</div>
|
|
48
|
-
<div class="toolbar">
|
|
49
|
-
<input type="text" placeholder="搜索记忆(支持中文片段)…" value="B+树 索引">
|
|
50
|
-
<button class="btn">搜索</button>
|
|
51
|
-
<button class="btn">刷新</button>
|
|
52
|
-
<button class="btn" style="background:#111;color:#fff;border-color:#111">🕸 知识图谱</button>
|
|
53
|
-
</div>
|
|
54
|
-
<div class="chips">
|
|
55
|
-
<button class="chip active">全部</button>
|
|
56
|
-
<button class="chip">项目</button>
|
|
57
|
-
<button class="chip">知识</button>
|
|
58
|
-
<button class="chip">内容</button>
|
|
59
|
-
<button class="chip">提示词</button>
|
|
60
|
-
<button class="chip">业务</button>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<div class="layout">
|
|
65
|
-
<div>
|
|
66
|
-
<div class="grid">
|
|
67
|
-
<div class="kcard">
|
|
68
|
-
<h4><span class="dot" style="background:#22c55e"></span>数据库索引原理与B+树加速机制</h4>
|
|
69
|
-
<p>索引是数据库为加速查找而额外维护的数据结构…复杂度从 O(n) 降到 O(log n),一次查询只需 3~4 次磁盘 I/O。</p>
|
|
70
|
-
<div class="tags"><span class="tag">数据库</span><span class="tag">索引</span><span class="tag">B+树</span><span class="tag">查询优化</span></div>
|
|
71
|
-
<footer><span>知识</span><span>08-21 20:04</span></footer>
|
|
72
|
-
</div>
|
|
73
|
-
<div class="kcard">
|
|
74
|
-
<h4><span class="dot" style="background:#22c55e"></span>B+树磁盘IO模型与覆盖索引回表优化</h4>
|
|
75
|
-
<p>磁盘随机读 5-10ms,内存纳秒级,相差 5-6 个数量级;覆盖索引避免回表,范围查询顺序读相邻叶子…</p>
|
|
76
|
-
<div class="tags"><span class="tag">MySQL</span><span class="tag">B+树</span><span class="tag">覆盖索引</span></div>
|
|
77
|
-
<footer><span>知识</span><span>08-21 20:06</span></footer>
|
|
78
|
-
</div>
|
|
79
|
-
<div class="kcard">
|
|
80
|
-
<h4><span class="dot" style="background:#3b82f6"></span>项目极光 · 技术选型</h4>
|
|
81
|
-
<p>项目代号:极光(Aurora)。数据库最终选用 PostgreSQL;前端用 Vite 构建。迁移成本可控…</p>
|
|
82
|
-
<div class="tags"><span class="tag">项目</span><span class="tag">PostgreSQL</span></div>
|
|
83
|
-
<footer><span>项目</span><span>08-21 19:59</span></footer>
|
|
84
|
-
</div>
|
|
85
|
-
<div class="kcard">
|
|
86
|
-
<h4><span class="dot" style="background:#22c55e"></span>Redis 缓存策略:TTL与雪崩防护</h4>
|
|
87
|
-
<p>缓存热点数据 TTL 10 分钟,穿透用空值缓存,随机过期防雪崩,大 key 分段缓存降低序列化开销…</p>
|
|
88
|
-
<div class="tags"><span class="tag">Redis</span><span class="tag">缓存</span></div>
|
|
89
|
-
<footer><span>知识</span><span>08-21 18:30</span></footer>
|
|
90
|
-
</div>
|
|
91
|
-
<div class="kcard">
|
|
92
|
-
<h4><span class="dot" style="background:#06b6d4"></span>DSH 插件发布范式(PUBLISH 工作流)</h4>
|
|
93
|
-
<p>npm keywords 带 dsh-plugin + GitHub 打 topic → 市场自动收录;prepublishOnly 自动 build client…</p>
|
|
94
|
-
<div class="tags"><span class="tag">DSH</span><span class="tag">插件</span></div>
|
|
95
|
-
<footer><span>提示词</span><span>08-20 22:11</span></footer>
|
|
96
|
-
</div>
|
|
97
|
-
<div class="kcard">
|
|
98
|
-
<h4><span class="dot" style="background:#f43f5e"></span>知识图谱可视化方案</h4>
|
|
99
|
-
<p>SVG 圆形布局按 kind 分区着色;wikilink 与共享标签连线;点击节点打开卡片阅读全文…</p>
|
|
100
|
-
<div class="tags"><span class="tag">可视化</span><span class="tag">图谱</span></div>
|
|
101
|
-
<footer><span>内容</span><span>08-19 15:02</span></footer>
|
|
102
|
-
</div>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
<div class="graph-card">
|
|
106
|
-
<h3>🕸 知识图谱 · 12 节点 · 9 连线</h3>
|
|
107
|
-
<svg viewBox="0 0 440 380" width="100%" role="img">
|
|
108
|
-
<defs>
|
|
109
|
-
<marker id="arrow" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" fill="#d1d5db"/></marker>
|
|
110
|
-
</defs>
|
|
111
|
-
<!-- edges -->
|
|
112
|
-
<line x1="220" y1="60" x2="90" y2="180" stroke="#d1d5db" stroke-width="1.2"/>
|
|
113
|
-
<line x1="220" y1="60" x2="350" y2="180" stroke="#d1d5db" stroke-width="1.2"/>
|
|
114
|
-
<line x1="90" y1="180" x2="220" y2="300" stroke="#d1d5db" stroke-width="1.2" stroke-dasharray="4 3"/>
|
|
115
|
-
<line x1="350" y1="180" x2="220" y2="300" stroke="#d1d5db" stroke-width="1.2"/>
|
|
116
|
-
<line x1="90" y1="180" x2="40" y2="300" stroke="#d1d5db" stroke-width="1.2" stroke-dasharray="4 3"/>
|
|
117
|
-
<line x1="350" y1="180" x2="400" y2="300" stroke="#d1d5db" stroke-width="1.2" stroke-dasharray="4 3"/>
|
|
118
|
-
<line x1="220" y1="300" x2="40" y2="300" stroke="#d1d5db" stroke-width="1" stroke-dasharray="2 3"/>
|
|
119
|
-
<line x1="220" y1="300" x2="400" y2="300" stroke="#d1d5db" stroke-width="1" stroke-dasharray="2 3"/>
|
|
120
|
-
<!-- nodes -->
|
|
121
|
-
<g transform="translate(220 60)"><circle r="11" fill="#22c55e"/><text x="15" y="4" font-size="11" fill="#374151">数据库索引原理</text></g>
|
|
122
|
-
<g transform="translate(90 180)"><circle r="9" fill="#22c55e"/><text x="13" y="3" font-size="10" fill="#374151">B+树IO模型</text></g>
|
|
123
|
-
<g transform="translate(350 180)"><circle r="9" fill="#22c55e"/><text x="-60" y="-8" font-size="10" fill="#374151">Redis缓存策略</text></g>
|
|
124
|
-
<g transform="translate(220 300)"><circle r="12" fill="#3b82f6"/><text x="16" y="4" font-size="11" fill="#374151">项目极光</text></g>
|
|
125
|
-
<g transform="translate(40 300)"><circle r="8" fill="#06b6d4"/><text x="12" y="3" font-size="10" fill="#374151">发布范式</text></g>
|
|
126
|
-
<g transform="translate(400 300)"><circle r="8" fill="#f43f5e"/><text x="-52" y="-10" font-size="10" fill="#374151">图谱方案</text></g>
|
|
127
|
-
</svg>
|
|
128
|
-
<div style="display:flex;gap:10px;flex-wrap:wrap;font-size:11px;color:#6b7280;margin-top:6px">
|
|
129
|
-
<span><span class="dot" style="background:#3b82f6"></span>项目</span>
|
|
130
|
-
<span><span class="dot" style="background:#22c55e"></span>知识</span>
|
|
131
|
-
<span><span class="dot" style="background:#f43f5e"></span>内容</span>
|
|
132
|
-
<span><span class="dot" style="background:#06b6d4"></span>提示词</span>
|
|
133
|
-
<span style="opacity:.7">连线=[[链接]]或共享标签 · 点击节点阅读卡片</span>
|
|
134
|
-
</div>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
</body>
|
|
139
|
-
</html>
|
|
Binary file
|