Skip to content

core: share parseColorRGBA and normalizeBorderRadius across renderers#1

Open
TSchonleber wants to merge 1 commit intorazroo:mainfrom
TSchonleber:contrib/shared-render-utils
Open

core: share parseColorRGBA and normalizeBorderRadius across renderers#1
TSchonleber wants to merge 1 commit intorazroo:mainfrom
TSchonleber:contrib/shared-render-utils

Conversation

@TSchonleber
Copy link
Copy Markdown

Summary

renderer-canvas and renderer-webgpu each carried their own near-identical implementations of CSS color parsing and border-radius clamping:

  • packages/renderer-canvas/src/renderer.ts:112parseColorRGB (hex + rgb only, 0–255 tuple)
  • packages/renderer-webgpu/src/index.ts:1610parseColor (hex + rgba with alpha, 0–1 tuple)
  • packages/renderer-webgpu/src/index.ts:1586normalizeBorderRadius([tl, tr, br, bl])
  • packages/renderer-canvas/src/renderer.ts:879 — same border-radius clamp inlined inside roundRect()

Any fix (e.g. hsl() support, tighter alpha parsing, additional corner syntax) would need to land twice and risk drift between paint targets.

This PR extracts both helpers into a new packages/core/src/render-utils.ts module and routes both renderers through it. Channels are returned normalized to [0, 1] so WebGPU consumes them directly; renderer-canvas's single contrast callsite now feeds parseColorRGBA output straight into an updated luminance() that also takes [0, 1] inputs. Round-rect border-radius clamping is shared verbatim — canvas's roundRect keeps its Path2D drawing code local and now calls normalizeBorderRadius for the per-corner clamp.

Net diff: +186 / −82 across 5 files (most of the insertions are the new module + tests).

Test plan

  • New packages/core/src/__tests__/render-utils.test.ts — 20 cases: 3/6-digit hex, rgb()/rgba() parsing with whitespace and alpha, malformed fallback to opaque black, uniform + per-corner radius clamping, negative clamps, zero-sized boxes, undefined/empty inputs.
  • bun run test — fast vitest suite goes from 2357 → 2377 passing.
  • bun run build — full workspace build green across all 14 @geometra/* packages + mcp.
  • bunx vitest run packages/renderer-canvas/src/__tests__ packages/renderer-webgpu/src/__tests__ — renderer suites pass unchanged.

Notes for reviewers

  • parseColorRGBA is deliberately the WebGPU shape (4-tuple, normalized, alpha-aware) because it's strictly more capable than the canvas parser. Canvas's previous parseColorRGB didn't handle alpha at all, and its malformed-input fallback ([59, 130, 246] — the same hue as the default selectionColor) still yields selectedTextColor === '#ffffff' after the new luminance() math, so observable behavior at its single callsite is unchanged.
  • Webgpu imports the helper under a local alias parseColorRGBA as parseColor so the 15+ callsites in that file don't need to change.
  • normalizeBorderRadius now also handles undefined input cleanly (returns [0, 0, 0, 0]); canvas callers always gate the call with if (borderRadius) already so this is a strict superset.

…utilities

The Canvas2D and WebGPU renderers each carried their own near-identical
implementations of CSS color parsing and border-radius clamping
(packages/renderer-canvas/src/renderer.ts:112 parseColorRGB, packages/
renderer-webgpu/src/index.ts:1610 parseColor and :1586 normalizeBorderRadius).
Any fix or additional color syntax would have to land twice and risk drift.

Moves both helpers into a new packages/core/src/render-utils.ts module and
routes both renderers through it. Channels are returned normalized to [0, 1]
so WebGPU can consume them directly; renderer-canvas's single contrast callsite
now feeds parseColorRGBA output straight into an updated luminance() that also
expects [0, 1] inputs. Round-rect border-radius clamping is shared verbatim.

Adds 20 vitest cases covering 3/6-digit hex, rgb/rgba parsing with whitespace
and alpha, malformed-input fallback to opaque black, per-corner radius clamping,
zero-sized boxes, and undefined/empty inputs. Fast suite goes from 2357 to 2377
passing; full workspace build stays green.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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