spec-gen-cli 1.1.0 → 1.2.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 +163 -77
- package/dist/api/analyze.d.ts.map +1 -1
- package/dist/api/analyze.js +56 -27
- package/dist/api/analyze.js.map +1 -1
- package/dist/api/drift.d.ts.map +1 -1
- package/dist/api/drift.js +26 -20
- package/dist/api/drift.js.map +1 -1
- package/dist/api/generate.d.ts.map +1 -1
- package/dist/api/generate.js +19 -43
- package/dist/api/generate.js.map +1 -1
- package/dist/api/init.d.ts.map +1 -1
- package/dist/api/init.js +6 -5
- package/dist/api/init.js.map +1 -1
- package/dist/api/run.d.ts.map +1 -1
- package/dist/api/run.js +67 -51
- package/dist/api/run.js.map +1 -1
- package/dist/api/specs.d.ts.map +1 -1
- package/dist/api/specs.js +5 -4
- package/dist/api/specs.js.map +1 -1
- package/dist/api/types.d.ts +7 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/verify.d.ts.map +1 -1
- package/dist/api/verify.js +31 -32
- package/dist/api/verify.js.map +1 -1
- package/dist/cli/commands/analyze.d.ts.map +1 -1
- package/dist/cli/commands/analyze.js +41 -62
- package/dist/cli/commands/analyze.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +9 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +273 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/drift.d.ts.map +1 -1
- package/dist/cli/commands/drift.js +18 -32
- package/dist/cli/commands/drift.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +40 -101
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +17 -15
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +2 -1
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/run.d.ts +0 -15
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +61 -111
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +18 -52
- package/dist/cli/commands/verify.js.map +1 -1
- package/dist/cli/commands/view.d.ts +4 -0
- package/dist/cli/commands/view.d.ts.map +1 -1
- package/dist/cli/commands/view.js +29 -24
- package/dist/cli/commands/view.js.map +1 -1
- package/dist/cli/index.js +22 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/constants.d.ts +254 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +320 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/analyzer/artifact-generator.d.ts +8 -0
- package/dist/core/analyzer/artifact-generator.d.ts.map +1 -1
- package/dist/core/analyzer/artifact-generator.js +59 -11
- package/dist/core/analyzer/artifact-generator.js.map +1 -1
- package/dist/core/analyzer/call-graph.d.ts.map +1 -1
- package/dist/core/analyzer/call-graph.js +135 -1
- package/dist/core/analyzer/call-graph.js.map +1 -1
- package/dist/core/analyzer/dependency-graph.d.ts +32 -0
- package/dist/core/analyzer/dependency-graph.d.ts.map +1 -1
- package/dist/core/analyzer/dependency-graph.js +104 -10
- package/dist/core/analyzer/dependency-graph.js.map +1 -1
- package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -1
- package/dist/core/analyzer/duplicate-detector.js +4 -0
- package/dist/core/analyzer/duplicate-detector.js.map +1 -1
- package/dist/core/analyzer/embedding-service.d.ts +6 -0
- package/dist/core/analyzer/embedding-service.d.ts.map +1 -1
- package/dist/core/analyzer/embedding-service.js +15 -1
- package/dist/core/analyzer/embedding-service.js.map +1 -1
- package/dist/core/analyzer/file-walker.d.ts.map +1 -1
- package/dist/core/analyzer/file-walker.js +4 -3
- package/dist/core/analyzer/file-walker.js.map +1 -1
- package/dist/core/analyzer/http-route-parser.d.ts +111 -0
- package/dist/core/analyzer/http-route-parser.d.ts.map +1 -0
- package/dist/core/analyzer/http-route-parser.js +466 -0
- package/dist/core/analyzer/http-route-parser.js.map +1 -0
- package/dist/core/analyzer/import-parser.d.ts.map +1 -1
- package/dist/core/analyzer/import-parser.js +19 -5
- package/dist/core/analyzer/import-parser.js.map +1 -1
- package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -1
- package/dist/core/analyzer/refactor-analyzer.js +8 -7
- package/dist/core/analyzer/refactor-analyzer.js.map +1 -1
- package/dist/core/analyzer/repository-mapper.d.ts.map +1 -1
- package/dist/core/analyzer/repository-mapper.js +12 -13
- package/dist/core/analyzer/repository-mapper.js.map +1 -1
- package/dist/core/analyzer/signature-extractor.d.ts +1 -1
- package/dist/core/analyzer/signature-extractor.d.ts.map +1 -1
- package/dist/core/analyzer/signature-extractor.js +69 -1
- package/dist/core/analyzer/signature-extractor.js.map +1 -1
- package/dist/core/analyzer/spec-vector-index.d.ts.map +1 -1
- package/dist/core/analyzer/spec-vector-index.js +4 -3
- package/dist/core/analyzer/spec-vector-index.js.map +1 -1
- package/dist/core/analyzer/vector-index.d.ts.map +1 -1
- package/dist/core/analyzer/vector-index.js +29 -1
- package/dist/core/analyzer/vector-index.js.map +1 -1
- package/dist/core/drift/drift-detector.d.ts.map +1 -1
- package/dist/core/drift/drift-detector.js +7 -6
- package/dist/core/drift/drift-detector.js.map +1 -1
- package/dist/core/drift/git-diff.d.ts.map +1 -1
- package/dist/core/drift/git-diff.js +28 -16
- package/dist/core/drift/git-diff.js.map +1 -1
- package/dist/core/generator/mapping-generator.d.ts.map +1 -1
- package/dist/core/generator/mapping-generator.js +11 -10
- package/dist/core/generator/mapping-generator.js.map +1 -1
- package/dist/core/generator/openspec-compat.d.ts.map +1 -1
- package/dist/core/generator/openspec-compat.js +3 -2
- package/dist/core/generator/openspec-compat.js.map +1 -1
- package/dist/core/generator/openspec-format-generator.js.map +1 -1
- package/dist/core/generator/openspec-writer.d.ts +0 -4
- package/dist/core/generator/openspec-writer.d.ts.map +1 -1
- package/dist/core/generator/openspec-writer.js +30 -41
- package/dist/core/generator/openspec-writer.js.map +1 -1
- package/dist/core/generator/spec-pipeline.d.ts.map +1 -1
- package/dist/core/generator/spec-pipeline.js +4 -4
- package/dist/core/generator/spec-pipeline.js.map +1 -1
- package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -1
- package/dist/core/generator/stages/stage1-survey.js +5 -3
- package/dist/core/generator/stages/stage1-survey.js.map +1 -1
- package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -1
- package/dist/core/generator/stages/stage2-entities.js +10 -9
- package/dist/core/generator/stages/stage2-entities.js.map +1 -1
- package/dist/core/generator/stages/stage3-services.d.ts.map +1 -1
- package/dist/core/generator/stages/stage3-services.js +9 -8
- package/dist/core/generator/stages/stage3-services.js.map +1 -1
- package/dist/core/generator/stages/stage4-api.d.ts.map +1 -1
- package/dist/core/generator/stages/stage4-api.js +10 -9
- package/dist/core/generator/stages/stage4-api.js.map +1 -1
- package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -1
- package/dist/core/generator/stages/stage5-architecture.js +5 -4
- package/dist/core/generator/stages/stage5-architecture.js.map +1 -1
- package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -1
- package/dist/core/generator/stages/stage6-adr.js +7 -2
- package/dist/core/generator/stages/stage6-adr.js.map +1 -1
- package/dist/core/services/chat-agent.d.ts.map +1 -1
- package/dist/core/services/chat-agent.js +46 -15
- package/dist/core/services/chat-agent.js.map +1 -1
- package/dist/core/services/config-manager.d.ts.map +1 -1
- package/dist/core/services/config-manager.js +32 -26
- package/dist/core/services/config-manager.js.map +1 -1
- package/dist/core/services/gitignore-manager.d.ts.map +1 -1
- package/dist/core/services/gitignore-manager.js +2 -13
- package/dist/core/services/gitignore-manager.js.map +1 -1
- package/dist/core/services/llm-service.d.ts.map +1 -1
- package/dist/core/services/llm-service.js +33 -35
- package/dist/core/services/llm-service.js.map +1 -1
- package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/analysis.js +23 -14
- package/dist/core/services/mcp-handlers/analysis.js.map +1 -1
- package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/graph.js +24 -23
- package/dist/core/services/mcp-handlers/graph.js.map +1 -1
- package/dist/core/services/mcp-handlers/semantic.d.ts +1 -1
- package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/semantic.js +17 -16
- package/dist/core/services/mcp-handlers/semantic.js.map +1 -1
- package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/utils.js +4 -3
- package/dist/core/services/mcp-handlers/utils.js.map +1 -1
- package/dist/core/services/project-detector.d.ts.map +1 -1
- package/dist/core/services/project-detector.js +2 -13
- package/dist/core/services/project-detector.js.map +1 -1
- package/dist/core/verifier/verification-engine.d.ts +9 -3
- package/dist/core/verifier/verification-engine.d.ts.map +1 -1
- package/dist/core/verifier/verification-engine.js +25 -13
- package/dist/core/verifier/verification-engine.js.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/command-helpers.d.ts +38 -0
- package/dist/utils/command-helpers.d.ts.map +1 -0
- package/dist/utils/command-helpers.js +82 -0
- package/dist/utils/command-helpers.js.map +1 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +4 -3
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +14 -3
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/progress.d.ts +1 -1
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +15 -12
- package/dist/utils/progress.js.map +1 -1
- package/dist/utils/shutdown.d.ts.map +1 -1
- package/dist/utils/shutdown.js +4 -3
- package/dist/utils/shutdown.js.map +1 -1
- package/package.json +9 -5
- package/src/viewer/InteractiveGraphViewer.jsx +182 -139
- package/src/viewer/components/ArchitectureView.jsx +19 -19
- package/src/viewer/components/ChatPanel.jsx +40 -40
- package/src/viewer/components/ClusterGraph.jsx +34 -22
- package/src/viewer/components/FilterBar.jsx +26 -26
- package/src/viewer/components/FlatGraph.jsx +22 -15
- package/src/viewer/components/MicroComponents.jsx +14 -12
- package/src/viewer/utils/constants.js +17 -0
- package/src/viewer/utils/graph-helpers.js +7 -3
- package/src/viewer/utils/graph-helpers.test.ts +39 -0
- package/src/viewer/utils/themes.js +170 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
function FToggle({ active, onChange, label, badge, activeColor = '
|
|
1
|
+
function FToggle({ active, onChange, label, badge, activeColor = 'var(--ac-primary)' }) {
|
|
2
2
|
return (
|
|
3
3
|
<button
|
|
4
4
|
onClick={() => onChange(!active)}
|
|
5
5
|
style={{
|
|
6
6
|
background: active ? `${activeColor}1a` : 'transparent',
|
|
7
|
-
border: `1px solid ${active ? activeColor : '
|
|
7
|
+
border: `1px solid ${active ? activeColor : 'var(--bd-muted)'}`,
|
|
8
8
|
borderRadius: 4,
|
|
9
|
-
color: active ? activeColor : '
|
|
9
|
+
color: active ? activeColor : 'var(--tx-ghost)',
|
|
10
10
|
padding: '2px 8px',
|
|
11
11
|
cursor: 'pointer',
|
|
12
12
|
fontSize: 9,
|
|
@@ -20,10 +20,10 @@ function FToggle({ active, onChange, label, badge, activeColor = '#7c6af7' }) {
|
|
|
20
20
|
{badge !== undefined && (
|
|
21
21
|
<span
|
|
22
22
|
style={{
|
|
23
|
-
background: '
|
|
23
|
+
background: 'var(--bg-input)',
|
|
24
24
|
borderRadius: 3,
|
|
25
25
|
padding: '0 4px',
|
|
26
|
-
color: '
|
|
26
|
+
color: 'var(--tx-ghost)',
|
|
27
27
|
fontSize: 8,
|
|
28
28
|
}}
|
|
29
29
|
>
|
|
@@ -49,13 +49,13 @@ export function FilterBar({ filters, setFilters, stats, clusterNames }) {
|
|
|
49
49
|
gap: 10,
|
|
50
50
|
flexWrap: 'wrap',
|
|
51
51
|
padding: '7px 18px',
|
|
52
|
-
borderBottom: '1px solid
|
|
53
|
-
background: '
|
|
52
|
+
borderBottom: '1px solid var(--bd-faint)',
|
|
53
|
+
background: 'var(--bg-base)',
|
|
54
54
|
flexShrink: 0,
|
|
55
55
|
fontSize: 9,
|
|
56
56
|
}}
|
|
57
57
|
>
|
|
58
|
-
<span style={{ color: '
|
|
58
|
+
<span style={{ color: 'var(--tx-faint)', letterSpacing: '0.08em', fontWeight: 700 }}>FILTERS</span>
|
|
59
59
|
|
|
60
60
|
<FToggle
|
|
61
61
|
active={filters.hideOrphans}
|
|
@@ -74,7 +74,7 @@ export function FilterBar({ filters, setFilters, stats, clusterNames }) {
|
|
|
74
74
|
/>
|
|
75
75
|
|
|
76
76
|
<div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
|
|
77
|
-
<span style={{ color: '
|
|
77
|
+
<span style={{ color: 'var(--tx-faint)' }}>Score ≥</span>
|
|
78
78
|
<input
|
|
79
79
|
type="range"
|
|
80
80
|
min={0}
|
|
@@ -82,20 +82,20 @@ export function FilterBar({ filters, setFilters, stats, clusterNames }) {
|
|
|
82
82
|
step={5}
|
|
83
83
|
value={filters.minScore}
|
|
84
84
|
onChange={(e) => setFilters((f) => ({ ...f, minScore: +e.target.value }))}
|
|
85
|
-
style={{ width: 72, accentColor: '
|
|
85
|
+
style={{ width: 72, accentColor: 'var(--ac-primary)' }}
|
|
86
86
|
/>
|
|
87
|
-
<span style={{ color: '
|
|
87
|
+
<span style={{ color: 'var(--ac-primary)', minWidth: 16 }}>{filters.minScore}</span>
|
|
88
88
|
</div>
|
|
89
89
|
|
|
90
90
|
<div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
|
|
91
|
-
<span style={{ color: '
|
|
91
|
+
<span style={{ color: 'var(--tx-faint)' }}>Top</span>
|
|
92
92
|
<select
|
|
93
93
|
value={filters.topN}
|
|
94
94
|
onChange={(e) => setFilters((f) => ({ ...f, topN: +e.target.value }))}
|
|
95
95
|
style={{
|
|
96
|
-
background: '
|
|
97
|
-
border: '1px solid
|
|
98
|
-
color: '
|
|
96
|
+
background: 'var(--bg-input)',
|
|
97
|
+
border: '1px solid var(--bd-muted)',
|
|
98
|
+
color: 'var(--tx-primary)',
|
|
99
99
|
borderRadius: 4,
|
|
100
100
|
padding: '2px 5px',
|
|
101
101
|
fontSize: 9,
|
|
@@ -108,18 +108,18 @@ export function FilterBar({ filters, setFilters, stats, clusterNames }) {
|
|
|
108
108
|
</option>
|
|
109
109
|
))}
|
|
110
110
|
</select>
|
|
111
|
-
<span style={{ color: '
|
|
111
|
+
<span style={{ color: 'var(--tx-faint)' }}>nodes</span>
|
|
112
112
|
</div>
|
|
113
113
|
|
|
114
114
|
<div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
|
|
115
|
-
<span style={{ color: '
|
|
115
|
+
<span style={{ color: 'var(--tx-faint)' }}>Cluster</span>
|
|
116
116
|
<select
|
|
117
117
|
value={filters.cluster}
|
|
118
118
|
onChange={(e) => setFilters((f) => ({ ...f, cluster: e.target.value }))}
|
|
119
119
|
style={{
|
|
120
|
-
background: '
|
|
121
|
-
border: '1px solid
|
|
122
|
-
color: '
|
|
120
|
+
background: 'var(--bg-input)',
|
|
121
|
+
border: '1px solid var(--bd-muted)',
|
|
122
|
+
color: 'var(--tx-primary)',
|
|
123
123
|
borderRadius: 4,
|
|
124
124
|
padding: '2px 5px',
|
|
125
125
|
fontSize: 9,
|
|
@@ -149,9 +149,9 @@ export function FilterBar({ filters, setFilters, stats, clusterNames }) {
|
|
|
149
149
|
}
|
|
150
150
|
style={{
|
|
151
151
|
background: 'none',
|
|
152
|
-
border: '1px solid
|
|
152
|
+
border: '1px solid var(--tx-faint)',
|
|
153
153
|
borderRadius: 4,
|
|
154
|
-
color: '
|
|
154
|
+
color: 'var(--tx-ghost)',
|
|
155
155
|
padding: '2px 7px',
|
|
156
156
|
cursor: 'pointer',
|
|
157
157
|
fontSize: 9,
|
|
@@ -162,13 +162,13 @@ export function FilterBar({ filters, setFilters, stats, clusterNames }) {
|
|
|
162
162
|
</button>
|
|
163
163
|
)}
|
|
164
164
|
|
|
165
|
-
<div style={{ marginLeft: 'auto', color: '
|
|
165
|
+
<div style={{ marginLeft: 'auto', color: 'var(--tx-faint)', display: 'flex', gap: 8 }}>
|
|
166
166
|
<span>
|
|
167
|
-
<span style={{ color: '
|
|
168
|
-
<span style={{ color: '
|
|
167
|
+
<span style={{ color: 'var(--ac-primary)' }}>{stats.visible}</span>/
|
|
168
|
+
<span style={{ color: 'var(--tx-dim)' }}>{stats.total}</span> nodes
|
|
169
169
|
</span>
|
|
170
170
|
<span>
|
|
171
|
-
<span style={{ color: '
|
|
171
|
+
<span style={{ color: 'var(--ac-teal)' }}>{stats.visibleEdges}</span> edges
|
|
172
172
|
</span>
|
|
173
173
|
{stats.orphanCount > 0 && !filters.hideOrphans && (
|
|
174
174
|
<span style={{ color: '#f77c6a66' }}>{stats.orphanCount} orphans</span>
|
|
@@ -12,6 +12,7 @@ export function FlatGraph({
|
|
|
12
12
|
onSelect,
|
|
13
13
|
refactorOnly,
|
|
14
14
|
linkedIds,
|
|
15
|
+
noGlow,
|
|
15
16
|
}) {
|
|
16
17
|
const posRef = useRef(null);
|
|
17
18
|
const prevKey = useRef(null);
|
|
@@ -68,16 +69,16 @@ export function FlatGraph({
|
|
|
68
69
|
>
|
|
69
70
|
<defs>
|
|
70
71
|
<marker id="arr" markerWidth="6" markerHeight="6" refX="5" refY="2.5" orient="auto">
|
|
71
|
-
<path d="M0,0 L0,5 L6,2.5z"
|
|
72
|
+
<path d="M0,0 L0,5 L6,2.5z" style={{ fill: 'var(--ac-arrow)' }} />
|
|
72
73
|
</marker>
|
|
73
74
|
<marker id="arr-sel" markerWidth="6" markerHeight="6" refX="5" refY="2.5" orient="auto">
|
|
74
|
-
<path d="M0,0 L0,5 L6,2.5z"
|
|
75
|
+
<path d="M0,0 L0,5 L6,2.5z" style={{ fill: 'var(--ac-primary)' }} />
|
|
75
76
|
</marker>
|
|
76
77
|
<marker id="arr-aff" markerWidth="6" markerHeight="6" refX="5" refY="2.5" orient="auto">
|
|
77
78
|
<path d="M0,0 L0,5 L6,2.5z" fill="#f77c6a" />
|
|
78
79
|
</marker>
|
|
79
80
|
<marker id="arr-type" markerWidth="6" markerHeight="6" refX="5" refY="2.5" orient="auto">
|
|
80
|
-
<path d="M0,0 L0,5 L6,2.5z"
|
|
81
|
+
<path d="M0,0 L0,5 L6,2.5z" style={{ fill: 'var(--ac-edge-type)' }} />
|
|
81
82
|
</marker>
|
|
82
83
|
<filter id="glow">
|
|
83
84
|
<feGaussianBlur stdDeviation="4" result="b" />
|
|
@@ -110,6 +111,7 @@ export function FlatGraph({
|
|
|
110
111
|
const cy = pts.reduce((s, p) => s + p.y, 0) / pts.length;
|
|
111
112
|
const r =
|
|
112
113
|
Math.max(...pts.map((p) => Math.sqrt((p.x - cx) ** 2 + (p.y - cy) ** 2)), 20) + 28;
|
|
114
|
+
const clusterName = cn[0]?.cluster?.name ?? cid;
|
|
113
115
|
return (
|
|
114
116
|
<ellipse
|
|
115
117
|
key={cid}
|
|
@@ -117,11 +119,13 @@ export function FlatGraph({
|
|
|
117
119
|
cy={cy}
|
|
118
120
|
rx={r}
|
|
119
121
|
ry={r * 0.85}
|
|
120
|
-
fill={`${color}07`}
|
|
121
|
-
stroke={`${color}18`}
|
|
122
|
+
fill={noGlow ? 'none' : `${color}07`}
|
|
123
|
+
stroke={`${color}${noGlow ? '40' : '18'}`}
|
|
122
124
|
strokeWidth={1}
|
|
123
125
|
strokeDasharray="5 3"
|
|
124
|
-
|
|
126
|
+
>
|
|
127
|
+
<title>{clusterName} — {cn.length} file{cn.length !== 1 ? 's' : ''}</title>
|
|
128
|
+
</ellipse>
|
|
125
129
|
);
|
|
126
130
|
})}
|
|
127
131
|
|
|
@@ -138,6 +142,8 @@ export function FlatGraph({
|
|
|
138
142
|
nr = 18;
|
|
139
143
|
const isSel = e.source === selectedId || e.target === selectedId;
|
|
140
144
|
const isAff = affectedIds.includes(e.target) && e.source === selectedId;
|
|
145
|
+
const isDimEdge = focusedIds.length > 0 && !isSel &&
|
|
146
|
+
!focusedIds.includes(e.source) && !focusedIds.includes(e.target);
|
|
141
147
|
return (
|
|
142
148
|
<line
|
|
143
149
|
key={e.id}
|
|
@@ -145,9 +151,9 @@ export function FlatGraph({
|
|
|
145
151
|
y1={s.y + ny * nr}
|
|
146
152
|
x2={t.x - nx * (nr + 5)}
|
|
147
153
|
y2={t.y - ny * (nr + 5)}
|
|
148
|
-
stroke={isSel ? '
|
|
154
|
+
stroke={isSel ? 'var(--ac-primary)' : isAff ? '#f77c6a' : e.isType ? 'var(--ac-edge-type)' : 'var(--bd-edge)'}
|
|
149
155
|
strokeWidth={isSel ? 1.5 : isAff ? 1.2 : 0.8}
|
|
150
|
-
strokeOpacity={isSel ? 0.9 : isAff ? 0.7 : e.isType ? 0.35 : 0.55}
|
|
156
|
+
strokeOpacity={isDimEdge ? 0.08 : isSel ? 0.9 : isAff ? 0.7 : e.isType ? 0.35 : 0.55}
|
|
151
157
|
strokeDasharray={e.isType ? '4 2' : undefined}
|
|
152
158
|
markerEnd={
|
|
153
159
|
isSel
|
|
@@ -189,8 +195,9 @@ export function FlatGraph({
|
|
|
189
195
|
}}
|
|
190
196
|
style={{ cursor: 'pointer' }}
|
|
191
197
|
opacity={isDim ? 0.08 : isZeroScore ? 0.18 : 1}
|
|
192
|
-
filter={isSel ? 'url(#glow)' : isAff ? 'url(#glow-aff)' : undefined}
|
|
198
|
+
filter={noGlow ? undefined : isSel ? 'url(#glow)' : isAff ? 'url(#glow-aff)' : undefined}
|
|
193
199
|
>
|
|
200
|
+
<title>{n.label}{n.path ? `\n${n.path}` : ''}</title>
|
|
194
201
|
<circle
|
|
195
202
|
r={r + 4}
|
|
196
203
|
fill="none"
|
|
@@ -209,8 +216,8 @@ export function FlatGraph({
|
|
|
209
216
|
)}
|
|
210
217
|
<circle
|
|
211
218
|
r={r}
|
|
212
|
-
fill={isSel ? `${col}1a` : isAff ? `${col}0d` : '
|
|
213
|
-
stroke={isSel ? col : isAff ? col : '
|
|
219
|
+
fill={isSel ? `${col}1a` : isAff ? `${col}0d` : 'var(--bg-node)'}
|
|
220
|
+
stroke={isSel ? col : isAff ? col : 'var(--bd-edge)'}
|
|
214
221
|
strokeWidth={isSel ? 2.5 : isAff ? 2 : 0.8}
|
|
215
222
|
/>
|
|
216
223
|
{n.isEntry && (
|
|
@@ -228,7 +235,7 @@ export function FlatGraph({
|
|
|
228
235
|
dominantBaseline="middle"
|
|
229
236
|
fontSize={7}
|
|
230
237
|
fontWeight={isSel ? 700 : 400}
|
|
231
|
-
fill={isSel ? '
|
|
238
|
+
fill={isSel ? 'var(--tx-node-sel)' : isAff ? col : 'var(--tx-node)'}
|
|
232
239
|
fontFamily="'JetBrains Mono',monospace"
|
|
233
240
|
style={{ pointerEvents: 'none' }}
|
|
234
241
|
>
|
|
@@ -257,11 +264,11 @@ export function FlatGraph({
|
|
|
257
264
|
style={{
|
|
258
265
|
fontSize: 8,
|
|
259
266
|
padding: '2px 6px',
|
|
260
|
-
background: '
|
|
261
|
-
border: `1px solid ${transform.x !== 0 || transform.y !== 0 || transform.k !== 1 ? '
|
|
267
|
+
background: 'var(--bg-input)',
|
|
268
|
+
border: `1px solid ${transform.x !== 0 || transform.y !== 0 || transform.k !== 1 ? 'var(--ac-primary)' : 'var(--bd-muted)'}`,
|
|
262
269
|
borderRadius: 4,
|
|
263
270
|
color:
|
|
264
|
-
transform.x !== 0 || transform.y !== 0 || transform.k !== 1 ? '
|
|
271
|
+
transform.x !== 0 || transform.y !== 0 || transform.k !== 1 ? 'var(--ac-primary)' : 'var(--tx-faint)',
|
|
265
272
|
cursor: 'pointer',
|
|
266
273
|
fontFamily: "'JetBrains Mono',monospace",
|
|
267
274
|
letterSpacing: '0.05em',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function Hint({ children }) {
|
|
2
2
|
return (
|
|
3
|
-
<div style={{ fontSize: 10, color: '
|
|
3
|
+
<div style={{ fontSize: 10, color: 'var(--tx-faint)', lineHeight: 1.8, marginTop: 4 }}>{children}</div>
|
|
4
4
|
);
|
|
5
5
|
}
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ export function SL({ children }) {
|
|
|
9
9
|
<div
|
|
10
10
|
style={{
|
|
11
11
|
fontSize: 8,
|
|
12
|
-
color: '
|
|
12
|
+
color: 'var(--tx-ghost)',
|
|
13
13
|
letterSpacing: '0.08em',
|
|
14
14
|
marginTop: 12,
|
|
15
15
|
marginBottom: 5,
|
|
@@ -29,11 +29,11 @@ export function Row({ label, value }) {
|
|
|
29
29
|
justifyContent: 'space-between',
|
|
30
30
|
alignItems: 'center',
|
|
31
31
|
padding: '3px 0',
|
|
32
|
-
borderBottom: '1px solid
|
|
32
|
+
borderBottom: '1px solid var(--bd-dim)',
|
|
33
33
|
}}
|
|
34
34
|
>
|
|
35
|
-
<span style={{ fontSize: 9, color: '
|
|
36
|
-
<span style={{ fontSize: 9, color: '
|
|
35
|
+
<span style={{ fontSize: 9, color: 'var(--tx-dim)' }}>{label}</span>
|
|
36
|
+
<span style={{ fontSize: 9, color: 'var(--tx-secondary)' }}>{value}</span>
|
|
37
37
|
</div>
|
|
38
38
|
);
|
|
39
39
|
}
|
|
@@ -58,20 +58,22 @@ export function Chip({ color, children }) {
|
|
|
58
58
|
|
|
59
59
|
export function KindBadge({ kind }) {
|
|
60
60
|
const map = {
|
|
61
|
-
class:
|
|
62
|
-
function:
|
|
63
|
-
interface:
|
|
64
|
-
type:
|
|
65
|
-
enum:
|
|
61
|
+
class: '#a78bfa',
|
|
62
|
+
function: '#4ecdc4',
|
|
63
|
+
interface: '#60a5fa',
|
|
64
|
+
type: '#f472b6',
|
|
65
|
+
enum: '#f5c518',
|
|
66
|
+
const: '#fb923c',
|
|
66
67
|
};
|
|
67
|
-
const
|
|
68
|
+
const c = map[kind] || '#64748b';
|
|
68
69
|
return (
|
|
69
70
|
<span
|
|
70
71
|
style={{
|
|
71
72
|
fontSize: 7,
|
|
72
73
|
padding: '1px 5px',
|
|
73
74
|
borderRadius: 3,
|
|
74
|
-
background:
|
|
75
|
+
background: `${c}18`,
|
|
76
|
+
border: `1px solid ${c}45`,
|
|
75
77
|
color: c,
|
|
76
78
|
minWidth: 44,
|
|
77
79
|
textAlign: 'center',
|
|
@@ -14,6 +14,23 @@ export const CLUSTER_PALETTE = [
|
|
|
14
14
|
'#ffb347',
|
|
15
15
|
];
|
|
16
16
|
|
|
17
|
+
/** Darker variant of CLUSTER_PALETTE for light backgrounds — same hues, lower luminance. */
|
|
18
|
+
export const CLUSTER_PALETTE_LIGHT = [
|
|
19
|
+
'#4a34d4',
|
|
20
|
+
'#0e8f8f',
|
|
21
|
+
'#c0412a',
|
|
22
|
+
'#1a9e50',
|
|
23
|
+
'#c08010',
|
|
24
|
+
'#b82090',
|
|
25
|
+
'#1a6ec0',
|
|
26
|
+
'#6e9800',
|
|
27
|
+
'#b05010',
|
|
28
|
+
'#4040c0',
|
|
29
|
+
'#c01060',
|
|
30
|
+
'#007a60',
|
|
31
|
+
'#b07000',
|
|
32
|
+
];
|
|
33
|
+
|
|
17
34
|
export const EXT_COLOR = {
|
|
18
35
|
'.ts': '#4ecdc4',
|
|
19
36
|
'.tsx': '#3ecfcf',
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { CLUSTER_PALETTE } from './constants.js';
|
|
2
|
+
export { CLUSTER_PALETTE } from './constants.js';
|
|
3
|
+
export { CLUSTER_PALETTE_LIGHT } from './constants.js';
|
|
2
4
|
|
|
3
5
|
export function parseSpecRequirements(mdText) {
|
|
4
6
|
const reqs = {};
|
|
@@ -39,7 +41,7 @@ export function normalizePath(p) {
|
|
|
39
41
|
return (p || '').replace(/\\/g, '/').replace(/^\/+/, '');
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
export function parseGraph(raw) {
|
|
44
|
+
export function parseGraph(raw, palette = CLUSTER_PALETTE) {
|
|
43
45
|
const clusterByNode = {};
|
|
44
46
|
(raw.clusters || []).forEach((cl, ci) => {
|
|
45
47
|
cl.files.forEach((fid) => {
|
|
@@ -47,7 +49,7 @@ export function parseGraph(raw) {
|
|
|
47
49
|
name: cl.name,
|
|
48
50
|
index: ci,
|
|
49
51
|
id: cl.id,
|
|
50
|
-
color:
|
|
52
|
+
color: palette[ci % palette.length],
|
|
51
53
|
};
|
|
52
54
|
});
|
|
53
55
|
});
|
|
@@ -83,13 +85,15 @@ export function parseGraph(raw) {
|
|
|
83
85
|
id: cl.id,
|
|
84
86
|
name: cl.name,
|
|
85
87
|
files: cl.files,
|
|
86
|
-
color:
|
|
88
|
+
color: palette[ci % palette.length],
|
|
87
89
|
}));
|
|
88
90
|
|
|
89
91
|
return {
|
|
90
92
|
nodes,
|
|
91
93
|
edges,
|
|
92
94
|
clusters,
|
|
95
|
+
structuralClusters: raw.structuralClusters || [],
|
|
96
|
+
directoryClusters: raw.directoryClusters || [],
|
|
93
97
|
statistics: raw.statistics || {},
|
|
94
98
|
rankings: raw.rankings || {},
|
|
95
99
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { parseGraph } from './graph-helpers.js';
|
|
2
|
+
|
|
3
|
+
describe('parseGraph', () => {
|
|
4
|
+
it('should parse structuralClusters and directoryClusters from raw data', () => {
|
|
5
|
+
const raw = {
|
|
6
|
+
nodes: [],
|
|
7
|
+
edges: [],
|
|
8
|
+
clusters: [],
|
|
9
|
+
structuralClusters: [
|
|
10
|
+
{ id: 'cluster1', name: 'Structural Cluster 1', files: ['file1.js'], color: '#ff0000' },
|
|
11
|
+
],
|
|
12
|
+
directoryClusters: [
|
|
13
|
+
{ id: 'dir1', name: 'Directory Cluster 1', files: ['dir/file.js'], color: '#00ff00' },
|
|
14
|
+
],
|
|
15
|
+
statistics: {},
|
|
16
|
+
rankings: {},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const result = parseGraph(raw);
|
|
20
|
+
|
|
21
|
+
expect(result.structuralClusters).toEqual(raw.structuralClusters);
|
|
22
|
+
expect(result.directoryClusters).toEqual(raw.directoryClusters);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should provide empty arrays for missing structuralClusters and directoryClusters', () => {
|
|
26
|
+
const raw = {
|
|
27
|
+
nodes: [],
|
|
28
|
+
edges: [],
|
|
29
|
+
clusters: [],
|
|
30
|
+
statistics: {},
|
|
31
|
+
rankings: {},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const result = parseGraph(raw);
|
|
35
|
+
|
|
36
|
+
expect(result.structuralClusters).toEqual([]);
|
|
37
|
+
expect(result.directoryClusters).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Viewer themes — each theme is a map of CSS custom properties.
|
|
3
|
+
*
|
|
4
|
+
* The root <div> of InteractiveGraphViewer applies `theme.vars` as inline style,
|
|
5
|
+
* making all CSS variables available to every descendant via `var(--xxx)`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const THEMES = {
|
|
9
|
+
space: {
|
|
10
|
+
label: '⬡ space',
|
|
11
|
+
vars: {
|
|
12
|
+
'--bg-base': '#07091a',
|
|
13
|
+
'--bg-panel': '#080a1c',
|
|
14
|
+
'--bg-raised': '#0e1028',
|
|
15
|
+
'--bg-input': '#0d0f22',
|
|
16
|
+
'--bg-hover': '#131630',
|
|
17
|
+
'--bg-select': '#181b38',
|
|
18
|
+
'--bg-deep': '#080b1e',
|
|
19
|
+
'--bg-node': '#0b0d1e',
|
|
20
|
+
'--bd-faint': '#0f1224',
|
|
21
|
+
'--bd-muted': '#1a1f38',
|
|
22
|
+
'--bd-dim': '#0e1025',
|
|
23
|
+
'--bd-edge': '#181c36',
|
|
24
|
+
'--tx-primary': '#c8cde8',
|
|
25
|
+
'--tx-secondary': '#8890b0',
|
|
26
|
+
'--tx-bright': '#e0e4f0',
|
|
27
|
+
'--tx-muted': '#6a70a0',
|
|
28
|
+
'--tx-dim': '#3a4060',
|
|
29
|
+
'--tx-faint': '#2a2f4a',
|
|
30
|
+
'--tx-ghost': '#3a3f5c',
|
|
31
|
+
'--tx-node': '#5a6090',
|
|
32
|
+
'--tx-node-sel': '#ffffff',
|
|
33
|
+
'--ac-primary': '#7c6af7',
|
|
34
|
+
'--ac-teal': '#3ecfcf',
|
|
35
|
+
'--ac-edge-type': '#252a4a',
|
|
36
|
+
'--ac-arrow': '#1e2340',
|
|
37
|
+
'--ac-cluster-arr': '#2a3060',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
terminal: {
|
|
42
|
+
label: '▶ terminal',
|
|
43
|
+
vars: {
|
|
44
|
+
'--bg-base': '#010d01',
|
|
45
|
+
'--bg-panel': '#020e02',
|
|
46
|
+
'--bg-raised': '#061206',
|
|
47
|
+
'--bg-input': '#041004',
|
|
48
|
+
'--bg-hover': '#0a1a0a',
|
|
49
|
+
'--bg-select': '#0d200d',
|
|
50
|
+
'--bg-deep': '#030e03',
|
|
51
|
+
'--bg-node': '#040f04',
|
|
52
|
+
'--bd-faint': '#071807',
|
|
53
|
+
'--bd-muted': '#0f2f0f',
|
|
54
|
+
'--bd-dim': '#061806',
|
|
55
|
+
'--bd-edge': '#0d200d',
|
|
56
|
+
'--tx-primary': '#80ee80',
|
|
57
|
+
'--tx-secondary': '#50a050',
|
|
58
|
+
'--tx-bright': '#c0ffc0',
|
|
59
|
+
'--tx-muted': '#408040',
|
|
60
|
+
'--tx-dim': '#285028',
|
|
61
|
+
'--tx-faint': '#1a3a1a',
|
|
62
|
+
'--tx-ghost': '#204020',
|
|
63
|
+
'--tx-node': '#40704a',
|
|
64
|
+
'--tx-node-sel': '#c0ffc0',
|
|
65
|
+
'--ac-primary': '#4ade80',
|
|
66
|
+
'--ac-teal': '#00d4aa',
|
|
67
|
+
'--ac-edge-type': '#103020',
|
|
68
|
+
'--ac-arrow': '#0d2010',
|
|
69
|
+
'--ac-cluster-arr': '#143820',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
midnight: {
|
|
74
|
+
label: '◈ midnight',
|
|
75
|
+
vars: {
|
|
76
|
+
'--bg-base': '#000000',
|
|
77
|
+
'--bg-panel': '#050505',
|
|
78
|
+
'--bg-raised': '#0a0a0a',
|
|
79
|
+
'--bg-input': '#080808',
|
|
80
|
+
'--bg-hover': '#111111',
|
|
81
|
+
'--bg-select': '#161616',
|
|
82
|
+
'--bg-deep': '#060606',
|
|
83
|
+
'--bg-node': '#090909',
|
|
84
|
+
'--bd-faint': '#0e0e0e',
|
|
85
|
+
'--bd-muted': '#1c1c1c',
|
|
86
|
+
'--bd-dim': '#0c0c0c',
|
|
87
|
+
'--bd-edge': '#141414',
|
|
88
|
+
'--tx-primary': '#00d4e8',
|
|
89
|
+
'--tx-secondary': '#007a88',
|
|
90
|
+
'--tx-bright': '#80eeff',
|
|
91
|
+
'--tx-muted': '#005060',
|
|
92
|
+
'--tx-dim': '#003040',
|
|
93
|
+
'--tx-faint': '#001a20',
|
|
94
|
+
'--tx-ghost': '#002530',
|
|
95
|
+
'--tx-node': '#004558',
|
|
96
|
+
'--tx-node-sel': '#80eeff',
|
|
97
|
+
'--ac-primary': '#00e5ff',
|
|
98
|
+
'--ac-teal': '#00ff88',
|
|
99
|
+
'--ac-edge-type': '#001828',
|
|
100
|
+
'--ac-arrow': '#001020',
|
|
101
|
+
'--ac-cluster-arr': '#001e30',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
warm: {
|
|
106
|
+
label: '☀ warm',
|
|
107
|
+
vars: {
|
|
108
|
+
'--bg-base': '#1c1711',
|
|
109
|
+
'--bg-panel': '#221e17',
|
|
110
|
+
'--bg-raised': '#2a2318',
|
|
111
|
+
'--bg-input': '#261f14',
|
|
112
|
+
'--bg-hover': '#302818',
|
|
113
|
+
'--bg-select': '#38301e',
|
|
114
|
+
'--bg-deep': '#201b11',
|
|
115
|
+
'--bg-node': '#201a0e',
|
|
116
|
+
'--bd-faint': '#2a2218',
|
|
117
|
+
'--bd-muted': '#3a3020',
|
|
118
|
+
'--bd-dim': '#281f14',
|
|
119
|
+
'--bd-edge': '#302810',
|
|
120
|
+
'--tx-primary': '#d4c59a',
|
|
121
|
+
'--tx-secondary': '#9a8060',
|
|
122
|
+
'--tx-bright': '#ede0c0',
|
|
123
|
+
'--tx-muted': '#806840',
|
|
124
|
+
'--tx-dim': '#605038',
|
|
125
|
+
'--tx-faint': '#483820',
|
|
126
|
+
'--tx-ghost': '#504030',
|
|
127
|
+
'--tx-node': '#706040',
|
|
128
|
+
'--tx-node-sel': '#ede0c0',
|
|
129
|
+
'--ac-primary': '#e8a020',
|
|
130
|
+
'--ac-teal': '#50b060',
|
|
131
|
+
'--ac-edge-type': '#382808',
|
|
132
|
+
'--ac-arrow': '#302408',
|
|
133
|
+
'--ac-cluster-arr': '#402808',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
light: {
|
|
137
|
+
label: '○ light',
|
|
138
|
+
vars: {
|
|
139
|
+
'--bg-base': '#f4f5f9',
|
|
140
|
+
'--bg-panel': '#eef0f6',
|
|
141
|
+
'--bg-raised': '#ffffff',
|
|
142
|
+
'--bg-input': '#ffffff',
|
|
143
|
+
'--bg-hover': '#e8eaf2',
|
|
144
|
+
'--bg-select': '#dde0ef',
|
|
145
|
+
'--bg-deep': '#f0f1f8',
|
|
146
|
+
'--bg-node': '#f8f9fd',
|
|
147
|
+
'--bd-faint': '#dde0ec',
|
|
148
|
+
'--bd-muted': '#8890c8',
|
|
149
|
+
'--bd-dim': '#d0d4e8',
|
|
150
|
+
'--bd-edge': '#6870b0',
|
|
151
|
+
'--tx-primary': '#1a1c2e',
|
|
152
|
+
'--tx-secondary': '#3a3f60',
|
|
153
|
+
'--tx-bright': '#0a0c1a',
|
|
154
|
+
'--tx-muted': '#505580',
|
|
155
|
+
'--tx-dim': '#7880a8',
|
|
156
|
+
'--tx-faint': '#9098b8',
|
|
157
|
+
'--tx-ghost': '#a8aec8',
|
|
158
|
+
'--tx-node': '#1a1f40',
|
|
159
|
+
'--tx-node-sel': '#0a0c1a',
|
|
160
|
+
'--ac-primary': '#5b4de0',
|
|
161
|
+
'--ac-teal': '#0e9e9e',
|
|
162
|
+
'--ac-edge-type': '#8890c8',
|
|
163
|
+
'--ac-arrow': '#6870b0',
|
|
164
|
+
'--ac-cluster-arr': '#5b4de0',
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export const THEME_KEYS = Object.keys(THEMES);
|
|
170
|
+
export const DEFAULT_THEME = 'space';
|