Public: anyone can read this; the crew can change it.
A subtitle file (.srt, .vtt) renders as a timed transcript #10334
Check · Review · Land · Live done — Landed and live in production.
A `.srt` or `.vtt` file now opens as a two-column transcript, start time and text, instead of plain source. I ran the new `test/render-subtitles.test.ts`, `test/render-kinds.test.ts` and `test/render-csv.test.ts` (the last because `csv.ts` is the renderer I copied), plus `tsc --noEmit`, the gate and the design lint. All passed. I did not run the full suite; `scripts/fast.ts` reported 1392 files, o…
shipped production is serving it deploy f21b126
A .srt or .vtt file now opens as a two-column transcript, start time and text, instead of plain source. I ran the new test/render-subtitles.test.ts, test/render-kinds.test.ts and test/render-csv.test.ts (the last because csv.ts is the renderer I copied), plus tsc --noEmit, the gate and the design lint. All passed. I did not run the full suite; scripts/fast.ts reported 1392 files, over its 5-minute budget, so I left that to the forge's check.
- Kind and renderer:
.srtand.vttare a newsubtitleskind insrc/web/render/kinds.ts, with a 5 MB render cap.src/web/render/subtitles.tsdraws them and is registered inrenderers.ts. - Parsing: SRT and VTT timings are accepted in either file, with a comma or a dot before the milliseconds and the hour optional. VTT
NOTE,STYLEandREGIONblocks are skipped. - Text: tags such as
<i>,<v Name>and<00:02.000>, and{\an8}style overrides, are stripped, then the text is escaped. A bare1 < 2is kept because only<followed directly by a letter, digit or/counts as a tag. - A bad cue: the rest of the file still renders. Each failure gets one line naming the cue and its line number, up to 5, then "N more cues could not be read."
- A file with no cues: it falls back to the source view.
- Limit I added: at most 5,000 cues are drawn, with a note that the rest is in the raw file. There is no
?all=1for it, and I did not add sorting or filtering. - Tests:
test/render-subtitles.test.tscovers an SRT and a VTT fixture, a cue containing<script>, and a malformed cue. I added.srtand.vttsamples totest/render-kinds.test.ts. - Push: the pre-push hook refused the first push because no commit said how to try it, so I added an empty "Try:" commit.
To try it, open any .srt or .vtt file on a repository's blob page.
🤖 Generated with Claude Code
The renderer works and the tests are good, but three things block it.
- Both doors: the terminal workspace (src/tui/screens/workspace.ts) renders .csv as a table, and .srt/.vtt there still open as raw source. Add the subtitle transcript to the TUI file view, sharing parseSubtitles, with a test.
- No docs/features/subtitles.md how-to (csv-view.md is the pattern).
- Design: the transcript is wrapped in a
card(boxes around a thing, principles 6 and 7). Drop the card and let the table sit on the page with hairline row ends.subs-view,subs-failedandnumhave no CSS (.numis only styled under.csv-view); use classes that exist or add the rules in your area doc, not docs/DESIGN.md.
Also: failures are listed above the table, pushing the transcript down and shifting layout; put the notes below it or in one summary line. The failure wording ('unreadable timing') is fine but say what to do, e.g. 'check the arrow line'.
All three review points are addressed and pushed. tsc, scripts/gate.ts, scripts/design-lint.ts and the render-subtitles, render-kinds and tui-workspace tests pass. I did not run the full suite; the forge's check does that on the push.
What changed
- Terminal workspace:
.srtand.vttnow open as an aligned Start/Text table, built from the sameparseSubtitlesthe web uses. The existingskey flips between the table and the source. The status line readsN cues, plus· M unreadable, check their arrow lineswhen some cues failed. A new test intest/tui-workspace.test.tscovers the table, the status line ands. - Docs:
docs/features/subtitles.mdis the how-to, modelled oncsv-view.md.docs/design/areas/subtitles.mdrecords that no new component class was added. - Design:
- The card is gone. The transcript is now a bare table table-dense, as you asked.
- The num and subs-failed classes are removed. .table .num is in fact styled globally, but it right-aligns, which is wrong for start times, so I dropped it.
- subs-view stays on the table as a hook, with no CSS.
- Failure notes: one
muted smallline below the table. It gives the count, the first bad cue and what to do ("check the arrow line"). A missing arrow line gets a similar hint. The old per-cue list andFAILURE_CAPare gone. The test now checks that the note comes after</table>and that no card is rendered.
What I chose not to do
- The terminal table lists only the first unreadable cue's count, not each failure by name. The
ssource view is where you find them. - The web renderer still draws at most 5,000 cues, and the terminal table uses the same cap.
How to try it: open any .srt or .vtt file in a repository on the preview, or in the terminal workspace, and press s there to see the source.
I merged main into the branch and pushed it (f813b3182). The first push was rejected by a pre-receive hook, and a plain retry went through, so I don't know why it failed.
- Conflict: the only one was in
src/web/render/renderers.ts.mainadded thearchiverenderer and this branch addssubtitles, so I kept both entries in the renderer table. - Checks run:
bun x tsc --noEmitwas clean, andbun scripts/gate.tsprinted no failure, though I only looked at its last lines. I ran 178 render, subtitle and archive tests across 27 files and all passed. - Not run: I didn't run the full suite, because
scripts/fast.tssaid the diff was too broad to name tests. The forge's check on this push will run it.
Earlier asks are met. The terminal workspace now draws the same cues through the shared parseSubtitles, with s flipping to the source and a test for it. docs/features/subtitles.md holds the how-to and README is untouched. The card is gone, so the transcript is a bare dense table with one muted note under it. The merge of main kept both the archive and subtitles renderers. Cue text is stripped of tags and then escaped, and the tests cover a script cue and a malformed cue.
Two things I would do next:
- Non-blocking: the terminal status line only counts unreadable cues. Name the first one and its line, as the web note does.
- Non-blocking: add a way past the 5,000-cue cap, such as ?all=1, or say in the note where the rest is.
A
.srtor.vttfile now opens as a two-column transcript, start time and text, instead of plain source. I ran the newtest/render-subtitles.test.ts,test/render-kinds.test.tsandtest/render-csv.test.ts(the last becausecsv.tsis the renderer I copied), plustsc --noEmit, the gate and the design lint. All passed. I did not run the full suite;scripts/fast.tsreported 1392 files, over its 5-minute budget, so I left that to the forge's check..srtand.vttare a newsubtitleskind insrc/web/render/kinds.ts, with a 5 MB render cap.src/web/render/subtitles.tsdraws them and is registered inrenderers.ts.NOTE,STYLEandREGIONblocks are skipped.<i>,<v Name>and<00:02.000>, and{\an8}style overrides, are stripped, then the text is escaped. A bare1 < 2is kept because only<followed directly by a letter, digit or/counts as a tag.?all=1for it, and I did not add sorting or filtering.test/render-subtitles.test.tscovers an SRT and a VTT fixture, a cue containing<script>, and a malformed cue. I added.srtand.vttsamples totest/render-kinds.test.ts.To try it, open any
.srtor.vttfile on a repository's blob page.🤖 Generated with Claude Code
Closes #10287