concept-atlas-dense-explain 0.1.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.
@@ -0,0 +1,238 @@
1
+ import React, { Children, isValidElement } from 'react';
2
+
3
+ /**
4
+ * Extracts structured node and relation data from MDX / React component elements
5
+ */
6
+ export function extractConceptData(explainPageElement) {
7
+ // MDX exports a component rather than an already-expanded element.
8
+ // Resolve that wrapper before reading page metadata or its children.
9
+ explainPageElement = unwrapComponent(explainPageElement);
10
+
11
+ const result = {
12
+ meta: {
13
+ id: explainPageElement.props.id || 'explain-page',
14
+ title: explainPageElement.props.title || '',
15
+ summary: explainPageElement.props.summary || '',
16
+ rootId: null,
17
+ layout: explainPageElement.props.layout || 'editorial',
18
+ density: explainPageElement.props.density || 'reading',
19
+ },
20
+ nodes: [],
21
+ relations: []
22
+ };
23
+
24
+ function traverse(child) {
25
+ if (!child || !isValidElement(child)) return;
26
+
27
+ const componentName = getComponentName(child);
28
+
29
+ // MDX compiles to a component function. The element passed from main.jsx
30
+ // has no children until that function is evaluated, so unwrap non-semantic
31
+ // component wrappers before walking the semantic tree.
32
+ if (typeof child.type === 'function' && !isSemanticComponent(componentName)) {
33
+ traverse(child.type(child.props));
34
+ return;
35
+ }
36
+
37
+ if (componentName === 'ConceptGraph') {
38
+ if (child.props.root) {
39
+ result.meta.rootId = child.props.root;
40
+ }
41
+ Children.forEach(child.props.children, traverse);
42
+ return;
43
+ }
44
+
45
+ if (componentName === 'ConceptNode') {
46
+ const nodeData = parseConceptNode(child);
47
+ result.nodes.push(nodeData);
48
+ return;
49
+ }
50
+
51
+ if (componentName === 'Relation') {
52
+ result.relations.push({
53
+ from: child.props.from,
54
+ to: child.props.to,
55
+ type: child.props.type || 'depends-on',
56
+ label: child.props.label,
57
+ description: child.props.description || child.props.children || ''
58
+ });
59
+ return;
60
+ }
61
+
62
+ if (child.props && child.props.children) {
63
+ Children.forEach(child.props.children, traverse);
64
+ }
65
+ }
66
+
67
+ if (explainPageElement.props && explainPageElement.props.children) {
68
+ Children.forEach(explainPageElement.props.children, traverse);
69
+ }
70
+
71
+ return result;
72
+ }
73
+
74
+ function unwrapComponent(element) {
75
+ let current = element;
76
+ while (
77
+ current &&
78
+ isValidElement(current) &&
79
+ typeof current.type === 'function' &&
80
+ !isSemanticComponent(getComponentName(current))
81
+ ) {
82
+ current = current.type(current.props);
83
+ }
84
+ return current;
85
+ }
86
+
87
+ const SEMANTIC_COMPONENTS = new Set([
88
+ 'ExplainPage',
89
+ 'ConceptGraph',
90
+ 'ConceptNode',
91
+ 'ConceptRef',
92
+ 'Children',
93
+ 'Relation',
94
+ 'Overview',
95
+ 'Definition',
96
+ 'Mechanism',
97
+ 'Input',
98
+ 'Output',
99
+ 'Prerequisite',
100
+ 'Implementation',
101
+ 'Example',
102
+ 'Counterexample',
103
+ 'Boundary',
104
+ 'Glossary',
105
+ 'Mermaid',
106
+ 'RelationMap',
107
+ 'Insight',
108
+ 'NoteGrid',
109
+ 'Stack',
110
+ 'Grid',
111
+ 'Split',
112
+ 'Tabs',
113
+ 'RelationPath',
114
+ 'LearningObjectives',
115
+ 'KeyQuestion',
116
+ 'Evidence',
117
+ 'Invariant',
118
+ 'FailureMode',
119
+ 'Tradeoff',
120
+ ]);
121
+
122
+ function isSemanticComponent(name) {
123
+ return SEMANTIC_COMPONENTS.has(name);
124
+ }
125
+
126
+ function getComponentName(element) {
127
+ if (!element || !element.type) return '';
128
+ if (typeof element.type === 'string') return element.type;
129
+ return element.type.displayName || element.type.name || '';
130
+ }
131
+
132
+ function parseConceptNode(nodeElement) {
133
+ const props = nodeElement.props;
134
+ const node = {
135
+ id: props.id,
136
+ title: props.title || props.id,
137
+ level: props.level || 'L2',
138
+ parent: props.parent !== undefined ? props.parent : null,
139
+ children: [],
140
+ summary: props.summary || '',
141
+ overview: null,
142
+ definition: null,
143
+ mechanism: null,
144
+ input: props.input || null,
145
+ output: props.output || null,
146
+ prerequisites: [],
147
+ implementation: null,
148
+ examples: [],
149
+ counterexamples: [],
150
+ boundaries: [],
151
+ glossary: [],
152
+ customSections: []
153
+ };
154
+
155
+ Children.forEach(props.children, child => {
156
+ if (!child || !isValidElement(child)) return;
157
+ const name = getComponentName(child);
158
+
159
+ switch (name) {
160
+ case 'Overview':
161
+ node.overview = child.props.children;
162
+ if (!node.summary && typeof child.props.children === 'string') {
163
+ node.summary = child.props.children.trim();
164
+ }
165
+ break;
166
+ case 'Definition':
167
+ node.definition = child.props.children;
168
+ break;
169
+ case 'Mechanism':
170
+ node.mechanism = child.props.children;
171
+ break;
172
+ case 'Input':
173
+ node.input = child.props.children;
174
+ break;
175
+ case 'Output':
176
+ node.output = child.props.children;
177
+ break;
178
+ case 'Prerequisite':
179
+ if (typeof child.props.children === 'string') {
180
+ node.prerequisites.push(child.props.children);
181
+ } else if (Array.isArray(child.props.children)) {
182
+ node.prerequisites.push(...child.props.children);
183
+ } else {
184
+ node.prerequisites.push(child.props.children);
185
+ }
186
+ break;
187
+ case 'Implementation':
188
+ node.implementation = {
189
+ language: child.props.language || 'text',
190
+ title: child.props.title || '代码实现',
191
+ code: child.props.children
192
+ };
193
+ break;
194
+ case 'Example':
195
+ node.examples.push({
196
+ title: child.props.title || '典型示例',
197
+ content: child.props.children
198
+ });
199
+ break;
200
+ case 'Counterexample':
201
+ node.counterexamples.push({
202
+ title: child.props.title || '边界反例 / 常见误区',
203
+ content: child.props.children
204
+ });
205
+ break;
206
+ case 'Boundary':
207
+ node.boundaries.push({
208
+ title: child.props.title || '边界条件 / 约束',
209
+ content: child.props.children
210
+ });
211
+ break;
212
+ case 'Glossary':
213
+ node.glossary.push({
214
+ term: child.props.term,
215
+ definition: child.props.children
216
+ });
217
+ break;
218
+ case 'Children':
219
+ Children.forEach(child.props.children, ref => {
220
+ if (isValidElement(ref) && (getComponentName(ref) === 'ConceptRef' || ref.props.id)) {
221
+ node.children.push(ref.props.id);
222
+ }
223
+ });
224
+ break;
225
+ case 'ConceptRef':
226
+ if (child.props.id) {
227
+ node.children.push(child.props.id);
228
+ }
229
+ break;
230
+ default:
231
+ // Semantic presentation components inside node
232
+ node.customSections.push(child);
233
+ break;
234
+ }
235
+ });
236
+
237
+ return node;
238
+ }
@@ -0,0 +1,90 @@
1
+ export const RELATION_TYPES = {
2
+ 'parent-child': {
3
+ label: '父子层级',
4
+ description: '表达概念归属与分解层级',
5
+ color: '#87a6ff',
6
+ strokeDasharray: 'none',
7
+ hasArrow: true,
8
+ isTree: true
9
+ },
10
+ 'prerequisite': {
11
+ label: '前置知识',
12
+ description: '理解当前概念所需的前置基础',
13
+ color: '#f1bc74',
14
+ strokeDasharray: '4 4',
15
+ hasArrow: true,
16
+ isTree: false
17
+ },
18
+ 'causes': {
19
+ label: '因果推动',
20
+ description: '引起或触发后续状态变化',
21
+ color: '#ef8585',
22
+ strokeDasharray: 'none',
23
+ hasArrow: true,
24
+ isTree: false
25
+ },
26
+ 'produces': {
27
+ label: '产出生成',
28
+ description: '阶段处理后产出的产物或实体',
29
+ color: '#72d0a1',
30
+ strokeDasharray: 'none',
31
+ hasArrow: true,
32
+ isTree: false
33
+ },
34
+ 'uses': {
35
+ label: '消费使用',
36
+ description: '调用或引用其他概念与数据',
37
+ color: '#38bdf8',
38
+ strokeDasharray: '6 3',
39
+ hasArrow: true,
40
+ isTree: false
41
+ },
42
+ 'implements': {
43
+ label: '实现关系',
44
+ description: '具体机制或代码实现特定抽象',
45
+ color: '#c084fc',
46
+ strokeDasharray: 'none',
47
+ hasArrow: true,
48
+ isTree: false
49
+ },
50
+ 'contrasts': {
51
+ label: '对比关系',
52
+ description: '概念间的异同对照或对立维度',
53
+ color: '#fb923c',
54
+ strokeDasharray: '2 2',
55
+ hasArrow: false,
56
+ isTree: false
57
+ },
58
+ 'depends-on': {
59
+ label: '依赖关系',
60
+ description: '运行或生效依赖外部条件',
61
+ color: '#94a3b8',
62
+ strokeDasharray: '5 5',
63
+ hasArrow: true,
64
+ isTree: false
65
+ },
66
+ 'exception-of': {
67
+ label: '异常/反例',
68
+ description: '特殊情况、破坏假定的边界或反例',
69
+ color: '#f43f5e',
70
+ strokeDasharray: '3 3',
71
+ hasArrow: true,
72
+ isTree: false
73
+ },
74
+ 'precedes': {
75
+ label: '时序先后',
76
+ description: '时间或处理流水线的前后相继',
77
+ color: '#2dd4bf',
78
+ strokeDasharray: 'none',
79
+ hasArrow: true,
80
+ isTree: false
81
+ }
82
+ };
83
+
84
+ export const LEVEL_DEFS = {
85
+ L0: { name: '全局概览', tag: 'L0 · 全局概览', desc: '系统全貌与全局定位', color: '#818cf8' },
86
+ L1: { name: '主要阶段 / 子系统', tag: 'L1 · 子系统', desc: '主要生命周期阶段或子系统划分', color: '#38bdf8' },
87
+ L2: { name: '局部机制', tag: 'L2 · 局部机制', desc: '具体工作机制与逻辑流动', color: '#34d399' },
88
+ L3: { name: '实现细节', tag: 'L3 · 实现细节', desc: '算法、数据结构或代码实现', color: '#fbbf24' },
89
+ L4: { name: '边界与反例', tag: 'L4 · 边界反例', desc: '边界情况、异常分支与典型反例', color: '#f87171' }
90
+ };