85 lines
4.4 KiB
Markdown
85 lines
4.4 KiB
Markdown
# Architecture
|
||
|
||
Colour Tools is a client-only React application built as relocatable static
|
||
files. The UI and calculation engine are intentionally separated so colour
|
||
operations can be tested without a browser and UI state can keep showing the
|
||
last valid result while an input is incomplete.
|
||
|
||
## Layers
|
||
|
||
1. `src/colour/` is the serialisable colour domain. It parses and formats
|
||
colours, converts gamuts, composites layers, interpolates stops, evaluates
|
||
contrast and Delta E, simulates colour-vision deficiencies, builds CSS
|
||
gradients, imports DTCG tokens, and builds or exports palettes. Its public
|
||
API is re-exported from `src/colour/index.ts`.
|
||
2. `src/palette/` contains browser-independent raster bounds, sampling and
|
||
deterministic OKLab clustering. `src/workers/palette.worker.ts` exposes the
|
||
expensive extraction path to a dedicated worker.
|
||
3. `src/components/` contains the seven workspaces and shared accessible
|
||
controls. Workspace selection is represented by a URL fragment, so a view
|
||
can be bookmarked without router or server support.
|
||
4. `src/hooks/` owns bounded device-local UI persistence. Image bytes are not
|
||
stored there.
|
||
5. `src/toolbox/` contains the checked manifest identity. The shared Toolbox
|
||
shell supplies navigation, contextual actions and theme behaviour without
|
||
changing the standalone application domain.
|
||
|
||
## Colour values
|
||
|
||
The engine's `ColourValue` is a plain object: colour-space identifier, three
|
||
numeric coordinates and alpha. It deliberately carries coordinates that may
|
||
sit outside a display gamut. Color.js 0.7.1 supplies standards-aligned parsing
|
||
and conversion primitives; Colour Tools wraps these in typed, bounded domain
|
||
operations and stable UI formats.
|
||
|
||
Browser preview is a separate operation. It maps a value to sRGB and reports
|
||
whether mapping occurred. This keeps display limitations from mutating the
|
||
source colour used by later conversions or comparisons.
|
||
|
||
`dtcg.ts` walks at most 32 group levels/10,000 tokens, validates DTCG 2025.10
|
||
structured colour components, resolves local curly aliases and local JSON
|
||
Pointer `$ref` token/property references with cycle detection, and resolves token type from
|
||
the token, its nearest group or its reference. It does not infer a missing
|
||
type. Numeric `none` semantics cannot be represented by `ColourValue`, so such
|
||
tokens produce an explicit diagnostic. Imported wide-gamut coordinates are
|
||
stored as CSS rather than being destructively mapped to the preview gamut.
|
||
|
||
`accessibility.ts` parses a maximum of 64 palette colours once before its
|
||
bounded 64×64 directional contrast loop. Each foreground/background cell
|
||
reports WCAG ratio thresholds after flattening transparency against a stated
|
||
canvas. `gradient.ts` validates 2–64 ascending stops and exports inert CSS plus
|
||
a schema-versioned recipe for linear, radial, conic and repeating syntax.
|
||
|
||
## Raster pipeline
|
||
|
||
The image picker validates the selected file before decode, rejects excessive
|
||
bytes/dimensions/pixels, then draws the decoded image to a same-origin in-memory
|
||
Canvas 2D surface. Sampling reads a bounded neighbourhood. Palette extraction
|
||
subsamples deterministically, converts points to OKLab and clusters them with a
|
||
fixed initialization and iteration bound. The worker client can fall back to
|
||
the same pure function when workers are unavailable.
|
||
|
||
Temporary object URLs are revoked and outstanding extraction is aborted when an
|
||
image is replaced or removed. No image or extracted palette is transmitted.
|
||
|
||
## Build and deployment
|
||
|
||
Vite builds with a relative base. `scripts/generate-toolbox-manifest.mjs`
|
||
checks package, application and Toolbox versions plus immutable source
|
||
identity. `scripts/prepare-release-files.mjs` places project documentation and
|
||
the exact installed runtime licence inventory beside the executable app.
|
||
|
||
The service worker is relative-scope and deployment-subpath aware. It is
|
||
progressive enhancement: the app remains usable when registration is rejected.
|
||
The deterministic packager rejects symbolic links, source maps, secret-like
|
||
filenames and root-absolute asset references before creating the release ZIP
|
||
and SHA-256 sidecar.
|
||
|
||
## Testing boundaries
|
||
|
||
Unit tests cover parsing, numerical operations, invalid inputs, raster bounds,
|
||
sampling and palette determinism. Browser smoke tests run the production build
|
||
under a restrictive policy at `/deep/nested/colour/`, exercise all major
|
||
workspaces and fail on uncaught errors or external network requests. The
|
||
Toolbox contract checker validates the static artifact independently.
|