Skip to content

[Feat] Annotation layer for feature-demo videos - #1267

Open
mrubens wants to merge 3 commits into
developfrom
feat/demo-annotations
Open

[Feat] Annotation layer for feature-demo videos#1267
mrubens wants to merge 3 commits into
developfrom
feat/demo-annotations

Conversation

@mrubens

@mrubens mrubens commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What

Adds capture-anchored annotations to the feature-demo skill: a show beat may carry one note, an attention cue anchored to a real element for exactly that beat's caption window.

  • Default style spotlight: dims the recording and leaves the target bright, with an optional label chip on the dimmed area. Attention comes from contrast rather than chrome drawn onto the page.
  • box / callout: accent-outline variants for sparse pages.
  • note.sel can anchor to a more specific element than the beat scrolls to (e.g. the command block under a heading).

How

  • Capture resolves each note's anchor after the beat's scroll settles and emits it into a new additive timeline.annotations array — the full rect was already measured for zoom/click targeting and previously discarded. Anchors use a tight content rect (Range over the element's contents) so block elements don't produce full-column boxes. Timing is scoped to the beat's caption window, because a viewport-relative rect is only valid for the scroll position it was measured at.
  • Render: new Annotations.tsx mounts in the window transform container alongside the cursor and click ripple, chrome counter-scaled by 1/S, and the whole layer clipped to the window rect so a spotlight dim can never touch the backdrop or the caption band. Old timelines have no annotations key and render unchanged.
  • SKILL.md documents the grammar (one note per beat, most beats want none); render/README replaces a stale edge-clamp bullet that predated the unconditional clamps and records the new invariants.

Validation

In the worker image against the live docs.roomote.dev/self-hosting page, one capture rendered through both presets:

  • Spotlight, callout, and box frames inspected at annotated moments in wide and vertical.
  • Numeric no-leak probe: a backdrop strip compared byte-wise between an annotated and an unannotated moment — max channel delta 1 (wide) / 0 (vertical), i.e. codec noise; a dim leak measures ~90.
  • Skill contract test extended (featureDemoSkill.test.ts, 17 passing): capture emits annotations with the tight-rect measurement and spotlight default, the layer clips to the window, and SKILL.md documents the one-per-beat rule.

Capture emits timeline.annotations from the full element rect (previously
resolved and discarded); a note modifier on show beats scopes each
annotation to its beat's caption window. New Annotations.tsx renders boxes
and label chips inside the window transform with counter-scaled chrome.
Prototype only: not validated beyond a wide-preset in-image render.
Iterating on the prototype: the default style becomes spotlight (dim the
window, leave the target bright) after outline boxes over dense real pages
read as stickers; anchors tighten to the content rect via a Range so block
elements do not produce full-column boxes; the label chip sits on the
dimmed area, width-clamped inside the window. SKILL.md documents the
one-note-per-beat grammar, render/README replaces the stale edge-clamp
bullet (it predated the unconditional clamps) and records the new
window-clip invariant, and the skill test asserts the contract.

Validated in the worker image against the live self-hosting docs page:
both presets rendered from one capture; a top-strip probe confirms the
dim never reaches the backdrop (max channel delta 1 wide / 0 vertical
between annotated and unannotated moments).
@roomote-community

roomote-community Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

No code issues found. See task

  • capture/capture.mjs:277 Uncaptioned annotations outlive their viewport-relative anchor when the next beat scrolls.

Reviewed cb3a2a1

The next beat may scroll the moment the hold ends, and the anchor rect is
only valid for its own scroll position — a 0.6s tail dimmed a stale
rectangle over the next screen. The renderer's fade-out completes inside
the hold.
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