Skip to content

Other browsers, captures and designs - #32

Merged
olehwebdev merged 19 commits into
mainfrom
feature/browsers-designs-research
Sep 29, 2026
Merged

olehwebdev merged 19 commits into
mainfrom
feature/browsers-designs-research

Conversation

@olehwebdev

Copy link
Copy Markdown
Owner

What and why

Milestone M5 (docs/BROWSERS_RESEARCH.md): check the page you're changing in other browsers, and against designs, pixel by pixel.

Other browsers (SPEC §6.16)

  • The browsers on this computer (Linux desktop entries, macOS apps, the Windows registry) are listed in a menu beside the address bar, with their icons and versions. The page opens in any of them; Settings › Browsers hides them or adds one.
  • With your changes:
    • Chromium browsers over CDP and Firefox over WebDriver BiDi. Each gets a profile of the app's own, and the workspace's overrides and rules are served in every tab. The browser stays open when let go of, and is reached again next time.
    • In Firefox, an override for a GraphQL operation answers by the request's body, which a BiDi data collector keeps.
    • WebKit: Playwright's build, downloaded on first use once agreed (progress in the menu, removable in Settings), driven through Playwright's route. The app downloads and unpacks it itself: Playwright's installer forks Node, which the packaged app's fuses refuse.
    • Your everyday Chrome: once remote debugging is turned on for it (chrome://inspect/#remote-debugging, Chrome 144+). Only the tabs the app opens there are served your changes.
  • Your open tabs: Firefox's session files, and on macOS your running Safari, Chrome, Edge, Brave, Arc and Vivaldi through JavaScript for Automation. Pick one to open its page in the app.

Captures and designs (SPEC §6.17, §6.18)

  • Captures of the viewport, the whole page or a picked element, here or in a driven browser, kept per workspace. Capture in every browser takes a group at the same size and compares it against a baseline.
  • Tall pages: full pages up to 32,767 device px. Past the 16,384 px texture limit they are captured in parts and joined at the PNG scanline level, without decoding them whole.
  • Designs from files, dropped or pasted, or from Figma by a frame's link, with a personal token kept encrypted with safeStorage.
  • The compare page: side by side, swipe, onion skin, or the difference, with anti-aliasing told apart.
  • A design over the live page in an isolated world (a strict CSP can't block it), and over driven browsers' tabs too.

Packaging: playwright-core becomes a pinned dependency, left out of the main bundle and shipped in the asar (the package grows from about 26 MB to 39 MB before Electron).

Left for later: the console, Network panel and inspector for a tab in another browser (they follow the app's own page only).

How it was tested

  • Unit:

    • browser discovery, the registry and the WebKit build;
    • Firefox's session file, and the macOS tabs script run against a stand-in for JXA;
    • the PNG joiner, whatever filter each row has;
    • the diff, anti-aliasing included, and BiDi answering with request bodies;
    • Figma links, the token and the import against a stand-in API;
    • the WebKit download against a stand-in host.
  • Integration, real browsers:

    • Chromium and Firefox driven with your changes (overrides, rules, a cross-origin preflight, a GraphQL operation, reloads, captures, the design, reached again, forgotten once quit);
    • a 40,000-row page captured in parts and checked row for row, at densities 1 and 2, in Chromium and Firefox;
    • your everyday Chrome, stood in for by a Chromium on an everyday profile;
    • the Playwright driver, with Playwright's Chromium standing in for WebKit.
  • End to end, built app:

    • the browser menu, captures, designs (41.9 % in two areas), the overlay and a Figma frame from a stand-in API;
    • a Chromium launcher with your changes and a group captured in every browser, the same with Firefox, and your own Chromium;
    • WebKit downloaded from a stand-in host, a build that can't start said so, and its download removed in Settings.
  • By hand: an unpacked Linux build (electron-builder --dir) lists WebKit, downloads a stand-in build and launches it from the asar. A 2880 × 32,766 capture took about 45 s.

  • Not checked for real:

    • WebKit's own build, and Figma's API: this environment can't reach them;
    • Chrome 144's permission prompt: its Chromium is 141;
    • the macOS tabs script: no Mac here.

    Each is covered by the stand-ins above.

Checklist

  • Comes from a git flow branch (feature/…, bugfix/…) into main, and does one thing (milestone M5: other browsers, captures and designs)
  • npm run typecheck, npm run lint:fsd, npm run lint:structure, npm run lint, npm run lint:unused, npm run lint:duplicates, npm run lint:secrets, npm test and npm run test:e2e pass
  • docs/SPEC.md describes any behaviour this changes
  • User-visible changes are noted under [Unreleased] in CHANGELOG.md

Generated by Claude Code

A research note with a phased to-do list: open the page in the browsers
installed on this computer (and serve the workspace's changes in Chromium
browsers and Firefox), capture it, keep captures and imported designs per
workspace, and check the page against a design pixel by pixel, over the
live page or on stills. SPEC gets the stories and an M5 milestone, and the
README's roadmap drops the two items that shipped.
A menu beside the address bar, in the editor and in the website's own
window, lists the browsers found the system's way: Linux launchers (with
their icons from the icon theme), apps in the Applications folders on
macOS, and the registry's list on Windows. Choosing one opens the page
there, with its everyday profile. Settings gets a Browsers section to
stop offering one, or to add one by picking its program, and the palette
gets an "Open in …" item per browser.
A menu at the toolbar's end keeps captures of the page with the
workspace: what it shows, all of it, or one element picked in it (in any
frame, cross-site ones too, placed through each frame's owner). Each
opens as a tab to zoom into, with the colour of the pixel under the
pointer. Designs come in from files, or dropped or pasted on the menu,
their scale read from their name or width. Compare with… lays the page
out at a design's width and captures it, then shows the two side by
side, swiped, faded over each other, or as their difference: how much
differs, and each area that does.
"Put over the page" draws a design or capture over the website's top
frame: on a canvas from an isolated world, so a strict CSP can't refuse
it, kept after reloads, and out of the pointer's way. A bar under the
preview's toolbar sets how see-through it is, blends it as a difference,
moves it (the arrow keys nudge it), pins it to the page or the viewport,
and lays the page out at the design's width, scaled to fit the preview.
Captures leave it out.
A Chromium browser found on the computer can now be launched with a
profile of the app's own and a debugging port. Every one of its tabs is
served the workspace's overrides and rules by the same interception
engine as the app's page; a new tab waits until that is set up.

Changes to overrides, rules and settings reach those tabs as they are
made, and the tabs reload when "Reload page after changes" is on. The
browser menu lists each driven browser's tabs: they can be brought to
the front, opened in the app or captured into the shots. Letting go of
a browser leaves it open. When the app starts again it reconnects to a
browser that is still open instead of launching a second one.
"In every browser" (in the shots menu's Capture, and in the palette)
captures the whole page in the app and then at the same address in
every browser driven with the workspace's changes. Each one is laid out
at the app's viewport and density, and captured once it has loaded and
been quiet for a moment. The captures are kept as one group. A browser
that fails is named in the notice rather than failing the rest.

The group's page shows the captures side by side against a baseline:
the app's capture, another capture of the group, or a design. Each cell
shows the share of pixels that differ, and can show the difference
itself. Choosing a cell opens the two in the compare page.

The status bar now counts the tabs of other browsers that are served
the workspace's changes.
Firefox can now be launched with a profile of the app's own and driven
over WebDriver BiDi, like the Chromium browsers over CDP. One
interception covers every tab and applies the engine's matching and
rules to each paused request:
- a block rule fails the request;
- an override answers before the request is sent, since Firefox can
  only replace a body then. The response has its kind's content type,
  a response override's status, headers and delay, and is readable
  cross-origin;
- the CORS preflight ahead of such a request is allowed;
- header and CORS rules edit the upstream response's head.

Requests are paused only while an override or a rule is on, and the
cache is bypassed then. Tabs are listed and captured, including in
"Capture in every browser", and the app reconnects to a Firefox that
is still open.

The drivers share one interface, a launch path and their tab list.
The engine-neutral part of deciding what to do with a request (used by
Firefox) now lives in the engine's answering module.

The browsers end-to-end test picks the plain "Open in" palette entry
by its exact name, now that "with your changes" follows it.
With Firefox installed, the browser menu offers "Your Firefox tabs".
Once asked, it lists the tabs open in each Firefox profile, the
install's default first, and choosing one loads its address in the
app. The search narrows them like the other tabs. The profiles come
from profiles.ini, in Firefox's own folder on each system (Linux's Snap
and Flatpak included).

The tabs are read from the session file Firefox keeps up to date
(recovery.jsonlz4, an LZ4 block behind a small header, decoded here),
and only http(s) pages are listed. That file is read only after the
user asks, and nothing of it leaves the app.
SPEC gains the sections on other browsers (finding and opening them,
driving Chromium over CDP and Firefox over WebDriver BiDi, your
everyday Firefox's tabs), captures and designs (capturing, the group
capture, the compare page) and the design over the page. It also covers
their data and storage, source layout, security, tests, stories and
milestones.

The CHANGELOG's Unreleased section and the README describe what users
get, what is kept where, and Firefox's limits. The research doc now
says what was built, item by item. It records why WebKit through
Playwright (phase 8) isn't built yet, and how to do it.
A design put over the app's page now goes over every tab of the
browsers driven with the workspace's changes too, including tabs
opened later. It is restyled with it, laid out at its width while
that is on, and taken off with it. A capture in those tabs takes it
off while it runs. Chromium tabs get the app page's own overlay code
over their CDP session. Firefox gets it as preload scripts in a
sandbox of every new document, and in each tab's current one.

The overlay's script now only draws in a top document. The app's own
page ran it in same-site frames' documents too, after a reload, so a
design also showed inside them.
With remote debugging turned on for it (chrome://inspect/#remote-
debugging, from Chrome 144), a Chromium browser writes its debugging
address in its everyday profile. The app finds that address in each
Chromium browser's profile folder, on each system and in Snap and
Flatpak ones. While the browser runs, its row in the browser menu (and
the palette) offers "Use your own … with your changes". The app checks
the port only by opening and closing a connection, so Chrome doesn't
ask anything for it.

Chrome asks you to allow the connection. After that, the app serves
the workspace's overrides and rules only in the tabs it opens there;
your own tabs are neither listed nor attached. Letting go leaves the
browser as it was.

The driven browsers now sit in a pool by key, so a browser can be
driven with the app's profile and with your own at once. Each lists
the installed browser it is, and which profile it uses.
@olehwebdev
olehwebdev merged commit 32656ec into main Sep 29, 2026
1 check 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