Skip to content

Slim down Kolu: extract reusable @kolu/solid-* packages#1038

Open
srid wants to merge 9 commits into
masterfrom
ralph2
Open

Slim down Kolu: extract reusable @kolu/solid-* packages#1038
srid wants to merge 9 commits into
masterfrom
ralph2

Conversation

@srid

@srid srid commented May 30, 2026

Copy link
Copy Markdown
Member

What

Kolu's client (packages/client/src) carried a lot of app-agnostic SolidJS code —
generic UI atoms, an icon set, popover primitives, a WebGL-context manager,
keyboard/clipboard helpers. This PR extracts that code into reusable workspace
packages, following the existing @kolu/solid-pierre precedent, so the main app
shrinks to the code that's actually Kolu-specific: −1,412 LOC (−5.5%), 189 → 174 files.

Driven by the Ralph loop (measure → profile → extract the biggest clean
candidate → re-measure → commit only on real reduction). Full methodology,
per-cycle log, and dead-ends in docs/client-loc-ralph-report.md.
Every extraction is behaviour-preserving (mechanical move + re-export).

Packages extracted

Package Modules Axis of change solid-dep?
@kolu/solid-icons Icons (38 SVG components) icon-set content yes
@kolu/solid-ui Toggle, Kbd, SegmentedControl, Row, Section, Surface, stackLayers, Tip design system / presentation yes
@kolu/solid-overlay useAnchoredPopover (+test), OptionMenu anchored positioning / overlays yes
@kolu/solid-xterm createXtermWebgl WebGL-context lifecycle yes
@kolu/platform keyboard, os, clipboard browser / OS API (no solid-js, hence no solid- prefix) no

The solid-xterm cycle (read this one)

Unlike the others, xterm is complected with Kolu's session wiring in the 950-LOC
Terminal.tsx, and its async mount is a minefield of heap-leak fixes (#591/#606/#575)
with no automated guard — caught only by manual heap snapshots; the e2e covers
terminal behaviour, not disposal. So a wholesale rip would be irresponsible.

The responsible seam is the WebGL-context lifecycle (createXtermWebgl): the most
reusable, hardest-won, self-contained xterm knowledge (Chrome's ~16-context limit, the
loseContext() GPU release xterm omits, link-layer canvas capture, texture-atlas
management). The leak-critical mount/cleanup ordering stays at the call site where
SolidJS owner capture must happen; only the WebGL function bodies move. The temporary
#591 zombie-context tracker stays in the client and is inverted into the primitive via
optional lifecycle hooks, so the library carries no Kolu debug dependency.

Measurements

Metric Before After Δ
client/src LOC 25,613 24,201 −1,412 (−5.5%)
client/src files 189 174 −15

Verification

All CI gates green locally: typecheck (pnpm -r), biome lint, unit
(server + client + the relocated solid-overlay test), fmt-check, nix build
(server + client), pnpm-hash-fresh, surface-example-build, smoke, and e2e
(106 scenarios — terminal/canvas/screenshot/sub-terminal + command-palette/keyboard-shortcuts;
canvas-multi-tile + screenshot exercise the WebGL paths).

One gotcha worth flagging: default.nix's src fileset is an explicit allowlist —
a new packages/<x> dir must be registered there or the nix build silently can't see it.

🤖 Generated with Claude Code

srid added 8 commits May 29, 2026 21:46
Baseline: packages/client/src = 25,613 LOC across 189 files.
Target: extract app-agnostic SolidJS into @kolu/solid-{icons,ui,overlay,platform}.
Move ui/Icons.tsx (38 SVG components, zero Kolu-domain coupling) into a
reusable workspace package; rewrite 26 import sites to @kolu/solid-icons.

Behaviour-preserving. client/src: 25,613 -> 24,998 LOC (-615).
just check + just test-unit green (200 client tests).
Lift the WebGL-context lifecycle (load/unload, GPU-context release that
xterm omits, link-layer canvas capture, texture-atlas management) out of
the 950-LOC Terminal.tsx into a reusable createXtermWebgl primitive.

The leak-critical async mount/cleanup ordering (#591/#606/#575) stays at
the call site where SolidJS owner capture must happen; only the WebGL
function bodies move. The temporary #591 zombie-context tracker stays in
the client and is inverted into the primitive via optional lifecycle
hooks, so the library carries no Kolu debug dependency.

Behaviour-preserving. client/src: 24,998 -> 24,942 LOC (-56);
Terminal.tsx 950 -> 894. Gated by just check + test-unit (200 client) +
74 e2e scenarios (canvas multi-tile + screenshot exercise WebGL paths).
Move the design-system atoms (Toggle, Kbd, SegmentedControl, Row, Section,
Surface, stackLayers, Tip) into a reusable workspace package. All eight are
fully self-contained (solid-js + @corvu/tooltip only). Subpath exports keep
each module's default/named contract, so the 36 import sites are a pure
path rewrite.

Behaviour-preserving. client/src: 24,942 -> 24,662 LOC (-280).
just check + just test-unit green (200 client tests).
…ives

Move useAnchoredPopover (open/dismiss + viewport-flip placement, portal-safe)
and OptionMenu (anchored option list built on it) into a reusable package.
The unit test moves with the primitive and now runs in-package (solid-overlay
vitest), so client drops from 200 to 199 tests with coverage preserved.

Behaviour-preserving. client/src: 24,662 -> 24,405 LOC (-257).
just check + just test-unit green.
Move keyboard (chord matching), os (Apple-modifier detection), and clipboard
(non-secure-context-safe writes + xterm provider) into a reusable package.
These are framework-agnostic — no solid-js dependency — so the package omits
the solid- prefix the other extractions carry. 23 import sites rewritten via
subpath exports; keyboard.test stays in client (it pairs keyboard with Kolu
ACTIONS/prohibitedKeybinds) and now consumes the package.

Behaviour-preserving. client/src: 24,405 -> 24,201 LOC (-204).
default.nix's src fileset is an explicit allowlist; the five extracted
packages (solid-icons, solid-ui, solid-overlay, solid-xterm, platform)
must be listed or they're absent from the sandboxed build and the client
vite build fails to resolve their imports. Also document them in the
README architecture table.

Verified: just build (nix server+client) green; pnpm-hash-fresh green.
@srid srid marked this pull request as ready for review May 30, 2026 02:19
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