mcp-react-toolkit 1.3.0 → 1.4.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 (268) hide show
  1. package/README.md +28 -28
  2. package/node_modules/@mcp-showcase/shared/build/McpServerBase.d.ts +13 -0
  3. package/node_modules/@mcp-showcase/shared/build/McpServerBase.d.ts.map +1 -1
  4. package/node_modules/@mcp-showcase/shared/build/McpServerBase.js +40 -0
  5. package/node_modules/@mcp-showcase/shared/build/McpServerBase.js.map +1 -1
  6. package/node_modules/@mcp-showcase/shared/build/types.d.ts +11 -0
  7. package/node_modules/@mcp-showcase/shared/build/types.d.ts.map +1 -1
  8. package/node_modules/@mcp-showcase/shared/src/McpServerBase.ts +45 -0
  9. package/node_modules/@mcp-showcase/shared/src/types.ts +12 -0
  10. package/node_modules/@mcp-showcase/ui-kit/README.md +30 -0
  11. package/node_modules/@mcp-showcase/ui-kit/build/components.d.ts +10 -0
  12. package/node_modules/@mcp-showcase/ui-kit/build/components.d.ts.map +1 -0
  13. package/node_modules/@mcp-showcase/ui-kit/build/components.js +177 -0
  14. package/node_modules/@mcp-showcase/ui-kit/build/components.js.map +1 -0
  15. package/node_modules/@mcp-showcase/ui-kit/build/escape.d.ts +3 -0
  16. package/node_modules/@mcp-showcase/ui-kit/build/escape.d.ts.map +1 -0
  17. package/node_modules/@mcp-showcase/ui-kit/build/escape.js +10 -0
  18. package/node_modules/@mcp-showcase/ui-kit/build/escape.js.map +1 -0
  19. package/node_modules/@mcp-showcase/ui-kit/build/fixture.d.ts +4 -0
  20. package/node_modules/@mcp-showcase/ui-kit/build/fixture.d.ts.map +1 -0
  21. package/node_modules/@mcp-showcase/ui-kit/build/fixture.js +52 -0
  22. package/node_modules/@mcp-showcase/ui-kit/build/fixture.js.map +1 -0
  23. package/node_modules/@mcp-showcase/ui-kit/build/generate-template.d.ts +2 -0
  24. package/node_modules/@mcp-showcase/ui-kit/build/generate-template.d.ts.map +1 -0
  25. package/node_modules/@mcp-showcase/ui-kit/build/generate-template.js +19 -0
  26. package/node_modules/@mcp-showcase/ui-kit/build/generate-template.js.map +1 -0
  27. package/node_modules/@mcp-showcase/ui-kit/build/index.d.ts +6 -0
  28. package/node_modules/@mcp-showcase/ui-kit/build/index.d.ts.map +1 -0
  29. package/node_modules/@mcp-showcase/ui-kit/build/index.js +7 -0
  30. package/node_modules/@mcp-showcase/ui-kit/build/index.js.map +1 -0
  31. package/node_modules/@mcp-showcase/ui-kit/build/render.d.ts +3 -0
  32. package/node_modules/@mcp-showcase/ui-kit/build/render.d.ts.map +1 -0
  33. package/node_modules/@mcp-showcase/ui-kit/build/render.js +38 -0
  34. package/node_modules/@mcp-showcase/ui-kit/build/render.js.map +1 -0
  35. package/node_modules/@mcp-showcase/ui-kit/build/result-components.d.ts +9 -0
  36. package/node_modules/@mcp-showcase/ui-kit/build/result-components.d.ts.map +1 -0
  37. package/node_modules/@mcp-showcase/ui-kit/build/result-components.js +105 -0
  38. package/node_modules/@mcp-showcase/ui-kit/build/result-components.js.map +1 -0
  39. package/node_modules/@mcp-showcase/ui-kit/build/result-fixture.d.ts +4 -0
  40. package/node_modules/@mcp-showcase/ui-kit/build/result-fixture.d.ts.map +1 -0
  41. package/node_modules/@mcp-showcase/ui-kit/build/result-fixture.js +39 -0
  42. package/node_modules/@mcp-showcase/ui-kit/build/result-fixture.js.map +1 -0
  43. package/node_modules/@mcp-showcase/ui-kit/build/result-render.d.ts +3 -0
  44. package/node_modules/@mcp-showcase/ui-kit/build/result-render.d.ts.map +1 -0
  45. package/node_modules/@mcp-showcase/ui-kit/build/result-render.js +37 -0
  46. package/node_modules/@mcp-showcase/ui-kit/build/result-render.js.map +1 -0
  47. package/node_modules/@mcp-showcase/ui-kit/build/result-runtime.d.ts +2 -0
  48. package/node_modules/@mcp-showcase/ui-kit/build/result-runtime.d.ts.map +1 -0
  49. package/node_modules/@mcp-showcase/ui-kit/build/result-runtime.js +72 -0
  50. package/node_modules/@mcp-showcase/ui-kit/build/result-runtime.js.map +1 -0
  51. package/node_modules/@mcp-showcase/ui-kit/build/runtime.d.ts +2 -0
  52. package/node_modules/@mcp-showcase/ui-kit/build/runtime.d.ts.map +1 -0
  53. package/node_modules/@mcp-showcase/ui-kit/build/runtime.js +221 -0
  54. package/node_modules/@mcp-showcase/ui-kit/build/runtime.js.map +1 -0
  55. package/node_modules/@mcp-showcase/ui-kit/build/theme.d.ts +2 -0
  56. package/node_modules/@mcp-showcase/ui-kit/build/theme.d.ts.map +1 -0
  57. package/node_modules/@mcp-showcase/ui-kit/build/theme.js +278 -0
  58. package/node_modules/@mcp-showcase/ui-kit/build/theme.js.map +1 -0
  59. package/node_modules/@mcp-showcase/ui-kit/build/types.d.ts +113 -0
  60. package/node_modules/@mcp-showcase/ui-kit/build/types.d.ts.map +1 -0
  61. package/node_modules/@mcp-showcase/ui-kit/build/types.js +35 -0
  62. package/node_modules/@mcp-showcase/ui-kit/build/types.js.map +1 -0
  63. package/node_modules/@mcp-showcase/ui-kit/demo/index.html +653 -0
  64. package/node_modules/@mcp-showcase/ui-kit/demo/result.html +445 -0
  65. package/node_modules/@mcp-showcase/ui-kit/package.json +19 -0
  66. package/node_modules/@mcp-showcase/ui-kit/src/components.ts +191 -0
  67. package/node_modules/@mcp-showcase/ui-kit/src/escape.ts +9 -0
  68. package/node_modules/@mcp-showcase/ui-kit/src/fixture.ts +53 -0
  69. package/node_modules/@mcp-showcase/ui-kit/src/generate-template.ts +21 -0
  70. package/node_modules/@mcp-showcase/ui-kit/src/index.test.ts +72 -0
  71. package/node_modules/@mcp-showcase/ui-kit/src/index.ts +6 -0
  72. package/node_modules/@mcp-showcase/ui-kit/src/render.ts +48 -0
  73. package/node_modules/@mcp-showcase/ui-kit/src/result-components.ts +112 -0
  74. package/node_modules/@mcp-showcase/ui-kit/src/result-fixture.ts +40 -0
  75. package/node_modules/@mcp-showcase/ui-kit/src/result-render.test.ts +47 -0
  76. package/node_modules/@mcp-showcase/ui-kit/src/result-render.ts +47 -0
  77. package/node_modules/@mcp-showcase/ui-kit/src/result-runtime.ts +72 -0
  78. package/node_modules/@mcp-showcase/ui-kit/src/runtime.smoke.test.ts +103 -0
  79. package/node_modules/@mcp-showcase/ui-kit/src/runtime.ts +221 -0
  80. package/node_modules/@mcp-showcase/ui-kit/src/theme.ts +278 -0
  81. package/node_modules/@mcp-showcase/ui-kit/src/types.ts +140 -0
  82. package/node_modules/@mcp-showcase/ui-kit/tsconfig.json +9 -0
  83. package/package.json +16 -5
  84. package/tools/accessibility-checker/build/health-report.d.ts +27 -0
  85. package/tools/accessibility-checker/build/health-report.d.ts.map +1 -0
  86. package/tools/accessibility-checker/build/health-report.js +140 -0
  87. package/tools/accessibility-checker/build/health-report.js.map +1 -0
  88. package/tools/accessibility-checker/build/index.js +7 -1
  89. package/tools/accessibility-checker/build/index.js.map +1 -1
  90. package/tools/accessibility-checker/package.json +1 -0
  91. package/tools/code-modernizer/build/index.js +60 -44
  92. package/tools/code-modernizer/build/index.js.map +1 -1
  93. package/tools/code-modernizer/build/result-report.d.ts +24 -0
  94. package/tools/code-modernizer/build/result-report.d.ts.map +1 -0
  95. package/tools/code-modernizer/build/result-report.js +101 -0
  96. package/tools/code-modernizer/build/result-report.js.map +1 -0
  97. package/tools/code-modernizer/package.json +2 -0
  98. package/tools/component-factory/build/index.js +7 -1
  99. package/tools/component-factory/build/index.js.map +1 -1
  100. package/tools/component-factory/build/result-report.d.ts +11 -0
  101. package/tools/component-factory/build/result-report.d.ts.map +1 -0
  102. package/tools/component-factory/build/result-report.js +104 -0
  103. package/tools/component-factory/build/result-report.js.map +1 -0
  104. package/tools/component-factory/package.json +1 -0
  105. package/tools/component-fixer/build/index.js +6 -6
  106. package/tools/component-fixer/build/index.js.map +1 -1
  107. package/tools/component-fixer/build/result-report.d.ts +37 -0
  108. package/tools/component-fixer/build/result-report.d.ts.map +1 -0
  109. package/tools/component-fixer/build/result-report.js +106 -0
  110. package/tools/component-fixer/build/result-report.js.map +1 -0
  111. package/tools/component-fixer/package.json +1 -0
  112. package/tools/component-reviewer/build/health-report.d.ts +37 -0
  113. package/tools/component-reviewer/build/health-report.d.ts.map +1 -0
  114. package/tools/component-reviewer/build/health-report.js +116 -0
  115. package/tools/component-reviewer/build/health-report.js.map +1 -0
  116. package/tools/component-reviewer/build/index.d.ts.map +1 -1
  117. package/tools/component-reviewer/build/index.js +7 -6
  118. package/tools/component-reviewer/build/index.js.map +1 -1
  119. package/tools/component-reviewer/package.json +1 -0
  120. package/tools/dep-auditor/build/health-report.d.ts +16 -0
  121. package/tools/dep-auditor/build/health-report.d.ts.map +1 -0
  122. package/tools/dep-auditor/build/health-report.js +187 -0
  123. package/tools/dep-auditor/build/health-report.js.map +1 -0
  124. package/tools/dep-auditor/build/index.d.ts.map +1 -1
  125. package/tools/dep-auditor/build/index.js +7 -1
  126. package/tools/dep-auditor/build/index.js.map +1 -1
  127. package/tools/dep-auditor/package.json +1 -0
  128. package/tools/generate-tests/build/index.js +8 -1
  129. package/tools/generate-tests/build/index.js.map +1 -1
  130. package/tools/generate-tests/build/result-report.d.ts +14 -0
  131. package/tools/generate-tests/build/result-report.d.ts.map +1 -0
  132. package/tools/generate-tests/build/result-report.js +124 -0
  133. package/tools/generate-tests/build/result-report.js.map +1 -0
  134. package/tools/generate-tests/package.json +1 -0
  135. package/tools/legacy-analyzer/build/health-report.d.ts +4 -0
  136. package/tools/legacy-analyzer/build/health-report.d.ts.map +1 -0
  137. package/tools/legacy-analyzer/build/health-report.js +164 -0
  138. package/tools/legacy-analyzer/build/health-report.js.map +1 -0
  139. package/tools/legacy-analyzer/build/index.js +15 -1
  140. package/tools/legacy-analyzer/build/index.js.map +1 -1
  141. package/tools/legacy-analyzer/build/tools/01-detect-project-tech.d.ts.map +1 -1
  142. package/tools/legacy-analyzer/build/tools/01-detect-project-tech.js +3 -8
  143. package/tools/legacy-analyzer/build/tools/01-detect-project-tech.js.map +1 -1
  144. package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.d.ts.map +1 -1
  145. package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.js +25 -3
  146. package/tools/legacy-analyzer/build/tools/05-analyze-api-layer.js.map +1 -1
  147. package/tools/legacy-analyzer/package.json +1 -0
  148. package/tools/lighthouse-runner/build/health-report.d.ts +14 -0
  149. package/tools/lighthouse-runner/build/health-report.d.ts.map +1 -0
  150. package/tools/lighthouse-runner/build/health-report.js +138 -0
  151. package/tools/lighthouse-runner/build/health-report.js.map +1 -0
  152. package/tools/lighthouse-runner/build/index.d.ts.map +1 -1
  153. package/tools/lighthouse-runner/build/index.js +7 -1
  154. package/tools/lighthouse-runner/build/index.js.map +1 -1
  155. package/tools/lighthouse-runner/package.json +1 -0
  156. package/tools/monorepo-manager/build/index.js +9 -1
  157. package/tools/monorepo-manager/build/index.js.map +1 -1
  158. package/tools/monorepo-manager/build/result-report.d.ts +20 -0
  159. package/tools/monorepo-manager/build/result-report.d.ts.map +1 -0
  160. package/tools/monorepo-manager/build/result-report.js +84 -0
  161. package/tools/monorepo-manager/build/result-report.js.map +1 -0
  162. package/tools/monorepo-manager/package.json +1 -0
  163. package/tools/performance-audit/build/health-report.d.ts +30 -0
  164. package/tools/performance-audit/build/health-report.d.ts.map +1 -0
  165. package/tools/performance-audit/build/health-report.js +152 -0
  166. package/tools/performance-audit/build/health-report.js.map +1 -0
  167. package/tools/performance-audit/build/index.d.ts.map +1 -1
  168. package/tools/performance-audit/build/index.js +7 -1
  169. package/tools/performance-audit/build/index.js.map +1 -1
  170. package/tools/performance-audit/package.json +1 -0
  171. package/tools/quality-pipeline/build/health-report.d.ts +11 -0
  172. package/tools/quality-pipeline/build/health-report.d.ts.map +1 -0
  173. package/tools/quality-pipeline/build/health-report.js +137 -0
  174. package/tools/quality-pipeline/build/health-report.js.map +1 -0
  175. package/tools/quality-pipeline/build/index.js +7 -1
  176. package/tools/quality-pipeline/build/index.js.map +1 -1
  177. package/tools/quality-pipeline/package.json +1 -0
  178. package/tools/render-analyzer/build/health-report.d.ts +33 -0
  179. package/tools/render-analyzer/build/health-report.d.ts.map +1 -0
  180. package/tools/render-analyzer/build/health-report.js +142 -0
  181. package/tools/render-analyzer/build/health-report.js.map +1 -0
  182. package/tools/render-analyzer/build/index.d.ts.map +1 -1
  183. package/tools/render-analyzer/build/index.js +7 -1
  184. package/tools/render-analyzer/build/index.js.map +1 -1
  185. package/tools/render-analyzer/package.json +1 -0
  186. package/tools/shared/build/McpServerBase.d.ts +13 -0
  187. package/tools/shared/build/McpServerBase.d.ts.map +1 -1
  188. package/tools/shared/build/McpServerBase.js +40 -0
  189. package/tools/shared/build/McpServerBase.js.map +1 -1
  190. package/tools/shared/build/types.d.ts +11 -0
  191. package/tools/shared/build/types.d.ts.map +1 -1
  192. package/tools/storybook-generator/build/index.d.ts.map +1 -1
  193. package/tools/storybook-generator/build/index.js +9 -1
  194. package/tools/storybook-generator/build/index.js.map +1 -1
  195. package/tools/storybook-generator/build/result-report.d.ts +22 -0
  196. package/tools/storybook-generator/build/result-report.d.ts.map +1 -0
  197. package/tools/storybook-generator/build/result-report.js +77 -0
  198. package/tools/storybook-generator/build/result-report.js.map +1 -0
  199. package/tools/storybook-generator/package.json +1 -0
  200. package/tools/test-gap-analyzer/build/health-report.d.ts +34 -0
  201. package/tools/test-gap-analyzer/build/health-report.d.ts.map +1 -0
  202. package/tools/test-gap-analyzer/build/health-report.js +190 -0
  203. package/tools/test-gap-analyzer/build/health-report.js.map +1 -0
  204. package/tools/test-gap-analyzer/build/index.d.ts.map +1 -1
  205. package/tools/test-gap-analyzer/build/index.js +7 -1
  206. package/tools/test-gap-analyzer/build/index.js.map +1 -1
  207. package/tools/test-gap-analyzer/package.json +1 -0
  208. package/tools/typescript-enforcer/build/health-report.d.ts +33 -0
  209. package/tools/typescript-enforcer/build/health-report.d.ts.map +1 -0
  210. package/tools/typescript-enforcer/build/health-report.js +143 -0
  211. package/tools/typescript-enforcer/build/health-report.js.map +1 -0
  212. package/tools/typescript-enforcer/build/index.js +6 -1
  213. package/tools/typescript-enforcer/build/index.js.map +1 -1
  214. package/tools/typescript-enforcer/package.json +1 -0
  215. package/tools/ui-kit/README.md +30 -0
  216. package/tools/ui-kit/build/components.d.ts +10 -0
  217. package/tools/ui-kit/build/components.d.ts.map +1 -0
  218. package/tools/ui-kit/build/components.js +177 -0
  219. package/tools/ui-kit/build/components.js.map +1 -0
  220. package/tools/ui-kit/build/escape.d.ts +3 -0
  221. package/tools/ui-kit/build/escape.d.ts.map +1 -0
  222. package/tools/ui-kit/build/escape.js +10 -0
  223. package/tools/ui-kit/build/escape.js.map +1 -0
  224. package/tools/ui-kit/build/fixture.d.ts +4 -0
  225. package/tools/ui-kit/build/fixture.d.ts.map +1 -0
  226. package/tools/ui-kit/build/fixture.js +52 -0
  227. package/tools/ui-kit/build/fixture.js.map +1 -0
  228. package/tools/ui-kit/build/generate-template.d.ts +2 -0
  229. package/tools/ui-kit/build/generate-template.d.ts.map +1 -0
  230. package/tools/ui-kit/build/generate-template.js +19 -0
  231. package/tools/ui-kit/build/generate-template.js.map +1 -0
  232. package/tools/ui-kit/build/index.d.ts +6 -0
  233. package/tools/ui-kit/build/index.d.ts.map +1 -0
  234. package/tools/ui-kit/build/index.js +7 -0
  235. package/tools/ui-kit/build/index.js.map +1 -0
  236. package/tools/ui-kit/build/render.d.ts +3 -0
  237. package/tools/ui-kit/build/render.d.ts.map +1 -0
  238. package/tools/ui-kit/build/render.js +38 -0
  239. package/tools/ui-kit/build/render.js.map +1 -0
  240. package/tools/ui-kit/build/result-components.d.ts +9 -0
  241. package/tools/ui-kit/build/result-components.d.ts.map +1 -0
  242. package/tools/ui-kit/build/result-components.js +105 -0
  243. package/tools/ui-kit/build/result-components.js.map +1 -0
  244. package/tools/ui-kit/build/result-fixture.d.ts +4 -0
  245. package/tools/ui-kit/build/result-fixture.d.ts.map +1 -0
  246. package/tools/ui-kit/build/result-fixture.js +39 -0
  247. package/tools/ui-kit/build/result-fixture.js.map +1 -0
  248. package/tools/ui-kit/build/result-render.d.ts +3 -0
  249. package/tools/ui-kit/build/result-render.d.ts.map +1 -0
  250. package/tools/ui-kit/build/result-render.js +37 -0
  251. package/tools/ui-kit/build/result-render.js.map +1 -0
  252. package/tools/ui-kit/build/result-runtime.d.ts +2 -0
  253. package/tools/ui-kit/build/result-runtime.d.ts.map +1 -0
  254. package/tools/ui-kit/build/result-runtime.js +72 -0
  255. package/tools/ui-kit/build/result-runtime.js.map +1 -0
  256. package/tools/ui-kit/build/runtime.d.ts +2 -0
  257. package/tools/ui-kit/build/runtime.d.ts.map +1 -0
  258. package/tools/ui-kit/build/runtime.js +221 -0
  259. package/tools/ui-kit/build/runtime.js.map +1 -0
  260. package/tools/ui-kit/build/theme.d.ts +2 -0
  261. package/tools/ui-kit/build/theme.d.ts.map +1 -0
  262. package/tools/ui-kit/build/theme.js +278 -0
  263. package/tools/ui-kit/build/theme.js.map +1 -0
  264. package/tools/ui-kit/build/types.d.ts +113 -0
  265. package/tools/ui-kit/build/types.d.ts.map +1 -0
  266. package/tools/ui-kit/build/types.js +35 -0
  267. package/tools/ui-kit/build/types.js.map +1 -0
  268. package/tools/ui-kit/package.json +19 -0
@@ -0,0 +1,653 @@
1
+ <!doctype html>
2
+ <html lang="en" data-theme="light">
3
+ <head>
4
+ <meta charset="utf-8"/>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1"/>
6
+ <title>Codebase Health Studio — acme-dashboard</title>
7
+ <style>
8
+ *,*::before,*::after{box-sizing:border-box}
9
+ html{-webkit-text-size-adjust:100%}
10
+ body{margin:0}
11
+
12
+ :root{
13
+ --font:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
14
+ --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
15
+
16
+ /* light (default) */
17
+ --bg:#f7f7f6;
18
+ --surface:#ffffff;
19
+ --surface-2:#fbfbfa;
20
+ --text:#16161a;
21
+ --muted:#6b7280;
22
+ --faint:#9aa0aa;
23
+ --border:#ebe9e4;
24
+ --border-strong:#dedcd5;
25
+ --shadow:0 1px 2px rgba(16,16,20,.04),0 8px 24px -12px rgba(16,16,20,.10);
26
+ --ring:rgba(99,102,241,.45);
27
+
28
+ --good:#059669; --good-soft:#ecfdf5;
29
+ --warn:#d97706; --warn-soft:#fffbeb;
30
+ --bad:#e11d48; --bad-soft:#fff1f2;
31
+
32
+ --sev-critical:#e11d48; --sev-high:#ea580c; --sev-medium:#d97706; --sev-low:#64748b;
33
+ --sev-critical-soft:#fff1f2; --sev-high-soft:#fff7ed; --sev-medium-soft:#fffbeb; --sev-low-soft:#f1f5f9;
34
+
35
+ --accent:var(--good);
36
+ --accent-soft:var(--good-soft);
37
+ --radius:14px;
38
+ --radius-sm:10px;
39
+ }
40
+
41
+ [data-theme="dark"]{
42
+ --bg:#0a0b0d;
43
+ --surface:#141519;
44
+ --surface-2:#0f1013;
45
+ --text:#f3f4f6;
46
+ --muted:#9aa1ad;
47
+ --faint:#6b7280;
48
+ --border:rgba(255,255,255,.07);
49
+ --border-strong:rgba(255,255,255,.12);
50
+ --shadow:0 1px 2px rgba(0,0,0,.4),0 16px 40px -20px rgba(0,0,0,.7);
51
+ --ring:rgba(129,140,248,.55);
52
+
53
+ --good:#34d399; --good-soft:rgba(52,211,153,.12);
54
+ --warn:#fbbf24; --warn-soft:rgba(251,191,36,.12);
55
+ --bad:#fb7185; --bad-soft:rgba(251,113,133,.12);
56
+
57
+ --sev-critical:#fb7185; --sev-high:#fb923c; --sev-medium:#fbbf24; --sev-low:#94a3b8;
58
+ --sev-critical-soft:rgba(251,113,133,.12); --sev-high-soft:rgba(251,146,60,.12);
59
+ --sev-medium-soft:rgba(251,191,36,.12); --sev-low-soft:rgba(148,163,184,.12);
60
+ }
61
+
62
+ [data-band="good"]{--accent:var(--good);--accent-soft:var(--good-soft)}
63
+ [data-band="warn"]{--accent:var(--warn);--accent-soft:var(--warn-soft)}
64
+ [data-band="bad"]{--accent:var(--bad);--accent-soft:var(--bad-soft)}
65
+
66
+ body{
67
+ font-family:var(--font);
68
+ background:var(--bg);
69
+ color:var(--text);
70
+ font-size:14px;
71
+ line-height:1.5;
72
+ -webkit-font-smoothing:antialiased;
73
+ font-variant-numeric:tabular-nums;
74
+ }
75
+ .num{font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
76
+
77
+ .wrap{max-width:1040px;margin:0 auto;padding:28px 24px 64px}
78
+
79
+ /* header */
80
+ .hdr{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:24px}
81
+ .brand{display:flex;align-items:center;gap:11px;min-width:0}
82
+ .brand .dot{width:30px;height:30px;border-radius:9px;background:var(--accent-soft);color:var(--accent);
83
+ display:grid;place-items:center;flex:none;border:1px solid var(--border)}
84
+ .brand h1{font-size:15px;font-weight:650;margin:0;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
85
+ .brand .sub{font-size:12px;color:var(--muted);margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
86
+ .hdr-actions{display:flex;align-items:center;gap:8px;flex:none}
87
+
88
+ .btn{appearance:none;font-family:inherit;font-size:12.5px;font-weight:550;color:var(--text);
89
+ background:var(--surface);border:1px solid var(--border-strong);border-radius:9px;padding:7px 11px;
90
+ cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:.16s ease;line-height:1}
91
+ .btn:hover{border-color:var(--accent);color:var(--accent);transform:translateY(-1px)}
92
+ .btn:active{transform:translateY(0)}
93
+ .btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--ring)}
94
+ .btn.icon{padding:7px;width:32px;height:32px;justify-content:center}
95
+ .btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}
96
+ [data-theme="dark"] .btn.primary{color:#0a0b0d}
97
+ .btn.primary:hover{filter:brightness(1.05);color:#fff}
98
+ [data-theme="dark"] .btn.primary:hover{color:#0a0b0d}
99
+
100
+ /* hero */
101
+ .hero{display:grid;grid-template-columns:auto 1fr;gap:28px;align-items:center;
102
+ background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
103
+ padding:26px 28px;box-shadow:var(--shadow);margin-bottom:18px}
104
+ .gauge{position:relative;width:148px;height:148px;flex:none}
105
+ .gauge svg{transform:rotate(-90deg)}
106
+ .gauge .track{stroke:var(--border-strong);fill:none}
107
+ .gauge .arc{stroke:var(--accent);fill:none;stroke-linecap:round;transition:stroke-dashoffset 1.1s cubic-bezier(.22,1,.36,1)}
108
+ .gauge .center{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;line-height:1}
109
+ .gauge .score{font-size:42px;font-weight:680;letter-spacing:-.03em;line-height:1}
110
+ .gauge .of{font-size:11px;color:var(--faint);margin-top:3px}
111
+ .hero-meta{min-width:0}
112
+ .grade-row{display:flex;align-items:baseline;gap:10px;margin-bottom:6px}
113
+ .grade{font-size:13px;font-weight:650;color:var(--accent);background:var(--accent-soft);
114
+ border:1px solid var(--border);border-radius:7px;padding:3px 9px}
115
+ .verdict{font-size:18px;font-weight:620;letter-spacing:-.01em}
116
+ .hero p{color:var(--muted);margin:2px 0 14px;font-size:13px}
117
+ .chips{display:flex;flex-wrap:wrap;gap:7px}
118
+ .chip{font-size:12px;color:var(--muted);background:var(--surface-2);border:1px solid var(--border);
119
+ border-radius:999px;padding:4px 10px;display:inline-flex;gap:5px}
120
+ .chip b{color:var(--text);font-weight:600}
121
+
122
+ /* section heading */
123
+ .sec{margin:26px 0 12px;display:flex;align-items:center;justify-content:space-between;gap:12px}
124
+ .sec h2{font-size:13px;font-weight:620;letter-spacing:.02em;text-transform:uppercase;color:var(--muted);margin:0}
125
+ .sec .count{font-size:12px;color:var(--faint)}
126
+
127
+ /* category cards */
128
+ .cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(232px,1fr));gap:12px}
129
+ .card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:15px 16px;
130
+ box-shadow:var(--shadow);transition:.16s ease;display:flex;flex-direction:column}
131
+ .card:hover{border-color:var(--border-strong);transform:translateY(-2px)}
132
+ .card .top{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:9px}
133
+ .card .name{font-weight:600;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
134
+ .card .badge{font-size:11px;font-weight:600;padding:2px 8px;border-radius:999px;flex:none}
135
+ .badge.good{color:var(--good);background:var(--good-soft)}
136
+ .badge.warn{color:var(--warn);background:var(--warn-soft)}
137
+ .badge.bad{color:var(--bad);background:var(--bad-soft)}
138
+ .card .sum{color:var(--muted);font-size:12.5px;line-height:1.45;min-height:3.9em;
139
+ display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}
140
+ .bar{height:5px;border-radius:999px;background:var(--border-strong);overflow:hidden;margin-top:11px}
141
+ .bar > i{display:block;height:100%;border-radius:999px;transition:width 1s cubic-bezier(.22,1,.36,1)}
142
+ .bar > i.good{background:var(--good)} .bar > i.warn{background:var(--warn)} .bar > i.bad{background:var(--bad)}
143
+ .card .foot{display:flex;justify-content:space-between;align-items:center;margin-top:9px;font-size:11.5px;color:var(--faint)}
144
+
145
+ /* toolbar */
146
+ .toolbar{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:10px}
147
+ .search{flex:1;min-width:180px;position:relative}
148
+ .search input{width:100%;font-family:inherit;font-size:13px;color:var(--text);background:var(--surface);
149
+ border:1px solid var(--border-strong);border-radius:9px;padding:8px 11px 8px 32px}
150
+ .search input:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--ring)}
151
+ .search svg{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--faint)}
152
+ .filter{display:flex;gap:4px;background:var(--surface-2);border:1px solid var(--border);border-radius:9px;padding:3px}
153
+ .filter button{appearance:none;font-family:inherit;font-size:12px;font-weight:550;color:var(--muted);
154
+ background:transparent;border:0;border-radius:6px;padding:5px 10px;cursor:pointer;transition:.14s}
155
+ .filter button[aria-pressed="true"]{background:var(--surface);color:var(--text);box-shadow:var(--shadow)}
156
+ .filter button:hover{color:var(--text)}
157
+
158
+ /* table */
159
+ .table-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
160
+ box-shadow:var(--shadow);overflow:hidden}
161
+ table{width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed}
162
+ thead th:nth-child(1),tbody td:nth-child(1){width:96px}
163
+ thead th:nth-child(3),tbody td:nth-child(3){width:120px}
164
+ thead th:nth-child(4),tbody td:nth-child(4){width:160px}
165
+ thead th{text-align:left;font-size:11px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;
166
+ color:var(--faint);padding:11px 16px;border-bottom:1px solid var(--border);cursor:pointer;user-select:none;white-space:nowrap}
167
+ thead th .arr{opacity:.4;font-size:9px;margin-left:3px}
168
+ thead th[aria-sort] .arr{opacity:1;color:var(--accent)}
169
+ tbody tr{border-bottom:1px solid var(--border);cursor:pointer;transition:background .12s}
170
+ tbody tr:last-child{border-bottom:0}
171
+ tbody tr:hover{background:var(--surface-2)}
172
+ tbody td{padding:11px 16px;vertical-align:top}
173
+ .sev{display:inline-flex;align-items:center;gap:6px;font-weight:600;font-size:12px;white-space:nowrap}
174
+ .sev .pip{width:7px;height:7px;border-radius:999px;flex:none}
175
+ .sev.critical{color:var(--sev-critical)} .sev.critical .pip{background:var(--sev-critical)}
176
+ .sev.high{color:var(--sev-high)} .sev.high .pip{background:var(--sev-high)}
177
+ .sev.medium{color:var(--sev-medium)} .sev.medium .pip{background:var(--sev-medium)}
178
+ .sev.low{color:var(--sev-low)} .sev.low .pip{background:var(--sev-low)}
179
+ .cell-title{font-weight:550;color:var(--text);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}
180
+ .cell-file{font-family:var(--mono);font-size:11.5px;color:var(--muted);overflow-wrap:anywhere;word-break:normal;max-width:200px}
181
+ .tag{font-size:11px;color:var(--muted);background:var(--surface-2);border:1px solid var(--border);
182
+ border-radius:6px;padding:2px 7px;white-space:nowrap}
183
+ .empty{padding:40px 16px;text-align:center;color:var(--muted)}
184
+ .empty .big{font-size:15px;color:var(--text);font-weight:600;margin-bottom:4px}
185
+
186
+ /* drawer */
187
+ .scrim{position:fixed;inset:0;background:rgba(10,11,13,.42);opacity:0;pointer-events:none;transition:.2s;z-index:40}
188
+ .scrim.open{opacity:1;pointer-events:auto}
189
+ .drawer{position:fixed;top:0;right:0;height:100%;width:min(440px,92vw);background:var(--surface);
190
+ border-left:1px solid var(--border);box-shadow:-24px 0 60px -30px rgba(0,0,0,.5);
191
+ transform:translateX(100%);transition:transform .26s cubic-bezier(.22,1,.36,1);z-index:50;
192
+ display:flex;flex-direction:column}
193
+ .drawer.open{transform:translateX(0)}
194
+ .drawer header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;
195
+ padding:20px 22px 16px;border-bottom:1px solid var(--border)}
196
+ .drawer header h3{margin:0;font-size:16px;font-weight:640;letter-spacing:-.01em;line-height:1.35}
197
+ .drawer .body{padding:18px 22px;overflow-y:auto;flex:1}
198
+ .drawer .field{margin-bottom:16px}
199
+ .drawer .field .k{font-size:11px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;color:var(--faint);margin-bottom:5px}
200
+ .drawer .field .v{font-size:13.5px;color:var(--text)}
201
+ .drawer .field .v.mono{font-family:var(--mono);font-size:12px;color:var(--muted);word-break:break-all}
202
+ .drawer .acts{padding:16px 22px;border-top:1px solid var(--border);display:flex;flex-wrap:wrap;gap:8px}
203
+ .kv{display:flex;gap:8px;flex-wrap:wrap}
204
+ .kv .pair{font-size:12px;color:var(--muted);background:var(--surface-2);border:1px solid var(--border);border-radius:7px;padding:4px 9px}
205
+ .kv .pair b{color:var(--text);font-weight:600}
206
+
207
+ /* fix-first */
208
+ .queue{display:flex;flex-direction:column;gap:8px}
209
+ .qitem{display:flex;align-items:center;gap:12px;background:var(--surface);border:1px solid var(--border);
210
+ border-radius:var(--radius-sm);padding:11px 14px;box-shadow:var(--shadow);cursor:pointer;transition:.14s}
211
+ .qitem:hover{border-color:var(--accent);transform:translateX(2px)}
212
+ .qitem .rank{width:22px;height:22px;border-radius:7px;background:var(--accent-soft);color:var(--accent);
213
+ font-size:12px;font-weight:680;display:grid;place-items:center;flex:none}
214
+ .qitem .qt{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
215
+ .qitem .qt .t{display:block;font-weight:550;font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
216
+ .qitem .qt .s{display:block;font-size:11.5px;color:var(--faint)}
217
+
218
+ /* toast */
219
+ .toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(20px);opacity:0;
220
+ background:var(--text);color:var(--bg);font-size:13px;font-weight:550;padding:10px 16px;border-radius:10px;
221
+ box-shadow:var(--shadow);transition:.22s;z-index:60;pointer-events:none;max-width:90vw}
222
+ .toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
223
+
224
+ .foot-note{margin-top:34px;text-align:center;font-size:11.5px;color:var(--faint)}
225
+ .foot-note a{color:var(--muted)}
226
+
227
+ @media (max-width:560px){
228
+ .hero{grid-template-columns:1fr;justify-items:center;text-align:center}
229
+ .hero-meta{text-align:center}.chips{justify-content:center}.grade-row{justify-content:center}
230
+ }
231
+ /* result view */
232
+ .result-hero{display:flex;align-items:center;gap:18px;flex-wrap:wrap;
233
+ background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);
234
+ padding:22px 26px;box-shadow:var(--shadow);margin-bottom:18px}
235
+ .result-hero .glyph{width:52px;height:52px;border-radius:14px;background:var(--accent-soft);color:var(--accent);
236
+ display:grid;place-items:center;flex:none;border:1px solid var(--border)}
237
+ .result-hero .rh-main{min-width:0;flex:1}
238
+ .result-hero .rh-title{font-size:20px;font-weight:660;letter-spacing:-.02em;line-height:1.2}
239
+ .result-hero .rh-sub{color:var(--muted);font-size:13px;margin-top:3px}
240
+ .status-pill{font-size:12px;font-weight:650;padding:4px 10px;border-radius:999px;white-space:nowrap}
241
+ .status-pill.good{color:var(--good);background:var(--good-soft)}
242
+ .status-pill.warn{color:var(--warn);background:var(--warn-soft)}
243
+ .status-pill.bad{color:var(--bad);background:var(--bad-soft)}
244
+ .result-hero .chips{margin-top:0}
245
+
246
+ .changes{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
247
+ .change-row{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border);transition:background .12s}
248
+ .change-row:last-child{border-bottom:0}
249
+ .change-row.clickable{cursor:pointer}
250
+ .change-row.clickable:hover{background:var(--surface-2)}
251
+ .kind{font-size:10.5px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:3px 8px;border-radius:6px;flex:none;width:74px;text-align:center}
252
+ .kind.created{color:var(--good);background:var(--good-soft)}
253
+ .kind.modified{color:var(--warn);background:var(--warn-soft)}
254
+ .kind.deleted{color:var(--bad);background:var(--bad-soft)}
255
+ .kind.renamed{color:var(--sev-low);background:var(--sev-low-soft)}
256
+ .change-main{flex:1;min-width:0}
257
+ .change-path{font-family:var(--mono);font-size:12.5px;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
258
+ .change-sum{font-size:12px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
259
+ .change-delta{font-family:var(--mono);font-size:11.5px;flex:none;white-space:nowrap}
260
+ .change-delta .add{color:var(--good)} .change-delta .del{color:var(--bad)}
261
+
262
+ .section{margin-top:10px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
263
+ .section-item{display:flex;align-items:flex-start;gap:10px;padding:11px 16px;border-bottom:1px solid var(--border)}
264
+ .section-item:last-child{border-bottom:0}
265
+ .section-item .ip{width:7px;height:7px;border-radius:999px;margin-top:6px;flex:none;background:var(--faint)}
266
+ .section-item .ip.ok{background:var(--good)} .section-item .ip.warn{background:var(--warn)} .section-item .ip.error{background:var(--bad)}
267
+ .section-item .it{font-size:13px;font-weight:550}
268
+ .section-item .id{font-size:12px;color:var(--muted)}
269
+ .next-steps{display:flex;flex-wrap:wrap;gap:8px;margin-top:6px}
270
+ .diff{font-family:var(--mono);font-size:12px;line-height:1.55;white-space:pre-wrap;word-break:break-word;
271
+ background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius-sm);padding:12px}
272
+ .diff .dl-add{color:var(--good)} .diff .dl-del{color:var(--bad)} .diff .dl-ctx{color:var(--muted)}
273
+
274
+ @media (prefers-reduced-motion:reduce){
275
+ *{transition:none!important;animation:none!important}
276
+ }
277
+ </style>
278
+ </head>
279
+ <body>
280
+ <div class="wrap">
281
+
282
+ <header class="hdr">
283
+ <div class="brand">
284
+ <span class="dot" aria-hidden="true">
285
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
286
+ </span>
287
+ <div style="min-width:0">
288
+ <h1>Codebase Health Studio</h1>
289
+ <p class="sub">acme-dashboard</p>
290
+ </div>
291
+ </div>
292
+ <div class="hdr-actions">
293
+ <button class="btn icon" id="theme-toggle" type="button" aria-label="Toggle colour theme" title="Toggle theme">
294
+ <span class="theme-sun" hidden><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg></span><span class="theme-moon"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg></span>
295
+ </button>
296
+ <button class="btn" id="export-btn" type="button" title="Copy report as Markdown">
297
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></svg>
298
+ Export
299
+ </button>
300
+ </div>
301
+ </header>
302
+
303
+ <section class="hero" data-band="warn">
304
+
305
+ <div class="gauge" role="img" aria-label="Health score 62 out of 100">
306
+ <svg width="148" height="148" viewBox="0 0 148 148">
307
+ <circle class="track" cx="74" cy="74" r="64" stroke-width="11"/>
308
+ <circle class="arc" cx="74" cy="74" r="64" stroke-width="11"
309
+ stroke-dasharray="402.1" stroke-dashoffset="402.1"
310
+ data-target="152.8"/>
311
+ </svg>
312
+ <div class="center">
313
+ <div class="score num" data-count="62">0</div>
314
+ <div class="of">/ 100</div>
315
+ </div>
316
+ </div>
317
+ <div class="hero-meta">
318
+ <div class="grade-row">
319
+ <span class="grade">Grade C</span>
320
+ <span class="verdict">Workable, but carrying debt</span>
321
+ </div>
322
+ <p>18 issues found across 6 areas · React 18 · CRA.</p>
323
+ <div class="chips"><span class="chip"><span>Framework</span><b>Create React App</b></span><span class="chip"><span>React</span><b>18.2.0</b></span><span class="chip"><span>Language</span><b>JavaScript</b></span><span class="chip"><span>Components</span><b>94</b></span></div>
324
+ </div>
325
+ </section>
326
+
327
+ <div class="sec"><h2>Fix-first queue</h2><span class="count">3 prioritised</span></div>
328
+ <div class="queue">
329
+ <div class="qitem" data-action="fix-god" role="button" tabindex="0">
330
+ <span class="rank num">1</span>
331
+ <span class="qt"><span class="t">Split the 3 god components</span><span class="s">Runs component-fixer</span></span>
332
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:var(--faint)"><path d="m9 18 6-6-6-6"/></svg>
333
+ </div>
334
+ <div class="qitem" data-action="plan" role="button" tabindex="0">
335
+ <span class="rank num">2</span>
336
+ <span class="qt"><span class="t">Generate full refactor plan</span><span class="s">Runs generate-refactor-plan</span></span>
337
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:var(--faint)"><path d="m9 18 6-6-6-6"/></svg>
338
+ </div>
339
+ <div class="qitem" data-action="migrate-ts" role="button" tabindex="0">
340
+ <span class="rank num">3</span>
341
+ <span class="qt"><span class="t">Migrate the API layer to TypeScript</span><span class="s">Asks the agent</span></span>
342
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:var(--faint)"><path d="m9 18 6-6-6-6"/></svg>
343
+ </div></div>
344
+
345
+ <div class="sec"><h2>Areas</h2><span class="count">6 analysed</span></div>
346
+ <div class="cards">
347
+ <div class="card" data-category="components" role="button" tabindex="0" aria-label="Filter issues by Components">
348
+ <div class="top">
349
+ <span class="name">Components</span>
350
+ <span class="badge warn">48</span>
351
+ </div>
352
+ <div class="sum">3 god components over 300 lines; 11 with mixed responsibilities.</div>
353
+ <div class="bar"><i class="warn" data-w="48" style="width:0"></i></div>
354
+ <div class="foot"><span>6 issues</span><span>View →</span></div>
355
+ </div>
356
+ <div class="card" data-category="state" role="button" tabindex="0" aria-label="Filter issues by State">
357
+ <div class="top">
358
+ <span class="name">State</span>
359
+ <span class="badge warn">55</span>
360
+ </div>
361
+ <div class="sum">Redux without normalisation; derived state stored in the store.</div>
362
+ <div class="bar"><i class="warn" data-w="55" style="width:0"></i></div>
363
+ <div class="foot"><span>3 issues</span><span>View →</span></div>
364
+ </div>
365
+ <div class="card" data-category="api" role="button" tabindex="0" aria-label="Filter issues by API layer">
366
+ <div class="top">
367
+ <span class="name">API layer</span>
368
+ <span class="badge bad">40</span>
369
+ </div>
370
+ <div class="sum">Scattered axios calls, 4 duplicated endpoints, no central client.</div>
371
+ <div class="bar"><i class="bad" data-w="40" style="width:0"></i></div>
372
+ <div class="foot"><span>4 issues</span><span>View →</span></div>
373
+ </div>
374
+ <div class="card" data-category="routing" role="button" tabindex="0" aria-label="Filter issues by Routing">
375
+ <div class="top">
376
+ <span class="name">Routing</span>
377
+ <span class="badge good">78</span>
378
+ </div>
379
+ <div class="sum">react-router v6, nested routes, but no lazy loading.</div>
380
+ <div class="bar"><i class="good" data-w="78" style="width:0"></i></div>
381
+ <div class="foot"><span>1 issue</span><span>View →</span></div>
382
+ </div>
383
+ <div class="card" data-category="styling" role="button" tabindex="0" aria-label="Filter issues by Styling">
384
+ <div class="top">
385
+ <span class="name">Styling</span>
386
+ <span class="badge warn">60</span>
387
+ </div>
388
+ <div class="sum">Mixed CSS + styled-components; 23 hardcoded colours.</div>
389
+ <div class="bar"><i class="warn" data-w="60" style="width:0"></i></div>
390
+ <div class="foot"><span>2 issues</span><span>View →</span></div>
391
+ </div>
392
+ <div class="card" data-category="deps" role="button" tabindex="0" aria-label="Filter issues by Dependencies">
393
+ <div class="top">
394
+ <span class="name">Dependencies</span>
395
+ <span class="badge good">84</span>
396
+ </div>
397
+ <div class="sum">2 unused deps; moment.js inflating the bundle.</div>
398
+ <div class="bar"><i class="good" data-w="84" style="width:0"></i></div>
399
+ <div class="foot"><span>2 issues</span><span>View →</span></div>
400
+ </div></div>
401
+
402
+ <div class="sec"><h2>Issues</h2><span class="count" id="issue-count">18 total</span></div>
403
+ <div class="toolbar">
404
+ <label class="search">
405
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
406
+ <input id="search" type="search" placeholder="Search issues, files, categories…" aria-label="Search issues"/>
407
+ </label>
408
+ <div class="filter" role="group" aria-label="Filter by severity"><button type="button" data-sev="all" aria-pressed="true">All</button><button type="button" data-sev="critical" aria-pressed="false">Critical</button><button type="button" data-sev="high" aria-pressed="false">High</button><button type="button" data-sev="medium" aria-pressed="false">Medium</button><button type="button" data-sev="low" aria-pressed="false">Low</button></div>
409
+ </div>
410
+ <div class="table-card">
411
+ <table>
412
+ <thead><tr>
413
+ <th data-sort="severity" aria-sort="descending">Severity<span class="arr">▼</span></th>
414
+ <th data-sort="title">Issue<span class="arr">▼</span></th>
415
+ <th data-sort="category">Area<span class="arr">▼</span></th>
416
+ <th data-sort="file">File<span class="arr">▼</span></th>
417
+ </tr></thead>
418
+ <tbody id="rows"></tbody>
419
+ </table>
420
+ <div class="empty" id="empty" hidden>
421
+ <div class="big">Nothing matches</div>
422
+ <div>Try clearing the search or severity filter.</div>
423
+ </div>
424
+ </div>
425
+ <div class="foot-note">legacy-analyzer · generated 2026-06-27 · mcp-react-toolkit</div>
426
+ </div>
427
+
428
+ <div class="scrim" id="scrim"></div>
429
+ <aside class="drawer" id="drawer" role="dialog" aria-modal="true" aria-labelledby="d-title">
430
+ <header>
431
+ <h3 id="d-title"></h3>
432
+ <button class="btn icon" id="d-close" type="button" aria-label="Close detail">
433
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
434
+ </button>
435
+ </header>
436
+ <div class="body" id="d-body"></div>
437
+ <div class="acts" id="d-acts"></div>
438
+ </aside>
439
+ <div class="toast" id="toast" role="status" aria-live="polite"></div>
440
+ <script id="report-data" type="application/json">{"meta":{"title":"Codebase Health Studio","subtitle":"React 18 · CRA","target":"acme-dashboard","generatedAt":"2026-06-27","tool":"legacy-analyzer"},"score":62,"totalIssues":18,"chips":[{"label":"Framework","value":"Create React App"},{"label":"React","value":"18.2.0"},{"label":"Language","value":"JavaScript"},{"label":"Components","value":"94"}],"topActions":[{"id":"fix-god","label":"Split the 3 god components","kind":"tool","tool":"component-fixer","params":{"target":"components"},"fallback":"Refactor Dashboard.jsx, Settings.jsx and Reports.jsx into smaller components."},{"id":"plan","label":"Generate full refactor plan","kind":"tool","tool":"generate-refactor-plan","params":{"path":"."},"fallback":"Generate a refactor plan for acme-dashboard."},{"id":"migrate-ts","label":"Migrate the API layer to TypeScript","kind":"prompt","prompt":"Convert src/api/*.js to TypeScript with typed responses and a centralised client."}],"categories":[{"id":"components","name":"Components","score":48,"status":"warn","summary":"3 god components over 300 lines; 11 with mixed responsibilities.","issueCount":6,"details":[{"label":"Total","value":"94"},{"label":"Large","value":"3"}]},{"id":"state","name":"State","score":55,"status":"warn","summary":"Redux without normalisation; derived state stored in the store.","issueCount":3},{"id":"api","name":"API layer","score":40,"status":"bad","summary":"Scattered axios calls, 4 duplicated endpoints, no central client.","issueCount":4},{"id":"routing","name":"Routing","score":78,"status":"good","summary":"react-router v6, nested routes, but no lazy loading.","issueCount":1},{"id":"styling","name":"Styling","score":60,"status":"warn","summary":"Mixed CSS + styled-components; 23 hardcoded colours.","issueCount":2},{"id":"deps","name":"Dependencies","score":84,"status":"good","summary":"2 unused deps; moment.js inflating the bundle.","issueCount":2}],"issues":[{"id":"i1","category":"components","severity":"critical","title":"God component: Dashboard.jsx (612 lines)","description":"Dashboard.jsx mixes data fetching, layout, and 4 unrelated widgets. It is the single largest re-render hotspot.","file":"src/pages/Dashboard.jsx","meta":[{"label":"Lines","value":"612"},{"label":"Responsibilities","value":"5"}],"actions":[{"id":"fix-i1","label":"Split with component-fixer","kind":"tool","tool":"component-fixer","params":{"file":"src/pages/Dashboard.jsx"},"fallback":"Split src/pages/Dashboard.jsx into smaller components."},{"id":"explain-i1","label":"Explain the risk","kind":"prompt","prompt":"Explain why a 612-line god component hurts maintainability and performance in React."}]},{"id":"i2","category":"api","severity":"critical","title":"Duplicated endpoint: GET /users called 4 ways","description":"Four components call GET /users with slightly different axios config — no shared client or cache.","file":"src/api/","meta":[{"label":"Call sites","value":"4"}],"actions":[{"id":"fix-i2","label":"Centralise the API client","kind":"prompt","prompt":"Create a centralised typed axios client and replace the 4 duplicate GET /users call sites."}]},{"id":"i3","category":"components","severity":"high","title":"God component: Settings.jsx (438 lines)","file":"src/pages/Settings.jsx","meta":[{"label":"Lines","value":"438"}],"actions":[{"id":"fix-i3","label":"Split with component-fixer","kind":"tool","tool":"component-fixer","params":{"file":"src/pages/Settings.jsx"},"fallback":"Split src/pages/Settings.jsx."}]},{"id":"i4","category":"api","severity":"high","title":"No central HTTP client — axios imported in 19 files","file":"src/","actions":[]},{"id":"i5","category":"state","severity":"high","title":"Derived state stored in Redux (totals recomputed on every action)","file":"src/store/cartSlice.js","actions":[{"id":"explain-i5","label":"How to fix","kind":"prompt","prompt":"Show how to replace derived state in Redux with reselect selectors."}]},{"id":"i6","category":"deps","severity":"high","title":"moment.js adds ~230KB — replace with date-fns or Temporal","file":"package.json","meta":[{"label":"Bundle impact","value":"~230KB"}],"actions":[]},{"id":"i7","category":"components","severity":"medium","title":"Prop drilling 4 levels deep for `user`","file":"src/pages/Reports.jsx","actions":[]},{"id":"i8","category":"styling","severity":"medium","title":"23 hardcoded hex colours outside the token system","file":"src/styles/","actions":[]},{"id":"i9","category":"state","severity":"medium","title":"No memoised selectors — components re-render on unrelated store changes","file":"src/store/","actions":[]},{"id":"i10","category":"api","severity":"medium","title":"Errors swallowed — 11 axios calls have no .catch","file":"src/api/","actions":[]},{"id":"i11","category":"components","severity":"medium","title":"List rendered with array index as key","file":"src/components/Table.jsx","actions":[]},{"id":"i12","category":"routing","severity":"medium","title":"No route-level code splitting (React.lazy)","file":"src/App.jsx","actions":[]},{"id":"i13","category":"deps","severity":"low","title":"2 unused dependencies in package.json","file":"package.json","actions":[]},{"id":"i14","category":"styling","severity":"low","title":"Duplicate utility classes across 6 CSS files","file":"src/styles/","actions":[]},{"id":"i15","category":"components","severity":"low","title":"12 components missing displayName","file":"src/components/","actions":[]},{"id":"i16","category":"api","severity":"low","title":"Base URL hardcoded instead of env var","file":"src/api/config.js","actions":[]},{"id":"i17","category":"components","severity":"low","title":"Inline arrow functions in 31 render paths","file":"src/","actions":[]},{"id":"i18","category":"state","severity":"low","title":"Local component state duplicates Redux store data","file":"src/pages/Profile.jsx","actions":[]}]}</script>
441
+ <script>(function(){
442
+ "use strict";
443
+ function readEmbedded(){
444
+ var el=document.getElementById('report-data');
445
+ if(!el) return null; try{ return JSON.parse(el.textContent||'null'); }catch(e){ return null; }
446
+ }
447
+ function readUrl(){
448
+ try{ var p=new URLSearchParams(location.search).get('data'); if(!p) return null;
449
+ var bytes=Uint8Array.from(atob(p), function(c){ return c.charCodeAt(0); });
450
+ return JSON.parse(new TextDecoder().decode(bytes)); }catch(e){ return null; }
451
+ }
452
+ var DATA = window.__REPORT__ || readEmbedded() || readUrl();
453
+ if(!DATA){ return; }
454
+ var EMBEDDED = false; try{ EMBEDDED = window.self !== window.top; }catch(e){ EMBEDDED = true; }
455
+ var HOST_ORIGIN = window.__MCP_HOST_ORIGIN__ || '*'; // host may inject its origin to harden postMessage
456
+ var lastFocus = null;
457
+ var root = document.documentElement;
458
+ var SEV_RANK = { critical:4, high:3, medium:2, low:1 };
459
+
460
+ function E(s){ return String(s==null?'':s)
461
+ .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
462
+ .replace(/"/g,'&quot;').replace(/'/g,'&#39;'); }
463
+ function $(id){ return document.getElementById(id); }
464
+
465
+ // ---------- theme ----------
466
+ function store(k,v){ try{ if(v==null) return localStorage.getItem(k); localStorage.setItem(k,v); }catch(e){ return null; } }
467
+ function applyTheme(t){
468
+ root.setAttribute('data-theme', t);
469
+ var sun=document.querySelector('.theme-sun'), moon=document.querySelector('.theme-moon');
470
+ if(sun&&moon){ sun.hidden = t!=='dark'; moon.hidden = t==='dark'; }
471
+ }
472
+ var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
473
+ applyTheme(store('mcp-ui-theme') || (prefersDark?'dark':'light'));
474
+ var tt=$('theme-toggle');
475
+ if(tt) tt.addEventListener('click', function(){
476
+ var next = root.getAttribute('data-theme')==='dark'?'light':'dark';
477
+ applyTheme(next); store('mcp-ui-theme', next);
478
+ });
479
+
480
+ // ---------- band + intro animation ----------
481
+ root.setAttribute('data-band', DATA.score>=70?'good':(DATA.score>=45?'warn':'bad'));
482
+ var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
483
+ requestAnimationFrame(function(){
484
+ var arc=document.querySelector('.gauge .arc');
485
+ if(arc) arc.style.strokeDashoffset = arc.getAttribute('data-target');
486
+ Array.prototype.forEach.call(document.querySelectorAll('.bar > i'), function(i){ i.style.width=i.getAttribute('data-w')+'%'; });
487
+ var el=document.querySelector('.score'); if(!el) return;
488
+ var target=parseInt(el.getAttribute('data-count'),10)||0;
489
+ if(reduce){ el.textContent=target; return; }
490
+ var start=null;
491
+ requestAnimationFrame(function step(ts){ if(!start)start=ts; var p=Math.min(1,(ts-start)/1000);
492
+ el.textContent=Math.round((1-Math.pow(1-p,3))*target); if(p<1) requestAnimationFrame(step); });
493
+ });
494
+
495
+ // ---------- actions (DataAdapter) ----------
496
+ var actionsById={};
497
+ (DATA.topActions||[]).forEach(function(a){ actionsById[a.id]=a; });
498
+ (DATA.issues||[]).forEach(function(it){ (it.actions||[]).forEach(function(a){ actionsById[a.id]=a; }); });
499
+ function copy(text){
500
+ try{ if(navigator.clipboard&&navigator.clipboard.writeText) return navigator.clipboard.writeText(text); }catch(e){}
501
+ try{ var t=document.createElement('textarea'); t.value=text; document.body.appendChild(t); t.select();
502
+ document.execCommand('copy'); document.body.removeChild(t); }catch(e){}
503
+ }
504
+ var toastTimer;
505
+ function toast(msg){
506
+ var el=$('toast'); if(!el) return; el.textContent=msg; el.classList.add('show');
507
+ clearTimeout(toastTimer); toastTimer=setTimeout(function(){ el.classList.remove('show'); }, 2600);
508
+ }
509
+ function runAction(id){
510
+ var a=actionsById[id]; if(!a) return;
511
+ if(a.kind==='link'){
512
+ if(!/^https?:\/\//i.test(a.href||'')){ toast('Unsafe link blocked'); return; }
513
+ window.open(a.href,'_blank','noopener'); return;
514
+ }
515
+ if(EMBEDDED){
516
+ var msg = a.kind==='tool'
517
+ ? { type:'tool', messageId:'mcp-ui-'+Date.now(), payload:{ toolName:a.tool, params:a.params||{} } }
518
+ : { type:'prompt', messageId:'mcp-ui-'+Date.now(), payload:{ prompt:a.prompt } };
519
+ try{ window.parent.postMessage(msg,HOST_ORIGIN); toast(a.kind==='tool'?('Running '+a.tool+' …'):'Sent to agent ✓'); return; }catch(e){}
520
+ }
521
+ var text = a.kind==='tool' ? (a.fallback||('Run '+a.tool)) : (a.prompt||a.fallback||a.label);
522
+ copy(text); toast('Copied — paste into your agent');
523
+ }
524
+ document.addEventListener('click', function(e){
525
+ var el=e.target.closest && e.target.closest('[data-action]');
526
+ if(el){ e.preventDefault(); runAction(el.getAttribute('data-action')); }
527
+ });
528
+ document.addEventListener('keydown', function(e){
529
+ if((e.key==='Enter'||e.key===' ')){ var el=e.target.closest && e.target.closest('.qitem[data-action]');
530
+ if(el){ e.preventDefault(); runAction(el.getAttribute('data-action')); } }
531
+ });
532
+
533
+ // ---------- triage table ----------
534
+ var state={ sev:'all', q:'', cat:null, key:'severity', dir:'desc' };
535
+ var rowsEl=$('rows'), emptyEl=$('empty'), countEl=$('issue-count');
536
+ function passes(it){
537
+ if(state.sev!=='all' && it.severity!==state.sev) return false;
538
+ if(state.cat && it.category!==state.cat) return false;
539
+ if(state.q){ var q=state.q.toLowerCase();
540
+ var hay=(it.title+' '+(it.file||'')+' '+(it.category||'')+' '+(it.description||'')).toLowerCase();
541
+ if(hay.indexOf(q)<0) return false; }
542
+ return true;
543
+ }
544
+ function cmp(a,b){
545
+ var k=state.key, av, bv;
546
+ if(k==='severity'){ av=SEV_RANK[a.severity]||0; bv=SEV_RANK[b.severity]||0; }
547
+ else { av=(a[k]||'').toString().toLowerCase(); bv=(b[k]||'').toString().toLowerCase(); }
548
+ if(av<bv) return state.dir==='asc'?-1:1;
549
+ if(av>bv) return state.dir==='asc'?1:-1;
550
+ return 0;
551
+ }
552
+ function catName(id){ var c=(DATA.categories||[]).filter(function(x){return x.id===id;})[0]; return c?c.name:id; }
553
+ function render(){
554
+ var list=(DATA.issues||[]).filter(passes).slice().sort(cmp);
555
+ rowsEl.innerHTML = list.map(function(it){
556
+ return '<tr data-id="'+E(it.id)+'" tabindex="0" role="button" aria-label="Open issue: '+E(it.title)+'">'+
557
+ '<td><span class="sev '+E(it.severity)+'"><span class="pip"></span>'+E(it.severity)+'</span></td>'+
558
+ '<td class="cell-title">'+E(it.title)+'</td>'+
559
+ '<td><span class="tag">'+E(catName(it.category))+'</span></td>'+
560
+ '<td class="cell-file">'+E(it.file||'—')+'</td></tr>';
561
+ }).join('');
562
+ emptyEl.hidden = list.length>0;
563
+ if(countEl) countEl.textContent = list.length+(state.cat?(' in '+catName(state.cat)):(' of '+(DATA.issues||[]).length));
564
+ }
565
+ if(rowsEl){
566
+ rowsEl.addEventListener('click', function(e){
567
+ var tr=e.target.closest('tr[data-id]'); if(tr) openDrawer(tr.getAttribute('data-id'));
568
+ });
569
+ rowsEl.addEventListener('keydown', function(e){
570
+ if(e.key==='Enter'||e.key===' '){ var tr=e.target.closest('tr[data-id]');
571
+ if(tr){ e.preventDefault(); openDrawer(tr.getAttribute('data-id')); } }
572
+ });
573
+ Array.prototype.forEach.call(document.querySelectorAll('.filter button'), function(b){
574
+ b.addEventListener('click', function(){
575
+ state.sev=b.getAttribute('data-sev');
576
+ Array.prototype.forEach.call(document.querySelectorAll('.filter button'), function(x){
577
+ x.setAttribute('aria-pressed', x===b?'true':'false'); });
578
+ render();
579
+ });
580
+ });
581
+ var search=$('search');
582
+ if(search) search.addEventListener('input', function(){ state.q=search.value; render(); });
583
+ Array.prototype.forEach.call(document.querySelectorAll('thead th[data-sort]'), function(th){
584
+ th.addEventListener('click', function(){
585
+ var k=th.getAttribute('data-sort');
586
+ if(state.key===k){ state.dir=state.dir==='asc'?'desc':'asc'; } else { state.key=k; state.dir=k==='severity'?'desc':'asc'; }
587
+ Array.prototype.forEach.call(document.querySelectorAll('thead th[data-sort]'), function(x){ x.removeAttribute('aria-sort'); });
588
+ th.setAttribute('aria-sort', state.dir==='asc'?'ascending':'descending');
589
+ render();
590
+ });
591
+ });
592
+ Array.prototype.forEach.call(document.querySelectorAll('.card[data-category]'), function(card){
593
+ function trigger(){
594
+ var id=card.getAttribute('data-category');
595
+ state.cat = state.cat===id ? null : id;
596
+ Array.prototype.forEach.call(document.querySelectorAll('.card[data-category]'), function(c){
597
+ c.style.borderColor = (c===card && state.cat) ? 'var(--accent)' : ''; });
598
+ render();
599
+ var sec=document.querySelector('.table-card'); if(sec&&state.cat) sec.scrollIntoView({behavior:reduce?'auto':'smooth', block:'start'});
600
+ }
601
+ card.addEventListener('click', trigger);
602
+ card.addEventListener('keydown', function(e){ if(e.key==='Enter'||e.key===' '){ e.preventDefault(); trigger(); } });
603
+ });
604
+ render();
605
+ }
606
+
607
+ // ---------- drawer ----------
608
+ var drawer=$('drawer'), scrim=$('scrim');
609
+ function field(k,v,mono){ return '<div class="field"><div class="k">'+E(k)+'</div><div class="v'+(mono?' mono':'')+'">'+E(v)+'</div></div>'; }
610
+ function openDrawer(id){
611
+ var it=(DATA.issues||[]).filter(function(x){return x.id===id;})[0]; if(!it) return;
612
+ lastFocus=document.activeElement;
613
+ $('d-title').textContent=it.title;
614
+ var body='';
615
+ body+='<div class="kv" style="margin-bottom:16px">'+
616
+ '<span class="pair"><b style="color:var(--sev-'+E(it.severity)+')">'+E(it.severity)+'</b></span>'+
617
+ '<span class="pair">'+E(catName(it.category))+'</span></div>';
618
+ if(it.description) body+=field('What it means', it.description);
619
+ if(it.file) body+=field('File', it.file, true);
620
+ (it.meta||[]).forEach(function(m){ body+=field(m.label, m.value); });
621
+ $('d-body').innerHTML=body;
622
+ $('d-acts').innerHTML=(it.actions||[]).map(function(a,i){
623
+ return '<button class="btn'+(i===0?' primary':'')+'" type="button" data-action="'+E(a.id)+'">'+E(a.label)+'</button>';
624
+ }).join('') || '<span style="color:var(--faint);font-size:12px">No automated fix available</span>';
625
+ drawer.classList.add('open'); scrim.classList.add('open');
626
+ var dcEl=$('d-close'); if(dcEl) dcEl.focus();
627
+ }
628
+ function closeDrawer(){
629
+ drawer.classList.remove('open'); scrim.classList.remove('open');
630
+ if(lastFocus&&lastFocus.focus){ lastFocus.focus(); lastFocus=null; }
631
+ }
632
+ if(scrim) scrim.addEventListener('click', closeDrawer);
633
+ var dc=$('d-close'); if(dc) dc.addEventListener('click', closeDrawer);
634
+ document.addEventListener('keydown', function(e){ if(e.key==='Escape') closeDrawer(); });
635
+
636
+ // ---------- export ----------
637
+ var ex=$('export-btn');
638
+ function md(s){ return String(s==null?'':s).replace(/[\r\n]+/g,' '); }
639
+ if(ex) ex.addEventListener('click', function(){
640
+ var L=[]; L.push('# '+md(DATA.meta.title)); L.push('');
641
+ L.push('**Target:** '+md(DATA.meta.target)+' ');
642
+ L.push('**Health score:** '+DATA.score+'/100 ');
643
+ L.push('**Issues:** '+DATA.totalIssues); L.push('');
644
+ L.push('## Areas');
645
+ (DATA.categories||[]).forEach(function(c){ L.push('- **'+md(c.name)+'** ('+c.status+(typeof c.score==='number'?', '+c.score:'')+') — '+md(c.summary)); });
646
+ L.push(''); L.push('## Issues');
647
+ (DATA.issues||[]).slice().sort(function(a,b){return (SEV_RANK[b.severity]||0)-(SEV_RANK[a.severity]||0);}).forEach(function(it){
648
+ L.push('- ['+it.severity.toUpperCase()+'] '+md(it.title)+(it.file?(' — '+md(it.file)):'')); });
649
+ copy(L.join('\n')); toast('Report copied as Markdown ✓');
650
+ });
651
+ })();</script>
652
+ </body>
653
+ </html>