Skip to content

refactor(frontend): decompose 4 hotspot components into focused submodules#56

Merged
ty13r merged 4 commits intomainfrom
refactor/frontend-hotspot-decomposition
Apr 20, 2026
Merged

refactor(frontend): decompose 4 hotspot components into focused submodules#56
ty13r merged 4 commits intomainfrom
refactor/frontend-hotspot-decomposition

Conversation

@ty13r
Copy link
Copy Markdown
Owner

@ty13r ty13r commented Apr 20, 2026

Summary

Decomposes the four remaining frontend hotspots called out in the Wave 6 PR. Each component above 400 LOC (docs/clean-code.md §2 TSX ceiling) now has its rendering split into focused sub-components and its fetches converted to typed React Query hooks.

Component Before After New submodules
PackageExplorer.tsx 553 197 packageExplorer/ × 6 files
SpecializationInput.tsx 561 344 specializationInput/ × 8 files
PipelineSteps.tsx 814 122 pipelineSteps/ × 3 files
EvolutionArena.tsx 541 335 evolutionArena/ × 5 files

Per-component highlights

PackageExplorer — tree-shaping logic (buildFiles.ts), the file-tree widget, the checklist rubric, and the PACKAGE.md template each moved to their own module. Pure logic landed with 5 focused unit tests + one jsdom smoke test.

SpecializationInput — the 5 raw fetch() calls collapsed to one typed hook (useSeeds) + one pure dispatcher (startEvolution.ts) that handles all three source modes (scratch / upload / fork). Cost estimator extracted as a pure function with 4 calibration-point tests. SourceModePicker / EvolutionModePicker / SeedPicker / RunEstimateCard are now reusable.

PipelineSteps — the 12 inline SVG mini-visualizations split into foundationVisuals.tsx (steps 1-6) + loopVisuals.tsx (steps 7-12). Step metadata lives in steps.ts. Container is now 122 LOC of observer + layout.

EvolutionArena — 4 raw fetch + 1 setInterval poller replaced with useRun + useRunDimensions(refetchInterval) + new useBenchSummary. Right-column cards (ArenaHeader, CompositeScoring, BaselineContext, PerDimensionPipeline, CompletedDimensions) are their own files.

New tests

  • packageExplorer/buildFiles.test.ts — 5 tests on the tree-shaping logic
  • PackageExplorer.test.tsx — jsdom smoke test
  • specializationInput/estimateCost.test.ts — 4 calibration tests

Test plan

  • cd frontend && npm run build — passes
  • cd frontend && npm run lint — clean
  • cd frontend && npm run format:check — clean
  • cd frontend && npm run test — 45 passed (up from 35)
  • Backend untouched — still green

🤖 Generated with Claude Code

Matt (via Claude Code) and others added 4 commits April 20, 2026 01:00
PackageExplorer was a 553-LOC god-component mixing file-list building,
tree rendering, file viewer, checklist rubric, and the master-detail
layout. Split along the obvious seams:

  packageExplorer/types.ts           shared VirtualFile + path helpers
  packageExplorer/buildMeta.ts       pure PACKAGE.md template builder
  packageExplorer/buildFiles.ts      pure partition into installable
                                     + metadata (fully unit-testable)
  packageExplorer/FileTree.tsx       collapsible directory widget
  packageExplorer/GoldStandardChecklist.tsx
                                     CLAUDE.md rubric comparison
  PackageExplorer.tsx (197 LOC)      orchestrator: master-detail layout,
                                     ExplorerHeader + FileViewer
                                     sub-components kept inline

Largest submodule is 132 LOC, under the 400-LOC TSX ceiling in
docs/clean-code.md §2.

Tests
-----
- packageExplorer/buildFiles.test.ts (5 tests) — pure tree-shaping logic:
  SKILL.md first, supporting_files grouping, PACKAGE.md counts,
  integration_report extraction, _meta/parents/*.md per winner.
- PackageExplorer.test.tsx (1 test, jsdom) — smoke test: renders with
  fixture data, asserts installable + metadata counts + download link.

QA: frontend build, lint, format:check, 41 vitest tests (+6) all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "Start an Evolution Run" form was 561 LOC with 12 useState, 5 raw
fetch() calls (one useEffect + one submit branching to 3 endpoints),
and every layout concern inline. Decomposed along obvious seams:

  specializationInput/types.ts            SourceMode / EvolutionMode /
                                          UploadResponse / SeedSummary
  specializationInput/estimateCost.ts     pure time+cost estimator
                                          (calibrated from live runs)
  specializationInput/startEvolution.ts   pure API dispatcher — handles
                                          all 3 source modes + 4
                                          request shapes through one
                                          typed signature
  specializationInput/SourceModePicker    Starting-Point radio grid
  specializationInput/EvolutionModePicker Auto/Atomic/Classic radio
  specializationInput/SeedPicker          category-filter + seed grid
  specializationInput/RunEstimateCard     footer estimate + submit btn

  api/hooks/seeds.ts                      typed useSeeds hook replacing
                                          the raw /api/seeds fetch

  SpecializationInput.tsx                 orchestrator — state + compose

Also inlined small ScratchBody / UploadBody / ForkBody /
GeneratedPackageBanner helpers in the main file; they're too
parent-coupled to merit their own module.

Tests
-----
- estimateCost.test.ts: 4 tests pinning the calibration points
  (5x3 = 53min/$7.50; 2x1 = 9min/$2) and the hrs-vs-min rollover.

QA: frontend build, lint, format:check, 45 vitest tests (+4) all green.
Largest submodule is 96 LOC, under the 400-LOC TSX ceiling.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Homepage "How SKLD Works" section was 814 LOC with 12 inline SVG
sub-components + the 12-entry STEPS config + the observer layout
container. Split at the obvious seams:

  pipelineSteps/foundationVisuals.tsx  (269) steps 1-6 SVG illustrations
  pipelineSteps/loopVisuals.tsx        (335) steps 7-12 SVG illustrations
  pipelineSteps/steps.ts               (119) Step type + STEPS data
  PipelineSteps.tsx                    (122) observer + scroll-reveal layout

Biggest submodule is 335 LOC, under the 400-LOC TSX ceiling.

QA: frontend build, lint, format:check, 45 vitest tests all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The live-run arena was 541 LOC with 4 raw fetch() calls (including a
setInterval poller), 4 inline right-column cards, and the header
inlined. Converted to TanStack hooks + extracted sub-components.

Fetch -> hooks
--------------
- fetch("/api/runs/:id")            -> useRun()
- fetch("/api/runs/:id/dimensions") -> useRunDimensions() with the new
  `refetchInterval` option so React Query does the 5s poll (replaces
  the hand-rolled setInterval + useEffect cleanup).
- fetch("/api/bench/summary")       -> new useBenchSummary() hook
- fetch("/api/runs/:id/cancel")     -> apiClient.post (POST action,
  no caching needed)

Sub-components extracted
------------------------
  evolutionArena/ArenaHeader           (85) title + status + elapsed
                                            + budget + Cancel button
  evolutionArena/CompositeScoring      (54) 6-layer rubric card
  evolutionArena/BaselineContext       (53) raw-Sonnet baseline card
  evolutionArena/PerDimensionPipeline  (54) 5-step mini tracker
  evolutionArena/CompletedDimensions   (47) per-dim fitness list + avg

Inline CurrentChallenge + CompetitionPanel helpers kept in the main
file — they're too parent-coupled to merit their own modules.

Biggest submodule is 85 LOC, under the 400-LOC TSX ceiling.

QA: frontend build, lint, format:check, 45 vitest tests all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ty13r ty13r merged commit 2c01a8d into main Apr 20, 2026
2 checks passed
@ty13r ty13r deleted the refactor/frontend-hotspot-decomposition branch April 20, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant