@duyluonganduin/acl-annotate
v1.0.1
Published
Design-review overlay for acl-web-components prototypes: a Figma-style inspector bound to ACL tokens, review notes, and a markdown handover for an agent.
Downloads
292
Maintainers
Readme
acl-annotate
A design-review overlay for acl-web-components prototypes. Reviewers pick any
element, adjust it in a Figma-style inspector, leave notes, and copy a markdown
handover an agent can act on.
Vanilla JS. No dependencies, no bundler, no node_modules. package.json exists only to publish to npm.
The loop
open dev/index.html in a browser → edit → save → reloadThat is the whole loop. dev/index.html is the source of truth and loads the
sources directly, so there is no build step while you work. CSS changes in
particular need nothing but a reload.
When you want the shareable single file:
./scripts/build.sh # → dist/annotation-demo.html
./scripts/test.sh # builds, then runs 325 assertions against BOTH entriesTo restyle the inspector
src/style/tokens.css — one block of named tokens for colour, type scale,
spacing, control sizes and radii. Nothing else in the panel hardcodes any of
them, so changing a value here moves the whole panel, its dropdowns and its
icon gallery together. The panel width (--fg-w) also re-docks the page and
repositions the floating toolbar.
Defaults are Figma UI3 light, measured from Figma's own right panel.
src/style/sidebar.css is the panel's structure. src/style/overlays.css is
the other layer — pins, marquees, the floating toolbar, the popover, the
toast — deliberately dark, with literal colours rather than --fg-* tokens,
so the two read as different tools.
Where things are
| File | What it owns |
| --- | --- |
| src/01-state.js | Constants, in-memory state, the undo stack |
| src/02-dom.js | Shadow-DOM hit testing, element naming, DOM paths |
| src/03-handover.js | The change list and the markdown an agent receives |
| src/04-tokens.js | Runtime ACL token discovery, value→token matching |
| src/05-inspect.js | Identity, Lit props, set-vs-default, icon catalogue |
| src/06-groups.js | Which properties each element type gets. Every curation rule |
| src/07-overlays.js | Marquees, floating toolbar, toasts, shared chrome state |
| src/08-panel.js | Inspector shell; every mutation (style, prop, removal) |
| src/09-fields.js | The Figma field renderer: glyphs, pairs, segments, clusters |
| src/10-render.js | Rendering the panel and every event inside it |
| src/11-pickers.js | Token dropdowns, icon gallery |
| src/12-chrome.js | Docking, popover, pins, picking, keyboard |
Numbered so load order is obvious. Every function is hoisted, so cross-file calls work in either direction — only values read at load time care.
These are plain classic scripts sharing one global scope, which is what lets
the dev page skip a build. scripts/build.sh wraps them in a single IIFE for
dist/, so the shipped file never touches the annotated page's globals.
scripts/test.sh runs both, because that asymmetry can hide a bug in one.
Size
The Figma look is not what makes the file big:
| | raw | | --- | --- | | ACL bundle (fixed floor) | 617 K — 76% | | tool JS | 150 K | | tool CSS | 38 K | | — of which the Figma look | 30 K — 3.6% of the file | | dist/annotation-demo.html | 816 K |
Stripping every comment out of the tool would save 44 K (5.4%). It isn't worth a minifier: the only lever that would actually move this file is subsetting the ACL bundle to the components a prototype uses, and that is a platform decision.
After upgrading the bundle
./scripts/sync-icons.sh # re-reads the icon catalogue out of the new bundle
./scripts/test.shThe icon map is a module-scoped closure in the bundle — not reachable from
customElements.get('anduin-icon'). dist/ reads it out of the inlined source
at runtime; the dev page can't (file:// won't let a page read its own external
script back), so this script pre-extracts the same list.
Putting it in someone else's prototype
./scripts/inject.sh path/to/their-screen.html # add, or refresh an older copy
./scripts/inject.sh --no-cdn path/to/screen.html # never load anything from the network
./scripts/inject.sh --remove path/to/their-screen.htmlEdits the file in place, before </body>, building the payload from your
working copy. The payload is fenced by <!-- acl-annotate:begin vX.Y.Z -->
markers, so re-running replaces the block rather than stacking a second one.
It does not ship the acl bundle (600K): every acl-prototype already inlines
its own. If the target has no <anduin-*> elements, or loads the bundle
externally, the script says so rather than failing silently at runtime.
How injected prototypes stay current
The block holds a full copy of the tool plus a small loader. The loader asks
jsDelivr for the newest 1.x (dist/acl-annotate.js) and uses it only if it
is strictly newer than the copy in the file; otherwise, and whenever the
request fails, is blocked (Cowork sidebar, sandboxes) or takes over 3s, the
copy in the file runs. So:
- a prototype shared last month picks up this month's release on its own;
- a local build that is ahead of the last release is never replaced by it;
- a
2.xrelease never reaches a file injected with1.x.
--no-cdn turns the loader off for files that must stay fully offline.
Publishing
The package is @duyluonganduin/acl-annotate. It ships two files, both built
by scripts/build-inject.sh:
| File | Used by |
| --- | --- |
| dist/annotate-inject.html | the skill's inject.py, which pulls the latest release from npm each run |
| dist/acl-annotate.js | the loader in injected prototypes, via jsDelivr |
npm login # once
./scripts/test.sh # then
./scripts/publish.sh # patch; or: minor | major | current (first release)publish.sh bumps package.json, builds, publishes, refreshes the skill's
offline fallback copy and purges jsDelivr's @1 cache. Commit package.json
afterwards. Bump major only for a change that would break a prototype
mid-review (the handover format, stored notes).
Tests
test/test.js — 325 assertions, headless Chromium via Playwright. Needs the
browser once: npx playwright install chromium.
