Skip to content

Commit 03d7111

Browse files
committed
feat(knowledge): v0.13.8 画布前端辐射布局与画布视图
- 依赖:@xyflow/react@12.11.3(React Flow 节点式无限画布) - layoutRadial:BFS 辐射布局纯函数(8 golden 单测)——核心/首根圆心、 ring+200px、父角度扇区 spread=60°/(子数+1)、碰撞外推、golden angle 兜底 - canvasElements:实体→RF 元素纯转换(q/c/m 前缀 key、parent_id→smoothstep 边、 links→概念/模型徽标 + 引用计数同树视图口径) - 三自定义节点:缩放分级(>0.7 全文 / 0.4-0.7 仅标题 / <0.4 缩略卡) - KnowledgeCanvasView:拖拽/视口防抖保存、切回 setViewport 恢复、首次缺位 批量初始化、自动排列覆盖;KnowledgePage「画布」标签 + 树视图浮钮双入口
1 parent a92eaad commit 03d7111

16 files changed

Lines changed: 1965 additions & 36 deletions

‎app/package-lock.json‎

Lines changed: 243 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎app/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"@tauri-apps/api": "^2",
1515
"@tauri-apps/plugin-dialog": "^2.7.2",
1616
"@types/katex": "^0.16.8",
17+
"@xyflow/react": "^12.11.3",
1718
"katex": "^0.18.4",
1819
"react": "^19.1.0",
1920
"react-dom": "^19.1.0",
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* CanvasNodeConcept — 画布概念节点(v0.13.8 §4.2)。
3+
*
4+
* @ai-context: 浮动参照(§五 兼容)——概念节点在画布只做展示与选中联动,
5+
* 新建/编辑概念仍走「概念」标签页(画布无编辑入口,§九 不做清单)。
6+
* 展示:概念名 + 本质摘要 1 行 + 状态指示(核心/关注/已归档)。
7+
* @ai-context: 缩放分级(§七)——与问题节点同口径;概念无父关系,无 Handle
8+
* (连线只反映问题树 parent_id,§二.4)。
9+
*/
10+
import { memo } from "react";
11+
import { useStore, type Node, type NodeProps } from "@xyflow/react";
12+
import type { CanvasNodeData } from "../utils/canvasElements";
13+
import { CANVAS_BBOX } from "../utils/layoutRadial";
14+
15+
export type ConceptRfNode = Node<CanvasNodeData, "concept">;
16+
17+
export default memo(function CanvasNodeConcept({ data, selected }: NodeProps<ConceptRfNode>) {
18+
const zoom = useStore((s) => s.transform[2]);
19+
const full = zoom > 0.7;
20+
const titleOnly = zoom >= 0.4;
21+
22+
return (
23+
<div
24+
data-testid={`canvas-node-c-${data.entityId}`}
25+
style={{
26+
width: CANVAS_BBOX.concept.w,
27+
borderRadius: 8,
28+
border: selected ? "2px solid #14b8a6" : "1px solid #e5e7eb",
29+
background: selected ? "#f0fdfa" : "#ffffff",
30+
boxShadow: "0 1px 4px rgba(0,0,0,0.08)",
31+
padding: 8,
32+
}}
33+
>
34+
{full ? (
35+
<>
36+
<div style={{ display: "flex", gap: 6, alignItems: "center", fontSize: 13, fontWeight: 500, color: "#374151" }}>
37+
<span>🧬</span>
38+
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{data.title}</span>
39+
</div>
40+
{data.subtitle && (
41+
<div style={{ fontSize: 11, color: "#6b7280", marginTop: 4, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
42+
本质:{data.subtitle}
43+
</div>
44+
)}
45+
{data.statusText && (
46+
<div style={{ fontSize: 10, color: data.statusColor ?? "#6b7280", marginTop: 4 }}>
47+
● {data.statusText}
48+
</div>
49+
)}
50+
</>
51+
) : titleOnly ? (
52+
<div style={{ display: "flex", gap: 6, alignItems: "center", fontSize: 12, color: "#374151" }}>
53+
<span>🧬</span>
54+
<span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{data.title}</span>
55+
</div>
56+
) : (
57+
<div style={{ display: "flex", gap: 4, alignItems: "center", fontSize: 11, color: "#4b5563" }}>
58+
<span>🧬</span>
59+
<span>{data.title.slice(0, 4)}{data.title.length > 4 ? "…" : ""}</span>
60+
</div>
61+
)}
62+
</div>
63+
);
64+
});

0 commit comments

Comments
 (0)