Skip to content

Reset the browser UI to one page, and keep the engine - #2

Merged
SyedSaribSultan merged 1 commit into
mainfrom
ui-reset-one-page
Aug 12, 2026
Merged

Reset the browser UI to one page, and keep the engine#2
SyedSaribSultan merged 1 commit into
mainfrom
ui-reset-one-page

Conversation

@SyedSaribSultan

Copy link
Copy Markdown
Owner

The web app had become a marketing site with a compressor inside it. This removes everything that was not part of compressing an image and rebuilds the interface as a single dashboard whose regions are all on screen at once — the bar, the plan and queue, the comparison stage, and the evidence.

The engine is untouched. worker.js, ss2.js and destinations.js are byte for byte what they were, and the message contract between them and the UI is unchanged. This is a UI reset, not a rewrite.

Removed

The hero and its headline, #learn, #desktop, #faq, the footer, the overflow nav, /compare and /download, the theme switch, the synthetic demo generator, the lifetime savings counter, the celebration bar, the keyboard shortcuts dialog, and the CSV/JSON/summary report export. tools/gen_compare_page.py went with the page it generated, and its CI step with it.

What replaces app.css and app.js

web/css/ Six sheets, one per concern. Every colour and space defined once in base.css and consumed by name. Deliberately not the --oz-* token layer — that now serves the desktop app alone — so this is a fresh baseline of system faces and six colour names.
web/js/ Thirteen ES modules with a one-way dependency graph. format/dom depend on nothing, state is the store, engine owns the worker pool, queue/compare/facts only render and bind no handlers, render schedules them, main is the only module that listens for an event.

6,544 lines of interface become 3,741.

Bugs this found

Each was seen red before it was fixed:

  • CSS grid clamps an item larger than its area to start, not centre, so the comparison sat off-centre and hung off the bottom. probe_zoom had documented this exact trap and caught the regression.
  • The header's four nowrap buttons have a min-content width of 497px, which propped the body open inside a 375px viewport — the page scrolled sideways on a phone.
  • A <select> cannot shrink below its longest option without min-width: 0.
  • The pan clamp allowed a quarter-viewport of slack, so the image could be dragged partly off the stage.
  • Two progress bars transitioned width, forcing layout every frame of a batch.
  • #batch was offset from the bar's top by a hardcoded 41px against a 56px bar.
  • Cancelling the JPEG-transparency dialog restored the wrong format — the pin was captured after the change. It reads state.settings now, which is what the engine is actually running.
  • The chips claimed every version cleared the quality floor. Some had not. Each lossy chip now carries its score and is marked under floor when it missed.
  • The Original chip sorted by size sat mid-ranking; it is the yardstick, not a competitor, so it is always last.
  • Two primary buttons on one screen. Now one.
  • Passthrough reported −0% rather than no saving.
  • A 60-character filename showed 19 legible characters at 1440px and 4 at 375px.
  • ORIGINAL_PICK was a bare "original" sharing a namespace with encoder keys.

The CSP allow-listed a sha256 for an inline script. The page has no inline script at all now, so the hash is gone and inline script is forbidden outright.

Gates

The harness reached the app through accidental globals, which modules do not provide, so main.js declares window.imgc as an explicit, write-only test seam — including holdWork(), because dispatch is a module binding and the "original painted before any encoder runs" assertion needs the run paused to be observable at all.

probe_states and probe_theme were rewritten rather than re-pointed: their subjects were the three-view architecture and the --app-* scale, neither of which exists. The other six probes and e2e keep their assertions.

test_design_system.py gains TheBrowserAppHasOnePlaceForValues, enforcing for the new baseline what the token layer bought for the old one. All four of its checks were broken deliberately and watched fail before being restored.

Verified

Check Result
ruff check . All checks passed
Python suite 109 tests, OK
e2e.mjs 82 passed, 0 failed
8 Chrome probes 219 assertions, 0 failed
verify_tokens.mjs OK

Note

web/heyoz-tokens.css, web/fonts.css and web/fonts/ stay in web/ and are not linked by the new page. tools/sync_webui_assets.py copies them from there into the desktop app, which still uses that design system and still works.

🤖 Generated with Claude Code

The web app had become a marketing site with a compressor inside it. This
removes everything that was not part of compressing an image and rebuilds the
interface as a single dashboard whose regions are all on screen at once.

Gone: the hero and its headline, #learn, #desktop, #faq, the footer, the
overflow nav, /compare and /download, the theme switch, the synthetic demo
generator, the lifetime savings counter, the celebration bar, the keyboard
shortcuts dialog, and the CSV/JSON/summary report export. tools/gen_compare_page.py
went with the page it generated, and its CI step with it.

The engine is untouched. worker.js, ss2.js and destinations.js are byte for byte
what they were; the message contract between them and the UI is unchanged, which
is why this is a UI reset and not a rewrite.

What replaces app.css and app.js:

  web/css/  six sheets, one per concern. Every colour and space is defined once
            in base.css and consumed by name. Deliberately NOT the --oz-* token
            layer - that now serves the desktop app alone - so this is a fresh
            baseline of system faces and six colour names.
  web/js/   thirteen ES modules with a one-way dependency graph. format and dom
            depend on nothing, state is the store, engine owns the worker pool,
            queue/compare/facts only render and bind no handlers, render
            schedules them, main is the only module that listens for an event.

6,544 lines of interface become 3,741.

Bugs this found, each seen red before it was fixed:

  - CSS grid clamps an item larger than its area to start, not centre, so the
    comparison sat off-centre and hung off the bottom. Centring is a transform
    now. probe_zoom had documented this exact trap; it caught the regression.
  - The header's four nowrap buttons have a min-content width of 497px and flex
    will not shrink them, which propped the body open inside a 375px viewport.
  - A <select> cannot shrink below its longest option without min-width: 0.
  - The pan clamp allowed a quarter-viewport of slack, so the image could be
    dragged partly off the stage.
  - Two progress bars transitioned width, forcing layout every frame of a batch.
  - #batch was offset from the bar's top by a hardcoded 41px against a 56px bar.
  - Cancelling the JPEG-transparency dialog restored the wrong format: the pin
    was captured after the change. It reads state.settings now, which is what
    the engine is actually running.
  - The chips claimed every version cleared the quality floor. Some had not.
    Each lossy chip carries its score and is marked "under floor" when it missed.
  - The Original chip sorted by size sat mid-ranking; it is the yardstick, not a
    competitor, so it is always last.
  - Two primary buttons on one screen. Now one.
  - Passthrough reported "-0%" rather than no saving.
  - A 60-character filename showed 19 legible characters at 1440px and 4 at 375px.
  - ORIGINAL_PICK was a bare "original" sharing a namespace with encoder keys.

The CSP allow-listed a sha256 for an inline script. The page has no inline
script at all now, so the hash is gone and inline script is forbidden outright.

Gates. The harness reached the app through accidental globals, which modules do
not provide, so main.js declares window.imgc as an explicit, write-only test
seam - including holdWork(), because dispatch is a module binding and the
"original painted before any encoder runs" assertion needs the run paused to be
observable at all. probe_states and probe_theme were rewritten rather than
re-pointed: their subjects were the three-view architecture and the --app-*
scale, neither of which exists. The other six probes and e2e keep their
assertions.

test_design_system.py gains TheBrowserAppHasOnePlaceForValues, which enforces
for the new baseline what the token layer bought for the old one. All four of
its checks were broken deliberately and watched fail before being restored.

Verified: ruff clean; 109 Python tests OK; e2e 82 passed 0 failed; eight Chrome
probes 219 assertions 0 failed; verify_tokens OK.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
image-compressor Ready Ready Preview Aug 12, 2026 12:34am
imgcompress Ready Ready Preview Aug 12, 2026 12:34am

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