quarantine-tape: tape individual images (stacked on #319)#320
quarantine-tape: tape individual images (stacked on #319)#320spencerc99 wants to merge 3 commits into
Conversation
commit: |
Code review
|
Extends the store so tape can mark a single image, not just a whole page. A mark is keyed by the image's src (query preserved — its identity often lives there), mirroring the page-strip shape: one row per tape-over, setness derived from the count per src, rips idempotent per player with rips_required snapshotted on the first rip. - Migration: quarantine_element_marks, indexed by src. - Three routes: GET /quarantine/element-verdict (bulk by src — the on-page renderer asks about every tapeable image at once, capped at 100), POST /quarantine/element-mark, POST /quarantine/element-rip. - Lifts normalizeArtifactUrl into lib/artifactUrl so both the page and element routes share one normalizer — the query-preserving behavior is migration-critical and must not diverge.
The client half of image taping. While a roll is armed, hovering an image outlines it; dragging across it lays an X of tape over that element. The verdict is keyed by the image's src, so one mark paints every copy on the page, and it travels with the content as you scroll — element tape re-measures the element's bounds each render, unlike viewport-fixed page tape. - types.ts: ElementMark; isFullyTorn now covers strips and marks. - quarantine-api.ts: getElementVerdicts (bulk by src), postElementMark, postElementRip — added to the same storage seam, so the manager still never touches the backend directly. - tape-render.ts: segmentCrossesRect helper (ported from the prototype). - QuarantineTapeManager: element-mark state + gElements layer, hover targeting via a host-page style injected into document.head, the drag-across-image tape gesture, content-bound X rendering re-measured on scroll/resize, and image ripping folded into the existing slash. The image gesture and the page-tape edge-commit gesture share the armed state but split on mousedown: a drag that starts on a hovered image tapes the image; anywhere else the click-edge commit flow runs untouched. Page tape only was the prior cut; this adds the image path on top.
Same gap the prototype had: dragging across an image rendered nothing until release, so there was no sign you were mid-gesture or that the drag would land. The X of tape now previews on the target as you drag, matching the wall-to-wall pull. Opacity ramps with drag distance and the ghost stays dashed until the drag passes the commit threshold, so "not far enough yet" reads in the gesture. Also stops re-targeting mid-drag — the image being taped stays the target even if the cursor leaves its bounds.
a9c2dfa to
fce625d
Compare
Stacked on #319 (page tape) — review/merge that first. This PR adds the ability to tape a single image, not just a whole page.
Marking a whole page is too blunt when the slop is one AI-generated image among real content. While a roll is armed, hovering an image outlines it; dragging across it lays an X of tape over that element.
What's here
Element store (extends the worker):
quarantine_element_marks, keyed by the image's src (query preserved), indexed by src.GET /quarantine/element-verdict(the on-page renderer asks about every tapeable image at once, capped at 100 — the pattern Phase-2 remote-surface rendering will reuse),POST /quarantine/element-mark,POST /quarantine/element-rip. 7 tests.normalizeArtifactUrlintolib/artifactUrlso page + element routes share one normalizer — the query-preserving behavior is migration-critical and must not diverge.Client (extends the same feature):
ElementMarktype + three functions on the storage seam (still the only backend touchpoint).Why this is a good property
Because the verdict is keyed by src, this is already the portable shape needed to mark the same image elsewhere (search-result thumbnails) in a later phase — no migration, just an added bulk-lookup surface, which this PR's
element-verdictroute already models.Notes for review
<style>injected intodocument.head(the overlay's shadow-root CSS can't reach the page's own<img>s), removed on destroy.