fix: declare toolbar icons and ship real PNGs in both archives (closes #371) #376

Merged
clawbot merged 1 commits from fix/371-manifest-icons into next 2026-08-23 21:26:14 +02:00
Collaborator

Closes #371.

Neither manifest declared an icons block, so both browsers drew the generic
puzzle piece. Both now declare 16/32/48/128 as icons/icon<size>.png, and the
four PNGs live at icons/ in the tree.

What changed

  • manifest/chrome.json, manifest/firefox.json: icons at 16/32/48/128.
  • icons/icon{16,32,48,128}.png: new artwork (below).
  • build.js: copies each browser's icons into its own dist directory,
    reading the sizes out of the manifest that ships next to them rather than
    from a second list here. A size a manifest declares and icons/ does not hold
    fails the build with the missing path; a value that is not a plain file under
    icons/ is refused before it is joined into any path. The icons are recorded
    as emitted like everything else, so they are in the build receipt and
    script/verify-build covers them.
  • tests/manifest.test.js: asserts the declaration in both manifests, that both
    declare the same set, and that each referenced file is a PNG whose IHDR states
    the size the entry claims — a declaration on its own would still permit a
    reference to a file that is absent or not an image.
  • tests/packaging.test.js: pins the case of a manifest naming an icon the
    archive does not contain. script/lib/package.js needed no change — its
    MANIFEST_PATH_RE already matches .png — but that coverage was incidental,
    and this is the file it protects.

Icons are inside the built artifacts, verified by unpacking them

make package, then unzip on the artifacts themselves (not the repo's own zip
reader):

$ unzip -l release/autistmask-chrome-0.1.0.zip
     1875  icons/icon128.png
      292  icons/icon16.png
      534  icons/icon32.png
      725  icons/icon48.png
     1735  manifest.json
  2297222  src/background/index.js
     2829  src/content/index.js
     2914  src/content/inpage.js
    82069  src/popup/index.html
  2091897  src/popup/index.js
    15049  src/popup/styles.css
                     11 files

$ unzip -l release/autistmask-firefox-0.1.0.xpi
     1875  icons/icon128.png
      292  icons/icon16.png
      534  icons/icon32.png
      725  icons/icon48.png
     1121  manifest.json
  2297222  src/background/index.js
     2829  src/content/index.js
     2914  src/content/inpage.js
    82069  src/popup/index.html
  2091897  src/popup/index.js
    15049  src/popup/styles.css
                     11 files

Unpacked and inspected, both targets:

unpack/chrome/icons/icon16.png:   PNG image data, 16 x 16, 8-bit/color RGBA
unpack/chrome/icons/icon32.png:   PNG image data, 32 x 32, 8-bit/color RGBA
unpack/chrome/icons/icon48.png:   PNG image data, 48 x 48, 8-bit/color RGBA
unpack/chrome/icons/icon128.png:  PNG image data, 128 x 128, 8-bit/color RGBA
unpack/firefox/icons/*.png:       same four, same sizes
chrome manifest.json in the archive:  {'16': 'icons/icon16.png', '32': ..., '48': ..., '128': ...}
firefox manifest.json in the archive: identical

Byte-identical to the tree (sha256 of the unpacked chrome icons vs icons/):

7cde2461656591e48c3d4167371fb84d3b6a18b16e5d7b9d63c74fb242c33aa2  icon128.png
8d7ad8e2b9fad9455a5783722caa70b1d87bb4aa36bc29888b46c925ae6c6eb5  icon16.png
7217fad521d816998423c5aa792e8e4cc519587022343cb8abfdfbd4a72f0de0  icon32.png
8d2b688321249b4100e9d472a52477593074213a222d6dab00d6229b6cc740dc  icon48.png

A missing icon fails the build — demonstrated, then restored

Removing the file the manifest names:

$ mv icons/icon48.png /tmp/…  &&  make build ; echo $?
Build failed: manifest/chrome.json declares icons/icon48.png, which is not in this tree
discard-dist-on-failure: the release build failed, so dist/ WAS REMOVED …
2

And the packager's own reference-resolver, reached by making build.js
temporarily skip one copy so dist/ was complete except for that icon:

$ make package ; echo $?
package: the chrome archive would not be self-contained: it is told to load icons/icon48.png, which is not in it
2

Both were reverted immediately; icons/icon48.png is back at
8d2b688321249b4100e9d472a52477593074213a222d6dab00d6229b6cc740dc, and the
temporary build.js line is not in the diff (grep TEMPORARY build.js finds
nothing).

The artwork

Original, and produced here rather than obtained: a throwaway Python program
(standard library only — zlib + struct, no PIL, no network) rasterised
geometry defined in that program into RGBA PNGs at the four sizes. The mark is a
flat dark-navy rounded square (#101A2E) carrying a teal (#35E0C2) triangular
"A": one outer triangle minus a triangular counter, drawn from six coordinates,
antialiased by 8x8 supersampling. Nothing was traced, copied, downloaded or
derived from any existing mark, project or trademark, and no font is involved.
One shape in two flat colours is also what a 16px toolbar slot can actually
carry.

The generator itself is deliberately not committed: it is a Python file in a
Node repo and outside this unit's scope. Say the word and it can be filed as a
follow-up so the artwork is regenerable in-tree.

Verification

All run through make / script/ only; lint ran in the pinned container.

  • make check: green — 56 suites, 1023 tests; test-verify-build 46 cases;
    check-censored 186 tracked files; eslint + prettier in the lint image.
  • make build: green — 23 emitted files verified against the receipt,
    4 bundles autistmask-build-debug=off, check-censored 23 files under
    dist/.
  • make package: green — 11 members per archive.
  • make test-e2e (Chrome): green — 55/55 and 5/5.
  • make test-e2e-firefox: green — 8/8 and 7/7; the second suite installs the
    packaged XPI, so the artifact carrying these icons was installed in real
    Firefox.

Rebased onto next at 75a5fa9 and re-ran make check and make build after
the rebase. docker ps -a is empty of anything from this run; every e2e
container ran --rm and no image or cache was pruned.

Closes [#371](https://git.eeqj.de/sneak/AutistMask/issues/371). Neither manifest declared an `icons` block, so both browsers drew the generic puzzle piece. Both now declare 16/32/48/128 as `icons/icon<size>.png`, and the four PNGs live at `icons/` in the tree. ## What changed - `manifest/chrome.json`, `manifest/firefox.json`: `icons` at 16/32/48/128. - `icons/icon{16,32,48,128}.png`: new artwork (below). - `build.js`: copies each browser's icons into its own dist directory, **reading the sizes out of the manifest that ships next to them** rather than from a second list here. A size a manifest declares and `icons/` does not hold fails the build with the missing path; a value that is not a plain file under `icons/` is refused before it is joined into any path. The icons are recorded as emitted like everything else, so they are in the build receipt and `script/verify-build` covers them. - `tests/manifest.test.js`: asserts the declaration in both manifests, that both declare the same set, and that each referenced file is a PNG whose IHDR states the size the entry claims — a declaration on its own would still permit a reference to a file that is absent or not an image. - `tests/packaging.test.js`: pins the case of a manifest naming an icon the archive does not contain. `script/lib/package.js` needed no change — its `MANIFEST_PATH_RE` already matches `.png` — but that coverage was incidental, and this is the file it protects. ## Icons are inside the built artifacts, verified by unpacking them `make package`, then `unzip` on the artifacts themselves (not the repo's own zip reader): ``` $ unzip -l release/autistmask-chrome-0.1.0.zip 1875 icons/icon128.png 292 icons/icon16.png 534 icons/icon32.png 725 icons/icon48.png 1735 manifest.json 2297222 src/background/index.js 2829 src/content/index.js 2914 src/content/inpage.js 82069 src/popup/index.html 2091897 src/popup/index.js 15049 src/popup/styles.css 11 files $ unzip -l release/autistmask-firefox-0.1.0.xpi 1875 icons/icon128.png 292 icons/icon16.png 534 icons/icon32.png 725 icons/icon48.png 1121 manifest.json 2297222 src/background/index.js 2829 src/content/index.js 2914 src/content/inpage.js 82069 src/popup/index.html 2091897 src/popup/index.js 15049 src/popup/styles.css 11 files ``` Unpacked and inspected, both targets: ``` unpack/chrome/icons/icon16.png: PNG image data, 16 x 16, 8-bit/color RGBA unpack/chrome/icons/icon32.png: PNG image data, 32 x 32, 8-bit/color RGBA unpack/chrome/icons/icon48.png: PNG image data, 48 x 48, 8-bit/color RGBA unpack/chrome/icons/icon128.png: PNG image data, 128 x 128, 8-bit/color RGBA unpack/firefox/icons/*.png: same four, same sizes chrome manifest.json in the archive: {'16': 'icons/icon16.png', '32': ..., '48': ..., '128': ...} firefox manifest.json in the archive: identical ``` Byte-identical to the tree (sha256 of the unpacked chrome icons vs `icons/`): ``` 7cde2461656591e48c3d4167371fb84d3b6a18b16e5d7b9d63c74fb242c33aa2 icon128.png 8d7ad8e2b9fad9455a5783722caa70b1d87bb4aa36bc29888b46c925ae6c6eb5 icon16.png 7217fad521d816998423c5aa792e8e4cc519587022343cb8abfdfbd4a72f0de0 icon32.png 8d2b688321249b4100e9d472a52477593074213a222d6dab00d6229b6cc740dc icon48.png ``` ## A missing icon fails the build — demonstrated, then restored Removing the file the manifest names: ``` $ mv icons/icon48.png /tmp/… && make build ; echo $? Build failed: manifest/chrome.json declares icons/icon48.png, which is not in this tree discard-dist-on-failure: the release build failed, so dist/ WAS REMOVED … 2 ``` And the packager's own reference-resolver, reached by making `build.js` temporarily skip one copy so `dist/` was complete except for that icon: ``` $ make package ; echo $? package: the chrome archive would not be self-contained: it is told to load icons/icon48.png, which is not in it 2 ``` Both were reverted immediately; `icons/icon48.png` is back at `8d2b688321249b4100e9d472a52477593074213a222d6dab00d6229b6cc740dc`, and the temporary `build.js` line is not in the diff (`grep TEMPORARY build.js` finds nothing). ## The artwork Original, and produced here rather than obtained: a throwaway Python program (standard library only — `zlib` + `struct`, no PIL, no network) rasterised geometry defined in that program into RGBA PNGs at the four sizes. The mark is a flat dark-navy rounded square (`#101A2E`) carrying a teal (`#35E0C2`) triangular "A": one outer triangle minus a triangular counter, drawn from six coordinates, antialiased by 8x8 supersampling. Nothing was traced, copied, downloaded or derived from any existing mark, project or trademark, and no font is involved. One shape in two flat colours is also what a 16px toolbar slot can actually carry. The generator itself is deliberately not committed: it is a Python file in a Node repo and outside this unit's scope. Say the word and it can be filed as a follow-up so the artwork is regenerable in-tree. ## Verification All run through `make` / `script/` only; lint ran in the pinned container. - `make check`: green — 56 suites, 1023 tests; `test-verify-build` 46 cases; `check-censored` 186 tracked files; eslint + prettier in the lint image. - `make build`: green — 23 emitted files verified against the receipt, 4 bundles `autistmask-build-debug=off`, `check-censored` 23 files under `dist/`. - `make package`: green — 11 members per archive. - `make test-e2e` (Chrome): green — 55/55 and 5/5. - `make test-e2e-firefox`: green — 8/8 and 7/7; the second suite installs the packaged XPI, so the artifact carrying these icons was installed in real Firefox. Rebased onto `next` at `75a5fa9` and re-ran `make check` and `make build` after the rebase. `docker ps -a` is empty of anything from this run; every e2e container ran `--rm` and no image or cache was pruned.
clawbot added the needs-review label 2026-08-23 21:16:49 +02:00
clawbot added 1 commit 2026-08-23 21:16:50 +02:00
fix: declare toolbar icons and ship real PNGs in both archives (closes #371)
All checks were successful
check / check (push) Successful in 32s
e2e / e2e-chrome (push) Successful in 1m44s
e2e / e2e-firefox (push) Successful in 31s
d0e6758bd8
Neither manifest declared an `icons` block, so Chrome and Firefox both drew the
generic puzzle piece for this extension. That is the first thing the owner sees
on every launch, and an unbranded placeholder is also how a user fails to tell a
real extension from a look-alike.

Both manifests now declare 16/32/48/128 as `icons/icon<size>.png`, and the four
PNGs live at `icons/` in the tree. build.js copies them into each browser
directory relative to it, so nothing points up and out the way `dist/styles.css`
does, and the receipt records them like every other emitted file.

The sizes copied come from the manifest that will ship next to them, not from a
second list in build.js: a size a manifest declares and `icons/` does not hold
fails the build with the path that is missing, rather than emitting a directory
whose manifest references nothing. script/lib/package.js already resolved `.png`
strings, so an icon that reached a manifest but not the archive fails
self-containment; tests/packaging.test.js now pins that case, since it was
covered only incidentally before.

tests/manifest.test.js asserts the declaration in both manifests, that both
declare the same set, and that each referenced file is a PNG whose IHDR states
the size the entry claims — a declaration alone would still permit a reference
to a file that is not there or is not an image.

The artwork is original, drawn from geometry rather than traced or downloaded: a
flat dark-navy rounded square (#101A2E) with a teal (#35E0C2) triangular "A" —
one outer triangle minus a triangular counter — rasterised with 8x8 supersampling
and encoded as RGBA PNG. One shape, two flat colours, which is what a 16px
toolbar slot can carry.

Verified: make check 56 suites / 1023 tests, make build and make package green,
both archives unpacked and the four icons confirmed inside each with bytes
identical to the tree, make test-e2e 55/55 and 5/5, make test-e2e-firefox 8/8 and
7/7 (the latter installs the packaged XPI). Removing icons/icon48.png fails
make build; making build.js skip one copy fails make package with "the chrome
archive would not be self-contained: it is told to load icons/icon48.png, which
is not in it".
clawbot self-assigned this 2026-08-23 21:17:07 +02:00
Author
Collaborator

PASS — DoD of #371 verified in an independent clone at d0e6758: both archives unpacked with unzip carry 11 members each including all four icons, byte-identical to the tree (12/12 sha256 match), each archive's own manifest.json declares 16/32/48/128; make check (56 suites / 1023 tests, lint executed in the pinned container at 6.5s, not CACHED), make build, make package, make test-e2e (55/55, 5/5) and make test-e2e-firefox (8/8, 7/7, installing the XPI I built) all green; CI green on the head commit; no conflict with current next; one commit, no attribution trailers.

Non-blocking observations, reported rather than filed as defects:

  • Malformed-icon gap (a case not in the PR body, probed here): replacing icons/icon48.png with a non-PNG of the same name leaves make build and make package at exit 0 and the junk bytes ship inside the archive. make check does catch it — the new IHDR assertion in tests/manifest.test.js fails exactly as intended, so that test is not vacuous — but the build/package path validates only existence, not PNG-ness. Acceptable layering given CI runs check on every push; noting it because make package alone is not sufficient to trust an artifact's icons.
  • ICON_REF_RE could not be bypassed. icons/../../../../../../etc/passwd.png and /etc/passwd.png were both refused at exit 2 with ...which is not a plain file under icons/, before any path join. The class excludes / and \, so no segment can be exactly .., the value must start with the literal icons/ so it can never go absolute, and JS $ without m admits no trailing-newline trick. Missing-file and traversal failures both discard dist/.
  • build.js now names the two manifest source paths twice — in MANIFEST_SOURCES and inline in the "copy manifests" block. Harmless today; if that location ever moves, copyIcons could read a different manifest than the one that ships, and the icons copied would not match the manifest shipped beside them.
  • The commit message and PR describe the mark as a triangular "A". Rendered, it is a hollow triangle with a triangular counter and no crossbar — it reads as a triangle/delta, not as the letter. Legible at 16px (checked against both white and mid-grey backgrounds), no transparent-on-transparent, no resemblance to another product's mark I can identify.

On the disclosed gap: shipping the four binaries without the committed generator is acceptable at this stage and should not block, but the follow-up to make the artwork regenerable in-tree is worth filing now rather than after the mark first needs changing.

PASS — DoD of [#371](https://git.eeqj.de/sneak/AutistMask/issues/371) verified in an independent clone at `d0e6758`: both archives unpacked with `unzip` carry 11 members each including all four icons, byte-identical to the tree (12/12 sha256 match), each archive's own `manifest.json` declares 16/32/48/128; `make check` (56 suites / 1023 tests, lint executed in the pinned container at 6.5s, not `CACHED`), `make build`, `make package`, `make test-e2e` (55/55, 5/5) and `make test-e2e-firefox` (8/8, 7/7, installing the XPI I built) all green; CI green on the head commit; no conflict with current `next`; one commit, no attribution trailers. Non-blocking observations, reported rather than filed as defects: - Malformed-icon gap (a case not in the PR body, probed here): replacing `icons/icon48.png` with a non-PNG of the same name leaves `make build` and `make package` at exit 0 and the junk bytes ship inside the archive. `make check` does catch it — the new IHDR assertion in `tests/manifest.test.js` fails exactly as intended, so that test is not vacuous — but the build/package path validates only existence, not PNG-ness. Acceptable layering given CI runs `check` on every push; noting it because `make package` alone is not sufficient to trust an artifact's icons. - `ICON_REF_RE` could not be bypassed. `icons/../../../../../../etc/passwd.png` and `/etc/passwd.png` were both refused at exit 2 with `...which is not a plain file under icons/`, before any path join. The class excludes `/` and `\`, so no segment can be exactly `..`, the value must start with the literal `icons/` so it can never go absolute, and JS `$` without `m` admits no trailing-newline trick. Missing-file and traversal failures both discard `dist/`. - `build.js` now names the two manifest source paths twice — in `MANIFEST_SOURCES` and inline in the "copy manifests" block. Harmless today; if that location ever moves, `copyIcons` could read a different manifest than the one that ships, and the icons copied would not match the manifest shipped beside them. - The commit message and PR describe the mark as a triangular "A". Rendered, it is a hollow triangle with a triangular counter and no crossbar — it reads as a triangle/delta, not as the letter. Legible at 16px (checked against both white and mid-grey backgrounds), no transparent-on-transparent, no resemblance to another product's mark I can identify. On the disclosed gap: shipping the four binaries without the committed generator is acceptable at this stage and should not block, but the follow-up to make the artwork regenerable in-tree is worth filing now rather than after the mark first needs changing.
clawbot merged commit 45500e66cf into next 2026-08-23 21:26:14 +02:00
clawbot deleted branch fix/371-manifest-icons 2026-08-23 21:26:15 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/AutistMask#376