Skip to content

v1.2.0 — Transforms, DOM-API refactor, CI workflow#8

Merged
zingiber96 merged 7 commits into
mainfrom
dev
May 30, 2026
Merged

v1.2.0 — Transforms, DOM-API refactor, CI workflow#8
zingiber96 merged 7 commits into
mainfrom
dev

Conversation

@zingiber96
Copy link
Copy Markdown
Owner

v1.2.0 — Transforms, DOM-API refactor, CI workflow

Summary

Adds a full image-processing pipeline (resize, rotate, flip, crop, resample
kernel) alongside the existing format conversion, replaces the ambiguous
"Convert all" with a context-aware split button, and structurally closes
the three lingering CodeQL XSS alerts by replacing innerHTML usage with
explicit DOM construction. Lands the GitHub Actions CI smoke workflow that
was deferred from v1.1 closing.

7 commits, 9 files changed, +1293 / −158 lines. WebP byte-identical output
preserved.

What's new (user-facing)

Transforms section

A new collapsible section between Privacy and Output folder:

  • Resize — three modes:
    • Max dimension — bounds the long edge, preserves aspect
    • Percentage — 1–1000%
    • Exact dimensions — auto-crops to fit (fit: 'cover')
    • Optional Allow upscale toggle gates enlargement on Max + Percentage
      modes; Exact always allows enlargement (the option would be contradictory).
  • Rotate — fixed 90° / 180° / 270° increments.
  • Flip horizontal and Flip vertical as independent toggles.
  • Crop to aspect — None / 1:1 / 4:3 / 3:2 / 16:9 / Custom W:H. Center
    crop only in v1.2; manual drag crop boxes deferred to v1.3.
  • Resample kernel — Lanczos 3 (default), Lanczos 2, Mitchell, Cubic,
    or Nearest (sharp pixels). Disabled until a resize is active.
  • A live "Resulting dimensions" preview computed from the first file
    in the list. A small "N active" badge in the section header signals
    when any control is non-default. A Reset button restores everything
    to defaults. Per-file row gets a Transforms active: … notice
    summarising what will happen.

Smart-label Convert button

The single ambiguous "Convert all" button becomes a context-aware split
button. The primary label adapts to file state:

State Primary Alt (dropdown)
All files unconverted Convert N files
Mix of new + done Convert N new Reconvert all (N+M)
All converted Reconvert all N files
Empty (disabled)

The ⌄ dropdown only appears when there's a meaningful alternative.

Architecture (the substance)

Server-side transforms

applyTransforms(pipeline, transforms) is a new helper called once at the
endpoint level, between buildSourcePipeline() and the encoder. Operations
apply in fixed order: orient → flip → crop → resize. Matches the
user's mental model and makes the pipeline predictable.

Closes the 3 CodeQL "DOM text reinterpreted as HTML" alerts and
eliminates the entire class of XSS sinks structurally. Introduces
el() and svgEl() helpers; every attribute and text node now flows
through setAttribute / createTextNode, never through the HTML parser.

14 innerHTML writes removed; 0 remain in public/index.html.
Wire in a transform pipeline between buildSourcePipeline() and the
encoder. Applied in fixed order (orient → crop → resize) to match
user mental model.

Resize modes: max (preserves aspect), exact (fit:cover), percentage.
Upscale toggle gates enlargement in max + percentage modes (exact
always allows enlargement since the option is contradictory there).
Crop supports 1:1 / 4:3 / 16:9 / 3:2 / custom WxH aspects via center
crop. Rotate accepts 90/180/270 only; other values are no-ops.

WebP byte-identical guarantee preserved: a missing transforms field
or any transforms-at-default makes the helper a no-op on the pipeline.

20 server tests pass including 8 dimension assertions and a
composition test with metadata toggles + format options.
Collapsible section between Privacy and Output folder. Default-collapsed
with an "N active" badge in the header when any control is non-default.

Resize: segmented mode picker (None / Max / Percentage / Exact) with
inputs that swap based on mode. Upscale toggle gates enlargement on
Max + Percentage modes (Exact always allows enlargement). Live
"resulting dimensions" preview computed from the first file in the list.

Rotate: segmented 0/90/180/270. Flip H + Flip V independent checkboxes.

Crop: aspect dropdown (None / 1:1 / 4:3 / 3:2 / 16:9 / Custom) with
W:H inputs revealed for Custom.

Kernel: Lanczos 3 default, disabled until a resize is active.

Per-file row gets a "Transforms active: …" notice describing what's set.
Reset button next to the badge restores all controls to defaults.

Payload sent as `transforms` form field only when any control is
non-default — preserves the WebP byte-identical guarantee for stock
conversions. End-to-end tested with the server-side applyTransforms()
from Phase 1.
Added:
- Transforms section (resize, rotate, flip, crop, kernel) with
  collapsible UI, live dimensions preview, active-count badge, and
  per-row notice. Sharp-pipeline operations apply in fixed order
  (orient → crop → resize). 20 server tests + JPEG alpha-flatten
  regression check all green.
- Smart-label Convert / Reconvert split button.
- LICENSE file at repo root.
- CI workflow on PRs to main (.github/workflows/ci.yml).

Changed:
- Full DOM-API refactor of public/index.html. 14 innerHTML writes
  eliminated; el() / svgEl() helpers introduced. Closes the three
  CodeQL "DOM text reinterpreted as HTML" alerts structurally.

Deferred:
- RAW input (magick-wasm only extracts embedded preview thumbnails;
  needs a real RAW decoder).
- Manual drag crop boxes (v1.3).

WebP byte-identical guarantee verified preserved.
@zingiber96 zingiber96 merged commit ef07aab into main May 30, 2026
7 checks passed
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