An aligner reports each word ending where the next one begins, and
`tokenFrameSpan` keeps the frame that instant falls in, so adjacent Word
windows overlap by one frame. Cue boundaries inherited that overlap, and a
renderer that draws Cues at a single position drew two of them on the same
frame.
`exclusiveActivationFrames` in `@hypit/caption-fine` already states the rule
for Atoms: as soon as the next authored unit starts, the previous one stops.
Apply it once where unit timings are built. A Cue envelope ends at its last
unit, so exclusive Cue boundaries follow. The Timeline keeps the raw Token
measurements.
Closes#297.
Place prepared Takes on an independent Timeline with gaps and overlaps. Present picture, sound and captions through explicit Use rules while preserving source timing and project-authored styles.
Unify Companion bands, parameter ownership and source writeback in Studio. Migrate components, examples and authoring guidance, remove retired Speech/Semantic Track interfaces, and retain protocol identifiers at @1.
Validation: repository suite (786 tests; six sandbox-blocked tests passed on permitted rerun), 43 Studio tests after audit fixes, TypeScript, public declarations, four example-package builds, chat Source check and npm file inventory.
`b32e8253` replaced both Playgrounds with `packages/studio`, but the rename
stopped at `examples/`. The skill and the docs still named the tool, the
command and the recipe sheet that no longer exist.
`studio.svs` becomes `recipes.svs` with its import alias, in the 39 files the
examples rename had left behind — docs, the `docs/zh` mirrors, the skill
references and the package manifests whose authoring snippets show the alias.
The old name was already loose, since an `.svs` file is a parameter-recipe
sheet rather than anything Studio reads; once `studio` names the application
it reads as Studio's own config file, which it is not. Two test fixtures keyed
the alias as a plain string rather than inside `{}`, so they moved with it —
`packages/text` had a real reference to `recipes.shot` resolving against a
`studio.shot` fixture.
`pnpm svml:playground -- --source main.svml` becomes `pnpm studio -- --run
build.svrun` throughout. The Run Source is not an optional extra any more:
Studio's unit of work is the Run, and it reads the Author SVML back out of it.
`preview-check.mjs` imported `svml-playground/src/pipeline/preview.js`, which
this branch deleted, so the skill's own build gate had been dead since
`b32e8253`. It is rebuilt on Studio's real entry path — `loadStudioDomain`,
`loadStudioRun`, `inspectStudioRun`, `readStudioSession` — and reports
`provenance.errors` and unresolved Candidates instead of the `waiting` and
`stand-in` fields Studio no longer has. It stays in URL space for Windows, as
`e0ecccb9` established, and closes the archive before exiting rather than
leaving it open behind a `process.exit` in a `try`.
The preview pages are rewritten rather than renamed, because what they
describe is gone. Studio has no stand-ins, no black frames and no estimated
timing; it refuses to open and names what is missing. So the pages document
the five refusals verbatim, and say plainly that a Source still declaring
ungenerated shots will not open — including the warning that no example in
this repository currently does. Running the ported check over all nine
committed Run Sources: six refuse on unresolved capabilities or a target that
is not a Film, and three on footage under the gitignored `examples/**/assets/`.
Claiming otherwise would have shipped a quickstart whose first command fails.
`pnpm check` clean; `pnpm test` 510 pass, 0 fail; `pnpm test:release` 8 pass.
Four conflicts. Two were mechanical: `align-transcript.ts` and
`svml-playground/src/pipeline/placeholder.ts` are modify/delete, and the
deletion is this branch's intent — local alignment removes the need to
fabricate a program-audio digest, and Studio has no stand-ins to draw.
The other two are the same bug fixed twice. Both sides found that CSS
`-webkit-text-stroke` is centred on the glyph edge, so half the stroke eats
the letterform, and both moved to `VisualTextPaintLayer` with
`placement: "outside"` — which already existed at the merge base on
`VisualTextFlowElement`. That shared starting point is why they converged an
hour apart.
`packages/caption-fine/src/render.ts` and its test take main's version. Main
extends `VisualTextElement` and the HyperFrames renderer rather than moving
Caption Fine onto text-flow, and it carries two fixes this branch does not:
CSS blur is a radius covering about two deviations while the filter takes a
deviation, so passing the authored number through doubles every shadow and
glow in the repository, and its filter-definition dedup takes 80 defs to 16.
Neither file is touched by `a4f2b31f`, so taking main's reverts `d5ee32c7`
exactly and nothing else. `renderFineCaption` works on `ProgramSpace`, which
survives here as a projection of `SemanticTrack`, so the semantic API change
does not reach it.
The Pill-as-paint-layer and structured-typography work in `d5ee32c7` is a
real gain and is not preserved here; it belongs on top of main's
implementation as its own change.
`pnpm check` clean; `pnpm test` 510 pass, 0 fail, 15 skipped.
A word shorter than a frame measures to the same frame at both ends once samples
are rounded to the frame grid, and the projection refused the empty span it
produced, taking the whole Caption Track with it.
The map is right to report what it measured: alignment states, and tests, that
deciding what a degenerate window means belongs to whoever renders it. So it is
decided here, where an Atom becomes a Cue that has to be drawn.
The three example package locks conflicted: both sides had regenerated
them. Neither side's digests were still correct, since the merged tree
combines their source refactor with the license field added to every
manifest, so the locks were rebuilt from the merged bytes rather than
resolved by picking a side.
Every workspace manifest omitted a license field, so a scanner reading
the published repository would resolve all 90 of them as unknown. Both
Python services went further and declared plain Apache-2.0, which is
looser than the LICENSE they actually ship under.
Package bytes feed the example package locks, so adding the field
invalidated all three; refreshed them. Only closureDigest and artifacts
move — same 31 selected packages, nothing added or removed.
Also record what the READMEs never said: ffmpeg is a real prerequisite
for a Build and had zero mentions, and the media Lambda layer is a GPL
FFmpeg build. That build is deliberate — libx264 is GPL-only and the
encode path requires it — and it stays lawful only because the operator
builds and deploys it into their own account. Publishing a prebuilt
layer would break that, so the boundary is now written down.
Rename the author frontend around Markup, replace PromptKit with graph-native Text templates, and keep video typography in its own package. Wire ordinary Text through explicit graph edges into model and visible consumers, refresh examples, locks, tests, and architecture documentation.
Three places quantised or paired a measurement and then forced the result
forwards when it came out backwards, and one assertion required exactly the
order those corrections manufactured. Together they meant a backwards
measurement either vanished silently or killed the Build, and a consumer could
never see that the evidence disagreed with itself.
Locating now reports what it measured. Selection occurrences are located
independently and returned in Script order: never sorted, merged or clipped
against one another, because reconciling them is a decision about the caller's
own material. Two tests hold that line, one at each level.
The map also stops being a structure consumers read. Caption asked it for token
windows by reaching into the array; it now calls tokenSpanSeconds, a boundary
test keeps every other package out of the fields, and the segments array, which
no longer had a reader, is gone.
The envelope in groupWindow and the interpolation bound in fillEstimated stay:
one aggregates a run of evidence, the other shapes a value for tokens that were
never measured. Neither edits a measurement.