Skip to content
Draft

v2 #10

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
677c875
chore: upgrade toolchain to rollup 4, vitest 3, typescript 5.9, prett…
mihar-22 Sep 2, 2026
ab55dea
fix(vtt)!: escape rendered cue HTML and harden tokenizer nesting
mihar-22 Sep 2, 2026
67f6aa1
fix(vtt): report reversed cue ranges, keep decimal percentages, parse…
mihar-22 Sep 2, 2026
2c705a5
feat(vtt): add HLS X-TIMESTAMP-MAP helpers
mihar-22 Sep 2, 2026
ee70060
feat(srt): strip coordinates, map font colours, apply numpad alignmen…
mihar-22 Sep 2, 2026
d55c63c
feat(ssa)!: rewrite parser with PlayRes scaling, override tags, and e…
mihar-22 Sep 2, 2026
f3e5c2f
feat: add TTML/IMSC1, SCC (CEA-608), LRC, and SBV parsers
mihar-22 Sep 2, 2026
c998c04
feat(renderer): sorted cue index, identity diffing, frame-accurate sy…
mihar-22 Sep 2, 2026
21e240e
feat(styles): balanced wrapping, container units, text-stroke outline…
mihar-22 Sep 2, 2026
7db9948
docs: document new formats, renderer APIs, styling, and security model
mihar-22 Sep 2, 2026
6ff0657
fix(renderer): apply cue position horizontally and size fonts from th…
mihar-22 Sep 2, 2026
9140499
test: add Vitest 4 browser mode with Playwright and real-layout posit…
mihar-22 Sep 2, 2026
c0a9899
chore: drop misplaced pure annotations
mihar-22 Sep 2, 2026
0b060e2
build: replace rollup with tsdown
mihar-22 Sep 2, 2026
6cc4878
fix(renderer): transform-aware collision boxes, fixed cues, reading-o…
mihar-22 Sep 2, 2026
e447158
feat(ssa): size cue boxes to their text and pin positioned cues
mihar-22 Sep 2, 2026
88e269b
test(browser): cover transforms, fixed cues, and SSA layout in Chromium
mihar-22 Sep 2, 2026
7e8967f
docs: regenerate README screenshots from sandbox scenarios
mihar-22 Sep 2, 2026
85869ae
feat(vtt): support STYLE blocks with scoped, sanitized ::cue styling
mihar-22 Sep 2, 2026
fe52207
fix(vtt)!: spec-conformant file parsing with explicit real-world tole…
mihar-22 Sep 2, 2026
f026f88
feat(scc): decode CC2 via the channel option
mihar-22 Sep 2, 2026
a27e262
feat(ttml): cell units, font sizes, vertical writing modes, sibling e…
mihar-22 Sep 2, 2026
26acd00
fix(renderer): spec-conformant line placement for vertical, negative,…
mihar-22 Sep 2, 2026
98ce53d
feat(renderer): event-driven sync mode driven by native TextTrack cue…
mihar-22 Sep 2, 2026
b5d486d
fix(ssa): honour \1c colour tags and keep toggled formatting properly…
mihar-22 Sep 2, 2026
a3d9de1
docs: document STYLE blocks, strict vs tolerant parsing, sync modes, …
mihar-22 Sep 2, 2026
c700e99
test(browser): screenshot-based visual regression for representative …
mihar-22 Sep 2, 2026
55f871d
chore: ignore vitest attachments
mihar-22 Sep 2, 2026
34ed96f
feat(cea): stream-oriented CEA-608 decoder with field 2 (CC3/CC4) and…
mihar-22 Sep 2, 2026
e22e32a
feat(cea): CEA-708 (DTVCC) decoder
mihar-22 Sep 2, 2026
4ed8285
docs: document CEA-608/708 stream decoding and visual regression testing
mihar-22 Sep 2, 2026
ddc9b8b
refactor(renderer): split positioning into measure, layout, and write…
mihar-22 Sep 2, 2026
91b5a52
feat(cue)!: structured layout and text style model on VTTCue with JSO…
mihar-22 Sep 2, 2026
9cf1f9c
feat(element): <media-captions> custom element
mihar-22 Sep 2, 2026
e8c39b3
feat(track): CueTrack with incremental sorted index, mutable end time…
mihar-22 Sep 2, 2026
dd362ee
feat(renderer): render from a CueTrack with live updates and retentio…
mihar-22 Sep 2, 2026
a9f94b2
feat(renderer): build cue DOM directly, dispatch enter/exit events, o…
mihar-22 Sep 2, 2026
9613e63
feat(styles)!: cascade layer, registered custom properties, and per-f…
mihar-22 Sep 2, 2026
b7b3b71
feat(cea): live mode emitting open-ended cues that are updated in place
mihar-22 Sep 2, 2026
5f41c74
docs: layout model, CueTrack, custom element, cue events, announcer, …
mihar-22 Sep 2, 2026
671bd0e
test(wpt): vendor the web-platform-tests WebVTT parsing suites
mihar-22 Sep 2, 2026
8ddfc26
fix(vtt)!: resolve every parser bug surfaced by the WPT suite
mihar-22 Sep 2, 2026
d186c31
docs: describe the WPT conformance harness
mihar-22 Sep 2, 2026
77062e1
feat(cue): layout height, background images, animations, class names,…
mihar-22 Sep 2, 2026
c21d1b1
ci: test, type-check, and build on pull requests; manual workflow to …
mihar-22 Sep 2, 2026
917998a
fix(cue): support open-ended (Infinity) end times on the native VTTCue
mihar-22 Sep 2, 2026
453f30e
docs(sandbox): layout model, live track, and custom element scenarios…
mihar-22 Sep 2, 2026
6baa190
feat(entities): optional full HTML named character reference table
mihar-22 Sep 2, 2026
c3c7d6b
docs: entities entry, open-ended cues, sandbox scenarios, and CI
mihar-22 Sep 2, 2026
a346195
fix(cue): keep Infinity end times intact on the fallback cue class
mihar-22 Sep 2, 2026
67bf627
feat(cea): CEA-708 pen sizes, edges, window fill and borders, print a…
mihar-22 Sep 2, 2026
1bb296a
feat(ttml): set animations, image cues, wall-clock time base, drop-fr…
mihar-22 Sep 2, 2026
b36c973
docs: TTML animations, images, clock time base; CEA-708 fidelity
mihar-22 Sep 2, 2026
c98cc90
style: format workflows and add .prettierignore for vendored and gene…
mihar-22 Sep 2, 2026
55085d8
style: format remaining files
mihar-22 Sep 2, 2026
38e6c56
build: adopt Vite+ (oxfmt, oxlint, vitest, tsdown) as the toolchain
mihar-22 Sep 3, 2026
24cf263
feat(cue): per-span styles, inline drawings, clip paths, and an anima…
mihar-22 Sep 7, 2026
a50b1f2
feat(renderer): media-synced animations, transform-aware collisions, …
mihar-22 Sep 7, 2026
4274edb
docs: renderer options, span styles, drawings, and animation model; C…
mihar-22 Sep 7, 2026
838850d
feat(cea): 708 pen backgrounds, font tags, horizontal scroll, wipe di…
mihar-22 Sep 7, 2026
ee8b606
feat(ssa): typesetting fidelity: per-span tags, fades, move, \t, kara…
mihar-22 Sep 7, 2026
26a4fe3
docs: SSA/ASS tag coverage after the typesetting work
mihar-22 Sep 7, 2026
213f4b0
feat(ttml): container animations, span styles, dropPAL, forced displa…
mihar-22 Sep 7, 2026
e93a4e7
test(imsc): vendor 62 W3C IMSC test documents and check parsing confo…
mihar-22 Sep 7, 2026
a5e8005
docs: TTML/IMSC coverage and test suites
mihar-22 Sep 7, 2026
12192d9
feat(track): dedupe option for cues repeated across HLS/DASH segments
mihar-22 Sep 7, 2026
5c29485
perf(tokenizer): cache tokens per cue, invalidated on text or span ch…
mihar-22 Sep 7, 2026
98232a1
feat(renderer): reduced-motion support and user preset attributes
mihar-22 Sep 7, 2026
1204074
ci: size budgets, publint and attw package checks, coverage, API docs…
mihar-22 Sep 7, 2026
e1073fb
docs: reduced motion, caption presets, track dedupe, and the expanded…
mihar-22 Sep 7, 2026
fa0b88e
refactor(renderer): split ordering, animation sync, announcer, and st…
mihar-22 Sep 7, 2026
f8be5ea
feat: add SAMI and MicroDVD parsers
mihar-22 Sep 7, 2026
af43fe5
feat(mp4): demux fMP4 wvtt and stpp subtitle tracks into cues
mihar-22 Sep 7, 2026
ddb95ff
test(bench): parse, tokenize, track, layout, and CEA decode benchmark…
mihar-22 Sep 7, 2026
918cee1
perf: O(1) CueTrack membership and 708 commits only after a completed…
mihar-22 Sep 7, 2026
4cbeaa7
test(corpus): real-world style fixtures for every format with reviewa…
mihar-22 Sep 7, 2026
f0f35c0
fix(parsers)!: harden against hostile input found by fuzzing
mihar-22 Sep 7, 2026
98fe852
chore: exclude corpus fixtures and snapshots from formatting and linting
mihar-22 Sep 7, 2026
30e2e0c
chore: enable noImplicitAny across the codebase
mihar-22 Sep 7, 2026
8231415
feat(playground): interactive visual playground for every parser and …
mihar-22 Sep 7, 2026
e3ca69a
fix(vtt)!: drop the non-spec settings-on-a-separate-line tolerance
mihar-22 Sep 7, 2026
ed315f7
fix(ssa): lazy tag emission and screen-fixed \clip rectangles on layo…
mihar-22 Sep 7, 2026
a0ca8f0
fix(styles): do not paint-contain cue boxes so rotated, scaled, and a…
mihar-22 Sep 7, 2026
5f47863
fix(ssa): rotate and scale around the alignment anchor or \org
mihar-22 Sep 7, 2026
548a680
fix(playground): make the SSA \clip sample visibly slice the glyphs
mihar-22 Sep 7, 2026
11782f6
fix(vtt): assigning a region no longer throws in Firefox and WebKit
mihar-22 Sep 7, 2026
4b40269
test(browser): run the layout suites in Firefox and WebKit too
mihar-22 Sep 7, 2026
179f1ee
feat(renderer): composable core with pluggable features and a media-c…
mihar-22 Sep 7, 2026
081f242
build: size budgets from tree-shaken usage probes
mihar-22 Sep 7, 2026
202e35b
feat(playground): renderer feature presets
mihar-22 Sep 7, 2026
2c0ab18
fix(styles): fit image cues to their box instead of tiling
mihar-22 Sep 7, 2026
0ba6707
feat(canvas): headless measurement and a canvas writer (media-caption…
mihar-22 Sep 7, 2026
adfc2e2
docs: canvas exploration findings and a typed style model proposal
mihar-22 Sep 7, 2026
3689a80
feat(playground): canvas view
mihar-22 Sep 7, 2026
100fab4
feat(model)!: typed cue style model, writers serialise it
mihar-22 Sep 7, 2026
d8b9cd9
docs: record the typed style model outcome
mihar-22 Sep 7, 2026
c347499
fix(renderer): keep vertical cues in place when the overlay is re-lai…
mihar-22 Sep 7, 2026
910519c
feat(canvas): vertical writing modes
mihar-22 Sep 7, 2026
522f12f
feat(playground): picture-in-picture and fullscreen through a composi…
mihar-22 Sep 7, 2026
bc04599
docs: migration guide from 1.x, PiP compositing, and vertical text notes
mihar-22 Sep 7, 2026
cb90180
ci: declare the pnpm version so pnpm/action-setup can install it
mihar-22 Sep 7, 2026
d8e7fbd
ci: record the pnpm overrides in the lockfile
mihar-22 Sep 7, 2026
73d0898
chore: format package.json
mihar-22 Sep 7, 2026
ec9df1b
test(fuzz): loosen the parse budget on CI runners
mihar-22 Sep 7, 2026
36965e9
ci: run the docs script explicitly and document the new entries
mihar-22 Sep 7, 2026
46dfe50
feat(canvas): ruby annotations and orthographic 3D rotations
mihar-22 Sep 7, 2026
793a1c9
fix(vtt): ignore mismatched end tags as the spec does
mihar-22 Sep 7, 2026
f1d88ea
feat(parse): add lenient option and run the WPT suite spec-exact
mihar-22 Sep 7, 2026
637ab62
ci: record linux visual baselines as an artifact
mihar-22 Sep 7, 2026
5a4e797
test(browser): add linux visual baselines and run the suite in CI
mihar-22 Sep 7, 2026
0da8071
docs: lenient parsing, spec end tags, canvas ruby and 3D, CI visual s…
mihar-22 Sep 7, 2026
af00e53
fix(canvas): let ruby annotations overflow the line box like browsers
mihar-22 Sep 7, 2026
eb613e7
test(browser): ruby box agreement is engine-aware
mihar-22 Sep 7, 2026
c601b68
fix(playground): drive the canvas view on every time change
mihar-22 Sep 7, 2026
2437af2
fix(canvas): composite cue opacity as a group, like CSS opacity
mihar-22 Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
47 changes: 47 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
# Formatting (oxfmt) and linting (oxlint) through Vite+.
- run: pnpm check
- run: pnpm typecheck
- run: pnpm coverage
- uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage
if-no-files-found: ignore
- run: pnpm exec playwright install --with-deps chromium firefox webkit
# Includes the visual suite: its Linux Chromium baselines are committed (re-record them with
# record-baselines.yml after an intentional rendering change).
- run: pnpm vp test --run --project browser
env:
BROWSERS: chromium,firefox,webkit
- run: pnpm build
- run: pnpm size
# `pnpm docs` is forwarded to npm's built-in `docs` (opens a browser); run the script.
- run: pnpm run docs
- uses: actions/upload-artifact@v4
with:
name: api-docs
path: docs/api
- uses: actions/upload-artifact@v4
if: failure()
with:
name: vitest-attachments
path: .vitest-attachments
if-no-files-found: ignore
30 changes: 30 additions & 0 deletions .github/workflows/record-baselines.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Record visual baselines

# Records the Chromium screenshot baselines on Linux (the CI platform) and uploads them as an
# artifact. Trigger manually after an intentional rendering change, then:
#
# gh run download <run-id> -n visual-baselines -D tests/browser/__screenshots__
#
# and commit the result. Baselines are per browser and platform, so the macOS ones recorded locally
# stay untouched.
on:
workflow_dispatch:

jobs:
record:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm exec playwright install --with-deps chromium
- run: pnpm vp test --run --project browser tests/browser/visual.test.ts --update
- uses: actions/upload-artifact@v4
with:
name: visual-baselines
path: tests/browser/__screenshots__/visual.test.ts/*-linux.png
if-no-files-found: error
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Release

# Publishes to npm with provenance when a version tag is pushed. Requires the `NPM_TOKEN` secret
# (or configure npm trusted publishing for this repository and drop the token).
on:
push:
tags: ['v*']

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
registry-url: https://registry.npmjs.org
- run: pnpm install --frozen-lockfile
- run: pnpm check
- run: pnpm typecheck
- run: pnpm test:unit
- run: pnpm build
- run: pnpm size
- run: pnpm changelog
- run: npm publish --provenance --access public --tag ${{ contains(github.ref_name, '-') && 'next' || 'latest' }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- uses: softprops/action-gh-release@v2
with:
body_path: CHANGELOG.md
generate_release_notes: true
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ yarn-debug.log*
yarn-error.log*
pnpm-debug.log*


.vitest-attachments/

docs/api/
coverage/
14 changes: 0 additions & 14 deletions .prettierrc.cjs

This file was deleted.

75 changes: 62 additions & 13 deletions .sandbox/index.html
Original file line number Diff line number Diff line change
@@ -1,42 +1,91 @@
<!doctype html>
<html>
<head>
<title>Media Captions</title>
<title>Media Captions Sandbox</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/png" sizes="32x32" href="/sandbox/favicon-32x32.png" />
<link rel="stylesheet" href="/styles/captions.css" />
<link rel="stylesheet" href="/styles/regions.css" />
<script type="module" src="/sandbox/main.ts"></script>
<script type="module" src="/.sandbox/main.ts"></script>
<style>
body {
margin: 0;
width: 100vw;
height: 100vh;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-sizing: border-box;
gap: 24px;
background: #111;
color: #eee;
font-family: system-ui, sans-serif;
}

.viewport {
position: relative;
width: 960px;
height: 540px;
background-color: #dcdcdc;
overflow: hidden;
border-radius: 8px;
background: linear-gradient(160deg, #4a5a7a 0%, #2b3550 45%, #151826 100%);
box-sizing: border-box;
}

#current-time {
margin-top: 48px;
.viewport.small {
width: 464px;
height: 261px;
}

.grid {
display: grid;
grid-template-columns: repeat(2, 464px);
gap: 16px;
}

.grid [data-part='captions'] {
--cue-font-size: 9cqh;
}

.label {
position: absolute;
top: 12px;
left: 12px;
padding: 4px 8px;
font-size: 13px;
border-radius: 4px;
background: rgba(0, 0, 0, 0.55);
pointer-events: none;
z-index: 2;
}

.controls {
display: flex;
gap: 12px;
align-items: center;
}

.controls a {
color: #9cc4ff;
}

/* Karaoke styling for the demo: sung words are highlighted. */
[data-part='timed'][data-past] {
color: #ffd166;
}

[data-part='timed'][data-future] {
opacity: 0.6;
}
</style>
</head>
<body>
<div class="viewport">
<div id="overlay"></div>
<div id="root"></div>
<div class="controls">
<label>
Time
<input id="current-time" type="number" value="3" step="0.5" style="width: 80px" />
</label>
<span id="links"></span>
</div>

<input id="current-time" type="number" value="0" />
</body>
</html>
22 changes: 1 addition & 21 deletions .sandbox/launch.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,3 @@
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';

const SANDBOX_TEMPLATE = path.resolve(process.cwd(), '.sandbox'),
SANDBOX_DIR = path.resolve(process.cwd(), 'sandbox'),
IGNORED_FILES = new Set(['launch.js']);

// Copy files from .sandbox template directory to sandbox.
if (!fs.existsSync(SANDBOX_DIR)) {
fs.mkdirSync(SANDBOX_DIR);
const files = fs.readdirSync(SANDBOX_TEMPLATE);
for (const file of files) {
if (IGNORED_FILES.has(file)) continue;
const from = path.resolve(SANDBOX_TEMPLATE, file);
const to = path.resolve(SANDBOX_DIR, file);
fs.writeFileSync(to, fs.readFileSync(from, 'utf-8'));
}
}

execSync('vite --open=/sandbox/index.html --port=3100 --host', {
stdio: 'inherit',
});
execSync('vp dev --open=/.sandbox/index.html --port=3100 --host', { stdio: 'inherit' });
Loading
Loading