feat(registry): make the 5 caption catalog components data-driven#2750
Conversation
…a runtime Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… lexicon Retrofits caption-emoji-pop onto the shared HyperFrames caption-data runtime (Blocks A-D) and the emphasis heuristic (Block E), replacing the hardcoded 7-word emoji map, fixed KEYWORDS set, and TRANSITION_WORDS set with a generic ~40-entry lexicon and HF_STOPWORDS/hfIsEmphasisWord. The component now consumes brand.primaryColor (hfPrimaryColor) and brand.accentColor (hfAccentColors) and rescales stage/font/emoji sizing to the runtime resolution via layout.fontScale/scaleX/scaleY. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…uping Replaces the hardcoded RAW_GROUPS index-pair array with hfMakeGroups, a real fitting/pause/punctuation-based grouper, and wires the component into the shared caption-data runtime (validate/gate/attach). Adds CSS brand hooks: .hl-word text color from --hf-caption-primary, .hl-word-bg gradient from --hf-caption-accent (second stop via color-mix()).
…asis heuristic Replaces the hand-authored BLOCKS literal with hfMakeBlocks, a heuristic that groups words into blocks/lines from timing (pauses, punctuation, max words per block) and hfIsEmphasisWord (long, non-stopword content words) to decide which word gets the large Playfair Display emphasis treatment and its own slide-in line. computeLineSize/buildBlocks/fitBlocks and timeline construction now live inside hfBuild, closing over layout-scaled font sizes and widths. Adds the shared Blocks A-E caption-data runtime (attach/gate/brand config) and the .word/.word--emphasis CSS brand hook for --hf-caption-primary. This is the last of the five caption identities to go data-driven. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ndant hide-all-others loop Both identities looped over every OTHER group/block to force its opacity to 0 at each group's own start time, in addition to each group already setting its own opacity to 0 at its own end. Since groups/blocks occupy non-overlapping time windows and already own their full opacity lifecycle, that loop was dead weight — but it made timeline construction O(n^2) in the number of groups/blocks. Under the Task 7 stress transcript (long, frequent "emphasis" words forcing near single-word blocks/groups), n reached ~2000 and the page hung well past a 30s test timeout for both identities. Verified behavior-preserving: full templates.test.ts (41 tests, incl. opacity/seek assertions) and the new limits.test.ts stress suite pass against both identities after the removal.
…ch; remove dead italic path hfApplyStageConfig (Block B, verbatim across all 5 retrofitted caption identities) only ever set --hf-caption-primary/--hf-caption-accent when the corresponding brand.primaryColor/accentColor key was present, with no else branch to clear it when absent. Re-attaching a brand-less payload after a branded one left the custom property (and any JS-cached color derived from it, e.g. caption-pill-karaoke's hfColorActive and caption-emoji-pop's hfAccentColors) stuck at the stale value instead of reverting to the CSS fallback, violating idempotent re-attach. Also removes caption-editorial-emphasis's dead .word--italic CSS rule and CLASS_MAP.i entry — hfMakeBlocks only ever emits "n"/"e" tags, "i" was only reachable via the old hand-authored BLOCKS literal this task replaced. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
caption-highlight was already IIFE-wrapped; the other four leaked their runtime (hfAttach/hfBuild/...) as script-scope globals. Harmless when boot was synchronous, but the data-driven retrofit defers attach behind fonts.ready + the sibling fetch — with two caption components pasted into one composition document, every script finishes before any deferred boot runs, the last script's definitions win the shared scope, and the first component never registers its timeline (a renderer waiting on it hangs to timeout). Wrapping each template's script keeps its internals private so each boot attaches its own component. window.__HF_CAPTION_ATTACH__ remains intentionally window-scoped (last-defined-wins). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
miga-heygen
left a comment
There was a problem hiding this comment.
SSOT Review: data-driven caption catalog components
PR: Make the 5 caption catalog components data-driven — 5 files, +2518/-1142
What this does
Retrofits 5 standalone HTML caption components (editorial-emphasis, emoji-pop, highlight, pill-karaoke, weight-shift) from hardcoded demo transcripts to a runtime-attached data model via window.__HF_CAPTION__ / window.__HF_CAPTION_ATTACH__ / caption-data.json fetch fallback. Each component now derives its own duration from attached data, applies brand colors via CSS custom properties, and publishes a health/gate signal on window.__HF_CAPTION_STATUS.
SSOT checks
-
Shared runtime contract is byte-identical across all 5 components.
hfFlattenSegments,hfDeriveDuration,hfValidate,hfApplyStageConfig,hfPublishStatus,hfRunGate— verified identical across all 5 files. Structurally forced by the standalone-HTML IIFE architecture (no import mechanism). Acceptable for v1. ✓ -
Boot sequence is uniform. All 5: check
window.__HF_CAPTION__→ fetchcaption-data.json→ fall back toHF_DEMO_DATA. All wait fordocument.fonts.ready. Same lifecycle: validate → teardown → applyStageConfig → build → pin timeline → render(0) → register → gate. ✓ -
window.__HF_CAPTION_STATUSgate signal is uniform. All 5 publish the same shape:{ ok, present, contained, version, durationS, reason? }. Published via identicalhfPublishStatusandhfRunGate. Thedata-caption-statusattribute is set on the root in all cases. ✓ -
Duration derivation replaces hardcoded
DURATION = 8. All 5 components now usehfDeriveDuration(words)(maxword.endscan). Hardcoded layout constants (e.g.x: -1920) replaced with data-derived values (e.g.x: -layout.W). ✓ -
Hardcoded groupings replaced with algorithms. highlight:
RAW_GROUPS→hfMakeGroups. editorial-emphasis:BLOCKS→hfMakeBlocks. emoji-pop: ~7 hardcoded keyword/emoji entries → ~50 generalized lexicon +hfIsEmphasisWordstopword heuristic. ✓
Bug fixes
GSAP tl.seek(0) no-op — CORRECT. GSAP 3.x treats seek(0) as "already there" on a fresh timeline (position already 0), skipping .set() calls at t=0. Fix: tl.render(0, true, true) with force=true. Applied uniformly to all 5 components — defensive and correct. ✓
O(n²) "hide all groups" loop — CORRECT. Old pattern: allGroupEls.forEach(other => if (oi !== bi) tl.set(other, { opacity: 0 }, visibleStart)) — n*(n-1) timeline entries. Fix: rely on each group's own lifecycle (opacity=1 at entry, opacity=0 at exit). Groups occupy non-overlapping time windows, so this is behavior-preserving. Applied to all 4 components that had this pattern (highlight used visibility-based show/hide instead). ✓
Observations (non-blocking)
-
window.__HF_CAPTION_ATTACH__is last-writer-wins. If two caption components coexist in one composition, the second overwrites the first's attach function. Consider namespacing per component ID if multi-caption-per-page is a real use case. -
Double normalization in 3 of 5 components.
hfFlattenSegmentsnormalizes words, then emoji-pop/pill-karaoke/weight-shift call their ownnormalizeWords()which re-processes and adds a DURATION clamp. The emoji-pop version also enriches with emoji associations — consider renaming toenrichWordsWithEmojito distinguish enrichment from normalization. -
Brand color consumption is intentionally asymmetric.
hfApplyStageConfigsets both--hf-caption-primaryand--hf-caption-accentin all 5, but editorial-emphasis and weight-shift never consume accent, and pill-karaoke never consumes primary. The no-op properties are harmless but could give users the false impression that all brand properties are supported. Consider documenting supported properties per component in descriptors.
Blocking SSOT issues
None found.
Verdict
Approve. The contract is consistent, the bug fixes are correct and well-documented, and the SSOT duplication is a known structural constraint of standalone HTML compositions. The non-blocking suggestions are improvements for the next iteration. Ship it.
— Miga
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Reviewed at e2846eb7cc81821f7fc21a9c4dccdd85c7ef3429.
Solid retrofit — five templates converted end-to-end from hardcoded demo transcript to runtime-attached ASR data, with real bug fixes threaded in (tl.seek(0) no-op, O(n²) hide loop) and honest test coverage in the sibling internal PR. The two auto-heuristic generalizations (caption-highlight grouping, caption-editorial-emphasis emphasis) look deterministic and terminate cleanly. Fallback-to-demo path preserves the public-catalog surface.
Two cross-cutting themes worth surfacing:
1. Brand-color surface is inconsistent across the five. caption-emoji-pop gates brand colors behind a strict ^#[0-9a-f]{6}$ regex — same brand payload that works in caption-highlight (#fff, rgb(...), hsl(...), red) silently falls back to the built-in defaults there. Meanwhile three templates SET one of the two CSS custom properties from brand.{primary,accent}Color but never CONSUME the property in their own CSS/JS — so brand.accentColor on caption-editorial-emphasis/caption-weight-shift, and brand.primaryColor on caption-pill-karaoke, are dead writes. The PR body claims all five apply both — worth either (a) actually consuming the ignored property or (b) narrowing the PR body / spec to say "each template applies whichever of the two its design uses". Inline anchors below.
2. The ~250-line hfFlattenSegments / hfDeriveDuration / hfValidate / hfApplyStageConfig / hfPublishStatus / hfRunGate block is byte-duplicated across all five templates. The section comment even labels it /* shared across caption templates */ but it isn't — every future edit needs five near-identical patches, and there's no CI check to catch drift. If future R2 tightens the displayMode grammar in hfValidate, the fix landing in three templates and missing two would ship unnoticed. Consider extracting to a registry/lib/caption-runtime.js shipped alongside each template, or an md5-parity CI check on the shared region.
Real bugs I want fixed (see inline):
- 🔴
caption-emoji-pophex-only brand-color gate silently drops any brand color that isn't exact 6-digit#RRGGBB(caption-emoji-pop.html:689-691). - 🟠 Dead brand-color writes in three templates (
caption-editorial-emphasis.html:240,caption-pill-karaoke.html:244,caption-weight-shift.html). - 🟠 Shared runtime block byte-duplicated across all five templates (
caption-highlight.html:187and equivalents). - 🟠
caption-weight-shift.fitFontSizefits joined-text width while rendering across two lines → over-shrinks (caption-weight-shift.html:462-473). - 🟠
avoidSingleWordGroupsmerges without re-checkingfitsInTwoLines→ downstreamsplitLinescan return overflowing split (caption-weight-shift.html:393-420). - 🟡
fitFontSizereturnsminSizeunchecked when text still overflows at min — visible clipping under narrow safe-zone (caption-highlight.html:132-141and identical in the other four).
Not blocking but worth a look:
- 🔵
MAX_WORDS_PER_BLOCK = 5/MAX_WORDS_PER_GROUP = 4/= 3— three templates use the same-looking constant with three different values. Not necessarily wrong (each design has its own density budget) but easy to misread. - ❓ Is the strict-hex gate on
caption-emoji-popintentional (some safety concern about the text-shadow color-mix?) or accidental divergence from the permissive pattern used by the sibling templates? If intentional, worth a code comment; if not, harmonize. - ❓
caption-editorial-emphasisemphasis density under the demo transcript trips ~14/28 words → ~1.75 block flashes/sec. Intended density, or should we cap? A future heuristic tweak could halve or double block count without any test failing — worth a snapshot fixture.
🟢 Verified-safe
tl.seek(0)→tl.render(0, true, true)correctly swapped everywhere;render(time, suppressEvents, force=true)bypasses GSAP 3.14.2's "already at time 0 → skip" no-op.- O(n²) hide-all-others loop cleanly removed in three templates; behavior preserved via each group/block owning its own opacity lifecycle (set at start / clear at nextStart), same-position tween pairs firing in add-order.
- Brand-color
removePropertycorrectly fires when thebrandblock is absent (all five) — CSSvar(--x, fallback)falls to the fallback for the three CSS-consumer templates. - IIFE encapsulation applied to the four previously-unwrapped templates;
caption-highlightwas already wrapped.window.__HF_CAPTION_ATTACH__remains last-defined-wins as documented. hfValidatefailure path uniform across all five: publishes__HF_CAPTION_STATUS = {ok:false, present:false, contained:false, reason, ...}.hfRunGatepublish shape identical across all five:{ok, present, contained, reason?, version, durationS}.caption-highlightgrouping algorithm is deterministic (pure function ofwords,fontPx,safeWidth,durationS), O(N) per word, caps at 4-word groups, handles single-word/empty/overlapping-word inputs.hfFlattenSegmentshandles missingw.word/w.text, negative/missing start, missing end (clamps to start), sorts by start.document.fonts.readygate awaited in every template'shfBootbeforehfAttach—fitFontSize/ canvas measurements use the intended font metrics.- Data-driven duration flows correctly:
hfDeriveDuration(words)→v.durationS→hfBuild(_, _, durationS)→tl.set({}, {}, v.durationS)pin →data-durationattribute updated on the root. - Fetch error path (
caption-data.jsonmissing / non-2xx / parse error) falls back toHF_DEMO_DATAcleanly. - Word emphasis heuristic (
clean.length >= 5 && !HF_STOPWORDS.has(clean)) — deterministic, case-insensitive. caption-emoji-popemoji lexicon lookup — case-insensitive via.toLowerCase()+replace(/[^a-z]/g, ""); no keyword collisions in the ~50-entry map (verified by inspection); no-match yieldsnulland the group publishes without an emoji sprite.caption-pill-karaokesplitLinesForGroupcorrectly rejects lines with any word exceedingmaxLineWidth(returnsnull), forcing that word into its own group.
| var accent = getComputedStyle(rootEl).getPropertyValue("--hf-caption-accent").trim(); | ||
| hfPrimaryColor = /^#[0-9a-f]{6}$/i.test(primary) ? primary : "#FFFFFF"; | ||
| hfAccentColors = /^#[0-9a-f]{6}$/i.test(accent) | ||
| ? [accent] |
There was a problem hiding this comment.
🔴 Silent brand-color drop — same brand payload accepted by 4 sibling templates in this PR gets silently rejected here.
hfPrimaryColor = /^#[0-9a-f]{6}$/i.test(primary) ? primary : "#FFFFFF";
hfAccentColors = /^#[0-9a-f]{6}$/i.test(accent)
? [accent]
: ["#FF76FF", "#FF0002", "#B2F7FF"];Compare caption-pill-karaoke.html:525-526:
var accent = getComputedStyle(rootEl).getPropertyValue("--hf-caption-accent").trim();
hfColorActive = accent || COLOR_ACTIVE;no format check, and caption-highlight / caption-editorial-emphasis / caption-weight-shift rely on CSS var(--x, fallback) (any valid CSS color works).
Repro: consumer sets brand: { primaryColor: "#fff", accentColor: "rgb(255,0,0)" } on a composition using caption-highlight (red gradient renders) AND caption-emoji-pop (falls back to the pink/red/blue random rotation, primary reverts to white because 3-digit #fff fails the 6-digit regex). Same brand payload, opposite outcomes. "#FFF", "red", "hsl(0 100% 50%)", "var(--tenant-red)" all silently fall back here while working elsewhere.
Either strip the regex (mirror pill-karaoke), or if there's a security reason (text-shadow color-mix?) document it and harmonize the pattern — but a silent brand-color loss inconsistent with the sibling templates in the same PR is a real regression on the wire contract.
— Review by Rames D Jusso
| root.style.removeProperty("--hf-caption-primary"); | ||
| } | ||
| if (typeof brand.accentColor === "string" && brand.accentColor) { | ||
| root.style.setProperty("--hf-caption-accent", brand.accentColor); |
There was a problem hiding this comment.
🟠 --hf-caption-accent is set/cleared here but never consumed anywhere in this file — grep 'var(--hf-caption-accent' caption-editorial-emphasis.html returns 0 hits, and no getPropertyValue("--hf-caption-accent") either. brand.accentColor is dead work on this template.
Same shape in caption-pill-karaoke.html:244-248 (--hf-caption-primary dead) and caption-weight-shift.html:235-238 (--hf-caption-accent dead).
PR body claims all 5 templates "apply brand.primaryColor / brand.accentColor via CSS custom properties" — in practice 3 of 5 apply only one of the two. Fix by either (a) actually consuming the ignored property in a sensible design mapping, or (b) narrowing the PR body / spec to say "each template applies whichever of the two its design uses". As-is, callers will spend cycles debugging why one brand color renders on one template and not another.
— Review by Rames D Jusso
|
|
||
| var container = document.getElementById("hl-container"); | ||
| var tl = gsap.timeline({ paused: true }); | ||
| function hfFlattenSegments(segments) { |
There was a problem hiding this comment.
🟠 The ~250-line block from here through the end of hfRunGate is byte-duplicated across all 5 templates. Section comment even says /* HyperFrames caption-data runtime (shared across caption templates) */ — but it's not actually shared. hfFlattenSegments, hfDeriveDuration, hfValidate, hfApplyStageConfig, hfPublishStatus, hfRunGate are identical to those in caption-editorial-emphasis:178-316, caption-emoji-pop:181-319, caption-pill-karaoke:187-325, caption-weight-shift:173-311.
Failure mode: a future tightening of hfValidate (e.g. accepting w.punctuation, stricter displayMode grammar) lands in 3 templates, misses 2 — no CI catches it, the two silently drift for a release. Same risk on any bug fix to the grouping/duration helpers.
Consider either (a) extracting to a registry/lib/caption-runtime.js shipped alongside each template, or (b) an md5-parity CI check on the shared region across the 5 files. This is the same class of drift risk as feedback_shared_skill_file_byte_identity in skills/{faceless-explainer,pr-to-video,product-launch-video}.
— Review by Rames D Jusso
| CAPTION_FONT_SIZE, | ||
| "700", | ||
| "Montserrat", | ||
| MAX_LINE_WIDTH, |
There was a problem hiding this comment.
🟠 fitFontSize is fitting the joined group text to MAX_LINE_WIDTH, but the group actually renders across two lines (see splitLines at :368-391 and the two-line .caption-line DOM at :479-493).
var groupText = group.words.map(function (w) { return w.text.toLowerCase(); }).join(" ");
var computedSize = fitFontSize(
groupText,
CAPTION_FONT_SIZE,
"700",
"Montserrat",
MAX_LINE_WIDTH,
);Repro: a 5-word group like ["professional","hyperframes","video","cinema","studio"] joined ≈ 1400px at base 72px, MAX_LINE_WIDTH ≈ 1360. fitFontSize shrinks to ~68px. Split as [a,b,c] / [d,e] each line is ~800px, well under MAX_LINE_WIDTH at 72px — so the shrink was unnecessary.
Fix: pass Math.max(measureLineWidth(line1), measureLineWidth(line2)) (widest of the two split lines) as the target, not joined text. Not correctness-breaking — visual only — but noticeable on ~half of two-line groups.
— Review by Rames D Jusso
| if ( | ||
| group.words.length === 1 && | ||
| out.length > 0 && | ||
| out[out.length - 1].words.length < MAX_WORDS_PER_GROUP |
There was a problem hiding this comment.
🟠 avoidSingleWordGroups merges single-word groups into the previous group without re-checking fitsInTwoLines. makeGroups at :428 guards every addition with !fitsInTwoLines(testGroup); this second-pass merge skips that guard, so it can produce a group that splitLines (:368-391) cannot fit within MAX_LINE_WIDTH.
Repro: a 3-word group filled to ~90% MAX_LINE_WIDTH (e.g. ["professional","hyperframes","video"]) followed by a single-word "cinema". Merge produces 4 words at ~110% width. splitLines finds no valid split, falls to naive bestSplit = Math.ceil(words.length/2) = 2, both halves still overflow. fitFontSize shrinks (per the F4 concern above, aggressively because it fits joined width), render survives via overflow-hidden. Not a crash but reasoning about correctness breaks.
Fix: mirror the initial fitsInTwoLines guard inside avoidSingleWordGroups, or refuse to merge when the concatenated group would overflow.
— Review by Rames D Jusso
| @@ -132,129 +140,375 @@ | |||
| return minSize; | |||
There was a problem hiding this comment.
🟡 fitFontSize never actually tests the text at minSize before returning it. Loop terminates when size <= minSize, then returns minSize unchecked.
var minSize = Math.floor(baseFontSize * 0.45);
while (size > minSize) {
_fitCtx.font = fontWeight + " " + size + "px " + fontFamily;
if (_fitCtx.measureText(text).width <= maxWidth) return size;
size -= 2;
}
return minSize;Repro: a pathological long single word or a group forced into a narrow safe zone (custom resolution < 1920 wide with a 4-word group of long words) → returns minSize = 45% of base font. On the 80px caption-highlight base that's ~36px, which may still overflow the container → visible clipping (overflow-hidden truncates or line-clamp cuts).
Cheap harden: check _fitCtx.measureText(text).width <= maxWidth at minSize too, return null/base-with-overflow-hint when it still doesn't fit so the caller can pick a fallback. Same shape in the identical fitFontSize in each of the other 4 templates.
— Review by Rames D Jusso
…ever clobbers a manual attach
Two review findings on the caption-data runtime, applied to all 5 templates:
- hfValidate's version gate used Math.floor(Number(v)) > HF_CONTRACT_VERSION,
and Number("v2") is NaN — NaN comparisons are always false, so malformed
versions slid through with no unsupported-version signal. An explicit
Number.isFinite check closes it.
- hfBoot's sibling-fetch .then called hfAttach unconditionally; a manual
window.__HF_CAPTION_ATTACH__ call landing while the fetch or fonts.ready
was still pending got clobbered by the late boot payload. Boot now yields
if a timeline already exists.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
R2 delta from e2846eb7c → 5c2981d066480000d623e4d023e9cf918b2772e3, +55/-10 across the 5 template files. Two additive fixes mirrored uniformly across every template:
-
hfValidateversion gate hardened againstNaN.Number("v2") → NaNandNaN > xis always false, so malformeddata.versionstrings (like"v2","abc",NaN,{}) slid past theunsupported-versiongate before this delta. NowNumber.isFinite(hfVersion)short-circuits the comparison. Comment at each site explains the reasoning. This closes a real correctness gap I missed in R1. -
Boot-fetch race guard. A manual
window.__HF_CAPTION_ATTACH__(data)call that landed whilecaption-data.jsonfetch ordocument.fonts.readywere still pending was getting clobbered by the late-arriving boot payload. Nowif (hfCurrentTimeline) return;at the start of the bootready.then(...)handler preserves the manual attach. Also a real correctness gap I missed in R1.
Delta is byte-identical across all 5 templates (each template got the same two hunks in hfValidate and the boot start function). Paired commit landed in HFI#521 (fa416450) with matching validator update + unit test asserting the version gate rejects ["v2", "abc", NaN, {}].
None of my R1 concerns are addressed by this delta — those are separate fixes still open. R1 findings stand as posted: caption-emoji-pop hex regex silently drops non-6-hex brand colors, dead brand-color writes in 3 templates, shared runtime byte-duplicated across 5, caption-weight-shift.fitFontSize fits joined-text width for 2-line render, avoidSingleWordGroups merges without re-checking two-line fit, fitFontSize returns unchecked minSize.
miguel-heygen
left a comment
There was a problem hiding this comment.
Freshness review at exact head 5c2981d066480000d623e4d023e9cf918b2772e3.
Miga's prior contract review covers the shared runtime and boot sequence; Rames subsequently identified the blocker below. I independently verified it at the current head.
blocker — registry/components/caption-emoji-pop/caption-emoji-pop.html:692-697: this template writes the same brand.primaryColor / brand.accentColor CSS variables as its four siblings, then uniquely accepts only six-digit hex when reading them back. Valid inputs such as #fff, rgb(...), hsl(...), or a named color work in the sibling templates but silently fall back here. This is a wire-contract inconsistency and directly matches the internal validator mismatch raised on HFI #521. Please use the same valid-CSS-color behavior across the five templates (or enforce one canonical grammar everywhere) and add a cross-template regression for an accepted non-six-digit color.
The coordinated version/boot-race guard delta itself is clean. The semantic title is now fixed, but the current required regression aggregate is red while its rerun settles, so this head is not approvable yet independently of the code blocker.
Verdict: REQUEST CHANGES
Reasoning: One template silently rejects brand-color values accepted by the shared contract and all four siblings; exact-head required CI is also not green.
— Magi
…, 8192 clamp - caption-emoji-pop: shadowForColor now builds its glow via color-mix() instead of hex-pair slicing, so the strict 6-digit brand-color gate is gone — any CSS color the sibling templates accept (#fff, rgb(), named) now renders instead of silently falling back to the default palette - caption-weight-shift: fitFontSize now sizes against the WIDEST split line rather than the joined group text (two-line groups no longer shrink unnecessarily), and avoidSingleWordGroups' merges re-check fitsInTwoLines like makeGroups' first pass does (merged groups can no longer overflow the split budget) - all 5: hfApplyStageConfig clamps resolution to the published validator's <=8192 bound (validator is optional pre-flight; unbounded stages OOM render workers), and fit floors carry a comment documenting that the minimum size is returned unverified by design Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Review feedback addressed in
|
miguel-heygen
left a comment
There was a problem hiding this comment.
Re-reviewed exact head 8bf939043fb44e05f6dd7caba81822ac5ef18334.
The prior blocker is resolved: caption-emoji-pop now preserves the same CSS-color input contract as the other four templates instead of silently rejecting non-6-digit colors, and the internal regression covers 3-digit hex values end to end. I also re-verified:
- the seven shared runtime functions are identical across all five components, with an internal parity test guarding drift;
- the version, resolution, and boot-race guards are consistently applied;
- the
caption-weight-shiftline-fit and widest-line sizing fixes address the reviewed edge cases; - all exact-head required checks are green, including both Windows jobs and the fresh eight-shard regression matrix.
No remaining blocking findings.
Verdict: APPROVE
Reasoning: The reported correctness issue is fixed with regression coverage, the shared contract remains synchronized, and exact-head CI is fully green.
— Magi
Summary
caption-editorial-emphasis,caption-emoji-pop,caption-highlight,caption-pill-karaoke,caption-weight-shift(registry/components/*) to attach real caption data (timed words, however sourced) at runtime instead of a hardcoded demo transcript — via inlinewindow.__HF_CAPTION__props or a siblingcaption-data.jsonfetch (sharedhfAttach/window.__HF_CAPTION_ATTACH__contract, see companion internal-repo PR for the full data-driven-captions design spec).brand.primaryColor/brand.accentColorits design consumes (per-identitybrandSupportis documented in the internal package's descriptors; both CSS custom properties are set/cleared uniformly on every attach, and the one a given design doesn't use is a deliberate no-op), and publishes awindow.__HF_CAPTION_STATUShealth/gate signal after layout settles — replacing per-request server-side compile + gates.caption-highlightadditionally replaces its hardcoded word-index groupings with a real automatic grouping algorithm;caption-editorial-emphasisreplaces its hand-authored emphasis annotations with a generalized long-non-stopword heuristic;caption-emoji-pop's keyword/emoji lexicon is generalized from ~7 hardcoded entries to ~50.tl.seek(0)is a no-op on a fresh timeline (fixed viatl.render(0,true,true)), and an O(n²) redundant "hide all other groups" loop that caused test timeouts under load.Context
Sibling internal-repo PR vendors these components into a new
@app/caption-templatespackage (test harness, validator, self-contained offline build, publish pipeline) — see that PR for the full design spec and rollout plan.Test plan
pnpm --filter @app/caption-templates testin the sibling internal repo, which exercises these components directly) — 77/77 passing🤖 Generated with Claude Code