concept-atlas-dense-explain 0.2.0 → 0.2.2

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.
@@ -45,11 +45,20 @@
45
45
  --code-text: #34d399;
46
46
  --code-border: rgba(255, 255, 255, 0.1);
47
47
 
48
- /* Shadows & Depth */
49
- --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
50
- --shadow-md: 0 4px 16px -2px rgba(0, 0, 0, 0.5), 0 2px 6px -1px rgba(0, 0, 0, 0.4);
51
- --shadow-lg: 0 10px 30px -4px rgba(0, 0, 0, 0.6), 0 4px 12px -2px rgba(0, 0, 0, 0.4);
52
- --shadow-glow: 0 0 20px rgba(99, 102, 241, 0.2);
48
+ /* Shadows & Depth - restrained, hairline-first. Offset shadows read as
49
+ printed paper rather than floating SaaS cards. */
50
+ --shadow-sm: 0 1px 1px rgba(0, 0, 0, 0.35);
51
+ --shadow-md: 0 2px 8px -4px rgba(0, 0, 0, 0.55);
52
+ --shadow-lg: 0 8px 24px -12px rgba(0, 0, 0, 0.6);
53
+ --shadow-glow: none;
54
+ --shadow-card: 2px 3px 0 rgba(0, 0, 0, 0.45);
55
+ --shadow-pop: 3px 4px 0 rgba(0, 0, 0, 0.55);
56
+
57
+ /* Radius scale - differentiated by component weight, not one value everywhere */
58
+ --radius-1: 3px;
59
+ --radius-2: 4px;
60
+ --radius-3: 6px;
61
+ --radius-4: 8px;
53
62
 
54
63
  /* Knowledge Badge & Callouts */
55
64
  --callout-info-bg: rgba(56, 189, 248, 0.08);
@@ -106,10 +115,17 @@
106
115
  --code-border: rgba(0, 0, 0, 0.08);
107
116
 
108
117
  /* Shadows & Depth */
109
- --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.05);
110
- --shadow-md: 0 4px 16px -2px rgba(15, 23, 42, 0.08), 0 2px 6px -1px rgba(15, 23, 42, 0.04);
111
- --shadow-lg: 0 12px 32px -4px rgba(15, 23, 42, 0.12), 0 4px 12px -2px rgba(15, 23, 42, 0.06);
112
- --shadow-glow: 0 0 20px rgba(79, 70, 229, 0.12);
118
+ --shadow-sm: 0 1px 1px rgba(15, 23, 42, 0.04);
119
+ --shadow-md: 0 2px 8px -4px rgba(15, 23, 42, 0.12);
120
+ --shadow-lg: 0 8px 24px -12px rgba(15, 23, 42, 0.14);
121
+ --shadow-glow: none;
122
+ --shadow-card: 2px 3px 0 rgba(15, 23, 42, 0.08);
123
+ --shadow-pop: 3px 4px 0 rgba(15, 23, 42, 0.12);
124
+
125
+ --radius-1: 3px;
126
+ --radius-2: 4px;
127
+ --radius-3: 6px;
128
+ --radius-4: 8px;
113
129
 
114
130
  /* Knowledge Badge & Callouts */
115
131
  --callout-info-bg: #eff6ff;
@@ -2423,3 +2439,268 @@ button.flow-box {
2423
2439
  .semantic-decision-matrix tr:last-child td, .semantic-compare-table tr:last-child td {
2424
2440
  border-bottom: none;
2425
2441
  }
2442
+
2443
+ .semantic-framework-model, .semantic-model-matrix, .semantic-model-formula {
2444
+ margin-top: 14px;
2445
+ padding: 14px;
2446
+ border: 1px solid var(--border-medium);
2447
+ border-radius: 10px;
2448
+ background: var(--bg-canvas);
2449
+ }
2450
+
2451
+ .framework-model-head {
2452
+ display: flex;
2453
+ align-items: center;
2454
+ justify-content: space-between;
2455
+ gap: 12px;
2456
+ margin-bottom: 10px;
2457
+ }
2458
+
2459
+ .framework-model-type {
2460
+ color: var(--text-muted);
2461
+ font-size: 11px;
2462
+ letter-spacing: .08em;
2463
+ text-transform: uppercase;
2464
+ }
2465
+
2466
+ .framework-model-question {
2467
+ margin-bottom: 10px;
2468
+ color: var(--text-secondary);
2469
+ font-size: 12px;
2470
+ }
2471
+
2472
+ .framework-model-elements {
2473
+ display: grid;
2474
+ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
2475
+ gap: 8px;
2476
+ }
2477
+
2478
+ .framework-model-element {
2479
+ display: grid;
2480
+ grid-template-columns: auto 1fr;
2481
+ gap: 3px 8px;
2482
+ padding: 10px;
2483
+ border: 1px solid var(--border-subtle);
2484
+ border-radius: 8px;
2485
+ background: var(--bg-surface);
2486
+ }
2487
+
2488
+ .framework-model-element strong { color: var(--text-primary); font-size: 13px; }
2489
+ .framework-model-element span:last-child { grid-column: 2; color: var(--text-secondary); font-size: 12px; line-height: 1.5; }
2490
+ .framework-model-index { color: var(--accent-cyan); font: 700 11px/1.4 ui-monospace, monospace; }
2491
+ .model-stages .framework-model-elements, .model-cycle .framework-model-elements { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
2492
+ .model-stages .framework-model-element:not(:last-child)::after { content: '→'; position: absolute; }
2493
+
2494
+ .model-matrix-grid {
2495
+ display: grid;
2496
+ grid-template-columns: 30px repeat(2, minmax(110px, 1fr));
2497
+ grid-template-rows: 24px repeat(2, minmax(70px, 1fr));
2498
+ gap: 5px;
2499
+ }
2500
+
2501
+ .model-matrix-axis { color: var(--text-muted); font-size: 11px; text-align: center; }
2502
+ .model-matrix-x { grid-column: 2 / 4; }
2503
+ .model-matrix-y { grid-row: 2 / 4; writing-mode: vertical-rl; transform: rotate(180deg); align-self: center; }
2504
+ .model-matrix-cell { display: flex; flex-direction: column; gap: 4px; padding: 9px; border: 1px solid var(--border-subtle); border-radius: 7px; background: var(--bg-surface); }
2505
+ .model-matrix-cell strong { color: var(--text-primary); font-size: 12px; }
2506
+ .model-matrix-cell span { color: var(--text-secondary); font-size: 11px; line-height: 1.4; }
2507
+ .tone-success { border-color: rgba(52, 211, 153, .45); }
2508
+ .tone-warn { border-color: rgba(251, 191, 36, .45); }
2509
+ .tone-danger { border-color: rgba(248, 113, 113, .45); }
2510
+
2511
+ .model-formula-expression { padding: 12px; border-radius: 8px; background: var(--bg-surface); color: var(--accent-cyan); text-align: center; font-size: 16px; }
2512
+ .model-formula-variables { display: grid; grid-template-columns: auto 1fr; gap: 5px 12px; margin: 10px 0 0; font-size: 12px; }
2513
+ .model-formula-variables dt { color: var(--accent-cyan); font-family: ui-monospace, monospace; }
2514
+ .model-formula-variables dd { margin: 0; color: var(--text-secondary); }
2515
+
2516
+ .semantic-model-pyramid, .semantic-model-funnel {
2517
+ margin-top: 14px;
2518
+ padding: 14px;
2519
+ border: 1px solid var(--border-medium);
2520
+ border-radius: 10px;
2521
+ background: var(--bg-canvas);
2522
+ }
2523
+
2524
+ .model-pyramid-levels, .model-funnel-steps { display: flex; flex-direction: column; align-items: center; gap: 5px; }
2525
+ .model-pyramid-level, .model-funnel-step {
2526
+ display: flex;
2527
+ flex-direction: column;
2528
+ gap: 3px;
2529
+ width: var(--pyramid-width, var(--funnel-width));
2530
+ padding: 9px 12px;
2531
+ border: 1px solid var(--border-subtle);
2532
+ border-radius: 7px;
2533
+ background: var(--bg-surface);
2534
+ text-align: center;
2535
+ }
2536
+ .model-pyramid-level strong, .model-funnel-step strong { color: var(--text-primary); font-size: 12px; }
2537
+ .model-pyramid-level span, .model-funnel-step span { color: var(--text-secondary); font-size: 11px; line-height: 1.4; }
2538
+
2539
+ /* ==========================================================================
2540
+ DESIGN REFINEMENT — remove the generic "SaaS card kit" defaults.
2541
+ Oversized uniform radii, a soft grey shadow on every surface, glass blur,
2542
+ gradient decoration and tracked all-caps labels are the tells this pass
2543
+ removes. Surfaces are separated by hairlines and a single print-like
2544
+ offset shadow; radii follow the 3/4/6/8 scale so weight maps to hierarchy.
2545
+ ========================================================================== */
2546
+
2547
+ /* Chrome: solid surfaces instead of glass blur */
2548
+ .app-toolbar,
2549
+ .graph-legend-ribbon,
2550
+ .draft-floating-tools {
2551
+ background: var(--bg-surface);
2552
+ backdrop-filter: none;
2553
+ -webkit-backdrop-filter: none;
2554
+ box-shadow: var(--shadow-card);
2555
+ }
2556
+
2557
+ .app-toolbar,
2558
+ .graph-legend-ribbon,
2559
+ .draft-floating-tools { border-radius: var(--radius-3); }
2560
+
2561
+ .draft-floating-tools:hover,
2562
+ .draft-floating-tools:focus-within { transform: none; }
2563
+
2564
+ .view-tabs { border-radius: var(--radius-2); }
2565
+ .view-tab { border-radius: var(--radius-1); }
2566
+ .view-tab.active { box-shadow: none; border-color: var(--border-strong); }
2567
+ .theme-toggle-btn { border-radius: var(--radius-2); box-shadow: none; }
2568
+
2569
+ /* Hover shows state through color and rules, not nudged cards */
2570
+ .theme-toggle-btn:hover,
2571
+ .view-graph-btn:hover,
2572
+ .subnode-card:hover,
2573
+ .relation-link-card:hover,
2574
+ .panel-rel-card:hover,
2575
+ .sibling-btn:hover,
2576
+ .flow-box.clickable:hover,
2577
+ .jump-explorer-btn:hover { transform: none; }
2578
+ .subnode-card:hover .sn-arrow { transform: none; }
2579
+
2580
+ .brand-logo-badge {
2581
+ background: var(--primary);
2582
+ border-radius: var(--radius-2);
2583
+ box-shadow: none;
2584
+ }
2585
+
2586
+ /* Graph controls */
2587
+ .graph-mode-switch,
2588
+ .search-box,
2589
+ .filter-group {
2590
+ border-radius: var(--radius-3);
2591
+ box-shadow: none;
2592
+ }
2593
+ .graph-mode-switch button { border-radius: var(--radius-1); }
2594
+ .graph-legend-ribbon { border-radius: var(--radius-3); }
2595
+ .graph-side-panel { box-shadow: none; }
2596
+
2597
+ /* Labels: sentence case instead of tracked all-caps eyebrows */
2598
+ .side-label,
2599
+ .insp-label,
2600
+ .legend-title,
2601
+ .box-type,
2602
+ .chip-label,
2603
+ .impl-lang,
2604
+ .hierarchy-nav-label,
2605
+ .panel-badge,
2606
+ .panel-section .section-title,
2607
+ .framework-model-type {
2608
+ text-transform: none;
2609
+ letter-spacing: 0.01em;
2610
+ }
2611
+ .side-label { color: var(--text-secondary); }
2612
+ .side-label::before { width: 2px; height: 11px; border-radius: 1px; }
2613
+
2614
+ /* Navigation */
2615
+ .breadcrumb-node,
2616
+ .sibling-btn,
2617
+ .tree-node-btn,
2618
+ .hierarchy-nav-node,
2619
+ .hierarchy-nav-current,
2620
+ .hierarchy-nav-children { border-radius: var(--radius-2); }
2621
+ .tree-node-btn.active::before { border-radius: 0; }
2622
+
2623
+ /* Cards: radius hierarchy + hairline separation */
2624
+ .concept-hero-card {
2625
+ border-radius: var(--radius-4);
2626
+ box-shadow: var(--shadow-pop);
2627
+ }
2628
+ .concept-hero-card::after { display: none; }
2629
+
2630
+ .node-block,
2631
+ .draft-board .node-block {
2632
+ border-radius: var(--radius-3);
2633
+ box-shadow: var(--shadow-card);
2634
+ }
2635
+
2636
+ .example-item,
2637
+ .counterexample-item,
2638
+ .subnode-card,
2639
+ .relation-link-card,
2640
+ .boundary-card,
2641
+ .panel-rel-card,
2642
+ .io-capsule,
2643
+ .local-flow-ribbon,
2644
+ .code-block,
2645
+ .inline-inspector,
2646
+ .semantic-learning-objectives,
2647
+ .semantic-key-question,
2648
+ .semantic-evidence,
2649
+ .semantic-invariant,
2650
+ .semantic-failure-mode,
2651
+ .semantic-tradeoff,
2652
+ .semantic-framework-model,
2653
+ .semantic-model-matrix,
2654
+ .semantic-model-formula,
2655
+ .semantic-model-pyramid,
2656
+ .semantic-model-funnel,
2657
+ .semantic-relation-path,
2658
+ .relation-path-node,
2659
+ .model-matrix-cell,
2660
+ .model-formula-expression,
2661
+ .semantic-decision-matrix,
2662
+ .semantic-compare-table,
2663
+ .semantic-callout,
2664
+ .hierarchy-navigator { border-radius: var(--radius-3); }
2665
+
2666
+ .draft-board .semantic-grid > .semantic-mermaid,
2667
+ .draft-board .semantic-grid > .semantic-relation-map { border-radius: var(--radius-3); }
2668
+
2669
+ .meta-chip,
2670
+ .flow-box,
2671
+ .insp-pill,
2672
+ .inline-relation,
2673
+ .level-pill,
2674
+ .view-graph-btn,
2675
+ .framework-model-element,
2676
+ .model-pyramid-level,
2677
+ .model-funnel-step,
2678
+ .semantic-note { border-radius: var(--radius-2); }
2679
+
2680
+ .rel-type-tag,
2681
+ .bc-level-pill { border-radius: var(--radius-1); }
2682
+
2683
+ /* Depth: only note-like surfaces keep an offset shadow, nothing floats */
2684
+ .code-block { box-shadow: none; }
2685
+ .semantic-note { box-shadow: var(--shadow-card); }
2686
+ .subnode-card:hover { box-shadow: var(--shadow-card); }
2687
+ .flow-box.current-focus,
2688
+ .jump-explorer-btn { box-shadow: none; }
2689
+
2690
+ .inline-inspector {
2691
+ border-style: solid;
2692
+ border-color: var(--border-medium);
2693
+ box-shadow: var(--shadow-card);
2694
+ }
2695
+
2696
+ /* Widget chrome: the type tag becomes a quiet rule, not monospace data */
2697
+ .semantic-widget-head code {
2698
+ padding: 1px 5px;
2699
+ border: 1px solid var(--border-subtle);
2700
+ border-radius: var(--radius-1);
2701
+ color: var(--text-dim);
2702
+ font-family: inherit;
2703
+ font-size: 9px;
2704
+ font-weight: 700;
2705
+ letter-spacing: 0.03em;
2706
+ }
@@ -17,24 +17,66 @@ export function NodeExplorer({
17
17
  const [isDragging, setIsDragging] = React.useState(false);
18
18
  const dragRef = React.useRef(null);
19
19
  const suppressClickRef = React.useRef(false);
20
+ const viewportRef = React.useRef(null);
21
+ const boardRef = React.useRef(null);
22
+ const canvasScaleRef = React.useRef(canvasScale);
23
+ const canvasPanRef = React.useRef(canvasPan);
24
+ canvasScaleRef.current = canvasScale;
25
+ canvasPanRef.current = canvasPan;
20
26
 
21
27
  const updateScale = (nextScale) => {
22
28
  const scale = Math.max(0.65, Math.min(1.6, +nextScale.toFixed(2)));
23
29
  setCanvasScale(scale);
24
30
  };
25
31
 
26
- const updateScaleAtPoint = (event) => {
27
- const rect = event.currentTarget.getBoundingClientRect();
28
- const factor = Math.exp(-event.deltaY * 0.002);
29
- const nextScale = Math.max(0.65, Math.min(1.6, +(canvasScale * factor).toFixed(3)));
30
- const focusX = event.clientX - rect.left;
31
- const focusY = event.clientY - rect.top;
32
- const contentX = (focusX - canvasPan.x) / canvasScale;
33
- const contentY = (focusY - canvasPan.y) / canvasScale;
34
- setCanvasScale(nextScale);
35
- setCanvasPan({ x: focusX - contentX * nextScale, y: focusY - contentY * nextScale });
32
+ // Keep the board from being scrolled entirely out of view.
33
+ const clampCanvasPan = (x, y, scale = canvasScale) => {
34
+ const viewport = viewportRef.current;
35
+ const board = boardRef.current;
36
+ if (!viewport || !board) return { x, y };
37
+ const margin = 64;
38
+ const clampAxis = (value, content, view) =>
39
+ Math.max(Math.min(view - content, 0) - margin, Math.min(margin, value));
40
+ return {
41
+ x: clampAxis(x, board.offsetWidth * scale, viewport.clientWidth),
42
+ y: clampAxis(y, board.offsetHeight * scale, viewport.clientHeight),
43
+ };
36
44
  };
37
45
 
46
+ // Wheel scrolls the canvas vertically; Ctrl/Cmd + wheel zooms around the
47
+ // pointer. React's onWheel is passive, so bind a native listener instead.
48
+ React.useEffect(() => {
49
+ const viewport = viewportRef.current;
50
+ if (!viewport) return undefined;
51
+
52
+ const handleWheel = (event) => {
53
+ if (event.ctrlKey || event.metaKey) {
54
+ event.preventDefault();
55
+ const scale = canvasScaleRef.current;
56
+ const pan = canvasPanRef.current;
57
+ const rect = viewport.getBoundingClientRect();
58
+ const factor = Math.exp(-event.deltaY * 0.002);
59
+ const nextScale = Math.max(0.65, Math.min(1.6, +(scale * factor).toFixed(3)));
60
+ const focusX = event.clientX - rect.left;
61
+ const focusY = event.clientY - rect.top;
62
+ const contentX = (focusX - pan.x) / scale;
63
+ const contentY = (focusY - pan.y) / scale;
64
+ setCanvasScale(nextScale);
65
+ setCanvasPan(clampCanvasPan(focusX - contentX * nextScale, focusY - contentY * nextScale, nextScale));
66
+ return;
67
+ }
68
+
69
+ event.preventDefault();
70
+ const stepX = event.shiftKey ? event.deltaY : event.deltaX;
71
+ const stepY = event.shiftKey ? 0 : event.deltaY;
72
+ const pan = canvasPanRef.current;
73
+ setCanvasPan(clampCanvasPan(pan.x - stepX, pan.y - stepY, canvasScaleRef.current));
74
+ };
75
+
76
+ viewport.addEventListener('wheel', handleWheel, { passive: false });
77
+ return () => viewport.removeEventListener('wheel', handleWheel);
78
+ }, []);
79
+
38
80
  const handleCanvasPointerDown = (event) => {
39
81
  if (event.target.closest('button, a, input, select, textarea')) return;
40
82
  dragRef.current = {
@@ -55,10 +97,11 @@ export function NodeExplorer({
55
97
  const distance = Math.hypot(event.clientX - dragRef.current.startX, event.clientY - dragRef.current.startY);
56
98
  if (distance < 6) return;
57
99
  dragRef.current.moved = true;
58
- setCanvasPan({
59
- x: dragRef.current.panX + event.clientX - dragRef.current.startX,
60
- y: dragRef.current.panY + event.clientY - dragRef.current.startY,
61
- });
100
+ setCanvasPan(clampCanvasPan(
101
+ dragRef.current.panX + event.clientX - dragRef.current.startX,
102
+ dragRef.current.panY + event.clientY - dragRef.current.startY,
103
+ canvasScale
104
+ ));
62
105
  };
63
106
 
64
107
  const handleCanvasPointerUp = (event) => {
@@ -174,6 +217,7 @@ export function NodeExplorer({
174
217
  </div>
175
218
 
176
219
  <div
220
+ ref={viewportRef}
177
221
  className={`draft-viewport ${isDragging ? 'is-dragging' : ''}`}
178
222
  onPointerDown={handleCanvasPointerDown}
179
223
  onPointerMove={handleCanvasPointerMove}
@@ -185,12 +229,6 @@ export function NodeExplorer({
185
229
  event.stopPropagation();
186
230
  suppressClickRef.current = false;
187
231
  }}
188
- onWheel={(event) => {
189
- // Two-finger trackpad scrolling remains document scrolling. Hold Ctrl/Cmd to zoom.
190
- if (!event.ctrlKey && !event.metaKey) return;
191
- event.preventDefault();
192
- updateScaleAtPoint(event);
193
- }}
194
232
  >
195
233
  <div className="draft-floating-tools">
196
234
  <button
@@ -215,6 +253,7 @@ export function NodeExplorer({
215
253
  </div>
216
254
  </div>
217
255
  <div
256
+ ref={boardRef}
218
257
  className="draft-board"
219
258
  style={{
220
259
  transform: `translate(${canvasPan.x}px, ${canvasPan.y}px) scale(${canvasScale})`,
@@ -165,7 +165,7 @@ export function RelationGraph({
165
165
  .filter((event) => event.type !== 'wheel' || event.ctrlKey || event.metaKey)
166
166
  .wheelDelta((event) => {
167
167
  const delta = event.deltaMode === 1 ? event.deltaY * 16 : event.deltaY;
168
- return -delta * 0.0015;
168
+ return -delta * 0.003;
169
169
  })
170
170
  .on('zoom', (event) => {
171
171
  g.attr('transform', event.transform);
@@ -173,6 +173,20 @@ export function RelationGraph({
173
173
 
174
174
  svg.call(zoomBehavior);
175
175
 
176
+ // Plain wheel scrolls the canvas vertically; Ctrl/Cmd + wheel zooms.
177
+ const handleWheelPan = (event) => {
178
+ if (event.ctrlKey || event.metaKey) return;
179
+ event.preventDefault();
180
+ const current = d3.zoomTransform(svgRef.current);
181
+ const stepX = event.shiftKey ? event.deltaY : event.deltaX;
182
+ const stepY = event.shiftKey ? 0 : event.deltaY;
183
+ const nextTransform = d3.zoomIdentity
184
+ .translate(current.x - stepX, current.y - stepY)
185
+ .scale(current.k);
186
+ svg.call(zoomBehavior.transform, nextTransform);
187
+ };
188
+ svg.on('wheel.pan', handleWheelPan);
189
+
176
190
  const isConceptMode = graphMode === 'concept';
177
191
  const visibleGraphLinks = isConceptMode
178
192
  ? graphLinks.filter(link => link.type !== 'parent-child')
@@ -334,6 +348,7 @@ export function RelationGraph({
334
348
 
335
349
  return () => {
336
350
  simulation?.stop();
351
+ svg.on('wheel.pan', null);
337
352
  svg.on('.zoom', null);
338
353
  };
339
354
  }, [graphNodes, graphLinks, selectedNodeId, theme, graphMode]);
@@ -1,21 +1,21 @@
1
- import { defineConfig } from 'vite';
2
- import react from '@vitejs/plugin-react';
3
- import mdx from '@mdx-js/rollup';
4
- import { viteSingleFile } from 'vite-plugin-singlefile';
5
-
6
- export default defineConfig({
7
- plugins: [
8
- {
9
- enforce: 'pre',
10
- ...mdx({
11
- providerImportSource: null,
12
- }),
13
- },
14
- react(),
15
- viteSingleFile(),
16
- ],
17
- build: {
18
- outDir: 'dist',
19
- emptyOutDir: true,
20
- },
21
- });
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import mdx from '@mdx-js/rollup';
4
+ import { viteSingleFile } from 'vite-plugin-singlefile';
5
+
6
+ export default defineConfig({
7
+ plugins: [
8
+ {
9
+ enforce: 'pre',
10
+ ...mdx({
11
+ providerImportSource: null,
12
+ }),
13
+ },
14
+ react(),
15
+ viteSingleFile(),
16
+ ],
17
+ build: {
18
+ outDir: 'dist',
19
+ emptyOutDir: true,
20
+ },
21
+ });
@@ -1,64 +1,74 @@
1
- # Component contract
2
-
3
- ## Structure
4
-
5
- ```mdx
6
- <ExplainPage id="topic" title="..." summary="...">
7
- <ConceptGraph root="root">
8
- <ConceptNode id="root" title="..." level="L0" summary="...">
9
- <Definition>...</Definition>
10
- <Input>...</Input><Output>...</Output>
11
- <Children><ConceptRef id="child" /></Children>
12
- </ConceptNode>
13
- <ConceptNode id="child" title="..." level="L1" parent="root">...</ConceptNode>
14
- <Relation from="child" to="other" type="depends-on" label="依赖" />
15
- </ConceptGraph>
16
- </ExplainPage>
17
- ```
18
-
19
- Supported semantic content includes `Overview`, `Definition`, `Mechanism`, `Implementation`, `Example`, `Counterexample`, `Boundary`, `Prerequisite`, `Input`, `Output`, and `Glossary`.
20
-
21
- High-value verification components: `LearningObjectives`, `KeyQuestion`, `Evidence(command, observes)`, `Invariant(title)`, `FailureMode(symptom, cause, evidence, remedy)`, and `Tradeoff(options)`.
22
-
23
- Presentation components: `Stack`, `Grid`, `Split`, `Tabs`, `Flow`, `Compare`, `DecisionMatrix`, `Timeline`, `Callout`, `Details`, `RelationMap`, `RelationPath`, `Insight`, and `NoteGrid`.
24
-
25
- ## Relation types (whitelist — only these render)
26
-
27
- Write cross-branch `Relation`s with `type` from this exact list. Any other value renders no styled edge.
28
-
29
- | type | label | use for |
30
- | --- | --- | --- |
31
- | `prerequisite` | 前置知识 | knowledge required before this concept |
32
- | `causes` | 因果推动 | one concept triggers a state change in another |
33
- | `produces` | 产出生成 | a stage outputs an artifact or entity |
34
- | `uses` | 消费使用 | a concept consumes or calls another |
35
- | `implements` | 实现关系 | a concrete mechanism implements an abstraction |
36
- | `contrasts` | 对比关系 | side-by-side contrast (no arrowhead) |
37
- | `depends-on` | 依赖关系 | runtime/effect depends on an external condition |
38
- | `exception-of` | 异常/反例 | a boundary case, counterexample, or broken assumption |
39
- | `precedes` | 时序先后 | pipeline or temporal ordering |
40
-
41
- Do **not** write `parent-child` as a `Relation`; the tree edge is generated automatically from `parent` / `Children`. Add a short `label` to every relation so the graph edge is readable.
42
-
43
- ## Levels L0–L4
44
-
45
- | Level | name | role | typical components |
46
- | --- | --- | --- | --- |
47
- | `L0` | 全局概览 | system-wide framing and the root claim | `Definition`, `Prerequisite`, `LearningObjectives`, `KeyQuestion`, `Input`/`Output`, `Callout`, `Tabs` |
48
- | `L1` | 主要阶段 / 子系统 | major lifecycle stage or subsystem | `Definition`, `Input`/`Output`, `Example`, `Children` |
49
- | `L2` | 局部机制 | concrete mechanism and flow | `Definition`, `Mechanism`, `Details`, `Evidence`, `Invariant`, `Tradeoff`, `FailureMode` |
50
- | `L3` | 实现细节 | algorithm, data structure, or code | `Definition`, `Example`, `Glossary` |
51
- | `L4` | 边界与反例 | boundary, exception, or counterexample | `Callout(danger/warn)`, `Example`, `FailureMode` |
52
-
53
- ## Density targets
54
-
55
- Match the exemplar (`assets/template/content/compile-runtime.mdx`):
56
-
57
- - 1 `L0` root, 4–5 `L1` branches, 2–3 children per branch, ~15–25 nodes total.
58
- - Every key node: 3–6 semantic components; `L3`/`L4` nodes may be leaner but still concrete.
59
- - At least a few nodes per document carry `Evidence`, `Invariant`, or `FailureMode` with a real command/observation.
60
- - ~8–15 cross-branch `Relation`s, each with a `label`.
61
-
62
- ## Request priorities
63
-
64
- The portable template covers the core `req.md` contract, node exploration, graph view, semantic relations, search, level/relation filters, URL node state, browsing history, keyboard shortcuts, Mermaid zoom, and responsive rendering. Evidence-oriented components and compact tradeoffs cover the highest-value `request.md` enhancements without adding permanent side panels.
1
+ # Component contract
2
+
3
+ ## Structure
4
+
5
+ ```mdx
6
+ <ExplainPage id="topic" title="..." summary="...">
7
+ <ConceptGraph root="root">
8
+ <ConceptNode id="root" title="..." level="L0" summary="...">
9
+ <Definition>...</Definition>
10
+ <Input>...</Input><Output>...</Output>
11
+ <Children><ConceptRef id="child" /></Children>
12
+ </ConceptNode>
13
+ <ConceptNode id="child" title="..." level="L1" parent="root">...</ConceptNode>
14
+ <Relation from="child" to="other" type="depends-on" label="依赖" />
15
+ </ConceptGraph>
16
+ </ExplainPage>
17
+ ```
18
+
19
+ Supported semantic content includes `Overview`, `Definition`, `Mechanism`, `Implementation`, `Example`, `Counterexample`, `Boundary`, `Prerequisite`, `Input`, `Output`, and `Glossary`.
20
+
21
+ High-value verification components: `LearningObjectives`, `KeyQuestion`, `Evidence(command, observes)`, `Invariant(title)`, `FailureMode(symptom, cause, evidence, remedy)`, and `Tradeoff(options)`.
22
+
23
+ Presentation components: `Stack`, `Grid`, `Split`, `Tabs`, `Flow`, `Compare`, `DecisionMatrix`, `Timeline`, `FrameworkModel`, `MatrixModel`, `FormulaModel`, `PyramidModel`, `FunnelModel`, `Callout`, `Details`, `RelationMap`, `RelationPath`, `Insight`, and `NoteGrid`.
24
+
25
+ 经典结构模型建议按问题选择,避免重复堆叠:
26
+
27
+ - `FrameworkModel`:一分为几、多因素并列、阶段、分层或循环;用 `type="elements|stages|layers|cycle"` 区分结构。
28
+ - `MatrixModel`:两个关键元素组成的二维象限,例如重要/紧急、增长率/市场份额。
29
+ - `FormulaModel`:用公式表达变量之间的乘加减关系,例如业绩、价值或行为模型。
30
+ - `PyramidModel`:从基础到顶层的分层归纳,例如学习目标、需求层次或论证结构。
31
+ - `FunnelModel`:从输入到筛选、转化、结果的逐步收敛,例如 AIDA 或销售漏斗。
32
+
33
+ 原有 `Flow`、`DecisionMatrix`、`Compare`、`Timeline` 继续保留,用于兼容已有内容;新内容优先使用上面的模型组件表达“关键元素与结构”的关系。
34
+
35
+ ## Relation types (whitelist — only these render)
36
+
37
+ Write cross-branch `Relation`s with `type` from this exact list. Any other value renders no styled edge.
38
+
39
+ | type | label | use for |
40
+ | --- | --- | --- |
41
+ | `prerequisite` | 前置知识 | knowledge required before this concept |
42
+ | `causes` | 因果推动 | one concept triggers a state change in another |
43
+ | `produces` | 产出生成 | a stage outputs an artifact or entity |
44
+ | `uses` | 消费使用 | a concept consumes or calls another |
45
+ | `implements` | 实现关系 | a concrete mechanism implements an abstraction |
46
+ | `contrasts` | 对比关系 | side-by-side contrast (no arrowhead) |
47
+ | `depends-on` | 依赖关系 | runtime/effect depends on an external condition |
48
+ | `exception-of` | 异常/反例 | a boundary case, counterexample, or broken assumption |
49
+ | `precedes` | 时序先后 | pipeline or temporal ordering |
50
+
51
+ Do **not** write `parent-child` as a `Relation`; the tree edge is generated automatically from `parent` / `Children`. Add a short `label` to every relation so the graph edge is readable.
52
+
53
+ ## Levels L0–L4
54
+
55
+ | Level | name | role | typical components |
56
+ | --- | --- | --- | --- |
57
+ | `L0` | 全局概览 | system-wide framing and the root claim | `Definition`, `Prerequisite`, `LearningObjectives`, `KeyQuestion`, `Input`/`Output`, `Callout`, `Tabs` |
58
+ | `L1` | 主要阶段 / 子系统 | major lifecycle stage or subsystem | `Definition`, `Input`/`Output`, `Example`, `Children` |
59
+ | `L2` | 局部机制 | concrete mechanism and flow | `Definition`, `Mechanism`, `Details`, `Evidence`, `Invariant`, `Tradeoff`, `FailureMode` |
60
+ | `L3` | 实现细节 | algorithm, data structure, or code | `Definition`, `Example`, `Glossary` |
61
+ | `L4` | 边界与反例 | boundary, exception, or counterexample | `Callout(danger/warn)`, `Example`, `FailureMode` |
62
+
63
+ ## Density targets
64
+
65
+ Match the exemplar (`assets/template/content/compile-runtime.mdx`):
66
+
67
+ - 1 `L0` root, 4–5 `L1` branches, 2–3 children per branch, ~15–25 nodes total.
68
+ - Every key node: 3–6 semantic components; `L3`/`L4` nodes may be leaner but still concrete.
69
+ - At least a few nodes per document carry `Evidence`, `Invariant`, or `FailureMode` with a real command/observation.
70
+ - ~8–15 cross-branch `Relation`s, each with a `label`.
71
+
72
+ ## Request priorities
73
+
74
+ The portable template covers the core `req.md` contract, node exploration, graph view, semantic relations, search, level/relation filters, URL node state, browsing history, keyboard shortcuts, Mermaid zoom, and responsive rendering. Evidence-oriented components and compact tradeoffs cover the highest-value `request.md` enhancements without adding permanent side panels.