快速开始(macOS / Linux,要 Python 3.11 和 Rust 工具链):
git clone https://github.com/lo2589/knowledge_database.git
git clone https://github.com/lo2589/FastNode.git Node # 底层节点库,必须放在同一层、目录名叫 Node
cd knowledge_database
./setup.sh # 建 .venv、从 ../Node 编 FastNode、装 PDF/Word 读取库
.venv/bin/python -m km --chat # 网页版:左边 Codex 对话,右边知识图装进 dsh 用:再跑 ./dsh-install.sh,重启 dsh(见第八节)。
跟 agent 聊一下午,真正学到的可能就三五句,混在几百句里,过几天就找不到了。这个工具做的事:
- 拆:对话、文档拆成一句一句;表格按一条字段数据拆(带行标识和列名);段落里连续的
A:… B:…/1) … 2) …记录逐条拆。公式、代码、图保持完整。 - 挑:在左边的对话里点句子(一句一张卡),或者按住 Ctrl 框一段话一次入库一批。
- 入库:留下的做成卡片,每张卡记得来自原文哪句,点一下跳回去。
- 检查:LLM 说的不一定对。每张卡标「对 / 改正过 / 存疑 / 错」,写上依据。
- 归纳:自己把卡挂进层级树,再连上别的关系(前提、例子、细化、矛盾、相关)。
- 看:严格层级树像 mermaid 那样从上往下画;一层排不下就折行,画布宽度有上限(卡片不会被甩到几千像素外);折叠起来的子树不占位置,兄弟节点自动靠拢;画面外的东西在四边留一条可点的「接字」;排版跟着面板宽度走,你看的位置、折叠、模式会被记住。
- 带走:一键导出 Markdown 大纲 / Mermaid 图 / JSON / PDF,或把某张卡连同它的下级写进对话框接着问。
- 不装 dsh 也能用:网页版
/chat左边是 Codex 对话、右边是同一张知识图——聊完一段,把有用的句子点进图里。
为什么结构要自己挂、不让 AI 自动挂:AI 画的结构图更整齐,但人记不住;自己挂的更乱,记得反而牢。挂的这一下就是学习。
(需求清单、功能对照这类工作笔记留在本地,不随仓库发布。)
┌──────────── dsh 对话 ────────────┬──────────────── 知识库 ────────────────┐
│ 你问:残差学习是什么?… │ 库里有什么 │ 知识图(严格层级树) │
│ 回答(每一句都能点): │ 25 张卡… │ ┌─────┐ │
│ ▍H(x) = F(x) + x ✓已入库 │ 最近入库 │ │ 根 │ │
│ ▍F(x) 是残差函数… ✓已入库 │ 目录(树) │ ┌──┴──┐ ┌──┐ │
│ ▍梯度里天然带着 +1… │ ● Block │ │Block│──│…│ 彩色线=关系 │
│ │ ● 残差… │ └─────┘ └──┘ │
└──────────────────────────────────┴─────────────────────────────────────────┘
- 左边是 dsh 自己的对话,回答用 dsh 原本的 Markdown 渲染(公式、代码、表格都不变),每一句都能点。
- 右边是知识库,一直开着:装成共享右栏(
dsh-plugin-rightbar)里的「知识图」标签,和别的插件并排住在一栏;没装那个右栏时退回自己贴右边的浮动面板(拖它的左边缘调宽度,宽度记在km-panel-w)。 - 白板页面自己的文件一改,页面 3 秒内自己重载(
/api/version轮询),不用手动刷新。
| 动作 | 结果 |
|---|---|
| 点一句 | 这句变成右边知识图里的一张卡,挂在「挂载点」下面;这句标上 ✓ 已入库 |
| 再点已入库的那句 | 不在右边找:直接跳到那张卡(高亮闪一下) |
| Ctrl / ⌘ + 拖一个框 | 框住的句子全部标蓝,下面出现小条:合成一张卡(这些句子合成一张卡,正文按顺序连起来,每句的出处都留着)/ 取消。已经入库的句子自动跳过,Esc 清空选择。多选就是一张卡——框出来的是一块知识,不是一堆卡 |
| 流式回答 | 回答还在生成时不显示可点句子,生成完自动变成可点 |
| 用户消息、追问消息 | 一样逐句可点,来源记的是你自己说的话(user:序号) |
入库时一并记下:会话 id、消息 id、句子在消息里的位置、这句话在第几轮、说话人,以及它回答的那个问题(卡片「原文」里显示的就是这个问题)。
| 控件 | 用法 |
|---|---|
☰ |
展开/收起左边「库里有什么」目录栏(收起后图面占满视野;开合状态按会话记住) |
☷ |
脑图 ⇄ Outliner(一条一条的列表视图)切换,状态记住 |
⌁ |
关系索引:六种关系的颜色、说明和计数 |
+ |
新建卡片:写标题(可留空,自动取正文首句)和正文(支持 Markdown / 公式),然后 挂到当前节点 或 挂到选中的卡下面(后者=在那张卡下写一条子信息,不动挂载点);⌘↵ 等于保存 |
| 搜索框 | 像 Word 的查找:回车 / ↓ 跳下一个,Shift+回车 / ↑ 跳上一个(也可以点旁边的 ↑ ↓),右边显示 3 / 12;被折叠起来的分支会自动打开。Esc 清空。搜的范围:卡片标题、卡片正文、每张卡的「原文」(正文被改掉、合并过,原句照样搜得到)、核对依据、以及导入资料里还没成卡的句子(命中资料句子时,镜头移到左边那一栏并高亮)。大小写、全角半角、前后空格都不影响( FASTNODE / fastnode 都能搜到 fastnode);打多个词就是要求同时命中。折叠起来的分支照样算命中:计数会写「共 3 处命中,其中 3 处在折叠的分支里」,回车跳过去时那条分支会自动展开并把卡闪给你看 |
自适应 |
重新自动排版并把整张图铺开:手动拖到空白处的卡片回到树上,视图缩放到刚好装下全部(缩不到可读尺寸时就停在 0.7 并定位到挂载点)。平时不用它——打开库时画面只对准活跃那一段(见下) |
导出 |
打开导出面板(见第五节) |
| 导入框 | 填路径(单个文件或整个文件夹)或直接粘贴文字,回车即导入;导入的资料出现在图的左边那一栏 |
| 表格 | 表就是表:点它 = 整张表做成一张卡;Ctrl 点某一行(Mac 上 Ctrl+点=右键,也可以 ⌘+点)= 只取那一行(带列名,单独成卡读得懂);⌥ 点某一格 = 只取那一个数据。已经取过的行/格会在表里标绿 |
右边 新知识挂到:X |
当前挂载点。聊天里点的句子都挂到它下面;点它一下,画面回到这一段 |
从左到右:
| 图标 | 作用 |
|---|---|
⌖ |
设为挂载点(聊天里点的句子挂到这张卡下面);当前挂载点会高亮 |
✎ |
改正文(等价于双击卡片):原地变成编辑框 + 实时预览公式,⌘↵ 保存、Esc 取消;原句永远留在「原文」里 |
↗ |
跳到原文(有原文时才出现):左边对话滚到那句话并高亮;跨会话会先打开那个会话;那句还没加载就点对话自己的轮次标记把它翻回来 |
◉n |
看原文:每条原句都带出处,每条后面还有 ↗ |
⌁n |
折叠/展开关系连线(角标是关系条数) |
+ |
新建关系:选关系类型 → 搜另一张卡 → 选中就连上(回车选第一条) |
↵ |
把这张卡和它所有下级按层级写成一段文字,落进左边对话框,你回车就发出去 |
T |
改标题(等价于双击标题) |
⊕ |
融合:点它进入融合态,再去目标卡上点 ↳ 并入这里 |
| (图片) | 见下面「图片」一节:粘、拖、或路径导入,都会进卡片正文 |
−n |
折叠/展开下级(角标是下级数量);等价于点卡片底边中间的小圆圈 |
× |
删除这张卡(点一次就删),它的下级按原顺序接到它的上级下面 |
| 手势 | 作用 |
|---|---|
| 点一下卡片 | 设为挂载点并选中它(选中的卡按 Delete / Backspace 删除,按 回车 写进对话框) |
| 点两下卡片 | 原地改正文(⌘↵ 保存 / Esc 取消) |
| 拖动卡片 | 丢到空白处=就地摆放(位置记下来,下次还在那儿);丢到另一张卡的左右边缘=排到它前面/后面;丢到另一张卡的中间=两次合并成一张;拖到画面边缘会自动滚动 |
| Ctrl / ⌘ 点第一张,再 Ctrl 点第二张 | 第二张挂到第一张下面,并从原来的上级断开(它的下级一起走)。Esc 取消 |
| Ctrl / ⌘ + 拖(空白画布或资料句子) | 拉出一个框,框里的一切合成一张卡:资料句子成为这张卡的正文(每句各自的出处都留着),框里的卡片并进这张卡(正文、原文、下级、关系都跟着走);只框到卡片时,就是原本的「多张并一张」(保留最上面那张)。根节点永远不参与合并 |
| 点一条关系线 | 弹出小面板:改关系类型(下拉)/ × 断开 / ⌕ 关闭 |
选中关系线后按 Delete |
断开这条关系 |
| 滚轮 | 缩放(1×–2.5×);触控板两指滑动=平移 |
| 拖动空白画布 | 平移 |
| 窗口/右栏变宽变窄 | 重新排版(卡片宽度、间距按面板宽度算,很窄时卡片会变窄,保证一层还放得下) |
- 一层排不下就折行:同一层的卡片按家族顺序排成行,一行到上限(4 张卡,或者 1.5 个面板宽,取大的那个)就换行;一家人整块搬下去,不拆两半;父母仍摆在自己孩子上方的中间。挪动之后每一行按比例收回它自己的宽度、回到同一条中轴——所以画布宽度有硬上限,不会出现"卡片被甩到几千像素外"。84d106 那份库全部展开:改之前 11 857 像素宽(60 张里 45 张在画面外),现在是 2 023 像素。
- 新卡落在离上级最近的那一格(上级正下方),不是一整排的最右边——所以越点越远这种事不会发生。
- 刚入图的卡:已经在眼前就只闪一下,画面一动不动;真在屏幕外,画面才挪刚好把它露出来的那一小步,绝不为了它把镜头甩到别处。
- 打开一份库 / 切会话时,画面对准活跃那一段(挂载点 + 它的子节点;挂载点没有子节点时带上它的上一级),不放大超过 1:1——其它分支不缩、也不抢镜头,就落在视野边上;
自适应才是"把整张图铺开给我看"。 - 顶栏
新知识挂到:X点一下,画面立刻回到这一段。 - 展开/折叠:不活跃的子树默认折起来(不折的话,一个兄弟的子树就能把兄弟之间撑开几千像素);切挂载点时,新挂载点这一条路径会自动展开;你自己展开过的分支不会被自动折回去,你自己折起来的也一直保持折着,都按会话记住。
- 被看到的卡片是完整的:取景框只收完整的卡——活跃节点和它的所有子节点永远整张在画面里(必要时可以缩到 0.32 那么小);画面边上露了半张的邻卡,能整张收进来就一起收,为此最多缩到 0.5。再往外就留在边上——清晰度优先。
- 折起来的卡不是空壳:卡片底部一条虚线,列出折起的前 3 条(点一条直接跳过去,顺路把那条分支展开),剩下写「…另 N 项(搜索能找到)」。
- 画面外有东西,四边会留「接字」(书里翻到页末那一行"接下页"):写明那边还有几张、最近的是哪张,点一下带过去。只算"大半在画面外"的卡——边上被切掉一条边的不算,所以接字不会压在卡片上;左右两条贴在画面下方两角。
- 一屏同时摆的卡片有上限(120 张):超过就把最占地方的分支自动折起来(活跃路径上的、以及你自己展开过的一律不动),提示只说一次。折起来的卡片照样列出下面几条,搜索也照样能找到。
- 手动摆放有墙:一张卡最多摆到离它在树上的位置 1200 像素的地方(层坐标),拖到头就停住,并说明一次原因。
- 摆在很远处的老坐标会被拉回来:打开库时,离树 1800 像素以外的坐标一律当作"摆丢了",直接回到树上,同时把这个决定告诉服务端——所以下次打开它不会又飘在那边。
- 画面不允许空着:任何时候画面被内容盖住的比例低于约 1/4、而外面还有卡没显示,就自动重新取景把最近的卡拉进来("地广行稀"是明确要避免的状态)。一个小库本来就填不满屏幕(那是没东西可填),不会来回晃。
- 换了排法之后,上一次记住的镜头不作数(视图版本 5 → 6):按会话记住的是折叠、模式、搜索词这些,画面位置按新的几何重算。
- 拖到卡片中间 /
⊕+↳/ Ctrl 拉框:正文拼接(留下的在前、并进来的在后)、原文两条都留、下级和所有关系一并转移,卡面直接显示合并后的正文。 - 删卡片:下级接到上级下面,顺序不变。
- 删资料:连用它做的卡片一起删(卡的原文已经不存在了,留着就是半截状态),提示里会说删了几张。
- 库里有什么:卡片数、关系数、带原文数、各核对状态的计数;
- 最近入库:最近 5 张,点一下就跳过去;
- 目录:整棵树的目录,点一下就跳过去;
- 导入的资料:点一下把那份资料移到眼前。
一条一张卡:▸/▾ 折叠下级、⌖ 设为挂载点、行里是标题 + #id · n 个下级 · 关系摘要、✎ 改正文、T 改标题、× 删除;双击行也是改正文。适合逐条读和重排。
一张图(截图、示意图、公式照片)可以这样进知识库:
| 怎么做 | 结果 |
|---|---|
| 在卡片编辑框里 ⌘V 粘贴 | 图插到光标处,⌘↵ 保存后卡片里就有它 |
| 在画布上 ⌘V 粘贴 | 直接做出一张新图卡(挂在当前挂载点下) |
| 把图片文件拖到某张卡上 | 追加到那张卡的正文末尾 |
| 把图片文件拖到空白画布 | 做出一张新图卡 |
| 导入框里填图片路径(.png/.jpg/.jpeg/.gif/.webp) | 做出一张新图卡 |
| Codex 对话页里点 图片(或直接粘图) | 图发给 Codex(codex exec -i),同时也出现在问题气泡里、可以点进知识图 |
- 图片存在
data/images/,按内容命名(同一张图存两份也只有一个文件),卡片正文里留一行;渲染、Markdown / PDF 导出、打印版式都带着这张图。 - 只收 png / jpeg / gif / webp(svg 会当脚本执行,不收),单张上限 12MB。
- 卡片里的图不会撑破卡片:宽度跟着卡片走,高度最多 60vh。
- 导入的资料在图的左边一栏,标题右上角
×点一次就删(连带用它做的卡片)。 - 资料里的句子可以直接点:点一下就是一张卡(挂到挂载点下),已用的句子标
✓ 已成卡;Ctrl 拖框把框住的几句合成一张卡。 - 表格按表显示(不再是"每格一行"),三层随你点:整张表 / 一行 / 一格(见上表)。对话里的表格也一样。
- 卡片「原文」里的句子点
↗:来自导入资料的,跳到那份资料里的那一句并高亮;来自 dsh 对话的,跳到左边对话的那一句。 - 长会话也跳得到:句子在第几轮入库时记在库里;那条消息还没被加载出来时,插件会点对话左侧的轮次标记让 dsh 把那一轮翻回页面,落地后再定位、再高亮(没有轮次信息的老卡片退回「逐个加载更早」)。
点开是一个小面板,选格式后先把全文显示在框里(文件名 / 行数 / 大小都写着),再按 下载文件 或 复制全部:
| 格式 | 内容 |
|---|---|
Markdown 大纲 |
整棵树变成文档:## 1 父卡 / ### 1.1 子卡 层级编号、正文、**原文**(资料):> 原句、相关 → 某卡、核对:对(依据…) |
Mermaid 图 |
graph TD:严格层级 + 非层级关系画成带标签虚线 + 核对过的卡带颜色(.mmd) |
Mermaid 图(含正文) |
同上,节点标签里带上卡片正文 |
JSON 数据 |
{nodes, structure: {root, entries: {level, parent, order, path}}, refs}——严格层级数据,knowledge_tree_viewer.html 能直接加载并通过校验 |
PDF |
按 A4 打印版式排好的一棵树(标题、正文、原文引用、关系、核对徽章、公式、代码),点了直接下载 .pdf(几秒钟) |
面板底部还有 打印 / 另存为 PDF:把同一个打印版式开在新标签页里,用系统打印对话框「另存为 PDF」。没有 Chrome / Chromium / Edge 时,点 PDF 会自动退到这条路,并说明原因。
服务端对应:GET /api/export?fmt=markdown|mermaid|mermaid-full|json、GET /api/pdf、GET /print(打印页)。
不经过 dsh,直接在浏览器里:
http://127.0.0.1:8795/chat
- 左边是 Codex 对话:回答用 dsh 之外的同一个渲染器画(公式、代码、表格都在),每一句都能点——点一下就变成右边知识图里的一张卡。
- 右边是同一张白板:排版、折叠、搜索、融合、导出、状态保持,全是上面第三、五节里那套,一点没变。中间那条分隔可以拖动,宽度会记住。
- 每个对话一份知识库:第一个问题会成为这份库的名字;「新对话」开新的库,「最近」列出以前的对话(每个对话记得自己的 Codex 线程,接着聊是同一个上下文)。
- Codex 是用你机器上已经装好的那个:
codex exec --json(桌面版自带,或用KM_CODEX=/path/to/codex指定),所以用的是你已经登录的账号和~/.codex/config.toml里的模型,不用再配一遍 key。 - 只读沙箱:默认
read-only,Codex 能读代码、能跑只读命令,但不会改你的文件;要放开就KM_CODEX_SANDBOX=workspace-write。 - **顶上的「目录」**就是 Codex 的工作目录(读写都限制在它的策略里),默认用 Codex 自己的默认值,改过会记住。
- 可以给 Codex 发图:composer 上点「图片」或直接粘一张,走
codex exec -i,图上还会出现在问题气泡里、可以点进知识图。同样支持:Ctrl/⌘ 拖框选多句 → 合成一张卡、已经入库的句子标✓ 已入库再点就跳回那张卡、卡片上的↗跳回左边那句原文并高亮、思考过程折起来、跑了哪些命令、token 用量。 - 首轮可能要等一两分钟:Codex CLI 会先试它的 WebSocket 通道,在这个网络上要重试五轮(约 113 秒)才回退到 HTTPS,然后几秒钟就答完。这是 Codex 自己的行为,不是页面卡住——页面会把「Reconnecting… / Falling back…」显示成一行灰色的状态提示,20 秒没动静还会写清楚在等什么。真没拿到回答时,那一轮下面有 重试这一轮。
- 对话存在
data/chats/<对话 id>.json(线程 id、目录、每一轮的问答),和卡片所在的库分开存——关掉浏览器再回来,两边都还在。
需要:Python 3.11、Rust 工具链(cargo,setup.sh 用 maturin 现编 FastNode)、FastNode 源码放在同级目录 Node:
git clone https://github.com/lo2589/FastNode.git ../Node # 只需一次;或用 FASTNODE_DIR=/别处/FastNode ./setup.sh
./setup.sh # Python 环境 + FastNode + 读 PDF/Word 的库
./dsh-install.sh # 装进 ~/.dsh/profiles/web(先备份 package.json);装完重启 dsh重启 dsh 之后,右栏会出现「知识图」标签;插件会自己用 .venv 的 Python 起知识库服务(默认 8795 端口,被占就往后找),并轮流探活、挂了自动拉起。同一个服务也提供上面第六节的网页版 /chat,不装 dsh 一样能开。
不想装 dsh 也能单独开一个库看(没有 dsh 对话,只能看图和导入):
.venv/bin/python -m km --open # 按会话分库(默认 data/repos)
.venv/bin/python -m km --db data/repos/某个库.db --open # 只看这一个文件python -m km 的参数:--port(默认 8790)、--repos(按会话分库的文件夹)、--db(只服务这一个库文件)、--open(启动后开白板)、--chat(启动后直接开网页版的 Codex 对话页)。
卸载:./dsh-install.sh remove。
别
pip install fastnode:PyPI 上那个同名包跟本项目无关。FastNode 只从 lo2589/FastNode 源码编。
PDF 直接用本机浏览器渲染。想指定用哪个:
KM_CHROME=/path/to/chrome。
没有默认库,也没有仓库切换。 白板永远只显示当前会话自己的那个库(data/repos/会话-<标题>-<尾号>.db):会话第一次打开时建好,以后回到这个会话还是它。别的会话的库不会出现在这里,也拿不到——请求不带会话 id 时服务端直接拒绝(这个白板只属于会话…)。
绑定关系写在 data/repos/repos.json 的 sessions 里(会话 id → 库文件)。想换机器或备份,整个 data/repos/ 拷走即可。
导入框里填路径(文件或文件夹),或者直接粘贴文字:
| 来源 | 说明 |
|---|---|
| 粘贴文字 / Markdown / 网页内容 | 直接粘进导入框 |
| 文件夹 / 代码仓库里的文档 | 填文件夹路径:递归收 .md .markdown .txt .tex .rst .html .htm .pdf .docx .ipynb,自动跳过 .git、node_modules、.venv、build、dist、target 等,最多 300 份、单份 20MB |
| 单个文件 | .md .txt .tex .rst .html .pdf .docx .ipynb |
| ChatGPT / Claude.ai 导出的 json | 填 .json 文件路径(按当前分支取,不把所有分支混在一起) |
| Claude Code 会话 | 填 ~/.claude/projects/**/*.jsonl 路径(GET /api/claude-sessions 能列出最近的) |
网页里 KaTeX / MathJax 渲染过的公式会还原成 TeX;Jupyter 的 markdown 单元原样、代码单元带语言标记;PDF 抽文字、docx 转 Markdown。
- 公式:
$…$、$$…$$、\(…\)、\[…\]、\begin{align}…、矩阵、```math、化学式\ce{H2O}; - 代码高亮、表格、引用、列表、图片、链接、mermaid 流程图;
$5 和 $10、$HOME/bin:$PATH这种不会被误当成公式;- 公式必须显示齐全:卡片会为宽公式让宽(到
MAX_W),还放不下就整条等比缩小(不低于 55%,再往下横向滚动并留滚动条);每次排版后页面自己复查一遍,把「没显示全」的公式报给服务端(data/repos/selfcheck.json),所以被切掉的公式不会悄悄混过去。
显示用的库(KaTeX、markdown-it、highlight.js、DOMPurify)都在 km/web/vendor/,断网也能用。
| 关系 | 读法 | 画法 |
|---|---|---|
| 属于 | A 属于 B(B 是 A 的上一级)。每张卡只有一个上级,组成层级树 | 灰色树线 |
| 前提是 | 先懂 B 才能懂 A | 橙 |
| 是…的例子 | A 是 B 的例子 | 绿 |
| 细化了 | A 把 B 讲细了 | 紫 |
| 矛盾 | A 和 B 说的对不上(比如 LLM 前后说法不一) | 红 |
| 相关 | 有关系但说不清是哪种 | 灰点线 |
| 正文提到 | 卡片正文里写了 [[B 的标题]],自动生成 |
灰虚线 |
正文里的链接:[[标题]]、[[标题|显示的字]]、[[#12]](按 id)。改名后别处的引用自动跟着改;先写了 [[还没有的卡]],等那张卡建好会自动连上;点一下链接就跳到那张卡。
- 每个库一个根节点,所有卡都在它下面,没有游离的卡;
- 每张卡有且只有一个上级,不许转圈;
- 同一级的顺序永远是
0, 1, 2…; - 删掉一张卡,它的下级按原来的顺序接到它的上级下面;
- 每一步都在服务端校验(
check_structure),不合格直接报错,不会画出坏图。
底层是 FastNode(克隆在同级目录 Node,SQLite 上的节点库):
source 导入的一份资料
unit 拆出来的一句 / 一块 unit -in_source-> source
card 一张知识卡片 card -from_unit-> unit (出处)
card -belongs_to-> card (层级,唯一)
card -<其他关系>-> card (六种语义关系)
card -mentions-> card (正文 [[ ]] 自动维护)
卡片额外的属性:check / check_note(核对)、pos(同级顺序)、x / y(手动摆放,null 表示回到自动排版)。来源(source)上记着 dsh_session / dsh_message / dsh_turn,原文跳转就靠它们。
用 FastNode 自己的命令行也能读:../Node/target/release/fastnode data/repos/你的库.db stats。
.venv/bin/python -m unittest discover -s tests -v # 76 项:拆句 / 导入 / 存储 / 层级 / 接口 / 五种导出 + PDF
node --test dsh-plugin-known-manage/test/host.test.js \
dsh-plugin-known-manage/test/pick.test.js # 8 项:读会话、去系统注入、公式原样;选句把「第几轮」带进库;一次入库多句(真起一个知识库服务跑)
cd ../deepseek-harness && node --import tsx/esm \
../known_manage/tests/slot-rightbar.mjs # 插件进右栏「知识图」标签(没右栏时退回浮动面板)tests/test_layout.py(7 项)是在白板自己身上量的,跑起来需要一个带 Chromium 的 Playwright;环境里没有就整文件跳过,不会假装通过:
| 检查 | 量的是什么 |
|---|---|
| 一层排不下就折行 | 一层 14 个兄弟、全部展开后画布宽度 ≤ 4200 像素(不折行的话是 4 536 像素一排) |
| 活跃节点和子节点完整 | 把取景对准活跃分支后,活跃那张卡和它的每一个子节点都整张在画面里 |
| 拖动停在墙上 | 往右下拖 3 200 像素,卡片位移不超过那面墙 |
| 远处的坐标被拉回 | 手动摆到 (8000, 6000) 的卡片,下次打开回到树上,服务端坐标被清空 |
| 折叠卡交代下面有什么 | 折叠的卡片上有汇总条目,点一条那张卡就出来 |
| 画面外留接字 | 把画面拖开后四边出现接字(带张数),点一下那张卡进画面 |
| 一屏有上限 | 400 张卡的树里,同时画出来的卡片不超过 140 张 |
界面上的关键动作都在真实 dsh 里点过(Playwright 驱动,含截图):逐句入库、框选批量入库、原文翻页跳转、导出面板(含拿到 .pdf)、打印页、窄栏排版与状态保持、折叠汇总条跳转、接字带人过去。
调试用的落盘证据:data/requests.log(每个请求一行,KM_REQLOG=off 可关)、data/repos/diag.json(页面自报的右栏几何和 JS 报错)、data/repos/selfcheck.json(没显示全的公式)。
km/split.py 拆句子 / 块(标点、块、表格、编号记录;公式代码图不拆)
km/importers.py 各种格式 → Markdown(md/txt/tex/rst/html/pdf/docx/ipynb/json/jsonl/文件夹)
km/store.py 卡片、关系、严格层级、核对、合并、Markdown / Mermaid / JSON 导出(FastNode)
km/repos.py 会话各自的库(一个会话一个库,没有默认库)
km/chat.py 把 codex exec --json 驱动成网页里的对话(事件流 → 页面能画的东西)
km/chats.py 每个 Codex 对话一条记录(线程 id、目录、每轮问答)
data/images/ 粘进卡片的图片(按内容命名;data/ 不进仓库)
km/pdf.py 用本机浏览器把打印页印成 PDF
km/server.py 本机 HTTP 接口 + 静态页面
km/__main__.py python -m km --port/--repos/--db/--open
km/web/index.html 白板页面
km/web/graph.js 白板交互(折行排版、折叠汇总、接字、拖拽、框选、编辑、关系、搜索、导出面板、状态保持)
km/web/render.js Markdown + KaTeX + 代码高亮渲染
km/web/graph.css 白板样式
km/web/print.* 打印版式 / PDF 用的那一页(print.html + print.js + print.css)
km/web/chat.* 网页版:左边 Codex 对话 + 右边知识图(chat.html + chat.js + chat.css)
km/web/vendor/ KaTeX / markdown-it / highlight.js / DOMPurify(本地,断网可用)
dsh-plugin-known-manage/
src/host.js dsh 宿主半边:起知识库服务、读会话文件、/plugins/known-manage/*
src/client.js dsh 页面半边:右栏「知识图」标签、逐句可点的回答、框选入库、原文跳转、写进对话框
cordis.patch.yml 插件挂载点
test/ node --test 用的测试
tests/ Python 单测(test_km.py)+ 白板几何检查(test_layout.py,要 Playwright)+ 右栏 slot 测试
GET /api/canvas 一次读全:卡片、层级、原文、关系、挂载点、资料列表(白板就用这一个)
GET /api/structure 严格层级结构 + refs(导出 JSON 用)
GET /api/tree /api/board /api/graph 树的骨架 / 画布数据 / 极简图
GET /api/meta 关系与核对状态的名字、统计、库名
GET /api/export?fmt=… markdown | mermaid | mermaid-full | json
GET /api/mermaid 只取 Mermaid 图(严格的层级 + 语义关系),不带别的东西
GET /api/pdf 浏览器渲染的 A4 PDF(字节流,带下载文件名)
GET /print 打印版式页面(/api/pdf 和「另存为 PDF」用的就是它)
GET /chat 网页版:Codex 对话 + 知识图(km/web/chat.*)
POST /api/images 收一张图(粘/拖来的 base64,或本机路径)→ 存成 /images/xxx.png
GET /images/<名字> 取回那张图(名字限定在图片目录里)
POST /api/chat 跑一轮 Codex,把事件流一行一个 JSON 地推回来(NDJSON)
GET /api/chat/<id> 某个对话的线程、目录和每一轮
GET /api/chats 最近对话(网页版的「最近」)
POST /api/pick 一句话入库(origin:kind=dsh|codex|claude + session/message/turn/…)
POST /api/pick-many 一批句子入库(merge=true 时合成一张)
GET /api/picked?message=… 这条消息里哪些句子已经是卡片
GET /api/target POST /api/target 读 / 设挂载点
GET /api/cards?q=… POST /api/cards 搜卡 / 新建卡
GET /api/cards/<id> PATCH DELETE 读 / 改标题正文 / 删(下级接到上级下)
POST /api/cards/merge 融合两张卡(正文、原文、关系都保留)
POST /api/cards/<id>/move 改上级 / 调同级顺序
POST /api/cards/<id>/place 手动摆放在 x,y(null 就是回到自动排版)
POST /api/cards/<id>/check 核对状态 + 依据
POST /api/links PATCH DELETE 连 / 改关系类型 / 断开
GET /api/sources POST 资料列表 / 导入(text|path|folder|claude_session|file_b64|messages)
PATCH /api/sources/<id> DELETE 改标题 / 删(连它做的卡一起删)
GET /api/sources/<id>/units 这份资料的句子
PATCH /api/units/<id> POST /api/units/merge 改句子 / 合并相邻句
POST /api/split 只拆句、不碰库(对话页面用它把回答切开)
GET /api/claude-sessions 最近的本机 Claude Code 会话
GET /api/version 页面自己的版本(改了文件就自动重载)
POST /api/selfcheck /api/diag 公式自查报告 / 页面自报几何与报错(只落盘,不影响画图)
dsh 插件自己的路由(由宿主半边提供):/plugins/known-manage/status | split | pick | pick-many | picked | session。
