concept-atlas-dense-explain 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/bin/cli.mjs +197 -80
- package/package.json +1 -1
- package/packaged-skill/SKILL.md +8 -7
- package/packaged-skill/references/scroll-guide.mdx +14 -0
- package/template/references/scroll-guide.mdx +14 -0
- package/template/src/app/App.jsx +77 -41
- package/template/src/app/search.js +14 -5
- package/template/src/app/use-appearance.js +29 -15
- package/template/src/components/MDXComponents.jsx +1029 -196
- package/template/src/components/SkinPicker.jsx +14 -6
- package/template/src/model/concept-schema.js +27 -12
- package/template/src/model/node-kinds.js +9 -9
- package/template/src/model/normalize-content.js +8 -8
- package/template/src/model/relation-types.js +37 -12
- package/template/src/model/validate-content.js +282 -59
- package/template/src/scroll-main.jsx +8 -1
- package/template/src/styles/core.css +191 -0
- package/template/src/styles/packs/elastic.css +37 -0
- package/template/src/styles/packs/manuscript.css +67 -0
- package/template/src/styles/packs/shadcn.css +33 -0
- package/template/src/views/NodeExplorer.jsx +364 -293
- package/template/src/views/RelationGraph.jsx +191 -118
- package/template/vite.config.js +32 -20
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { Check, Palette } from 'lucide-react';
|
|
3
3
|
import { SKINS, COMPONENT_STYLES } from '../model/skins.js';
|
|
4
4
|
|
|
@@ -24,10 +24,10 @@ export function SkinPicker({ skin, style, onSkinChange, onStyleChange }) {
|
|
|
24
24
|
|
|
25
25
|
useEffect(() => {
|
|
26
26
|
if (!open) return undefined;
|
|
27
|
-
const handlePointer =
|
|
27
|
+
const handlePointer = event => {
|
|
28
28
|
if (!rootRef.current?.contains(event.target)) setOpen(false);
|
|
29
29
|
};
|
|
30
|
-
const handleKey =
|
|
30
|
+
const handleKey = event => {
|
|
31
31
|
if (event.key !== 'Escape') return;
|
|
32
32
|
setOpen(false);
|
|
33
33
|
// Stop Escape from also reaching the window-level handler in App.jsx,
|
|
@@ -63,11 +63,16 @@ export function SkinPicker({ skin, style, onSkinChange, onStyleChange }) {
|
|
|
63
63
|
key={item.id}
|
|
64
64
|
selected={item.id === skin}
|
|
65
65
|
label={`配色 ${item.label}`}
|
|
66
|
-
onClick={() => {
|
|
66
|
+
onClick={() => {
|
|
67
|
+
onSkinChange(item.id);
|
|
68
|
+
setOpen(false);
|
|
69
|
+
}}
|
|
67
70
|
>
|
|
68
71
|
<span
|
|
69
72
|
className="skin-swatch"
|
|
70
|
-
style={{
|
|
73
|
+
style={{
|
|
74
|
+
background: `linear-gradient(135deg, ${item.swatch.dark} 0 42%, ${item.swatch.accent} 42% 58%, ${item.swatch.light} 58% 100%)`
|
|
75
|
+
}}
|
|
71
76
|
aria-hidden="true"
|
|
72
77
|
/>
|
|
73
78
|
<span className="skin-option-label">{item.label}</span>
|
|
@@ -79,7 +84,10 @@ export function SkinPicker({ skin, style, onSkinChange, onStyleChange }) {
|
|
|
79
84
|
key={item.id}
|
|
80
85
|
selected={item.id === style}
|
|
81
86
|
label={`组件风格 ${item.label}`}
|
|
82
|
-
onClick={() => {
|
|
87
|
+
onClick={() => {
|
|
88
|
+
onStyleChange(item.id);
|
|
89
|
+
setOpen(false);
|
|
90
|
+
}}
|
|
83
91
|
>
|
|
84
92
|
<span className={`style-glyph style-glyph-${item.id}`} aria-hidden="true" />
|
|
85
93
|
<span className="skin-option-label">{item.label}</span>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { RELATION_TYPES, LEVEL_DEFS } from './relation-types.js';
|
|
3
2
|
import { NODE_KIND_SET, normalizeKind } from './node-kinds.js';
|
|
4
3
|
|
|
@@ -14,7 +13,7 @@ export function createConceptGraphState() {
|
|
|
14
13
|
rootId: ''
|
|
15
14
|
},
|
|
16
15
|
nodes: new Map(), // id -> NodeObject
|
|
17
|
-
relations: []
|
|
16
|
+
relations: [] // Array<{ from, to, type, label, description }>
|
|
18
17
|
};
|
|
19
18
|
}
|
|
20
19
|
|
|
@@ -30,7 +29,7 @@ export function normalizeNode(raw) {
|
|
|
30
29
|
kind: normalizeKind(raw.kind),
|
|
31
30
|
parent: raw.parent || null,
|
|
32
31
|
children: Array.isArray(raw.children) ? raw.children : [],
|
|
33
|
-
|
|
32
|
+
|
|
34
33
|
// Semantic Content Sections
|
|
35
34
|
summary: raw.summary || '',
|
|
36
35
|
overview: raw.overview || null,
|
|
@@ -54,7 +53,7 @@ export function normalizeNode(raw) {
|
|
|
54
53
|
evidence: Array.isArray(raw.evidence) ? raw.evidence : [],
|
|
55
54
|
invariants: Array.isArray(raw.invariants) ? raw.invariants : [],
|
|
56
55
|
failureModes: Array.isArray(raw.failureModes) ? raw.failureModes : [],
|
|
57
|
-
tradeoffs: Array.isArray(raw.tradeoffs) ? raw.tradeoffs : []
|
|
56
|
+
tradeoffs: Array.isArray(raw.tradeoffs) ? raw.tradeoffs : []
|
|
58
57
|
};
|
|
59
58
|
}
|
|
60
59
|
|
|
@@ -73,7 +72,8 @@ export function buildGraphModel(rawGraph) {
|
|
|
73
72
|
if (n.kind && !NODE_KIND_SET.has(n.kind)) {
|
|
74
73
|
diagnostics.push({ level: 'warning', code: 'UNKNOWN_KIND', nodeId: node.id, kind: n.kind });
|
|
75
74
|
}
|
|
76
|
-
if (nodes.has(node.id))
|
|
75
|
+
if (nodes.has(node.id))
|
|
76
|
+
diagnostics.push({ level: 'error', code: 'DUPLICATE_NODE_ID', nodeId: node.id });
|
|
77
77
|
nodes.set(node.id, node);
|
|
78
78
|
});
|
|
79
79
|
|
|
@@ -95,11 +95,28 @@ export function buildGraphModel(rawGraph) {
|
|
|
95
95
|
if (!childNode.parent) {
|
|
96
96
|
childNode.parent = node.id;
|
|
97
97
|
}
|
|
98
|
-
}
|
|
99
|
-
|
|
98
|
+
} else
|
|
99
|
+
diagnostics.push({
|
|
100
|
+
level: 'warning',
|
|
101
|
+
code: 'MISSING_CHILD',
|
|
102
|
+
nodeId: node.id,
|
|
103
|
+
targetId: childId
|
|
104
|
+
});
|
|
100
105
|
});
|
|
101
|
-
if (node.parent && !nodes.has(node.parent))
|
|
102
|
-
|
|
106
|
+
if (node.parent && !nodes.has(node.parent))
|
|
107
|
+
diagnostics.push({
|
|
108
|
+
level: 'warning',
|
|
109
|
+
code: 'ORPHAN_NODE',
|
|
110
|
+
nodeId: node.id,
|
|
111
|
+
targetId: node.parent
|
|
112
|
+
});
|
|
113
|
+
if (!LEVEL_DEFS[node.level])
|
|
114
|
+
diagnostics.push({
|
|
115
|
+
level: 'warning',
|
|
116
|
+
code: 'UNKNOWN_LEVEL',
|
|
117
|
+
nodeId: node.id,
|
|
118
|
+
nodeLevel: node.level
|
|
119
|
+
});
|
|
103
120
|
});
|
|
104
121
|
|
|
105
122
|
// Filter valid relations
|
|
@@ -169,9 +186,7 @@ export function getSiblingNodes(nodes, nodeId) {
|
|
|
169
186
|
}
|
|
170
187
|
const parent = nodes.get(current.parent);
|
|
171
188
|
if (!parent) return [];
|
|
172
|
-
return parent.children
|
|
173
|
-
.filter(id => id !== nodeId && nodes.has(id))
|
|
174
|
-
.map(id => nodes.get(id));
|
|
189
|
+
return parent.children.filter(id => id !== nodeId && nodes.has(id)).map(id => nodes.get(id));
|
|
175
190
|
}
|
|
176
191
|
|
|
177
192
|
/**
|
|
@@ -15,43 +15,43 @@ export const NODE_KINDS = {
|
|
|
15
15
|
system: {
|
|
16
16
|
label: '系统',
|
|
17
17
|
description: '整体系统、产品或问题域的全貌',
|
|
18
|
-
tone: 'var(--kind-system)'
|
|
18
|
+
tone: 'var(--kind-system)'
|
|
19
19
|
},
|
|
20
20
|
stage: {
|
|
21
21
|
label: '阶段',
|
|
22
22
|
description: '生命周期阶段、处理步骤或子系统',
|
|
23
|
-
tone: 'var(--kind-stage)'
|
|
23
|
+
tone: 'var(--kind-stage)'
|
|
24
24
|
},
|
|
25
25
|
mechanism: {
|
|
26
26
|
label: '机制',
|
|
27
27
|
description: '可解释、可验证的工作机制',
|
|
28
|
-
tone: 'var(--kind-mechanism)'
|
|
28
|
+
tone: 'var(--kind-mechanism)'
|
|
29
29
|
},
|
|
30
30
|
artifact: {
|
|
31
31
|
label: '产物',
|
|
32
32
|
description: '阶段产出的实体、数据或文件',
|
|
33
|
-
tone: 'var(--kind-artifact)'
|
|
33
|
+
tone: 'var(--kind-artifact)'
|
|
34
34
|
},
|
|
35
35
|
failure: {
|
|
36
36
|
label: '故障',
|
|
37
37
|
description: '失败模式、异常与边界情况',
|
|
38
|
-
tone: 'var(--kind-failure)'
|
|
38
|
+
tone: 'var(--kind-failure)'
|
|
39
39
|
},
|
|
40
40
|
tool: {
|
|
41
41
|
label: '工具',
|
|
42
42
|
description: '使用的工具、库或外部依赖',
|
|
43
|
-
tone: 'var(--kind-tool)'
|
|
43
|
+
tone: 'var(--kind-tool)'
|
|
44
44
|
},
|
|
45
45
|
boundary: {
|
|
46
46
|
label: '边界',
|
|
47
47
|
description: '约束、前提、限制与非目标',
|
|
48
|
-
tone: 'var(--kind-boundary)'
|
|
48
|
+
tone: 'var(--kind-boundary)'
|
|
49
49
|
},
|
|
50
50
|
decision: {
|
|
51
51
|
label: '决策',
|
|
52
52
|
description: '设计取舍与选择点',
|
|
53
|
-
tone: 'var(--kind-decision)'
|
|
54
|
-
}
|
|
53
|
+
tone: 'var(--kind-decision)'
|
|
54
|
+
}
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
export const NODE_KIND_NAMES = Object.keys(NODE_KINDS);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Children, isValidElement } from 'react';
|
|
2
2
|
import { KNOWN_COMPONENT_SET } from './validate-content.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -16,7 +16,7 @@ export function extractConceptData(explainPageElement) {
|
|
|
16
16
|
summary: explainPageElement.props.summary || '',
|
|
17
17
|
rootId: null,
|
|
18
18
|
layout: explainPageElement.props.layout || 'editorial',
|
|
19
|
-
density: explainPageElement.props.density || 'reading'
|
|
19
|
+
density: explainPageElement.props.density || 'reading'
|
|
20
20
|
},
|
|
21
21
|
nodes: [],
|
|
22
22
|
relations: []
|
|
@@ -124,7 +124,7 @@ function parseConceptNode(nodeElement) {
|
|
|
124
124
|
evidence: [],
|
|
125
125
|
invariants: [],
|
|
126
126
|
failureModes: [],
|
|
127
|
-
tradeoffs: []
|
|
127
|
+
tradeoffs: []
|
|
128
128
|
};
|
|
129
129
|
|
|
130
130
|
Children.forEach(props.children, child => {
|
|
@@ -207,7 +207,7 @@ function parseConceptNode(nodeElement) {
|
|
|
207
207
|
// original element in customSections so existing rendering is unchanged.
|
|
208
208
|
case 'LearningObjectives':
|
|
209
209
|
node.learningObjectives.push(
|
|
210
|
-
...(Array.isArray(child.props.items) ? child.props.items.filter(Boolean) : [])
|
|
210
|
+
...(Array.isArray(child.props.items) ? child.props.items.filter(Boolean) : [])
|
|
211
211
|
);
|
|
212
212
|
node.customSections.push(child);
|
|
213
213
|
break;
|
|
@@ -219,14 +219,14 @@ function parseConceptNode(nodeElement) {
|
|
|
219
219
|
node.evidence.push({
|
|
220
220
|
command: child.props.command || '',
|
|
221
221
|
observes: child.props.observes || '',
|
|
222
|
-
content: child.props.children
|
|
222
|
+
content: child.props.children
|
|
223
223
|
});
|
|
224
224
|
node.customSections.push(child);
|
|
225
225
|
break;
|
|
226
226
|
case 'Invariant':
|
|
227
227
|
node.invariants.push({
|
|
228
228
|
title: child.props.title || '不变量',
|
|
229
|
-
content: child.props.children
|
|
229
|
+
content: child.props.children
|
|
230
230
|
});
|
|
231
231
|
node.customSections.push(child);
|
|
232
232
|
break;
|
|
@@ -236,14 +236,14 @@ function parseConceptNode(nodeElement) {
|
|
|
236
236
|
cause: child.props.cause || '',
|
|
237
237
|
evidence: child.props.evidence || '',
|
|
238
238
|
remedy: child.props.remedy || '',
|
|
239
|
-
content: child.props.children
|
|
239
|
+
content: child.props.children
|
|
240
240
|
});
|
|
241
241
|
node.customSections.push(child);
|
|
242
242
|
break;
|
|
243
243
|
case 'Tradeoff':
|
|
244
244
|
node.tradeoffs.push({
|
|
245
245
|
title: child.props.title || '工程权衡',
|
|
246
|
-
options: Array.isArray(child.props.options) ? child.props.options : []
|
|
246
|
+
options: Array.isArray(child.props.options) ? child.props.options : []
|
|
247
247
|
});
|
|
248
248
|
node.customSections.push(child);
|
|
249
249
|
break;
|
|
@@ -7,7 +7,7 @@ export const RELATION_TYPES = {
|
|
|
7
7
|
hasArrow: true,
|
|
8
8
|
isTree: true
|
|
9
9
|
},
|
|
10
|
-
|
|
10
|
+
prerequisite: {
|
|
11
11
|
label: '前置知识',
|
|
12
12
|
description: '理解当前概念所需的前置基础',
|
|
13
13
|
color: 'var(--rel-prerequisite)',
|
|
@@ -15,7 +15,7 @@ export const RELATION_TYPES = {
|
|
|
15
15
|
hasArrow: true,
|
|
16
16
|
isTree: false
|
|
17
17
|
},
|
|
18
|
-
|
|
18
|
+
causes: {
|
|
19
19
|
label: '因果推动',
|
|
20
20
|
description: '引起或触发后续状态变化',
|
|
21
21
|
color: 'var(--rel-causes)',
|
|
@@ -23,7 +23,7 @@ export const RELATION_TYPES = {
|
|
|
23
23
|
hasArrow: true,
|
|
24
24
|
isTree: false
|
|
25
25
|
},
|
|
26
|
-
|
|
26
|
+
produces: {
|
|
27
27
|
label: '产出生成',
|
|
28
28
|
description: '阶段处理后产出的产物或实体',
|
|
29
29
|
color: 'var(--rel-produces)',
|
|
@@ -31,7 +31,7 @@ export const RELATION_TYPES = {
|
|
|
31
31
|
hasArrow: true,
|
|
32
32
|
isTree: false
|
|
33
33
|
},
|
|
34
|
-
|
|
34
|
+
uses: {
|
|
35
35
|
label: '消费使用',
|
|
36
36
|
description: '调用或引用其他概念与数据',
|
|
37
37
|
color: 'var(--rel-uses)',
|
|
@@ -39,7 +39,7 @@ export const RELATION_TYPES = {
|
|
|
39
39
|
hasArrow: true,
|
|
40
40
|
isTree: false
|
|
41
41
|
},
|
|
42
|
-
|
|
42
|
+
implements: {
|
|
43
43
|
label: '实现关系',
|
|
44
44
|
description: '具体机制或代码实现特定抽象',
|
|
45
45
|
color: 'var(--rel-implements)',
|
|
@@ -47,7 +47,7 @@ export const RELATION_TYPES = {
|
|
|
47
47
|
hasArrow: true,
|
|
48
48
|
isTree: false
|
|
49
49
|
},
|
|
50
|
-
|
|
50
|
+
contrasts: {
|
|
51
51
|
label: '对比关系',
|
|
52
52
|
description: '概念间的异同对照或对立维度',
|
|
53
53
|
color: 'var(--rel-contrasts)',
|
|
@@ -71,7 +71,7 @@ export const RELATION_TYPES = {
|
|
|
71
71
|
hasArrow: true,
|
|
72
72
|
isTree: false
|
|
73
73
|
},
|
|
74
|
-
|
|
74
|
+
precedes: {
|
|
75
75
|
label: '时序先后',
|
|
76
76
|
description: '时间或处理流水线的前后相继',
|
|
77
77
|
color: 'var(--rel-precedes)',
|
|
@@ -82,9 +82,34 @@ export const RELATION_TYPES = {
|
|
|
82
82
|
};
|
|
83
83
|
|
|
84
84
|
export const LEVEL_DEFS = {
|
|
85
|
-
L0: {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
L0: {
|
|
86
|
+
name: '全局概览',
|
|
87
|
+
tag: 'L0 · 全局概览',
|
|
88
|
+
desc: '系统全貌与全局定位',
|
|
89
|
+
color: 'var(--level-l0)'
|
|
90
|
+
},
|
|
91
|
+
L1: {
|
|
92
|
+
name: '主要阶段 / 子系统',
|
|
93
|
+
tag: 'L1 · 子系统',
|
|
94
|
+
desc: '主要生命周期阶段或子系统划分',
|
|
95
|
+
color: 'var(--level-l1)'
|
|
96
|
+
},
|
|
97
|
+
L2: {
|
|
98
|
+
name: '局部机制',
|
|
99
|
+
tag: 'L2 · 局部机制',
|
|
100
|
+
desc: '具体工作机制与逻辑流动',
|
|
101
|
+
color: 'var(--level-l2)'
|
|
102
|
+
},
|
|
103
|
+
L3: {
|
|
104
|
+
name: '实现细节',
|
|
105
|
+
tag: 'L3 · 实现细节',
|
|
106
|
+
desc: '算法、数据结构或代码实现',
|
|
107
|
+
color: 'var(--level-l3)'
|
|
108
|
+
},
|
|
109
|
+
L4: {
|
|
110
|
+
name: '边界与反例',
|
|
111
|
+
tag: 'L4 · 边界反例',
|
|
112
|
+
desc: '边界情况、异常分支与典型反例',
|
|
113
|
+
color: 'var(--level-l4)'
|
|
114
|
+
}
|
|
90
115
|
};
|