spec-gen-cli 1.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.
Files changed (303) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1078 -0
  3. package/dist/api/analyze.d.ts +17 -0
  4. package/dist/api/analyze.d.ts.map +1 -0
  5. package/dist/api/analyze.js +109 -0
  6. package/dist/api/analyze.js.map +1 -0
  7. package/dist/api/drift.d.ts +21 -0
  8. package/dist/api/drift.d.ts.map +1 -0
  9. package/dist/api/drift.js +145 -0
  10. package/dist/api/drift.js.map +1 -0
  11. package/dist/api/generate.d.ts +18 -0
  12. package/dist/api/generate.d.ts.map +1 -0
  13. package/dist/api/generate.js +251 -0
  14. package/dist/api/generate.js.map +1 -0
  15. package/dist/api/index.d.ts +39 -0
  16. package/dist/api/index.d.ts.map +1 -0
  17. package/dist/api/index.js +32 -0
  18. package/dist/api/index.js.map +1 -0
  19. package/dist/api/init.d.ts +18 -0
  20. package/dist/api/init.d.ts.map +1 -0
  21. package/dist/api/init.js +82 -0
  22. package/dist/api/init.js.map +1 -0
  23. package/dist/api/run.d.ts +19 -0
  24. package/dist/api/run.d.ts.map +1 -0
  25. package/dist/api/run.js +291 -0
  26. package/dist/api/run.js.map +1 -0
  27. package/dist/api/specs.d.ts +49 -0
  28. package/dist/api/specs.d.ts.map +1 -0
  29. package/dist/api/specs.js +136 -0
  30. package/dist/api/specs.js.map +1 -0
  31. package/dist/api/types.d.ts +176 -0
  32. package/dist/api/types.d.ts.map +1 -0
  33. package/dist/api/types.js +9 -0
  34. package/dist/api/types.js.map +1 -0
  35. package/dist/api/verify.d.ts +20 -0
  36. package/dist/api/verify.d.ts.map +1 -0
  37. package/dist/api/verify.js +117 -0
  38. package/dist/api/verify.js.map +1 -0
  39. package/dist/cli/commands/analyze.d.ts +27 -0
  40. package/dist/cli/commands/analyze.d.ts.map +1 -0
  41. package/dist/cli/commands/analyze.js +485 -0
  42. package/dist/cli/commands/analyze.js.map +1 -0
  43. package/dist/cli/commands/drift.d.ts +9 -0
  44. package/dist/cli/commands/drift.d.ts.map +1 -0
  45. package/dist/cli/commands/drift.js +540 -0
  46. package/dist/cli/commands/drift.js.map +1 -0
  47. package/dist/cli/commands/generate.d.ts +9 -0
  48. package/dist/cli/commands/generate.d.ts.map +1 -0
  49. package/dist/cli/commands/generate.js +633 -0
  50. package/dist/cli/commands/generate.js.map +1 -0
  51. package/dist/cli/commands/init.d.ts +9 -0
  52. package/dist/cli/commands/init.d.ts.map +1 -0
  53. package/dist/cli/commands/init.js +171 -0
  54. package/dist/cli/commands/init.js.map +1 -0
  55. package/dist/cli/commands/mcp.d.ts +638 -0
  56. package/dist/cli/commands/mcp.d.ts.map +1 -0
  57. package/dist/cli/commands/mcp.js +574 -0
  58. package/dist/cli/commands/mcp.js.map +1 -0
  59. package/dist/cli/commands/run.d.ts +24 -0
  60. package/dist/cli/commands/run.d.ts.map +1 -0
  61. package/dist/cli/commands/run.js +546 -0
  62. package/dist/cli/commands/run.js.map +1 -0
  63. package/dist/cli/commands/verify.d.ts +9 -0
  64. package/dist/cli/commands/verify.d.ts.map +1 -0
  65. package/dist/cli/commands/verify.js +417 -0
  66. package/dist/cli/commands/verify.js.map +1 -0
  67. package/dist/cli/commands/view.d.ts +9 -0
  68. package/dist/cli/commands/view.d.ts.map +1 -0
  69. package/dist/cli/commands/view.js +511 -0
  70. package/dist/cli/commands/view.js.map +1 -0
  71. package/dist/cli/index.d.ts +9 -0
  72. package/dist/cli/index.d.ts.map +1 -0
  73. package/dist/cli/index.js +83 -0
  74. package/dist/cli/index.js.map +1 -0
  75. package/dist/core/analyzer/architecture-writer.d.ts +67 -0
  76. package/dist/core/analyzer/architecture-writer.d.ts.map +1 -0
  77. package/dist/core/analyzer/architecture-writer.js +209 -0
  78. package/dist/core/analyzer/architecture-writer.js.map +1 -0
  79. package/dist/core/analyzer/artifact-generator.d.ts +222 -0
  80. package/dist/core/analyzer/artifact-generator.d.ts.map +1 -0
  81. package/dist/core/analyzer/artifact-generator.js +726 -0
  82. package/dist/core/analyzer/artifact-generator.js.map +1 -0
  83. package/dist/core/analyzer/call-graph.d.ts +83 -0
  84. package/dist/core/analyzer/call-graph.d.ts.map +1 -0
  85. package/dist/core/analyzer/call-graph.js +827 -0
  86. package/dist/core/analyzer/call-graph.js.map +1 -0
  87. package/dist/core/analyzer/code-shaper.d.ts +33 -0
  88. package/dist/core/analyzer/code-shaper.d.ts.map +1 -0
  89. package/dist/core/analyzer/code-shaper.js +149 -0
  90. package/dist/core/analyzer/code-shaper.js.map +1 -0
  91. package/dist/core/analyzer/dependency-graph.d.ts +179 -0
  92. package/dist/core/analyzer/dependency-graph.d.ts.map +1 -0
  93. package/dist/core/analyzer/dependency-graph.js +574 -0
  94. package/dist/core/analyzer/dependency-graph.js.map +1 -0
  95. package/dist/core/analyzer/duplicate-detector.d.ts +52 -0
  96. package/dist/core/analyzer/duplicate-detector.d.ts.map +1 -0
  97. package/dist/core/analyzer/duplicate-detector.js +279 -0
  98. package/dist/core/analyzer/duplicate-detector.js.map +1 -0
  99. package/dist/core/analyzer/embedding-service.d.ts +50 -0
  100. package/dist/core/analyzer/embedding-service.d.ts.map +1 -0
  101. package/dist/core/analyzer/embedding-service.js +104 -0
  102. package/dist/core/analyzer/embedding-service.js.map +1 -0
  103. package/dist/core/analyzer/file-walker.d.ts +78 -0
  104. package/dist/core/analyzer/file-walker.d.ts.map +1 -0
  105. package/dist/core/analyzer/file-walker.js +531 -0
  106. package/dist/core/analyzer/file-walker.js.map +1 -0
  107. package/dist/core/analyzer/import-parser.d.ts +91 -0
  108. package/dist/core/analyzer/import-parser.d.ts.map +1 -0
  109. package/dist/core/analyzer/import-parser.js +720 -0
  110. package/dist/core/analyzer/import-parser.js.map +1 -0
  111. package/dist/core/analyzer/index.d.ts +10 -0
  112. package/dist/core/analyzer/index.d.ts.map +1 -0
  113. package/dist/core/analyzer/index.js +10 -0
  114. package/dist/core/analyzer/index.js.map +1 -0
  115. package/dist/core/analyzer/refactor-analyzer.d.ts +80 -0
  116. package/dist/core/analyzer/refactor-analyzer.d.ts.map +1 -0
  117. package/dist/core/analyzer/refactor-analyzer.js +339 -0
  118. package/dist/core/analyzer/refactor-analyzer.js.map +1 -0
  119. package/dist/core/analyzer/repository-mapper.d.ts +150 -0
  120. package/dist/core/analyzer/repository-mapper.d.ts.map +1 -0
  121. package/dist/core/analyzer/repository-mapper.js +731 -0
  122. package/dist/core/analyzer/repository-mapper.js.map +1 -0
  123. package/dist/core/analyzer/signature-extractor.d.ts +31 -0
  124. package/dist/core/analyzer/signature-extractor.d.ts.map +1 -0
  125. package/dist/core/analyzer/signature-extractor.js +387 -0
  126. package/dist/core/analyzer/signature-extractor.js.map +1 -0
  127. package/dist/core/analyzer/significance-scorer.d.ts +79 -0
  128. package/dist/core/analyzer/significance-scorer.d.ts.map +1 -0
  129. package/dist/core/analyzer/significance-scorer.js +407 -0
  130. package/dist/core/analyzer/significance-scorer.js.map +1 -0
  131. package/dist/core/analyzer/subgraph-extractor.d.ts +43 -0
  132. package/dist/core/analyzer/subgraph-extractor.d.ts.map +1 -0
  133. package/dist/core/analyzer/subgraph-extractor.js +129 -0
  134. package/dist/core/analyzer/subgraph-extractor.js.map +1 -0
  135. package/dist/core/analyzer/vector-index.d.ts +63 -0
  136. package/dist/core/analyzer/vector-index.d.ts.map +1 -0
  137. package/dist/core/analyzer/vector-index.js +169 -0
  138. package/dist/core/analyzer/vector-index.js.map +1 -0
  139. package/dist/core/drift/drift-detector.d.ts +102 -0
  140. package/dist/core/drift/drift-detector.d.ts.map +1 -0
  141. package/dist/core/drift/drift-detector.js +597 -0
  142. package/dist/core/drift/drift-detector.js.map +1 -0
  143. package/dist/core/drift/git-diff.d.ts +55 -0
  144. package/dist/core/drift/git-diff.d.ts.map +1 -0
  145. package/dist/core/drift/git-diff.js +356 -0
  146. package/dist/core/drift/git-diff.js.map +1 -0
  147. package/dist/core/drift/index.d.ts +12 -0
  148. package/dist/core/drift/index.d.ts.map +1 -0
  149. package/dist/core/drift/index.js +9 -0
  150. package/dist/core/drift/index.js.map +1 -0
  151. package/dist/core/drift/spec-mapper.d.ts +73 -0
  152. package/dist/core/drift/spec-mapper.d.ts.map +1 -0
  153. package/dist/core/drift/spec-mapper.js +353 -0
  154. package/dist/core/drift/spec-mapper.js.map +1 -0
  155. package/dist/core/generator/adr-generator.d.ts +32 -0
  156. package/dist/core/generator/adr-generator.d.ts.map +1 -0
  157. package/dist/core/generator/adr-generator.js +192 -0
  158. package/dist/core/generator/adr-generator.js.map +1 -0
  159. package/dist/core/generator/index.d.ts +9 -0
  160. package/dist/core/generator/index.d.ts.map +1 -0
  161. package/dist/core/generator/index.js +12 -0
  162. package/dist/core/generator/index.js.map +1 -0
  163. package/dist/core/generator/mapping-generator.d.ts +54 -0
  164. package/dist/core/generator/mapping-generator.d.ts.map +1 -0
  165. package/dist/core/generator/mapping-generator.js +239 -0
  166. package/dist/core/generator/mapping-generator.js.map +1 -0
  167. package/dist/core/generator/openspec-compat.d.ts +160 -0
  168. package/dist/core/generator/openspec-compat.d.ts.map +1 -0
  169. package/dist/core/generator/openspec-compat.js +523 -0
  170. package/dist/core/generator/openspec-compat.js.map +1 -0
  171. package/dist/core/generator/openspec-format-generator.d.ts +111 -0
  172. package/dist/core/generator/openspec-format-generator.d.ts.map +1 -0
  173. package/dist/core/generator/openspec-format-generator.js +817 -0
  174. package/dist/core/generator/openspec-format-generator.js.map +1 -0
  175. package/dist/core/generator/openspec-writer.d.ts +131 -0
  176. package/dist/core/generator/openspec-writer.d.ts.map +1 -0
  177. package/dist/core/generator/openspec-writer.js +379 -0
  178. package/dist/core/generator/openspec-writer.js.map +1 -0
  179. package/dist/core/generator/prompts.d.ts +35 -0
  180. package/dist/core/generator/prompts.d.ts.map +1 -0
  181. package/dist/core/generator/prompts.js +212 -0
  182. package/dist/core/generator/prompts.js.map +1 -0
  183. package/dist/core/generator/spec-pipeline.d.ts +94 -0
  184. package/dist/core/generator/spec-pipeline.d.ts.map +1 -0
  185. package/dist/core/generator/spec-pipeline.js +474 -0
  186. package/dist/core/generator/spec-pipeline.js.map +1 -0
  187. package/dist/core/generator/stages/stage1-survey.d.ts +19 -0
  188. package/dist/core/generator/stages/stage1-survey.d.ts.map +1 -0
  189. package/dist/core/generator/stages/stage1-survey.js +105 -0
  190. package/dist/core/generator/stages/stage1-survey.js.map +1 -0
  191. package/dist/core/generator/stages/stage2-entities.d.ts +11 -0
  192. package/dist/core/generator/stages/stage2-entities.d.ts.map +1 -0
  193. package/dist/core/generator/stages/stage2-entities.js +67 -0
  194. package/dist/core/generator/stages/stage2-entities.js.map +1 -0
  195. package/dist/core/generator/stages/stage3-services.d.ts +11 -0
  196. package/dist/core/generator/stages/stage3-services.d.ts.map +1 -0
  197. package/dist/core/generator/stages/stage3-services.js +75 -0
  198. package/dist/core/generator/stages/stage3-services.js.map +1 -0
  199. package/dist/core/generator/stages/stage4-api.d.ts +11 -0
  200. package/dist/core/generator/stages/stage4-api.d.ts.map +1 -0
  201. package/dist/core/generator/stages/stage4-api.js +65 -0
  202. package/dist/core/generator/stages/stage4-api.js.map +1 -0
  203. package/dist/core/generator/stages/stage5-architecture.d.ts +10 -0
  204. package/dist/core/generator/stages/stage5-architecture.d.ts.map +1 -0
  205. package/dist/core/generator/stages/stage5-architecture.js +62 -0
  206. package/dist/core/generator/stages/stage5-architecture.js.map +1 -0
  207. package/dist/core/generator/stages/stage6-adr.d.ts +8 -0
  208. package/dist/core/generator/stages/stage6-adr.d.ts.map +1 -0
  209. package/dist/core/generator/stages/stage6-adr.js +41 -0
  210. package/dist/core/generator/stages/stage6-adr.js.map +1 -0
  211. package/dist/core/services/chat-agent.d.ts +45 -0
  212. package/dist/core/services/chat-agent.d.ts.map +1 -0
  213. package/dist/core/services/chat-agent.js +310 -0
  214. package/dist/core/services/chat-agent.js.map +1 -0
  215. package/dist/core/services/chat-tools.d.ts +32 -0
  216. package/dist/core/services/chat-tools.d.ts.map +1 -0
  217. package/dist/core/services/chat-tools.js +270 -0
  218. package/dist/core/services/chat-tools.js.map +1 -0
  219. package/dist/core/services/config-manager.d.ts +61 -0
  220. package/dist/core/services/config-manager.d.ts.map +1 -0
  221. package/dist/core/services/config-manager.js +143 -0
  222. package/dist/core/services/config-manager.js.map +1 -0
  223. package/dist/core/services/gitignore-manager.d.ts +29 -0
  224. package/dist/core/services/gitignore-manager.d.ts.map +1 -0
  225. package/dist/core/services/gitignore-manager.js +106 -0
  226. package/dist/core/services/gitignore-manager.js.map +1 -0
  227. package/dist/core/services/index.d.ts +8 -0
  228. package/dist/core/services/index.d.ts.map +1 -0
  229. package/dist/core/services/index.js +8 -0
  230. package/dist/core/services/index.js.map +1 -0
  231. package/dist/core/services/llm-service.d.ts +336 -0
  232. package/dist/core/services/llm-service.d.ts.map +1 -0
  233. package/dist/core/services/llm-service.js +1155 -0
  234. package/dist/core/services/llm-service.js.map +1 -0
  235. package/dist/core/services/mcp-handlers/analysis.d.ts +42 -0
  236. package/dist/core/services/mcp-handlers/analysis.d.ts.map +1 -0
  237. package/dist/core/services/mcp-handlers/analysis.js +300 -0
  238. package/dist/core/services/mcp-handlers/analysis.js.map +1 -0
  239. package/dist/core/services/mcp-handlers/graph.d.ts +65 -0
  240. package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -0
  241. package/dist/core/services/mcp-handlers/graph.js +509 -0
  242. package/dist/core/services/mcp-handlers/graph.js.map +1 -0
  243. package/dist/core/services/mcp-handlers/semantic.d.ts +38 -0
  244. package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -0
  245. package/dist/core/services/mcp-handlers/semantic.js +172 -0
  246. package/dist/core/services/mcp-handlers/semantic.js.map +1 -0
  247. package/dist/core/services/mcp-handlers/utils.d.ts +21 -0
  248. package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -0
  249. package/dist/core/services/mcp-handlers/utils.js +62 -0
  250. package/dist/core/services/mcp-handlers/utils.js.map +1 -0
  251. package/dist/core/services/project-detector.d.ts +32 -0
  252. package/dist/core/services/project-detector.d.ts.map +1 -0
  253. package/dist/core/services/project-detector.js +111 -0
  254. package/dist/core/services/project-detector.js.map +1 -0
  255. package/dist/core/verifier/index.d.ts +5 -0
  256. package/dist/core/verifier/index.d.ts.map +1 -0
  257. package/dist/core/verifier/index.js +5 -0
  258. package/dist/core/verifier/index.js.map +1 -0
  259. package/dist/core/verifier/verification-engine.d.ts +226 -0
  260. package/dist/core/verifier/verification-engine.d.ts.map +1 -0
  261. package/dist/core/verifier/verification-engine.js +681 -0
  262. package/dist/core/verifier/verification-engine.js.map +1 -0
  263. package/dist/types/index.d.ts +252 -0
  264. package/dist/types/index.d.ts.map +1 -0
  265. package/dist/types/index.js +5 -0
  266. package/dist/types/index.js.map +1 -0
  267. package/dist/types/pipeline.d.ts +148 -0
  268. package/dist/types/pipeline.d.ts.map +1 -0
  269. package/dist/types/pipeline.js +5 -0
  270. package/dist/types/pipeline.js.map +1 -0
  271. package/dist/utils/errors.d.ts +51 -0
  272. package/dist/utils/errors.d.ts.map +1 -0
  273. package/dist/utils/errors.js +128 -0
  274. package/dist/utils/errors.js.map +1 -0
  275. package/dist/utils/logger.d.ts +149 -0
  276. package/dist/utils/logger.d.ts.map +1 -0
  277. package/dist/utils/logger.js +331 -0
  278. package/dist/utils/logger.js.map +1 -0
  279. package/dist/utils/progress.d.ts +142 -0
  280. package/dist/utils/progress.d.ts.map +1 -0
  281. package/dist/utils/progress.js +280 -0
  282. package/dist/utils/progress.js.map +1 -0
  283. package/dist/utils/prompts.d.ts +53 -0
  284. package/dist/utils/prompts.d.ts.map +1 -0
  285. package/dist/utils/prompts.js +199 -0
  286. package/dist/utils/prompts.js.map +1 -0
  287. package/dist/utils/shutdown.d.ts +89 -0
  288. package/dist/utils/shutdown.d.ts.map +1 -0
  289. package/dist/utils/shutdown.js +237 -0
  290. package/dist/utils/shutdown.js.map +1 -0
  291. package/package.json +114 -0
  292. package/src/viewer/InteractiveGraphViewer.jsx +1486 -0
  293. package/src/viewer/app/index.html +17 -0
  294. package/src/viewer/app/main.jsx +13 -0
  295. package/src/viewer/components/ArchitectureView.jsx +177 -0
  296. package/src/viewer/components/ChatPanel.jsx +448 -0
  297. package/src/viewer/components/ClusterGraph.jsx +441 -0
  298. package/src/viewer/components/FilterBar.jsx +179 -0
  299. package/src/viewer/components/FlatGraph.jsx +275 -0
  300. package/src/viewer/components/MicroComponents.jsx +83 -0
  301. package/src/viewer/hooks/usePanZoom.js +79 -0
  302. package/src/viewer/utils/constants.js +47 -0
  303. package/src/viewer/utils/graph-helpers.js +291 -0
@@ -0,0 +1,275 @@
1
+ import { useRef, useState, useEffect } from 'react';
2
+ import { usePanZoom } from '../hooks/usePanZoom.js';
3
+ import { extColor } from '../utils/constants.js';
4
+ import { computeLayout } from '../utils/graph-helpers.js';
5
+
6
+ export function FlatGraph({
7
+ nodes,
8
+ edges,
9
+ selectedId,
10
+ affectedIds,
11
+ focusedIds,
12
+ onSelect,
13
+ refactorOnly,
14
+ linkedIds,
15
+ }) {
16
+ const posRef = useRef(null);
17
+ const prevKey = useRef(null);
18
+ const [, tick] = useState(0);
19
+ const key = nodes
20
+ .map((n) => n.id)
21
+ .sort()
22
+ .join('|');
23
+ const {
24
+ transform,
25
+ onMouseDown,
26
+ onMouseMove,
27
+ onMouseUp,
28
+ onWheel,
29
+ onMouseLeave,
30
+ onDblClick,
31
+ reset,
32
+ isDrag,
33
+ } = usePanZoom();
34
+
35
+ useEffect(() => {
36
+ if (key !== prevKey.current) {
37
+ posRef.current = computeLayout(nodes, edges);
38
+ prevKey.current = key;
39
+ tick((t) => t + 1);
40
+ reset();
41
+ }
42
+ }, [key]);
43
+
44
+ const pos = posRef.current || {};
45
+
46
+ const getState = (id) => {
47
+ if (id === selectedId) return 'selected';
48
+ if (affectedIds.includes(id)) return 'affected';
49
+ if (focusedIds.length && focusedIds.includes(id)) return 'focused';
50
+ if (focusedIds.length) return 'dimmed';
51
+ return 'default';
52
+ };
53
+
54
+ return (
55
+ <svg
56
+ viewBox="0 0 900 540"
57
+ style={{ width: '100%', height: '100%', cursor: 'grab' }}
58
+ onMouseDown={onMouseDown}
59
+ onMouseMove={onMouseMove}
60
+ onMouseUp={onMouseUp}
61
+ onMouseLeave={onMouseLeave}
62
+ onWheel={onWheel}
63
+ onDoubleClick={onDblClick}
64
+ onClick={(e) => {
65
+ if (!isDrag() && (e.target === e.currentTarget || e.target.tagName === 'svg'))
66
+ onSelect(null);
67
+ }}
68
+ >
69
+ <defs>
70
+ <marker id="arr" markerWidth="6" markerHeight="6" refX="5" refY="2.5" orient="auto">
71
+ <path d="M0,0 L0,5 L6,2.5z" fill="#1e2340" />
72
+ </marker>
73
+ <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" fill="#7c6af7" />
75
+ </marker>
76
+ <marker id="arr-aff" markerWidth="6" markerHeight="6" refX="5" refY="2.5" orient="auto">
77
+ <path d="M0,0 L0,5 L6,2.5z" fill="#f77c6a" />
78
+ </marker>
79
+ <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" fill="#2a2f5a" />
81
+ </marker>
82
+ <filter id="glow">
83
+ <feGaussianBlur stdDeviation="4" result="b" />
84
+ <feMerge>
85
+ <feMergeNode in="b" />
86
+ <feMergeNode in="SourceGraphic" />
87
+ </feMerge>
88
+ </filter>
89
+ <filter id="glow-aff">
90
+ <feGaussianBlur stdDeviation="2.5" result="b" />
91
+ <feMerge>
92
+ <feMergeNode in="b" />
93
+ <feMergeNode in="SourceGraphic" />
94
+ </feMerge>
95
+ </filter>
96
+ </defs>
97
+
98
+ <g transform={`translate(${transform.x},${transform.y}) scale(${transform.k})`}>
99
+ {/* Cluster halos */}
100
+ {Object.entries(
101
+ nodes.reduce((acc, n) => {
102
+ if (!acc[n.cluster.id]) acc[n.cluster.id] = { nodes: [], color: n.cluster.color };
103
+ acc[n.cluster.id].nodes.push(n);
104
+ return acc;
105
+ }, {})
106
+ ).map(([cid, { nodes: cn, color }]) => {
107
+ const pts = cn.map((n) => pos[n.id]).filter(Boolean);
108
+ if (!pts.length) return null;
109
+ const cx = pts.reduce((s, p) => s + p.x, 0) / pts.length;
110
+ const cy = pts.reduce((s, p) => s + p.y, 0) / pts.length;
111
+ const r =
112
+ Math.max(...pts.map((p) => Math.sqrt((p.x - cx) ** 2 + (p.y - cy) ** 2)), 20) + 28;
113
+ return (
114
+ <ellipse
115
+ key={cid}
116
+ cx={cx}
117
+ cy={cy}
118
+ rx={r}
119
+ ry={r * 0.85}
120
+ fill={`${color}07`}
121
+ stroke={`${color}18`}
122
+ strokeWidth={1}
123
+ strokeDasharray="5 3"
124
+ />
125
+ );
126
+ })}
127
+
128
+ {/* Edges */}
129
+ {edges.map((e) => {
130
+ const s = pos[e.source],
131
+ t = pos[e.target];
132
+ if (!s || !t) return null;
133
+ const dx = t.x - s.x,
134
+ dy = t.y - s.y,
135
+ len = Math.sqrt(dx * dx + dy * dy) || 1;
136
+ const nx = dx / len,
137
+ ny = dy / len,
138
+ nr = 18;
139
+ const isSel = e.source === selectedId || e.target === selectedId;
140
+ const isAff = affectedIds.includes(e.target) && e.source === selectedId;
141
+ return (
142
+ <line
143
+ key={e.id}
144
+ x1={s.x + nx * nr}
145
+ y1={s.y + ny * nr}
146
+ x2={t.x - nx * (nr + 5)}
147
+ y2={t.y - ny * (nr + 5)}
148
+ stroke={isSel ? '#7c6af7' : isAff ? '#f77c6a' : e.isType ? '#252a4a' : '#181c36'}
149
+ strokeWidth={isSel ? 1.5 : isAff ? 1.2 : 0.8}
150
+ strokeOpacity={isSel ? 0.9 : isAff ? 0.7 : e.isType ? 0.35 : 0.55}
151
+ strokeDasharray={e.isType ? '4 2' : undefined}
152
+ markerEnd={
153
+ isSel
154
+ ? 'url(#arr-sel)'
155
+ : isAff
156
+ ? 'url(#arr-aff)'
157
+ : e.isType
158
+ ? 'url(#arr-type)'
159
+ : 'url(#arr)'
160
+ }
161
+ />
162
+ );
163
+ })}
164
+
165
+ {/* Nodes */}
166
+ {nodes.map((n) => {
167
+ const p = pos[n.id];
168
+ if (!p) return null;
169
+ const state = getState(n.id);
170
+ const col = extColor(n.ext);
171
+ const isSel = state === 'selected';
172
+ const isAff = state === 'affected';
173
+ const isDim = state === 'dimmed';
174
+ const isZeroScore = refactorOnly && n.score === 0 && !linkedIds.has(n.id);
175
+ const r = 15 + Math.min(n.score / 14, 6);
176
+ const hasRef = !!n.refactor;
177
+ let refColor = null;
178
+ if (hasRef) {
179
+ const p = n.refactor.maxPriority || 0;
180
+ refColor = p >= 5 ? '#f97373' : p >= 3 ? '#fbbf24' : '#4ade80';
181
+ }
182
+
183
+ return (
184
+ <g
185
+ key={n.id}
186
+ transform={`translate(${p.x},${p.y})`}
187
+ onClick={() => {
188
+ if (!isDrag()) onSelect(n.id);
189
+ }}
190
+ style={{ cursor: 'pointer' }}
191
+ opacity={isDim ? 0.08 : isZeroScore ? 0.18 : 1}
192
+ filter={isSel ? 'url(#glow)' : isAff ? 'url(#glow-aff)' : undefined}
193
+ >
194
+ <circle
195
+ r={r + 4}
196
+ fill="none"
197
+ stroke={n.cluster.color}
198
+ strokeWidth={1}
199
+ strokeOpacity={isSel ? 0.5 : 0.1}
200
+ />
201
+ {hasRef && (
202
+ <circle
203
+ r={r + 7}
204
+ fill="none"
205
+ stroke={refColor}
206
+ strokeWidth={1.3}
207
+ strokeOpacity={0.9}
208
+ />
209
+ )}
210
+ <circle
211
+ r={r}
212
+ fill={isSel ? `${col}1a` : isAff ? `${col}0d` : '#0b0d1e'}
213
+ stroke={isSel ? col : isAff ? col : '#1c2038'}
214
+ strokeWidth={isSel ? 2.5 : isAff ? 2 : 0.8}
215
+ />
216
+ {n.isEntry && (
217
+ <circle
218
+ r={r + 8}
219
+ fill="none"
220
+ stroke={col}
221
+ strokeWidth={0.7}
222
+ strokeDasharray="3 2"
223
+ strokeOpacity={0.28}
224
+ />
225
+ )}
226
+ <text
227
+ textAnchor="middle"
228
+ dominantBaseline="middle"
229
+ fontSize={7}
230
+ fontWeight={isSel ? 700 : 400}
231
+ fill={isSel ? '#fff' : isAff ? col : '#5a6090'}
232
+ fontFamily="'JetBrains Mono',monospace"
233
+ style={{ pointerEvents: 'none' }}
234
+ >
235
+ {n.label.length > 13 ? n.label.slice(0, 12) + '...' : n.label}
236
+ </text>
237
+ {n.score > 0 && (
238
+ <text
239
+ x={r + 1}
240
+ y={-r + 2}
241
+ fontSize={6}
242
+ fill={col}
243
+ fontFamily="monospace"
244
+ style={{ pointerEvents: 'none' }}
245
+ >
246
+ {n.score}
247
+ </text>
248
+ )}
249
+ </g>
250
+ );
251
+ })}
252
+ </g>
253
+ <foreignObject x="8" y="8" width="52" height="20" style={{ pointerEvents: 'all' }}>
254
+ <button
255
+ onClick={reset}
256
+ title="Reset pan/zoom (or double-click background)"
257
+ style={{
258
+ fontSize: 8,
259
+ padding: '2px 6px',
260
+ background: '#0d0f22',
261
+ border: `1px solid ${transform.x !== 0 || transform.y !== 0 || transform.k !== 1 ? '#7c6af7' : '#1a1f38'}`,
262
+ borderRadius: 4,
263
+ color:
264
+ transform.x !== 0 || transform.y !== 0 || transform.k !== 1 ? '#7c6af7' : '#2a2f4a',
265
+ cursor: 'pointer',
266
+ fontFamily: "'JetBrains Mono',monospace",
267
+ letterSpacing: '0.05em',
268
+ }}
269
+ >
270
+ ⌖ reset view
271
+ </button>
272
+ </foreignObject>
273
+ </svg>
274
+ );
275
+ }
@@ -0,0 +1,83 @@
1
+ export function Hint({ children }) {
2
+ return (
3
+ <div style={{ fontSize: 10, color: '#2a2f4a', lineHeight: 1.8, marginTop: 4 }}>{children}</div>
4
+ );
5
+ }
6
+
7
+ export function SL({ children }) {
8
+ return (
9
+ <div
10
+ style={{
11
+ fontSize: 8,
12
+ color: '#3a3f5c',
13
+ letterSpacing: '0.08em',
14
+ marginTop: 12,
15
+ marginBottom: 5,
16
+ textTransform: 'uppercase',
17
+ }}
18
+ >
19
+ {children}
20
+ </div>
21
+ );
22
+ }
23
+
24
+ export function Row({ label, value }) {
25
+ return (
26
+ <div
27
+ style={{
28
+ display: 'flex',
29
+ justifyContent: 'space-between',
30
+ alignItems: 'center',
31
+ padding: '3px 0',
32
+ borderBottom: '1px solid #0e1025',
33
+ }}
34
+ >
35
+ <span style={{ fontSize: 9, color: '#3a4070' }}>{label}</span>
36
+ <span style={{ fontSize: 9, color: '#8890b0' }}>{value}</span>
37
+ </div>
38
+ );
39
+ }
40
+
41
+ export function Chip({ color, children }) {
42
+ return (
43
+ <span
44
+ style={{
45
+ fontSize: 8,
46
+ padding: '2px 6px',
47
+ borderRadius: 3,
48
+ background: `${color}1a`,
49
+ border: `1px solid ${color}45`,
50
+ color,
51
+ letterSpacing: '0.02em',
52
+ }}
53
+ >
54
+ {children}
55
+ </span>
56
+ );
57
+ }
58
+
59
+ export function KindBadge({ kind }) {
60
+ const map = {
61
+ class: ['#a78bfa', '#1a1060'],
62
+ function: ['#4ecdc4', '#00301a'],
63
+ interface: ['#60a5fa', '#001a30'],
64
+ type: ['#f472b6', '#2a0a20'],
65
+ enum: ['#f5c518', '#2a1a00'],
66
+ };
67
+ const [c, bg] = map[kind] || ['#64748b', '#1a1a2a'];
68
+ return (
69
+ <span
70
+ style={{
71
+ fontSize: 7,
72
+ padding: '1px 5px',
73
+ borderRadius: 3,
74
+ background: bg,
75
+ color: c,
76
+ minWidth: 44,
77
+ textAlign: 'center',
78
+ }}
79
+ >
80
+ {kind || '--'}
81
+ </span>
82
+ );
83
+ }
@@ -0,0 +1,79 @@
1
+ import { useState, useCallback, useRef } from 'react';
2
+
3
+ export function usePanZoom() {
4
+ const [transform, setTransform] = useState({ x: 0, y: 0, k: 1 });
5
+ const dragging = useRef(false);
6
+ const hasDragged = useRef(false);
7
+ const last = useRef({ x: 0, y: 0 });
8
+
9
+ const onMouseDown = useCallback((e) => {
10
+ if (e.button !== 0) return;
11
+ dragging.current = true;
12
+ hasDragged.current = false;
13
+ last.current = { x: e.clientX, y: e.clientY };
14
+ e.currentTarget.style.cursor = 'grabbing';
15
+ }, []);
16
+
17
+ const onMouseMove = useCallback((e) => {
18
+ if (!dragging.current) return;
19
+ const dx = e.clientX - last.current.x;
20
+ const dy = e.clientY - last.current.y;
21
+ if (Math.abs(dx) > 2 || Math.abs(dy) > 2) hasDragged.current = true;
22
+ last.current = { x: e.clientX, y: e.clientY };
23
+ setTransform((t) => ({ ...t, x: t.x + dx, y: t.y + dy }));
24
+ }, []);
25
+
26
+ const onMouseUp = useCallback((e) => {
27
+ dragging.current = false;
28
+ e.currentTarget.style.cursor = 'grab';
29
+ }, []);
30
+
31
+ const onWheel = useCallback((e) => {
32
+ e.preventDefault();
33
+ const factor = e.deltaY < 0 ? 1.06 : 1 / 1.06;
34
+ const rect = e.currentTarget.getBoundingClientRect();
35
+ const cx = e.clientX - rect.left;
36
+ const cy = e.clientY - rect.top;
37
+ setTransform((t) => {
38
+ const k = Math.max(0.15, Math.min(8, t.k * factor));
39
+ const ratio = k / t.k;
40
+ return {
41
+ k,
42
+ x: cx - ratio * (cx - t.x),
43
+ y: cy - ratio * (cy - t.y),
44
+ };
45
+ });
46
+ }, []);
47
+
48
+ const onDblClick = useCallback((e) => {
49
+ if (e.target === e.currentTarget || e.target.tagName === 'svg') {
50
+ setTransform({ x: 0, y: 0, k: 1 });
51
+ }
52
+ }, []);
53
+
54
+ const onMouseLeave = useCallback((e) => {
55
+ dragging.current = false;
56
+ e.currentTarget.style.cursor = 'grab';
57
+ }, []);
58
+
59
+ const reset = useCallback(() => setTransform({ x: 0, y: 0, k: 1 }), []);
60
+
61
+ const panToCenter = useCallback((svgX, svgY) => {
62
+ setTransform((t) => ({ ...t, x: 450 - svgX * t.k, y: 270 - svgY * t.k }));
63
+ }, []);
64
+
65
+ const isDrag = useCallback(() => hasDragged.current, []);
66
+
67
+ return {
68
+ transform,
69
+ onMouseDown,
70
+ onMouseMove,
71
+ onMouseUp,
72
+ onWheel,
73
+ onMouseLeave,
74
+ onDblClick,
75
+ reset,
76
+ panToCenter,
77
+ isDrag,
78
+ };
79
+ }
@@ -0,0 +1,47 @@
1
+ export const CLUSTER_PALETTE = [
2
+ '#7c6af7',
3
+ '#3ecfcf',
4
+ '#f77c6a',
5
+ '#6af7a0',
6
+ '#f7c76a',
7
+ '#f76ac8',
8
+ '#6aaff7',
9
+ '#c8f76a',
10
+ '#f7a06a',
11
+ '#a0a0ff',
12
+ '#ff6b9d',
13
+ '#00d4aa',
14
+ '#ffb347',
15
+ ];
16
+
17
+ export const EXT_COLOR = {
18
+ '.ts': '#4ecdc4',
19
+ '.tsx': '#3ecfcf',
20
+ '.js': '#f5c518',
21
+ '.jsx': '#f5a018',
22
+ '.css': '#a78bfa',
23
+ '.html': '#fb923c',
24
+ '.json': '#34d399',
25
+ '.toml': '#f472b6',
26
+ '.md': '#94a3b8',
27
+ '.yml': '#60a5fa',
28
+ '': '#64748b',
29
+ };
30
+
31
+ export const extColor = (ext) => EXT_COLOR[ext] || '#64748b';
32
+
33
+ export const ROLE_COLOR = {
34
+ entry_layer: '#4ade80',
35
+ orchestrator: '#7c6af7',
36
+ core_utilities: '#f97316',
37
+ api_layer: '#3ecfcf',
38
+ internal: '#475569',
39
+ };
40
+
41
+ export const ROLE_LABEL = {
42
+ entry_layer: 'entry layer',
43
+ orchestrator: 'orchestrator',
44
+ core_utilities: 'core utilities',
45
+ api_layer: 'API layer',
46
+ internal: 'internal',
47
+ };