Skip to content

Fix loadFile bugs, add CI, lint, tests on Vitest, split ImageTool - #9

Merged
Sting29 merged 12 commits into
masterfrom
fix-load-file-and-ci
Sep 24, 2026
Merged

Sting29 merged 12 commits into
masterfrom
fix-load-file-and-ci

Conversation

@Sting29

@Sting29 Sting29 commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

Summary

One branch, one commit per item from the code review.

Bugs

  • loadFile: the "too large" message read width and height after close() and said "0×0". Read before closing.
  • loadFile: the decoded bitmap (up to 100 MP) was never closed if the worker call failed. It is now owned by loadFile until handed to state.
  • loadFile: two quick loads could apply the older one last. Each load has a number; a stale one closes its bitmap and stops.

Infrastructure

  • CI (GitHub Actions): lint, format check, tests, build on every PR and on master. The check job is required in branch protection.
  • oxlint (react, react-hooks, jsx-a11y) and Prettier, with exact-pinned versions. oxlint instead of ESLint because typescript-eslint does not support TypeScript 7. Findings were fixed or carry a targeted disable comment with the reason. Formatting is its own commit.
  • Tests run on Vitest (30 tests). buildSwatch is imported directly; the copy in the test is gone.
  • Dev port pinned to 5180 in vite.config.ts, engines + .nvmrc for Node 22, four exports that were only used in their own file are no longer exported.
  • Dependency versions pinned exactly, index.html reformatted (commit by the author, included as is).

Code quality

  • Worker RPC is typed with request and result maps; the six as Promise<...> casts are gone.
  • The mask is computed once per set of parameters instead of once each for preview, HDR build and PQ build.
  • Failures are logged with their cause instead of swallowed.
  • ImageTool.tsx (647 lines, 12 useState, 7 useEffect) is now a 280-line composition of useImageSession, useMaskPreview, useBuiltFile, DropZone, GlowColors, GlowSliders, ResultCard. Same data flow: React state -> ImageWorker -> Pipeline.

Test plan

  • Clean npm ci, then lint, format:check, 30 tests, tsc, production build all pass
  • Dev and production build in the browser: samples, chips (add/remove/found), hex add and error, 12-color cap, sliders, background, preview toggle, empty state, oversized image message, fast double load, swatch generator
  • Production build: HDR JPEG has gain map XMP and MPF, PQ JPEG has an ICC profile, no console errors
  • CI check job and Cloudflare Workers Build pass on this PR

🤖 Generated with Claude Code

Sting29 and others added 2 commits September 24, 2026 04:07
Two bugs in the image loader:
- The "too large" message read width and height after decoded.close(),
  and a closed ImageBitmap reports 0, so the message said "0×0".
  Size is now read before closing.
- If worker.load (or anything after the decode) threw, the decoded
  bitmap was never closed, leaving up to 100 megapixels in memory. The
  bitmap is now owned by loadFile until it is handed to state, and the
  catch closes it.

Verified with a 10100x10100 PNG: message shows the real size.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cloudflare only ran the build, so a failing npm run check could be
merged. The workflow installs with npm ci, runs the Node checks, then
the production build (which includes tsc).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://fix-load-file-and-ci-hdr-glow.beregovyyk.workers.dev (commit 2a6abe9)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://2d5f723d-hdr-glow.beregovyyk.workers.dev 2a6abe9 2026-09-24T07:30:51.495Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://4b98b3e1-hdr-glow.beregovyyk.workers.dev bd14364 2026-09-24T01:07:59.260Z Visit the dashboard ↗

Sting29 and others added 10 commits September 24, 2026 10:21
Versions in package.json and package-lock.json no longer use ^ ranges,
so installs are reproducible. index.html was reformatted, and its
viewport meta is now "width=device-width" (no initial-scale=1).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
protocol.ts now has Requests (payload per request type) and Results
(result per request type), and WorkerRequest is derived from Requests.
ImageWorker.ask is overloaded on them, so a call site gets its exact
result type, and the newest-wins requests (Skippable) are typed
"| null". The six "as Promise<...>" casts are gone; one cast remains,
at the postMessage boundary.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
preview, build and buildPq each recomputed the mask for the same
colors, tolerance and softness, so one slider position cost three
passes over up to four million pixels. The mask is now keyed by its
parameters and dropped when the picture or its background changes.
Output is unchanged (Lighthouse sample: 181 KB HDR, 127 KB PQ, 5% mask).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Six catch blocks showed the visitor a generic message and threw the
error away, so a real failure left nothing to debug from. logError()
now writes the cause to the console next to each message. The two
catches left in copyText are intended fallbacks and are commented as
such.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
oxlint (with the react, react-hooks and jsx-a11y rules) is used instead
of ESLint because typescript-eslint does not support TypeScript 7 yet.
Findings fixed or explained:
- The status lines are <output> elements instead of role="status".
- useBuiltFile no longer sets state in an effect to clear its result;
  it returns null when no colors are chosen.
- The deliberate exhaustive-deps omission, the focusable code block,
  the drop zone, the preview-mode group and the swatch object URL now
  carry a targeted disable comment with the reason.
Adds npm run lint, format and format:check, and runs lint and
format:check in CI (the formatting itself lands in the next commit).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Formatting only, no behavior change: tsc, npm run check and the linter
pass before and after.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ImageTool.tsx was 647 lines with 12 useState and 7 useEffect calls. It
is now a 280-line composition of:
- useImageSession: the worker, the loaded bitmap, the background, the
  pixel version and the error, plus loadFile and loadSample. loadFile
  takes an onLoaded callback that runs in the same tick as its state
  updates, so a new picture still renders with its suggested colors.
- useMaskPreview: the debounced mask canvas and its coverage.
- useBuiltFile and ResultCard: moved out unchanged.
- DropZone, GlowColors (chips, hex form, found-in-image chips) and
  GlowSliders: the markup, each owning only its own local state.
- config.ts: the limits and delays; toHex moved into tool/colors.ts
  and gained a test.
The data flow is the same as before: React state -> ImageWorker ->
Pipeline. Verified in the browser end to end: samples, chip add and
remove, hex add and error, the 12-color cap, sliders, background,
preview toggle, empty state, and the oversized-image message.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- vite.config.ts sets the dev server port to 5180 (5173 is Vite's
  default and often taken by another project).
- package.json engines and .nvmrc state Node 22: npm run check runs .ts
  files directly, which needs 22.18 or newer.
- displaySize, stripMetadata, REFERENCE_WHITE_NITS and
  GainMapJpegInput were exported but only used in their own file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two quick sample clicks could apply the slower, older picture last. Each
load takes a number, and a load that is no longer the latest closes its
bitmap and stops after the decode and after the worker call.
Verified by clicking Arctic then Lighthouse, Arctic then KB Tech, and
three in a row: the last click always ends up shown.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
tools/check/check.mjs had its own mini runner and could not import
src/tool/swatch.ts (Node's loader needs file extensions that the
Vite-style imports omit), so buildSwatch was copied into the test.
Vitest runs the same tests through Vite's resolver: the test now imports
buildSwatch directly and the runner code is gone. npm run check is
"vitest run" (30 tests). Comments that explained the old limitation are
updated, engines is set to Vite's own minimum (Node 22.12), and the
README lists the scripts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Sting29 Sting29 changed the title Fix loadFile bugs and add CI Fix loadFile bugs, add CI, lint, tests on Vitest, split ImageTool Sep 24, 2026
@Sting29
Sting29 merged commit 9e5a971 into master Sep 24, 2026
2 checks passed
@Sting29
Sting29 deleted the fix-load-file-and-ci branch September 24, 2026 08:17
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