Skip to content

Releases: jbrecht/tutorial-forge

v0.13.0 — parallel batch rendering

16 Jun 06:13
21d55b5

Choose a tag to compare

Render whole tutorial sets in parallel, plus authoring-loop fixes.

✨ Parallel batch rendering (#62)

Rendering a set of tutorials is now much faster. The record phase is mostly real-time waiting (each step holds for its narration), so the machine sits near-idle during it — running renders concurrently reclaims that.

  • Opt in with --render-concurrency <n> (or renderConcurrency in forge.config.ts). Default is 1 (serial — unchanged).
  • Measured ~1.95× on a 2-job batch, with byte-identical output (concurrency changes only when jobs run).
  • Requires a parallel-safe adapter — concurrent renders each run their own setup/teardown, so a shared seed DB must isolate per render. See Adapters → Parallel rendering. If unsure, leave it at 1.

🛠 Authoring-loop fixes

  • render --phase record works without a prior tts phase (#50) — the TTS-free --contact-sheet framing check (verify selectors/framing across a whole tutorial before paying for narration) no longer errors on a fresh work dir.
  • Recap-framing lint nags less (#49) — it no longer false-warns on accomplishment-style recaps ("you created an event, set up ticketing… from here you can…").

⚠️ Upgrade notes

Minor release — additive only (new --render-concurrency flag, renderConcurrency config, and exports mapLimit / silentTTSResult / loadTTSResultIfPresent); no breaking changes. As a 0.x minor, a ^0.12.x range won't auto-pull it:

pnpm up tutorial-forge@latest tutorial-forge-cli@latest

Source-available under the PolyForm Small Business License — free for individuals and small businesses.

Full changelog: https://github.com/jbrecht/tutorial-forge/blob/main/CHANGELOG.md

v0.12.0 — chapters that work on YouTube & Vimeo

15 Jun 19:58
450e27e

Choose a tag to compare

Chapters that you can actually use where you publish. Plus a release-stability fix.

✨ Chapters now activate on YouTube & Vimeo (#52)

Every render already emitted chapter markers — but each platform has activation rules our output could silently trip, so chapters quietly failed on the two places people actually upload tutorials. This release makes the emitted artifacts respect those rules:

  • YouTube — paste <id>.chapters.txt into your video description. YouTube ignores the entire chapter list if any single chapter is under 10 seconds, so short steps used to disable chapters with no warning. The .txt now folds any sub-10s chapter into its neighbor, so the list stays valid. (YouTube also needs ≥3 chapters and a 0:00 start, both handled.)
  • Vimeo — upload <id>.chapters.vtt under Settings → Interactive → Chapters. Chapter titles are now capped at 50 characters (Vimeo's limit) so they aren't rejected or truncated.
  • The MP4 chapter track and .chapters.vtt keep the full per-step list for desktop/web players, which have no minimum-chapter rule.

New docs walk through getting chapters onto each platform. New exported helpers for advanced use: enforceMinChapterDuration, YOUTUBE_MIN_CHAPTER_MS.

🐛 Fixes

  • Stabilized the calibration-flash check (#54). A flaky internal test assertion misread a valid frame-0 flash detection as a failure. Test-only; rendering was always correct.

⚠️ Notes for upgraders

  • This is a minor release because of the two new exports and one behavior change: the default chapter-title cap dropped from 60 → 50 characters. If you relied on 60-char titles, longer first sentences will now truncate at 50. Override with chapters: { maxTitleChars } if needed.
  • Because it's a minor, a ^0.11.x version range will not pick this up automatically — upgrade deliberately:
pnpm up tutorial-forge@latest tutorial-forge-cli@latest

Tutorial Forge is source-available under the PolyForm Small Business License — free for individuals and small businesses.

Full changelog: https://github.com/jbrecht/tutorial-forge/blob/main/CHANGELOG.md

v0.11.0 — teaching-first rendering

15 Jun 01:59
3e6a38c

Choose a tag to compare

A pedagogy-focused release: the engine already nailed the mechanics (narration-first pacing, signaling, coherence), so this round makes the tutorials it produces teach better, acting on the instructional-designer review. All three additions are additive and opt-in/presence-driven — existing tutorials and adapters render unchanged, and no public API was removed or changed. Update both packages in lockstep.

Highlights

  • Chapters / segmenting (#35). Every render now emits chapter markers derived from the per-step timeline: an MP4 chapter track (QuickTime/VLC/most players), a <id>.chapters.vtt sidecar for web players, and a <id>.chapters.txt YouTube-style timestamp list. One chapter per narrated step (silent steps fold into the prior chapter); the title is the first sentence of the step's narration. On by default; disable with --no-chapters or chapters: false.

  • Teaching-narration guidance + germane lints (#36). New "Writing narration that teaches" guide, backed by advisory load-time lints in tutorial() that warn (never fail) on narration that demonstrates without teaching: over-long narration per step (on by default, threshold via lint.maxNarrationWords), plus strict-mode heuristics for steps bundling multiple instrumented actions and a missing objective/recap. Suppress per step with step({ lint: false }) or globally with tutorial(..., { lint: false }). New LintOptions type.

  • Objective + recap cards (#37). Declare objectives?: string[] and summary?: string and get an intro title/objective card before the first step and a recap card after the last (advance-organizer + summary principles). Card durations fold into the timeline — subtitles, chapter markers, and GIF excerpts stay aligned, and the chapter track gains Objectives/Recap entries. Visual-only, localizable via the reserved __objectives__ / __summary__ translation keys, and suppressible with --no-cards or cards: false. New CardContent type and card helpers.

Install

pnpm up tutorial-forge@0.11.0 tutorial-forge-cli@0.11.0

Full changelog: https://github.com/jbrecht/tutorial-forge/blob/main/CHANGELOG.md

v0.10.0 — teardown safety & ctx.state

13 Jun 22:41
3d313d0

Choose a tag to compare

Second umami dogfooding pass (on 0.9.0) surfaced a setup/teardown lifecycle cluster plus the ergonomic gap per-tutorial setup opened. Verified green on the umami consumer (before/after TEST-DB row counts on every leak-sensitive path). Additive for normal use — existing tutorials and adapters render unchanged. One type-only caveat: StepContext gains a required state field, so a hand-constructed StepContext literal (e.g. a test harness) now needs it; code that merely receives ctx is unaffected.

Bug fixes (teardown coverage)

  • #15adapter.setup/tutorial.setup failures now run the full teardown chain before rethrowing, instead of leaking seeded data.
  • #16preview runs the full teardown chain on every exit path, not just step thunks — no more orphaned adapter seed on the run-repeatedly iterate tool.
  • #20 — a failed render with --contact-sheet emits a partial sheet (completed steps + the failure frame).
  • #21onTeardown return type widened to () => unknown | Promise<unknown>.

Additive API

  • #17 — typed ctx.state: adapter.setup's return value lands on ctx.state, read by tutorial.setup and steps. Replaces the module-global + ! handoff with a per-render, parallel-safe bag, typed end-to-end via TutorialAdapter<S> / tutorial<S> / step<S>.
  • #19tutorial-forge doctor --setup (and probeAdapterSetup) actually run adapter.setup and tear it down, catching the reachable-but-wrong-database failure class. Off by default.

Docs

  • #18 (docs) — the "Settling" guide warns that networkidle races React startTransition-deferred Server Actions and steers to waitFor on committed UI. (Runtime settle variant tracked in #24.)
  • #23ctx.state section + teardown-coverage matrix in the adapters guide.

Full notes: CHANGELOG

v0.8.0 — screencast recorder

11 Jun 22:59

Choose a tag to compare

New: --recorder screencast captures via CDP with explicit per-frame timestamps. The assembled video starts exactly at the recording clock's zero — the calibration-flash mechanism (source of two past bugs) is skipped entirely for screencast renders, and frames arrive only when content changes. recordVideo remains the default; both live behind a new Recorder interface. Honest scope note: Chromium delivers screencast frames at CSS-viewport size regardless of deviceScaleFactor, so high-DPI capture (the issue's other goal) is not achievable this way — documented on #5. Closes #5.

v0.7.0 — GIF export

11 Jun 22:29

Choose a tag to compare

New: --gif writes an optimized animated GIF next to the MP4 — narration captions burned in (GIFs are silent), two-pass palette, configurable width/fps. --gif-steps open-modal..create-event excerpts a step range, resolved from the timing manifest (and remapped through --idle-speedup when active). The attached GIF was produced by exactly that command against the example app. Closes #4.

v0.6.0 — burned-in captions everywhere

11 Jun 22:15

Choose a tag to compare

subtitles: 'burn' now works on every ffmpeg build. Instead of libass (which Homebrew's ffmpeg 8 no longer ships), each cue is rendered as a transparent caption pill by the same browser that records the tutorial, then composited with ffmpeg's built-in overlay filter — identical rendering on every machine, styled with CSS (captionStyle: { fontSizePx, maxWidthPx, bottomMarginPx }). Captions composite after scale and zoom, so --zoom never distorts them, and cue timing shares the same map as SRT/localization/idle-speedup. Closes #7.

v0.5.0 — idle speed-up

11 Jun 22:03

Choose a tag to compare

New: --idle-speedup (or idleSpeedup: true | { maxIdleMs, speed }) fast-forwards the boring parts — spinners, slow loads, long silent waits — at 3x by default. The design guarantee: narration playback and click choreography always play at 1x; only narration-free spans longer than maxIdleMs compress, with gentle 1x margins at each edge. Audio offsets, subtitle cues, and --zoom windows all remap through the same time map, in the same single ffmpeg pass. In the e2e suite a tutorial with a 4s silent wait renders 10.5s → 7.5s with narration still in sync. Closes #3.

v0.4.0 — failure diagnostics

11 Jun 21:47

Choose a tag to compare

Real apps fail in interesting ways; now the pipeline tells you how. Every step failure throws a StepError with artifact paths: a screenshot at failure and the recent browser console/pageerror/failed-request log. Run with --debug for the full treatment: a Playwright trace (npx playwright show-trace trace.zip), the complete console log, and before/after screenshots per step — work dir always kept. doctor now also flags ffmpeg builds without libass (subtitles: 'burn' needs it; Homebrew's ffmpeg 8 dropped it), and burn mode fails fast with a clear message instead of a cryptic filter error. Closes #6.

v0.3.0 — zoom-on-callout

11 Jun 21:06

Choose a tag to compare

New: --zoom (or zoom: true | { factor } in config) smoothly zooms toward each click target and back out — the camera leads the click by a beat, holds through what the click reveals, then releases. Composited in the post phase from callout boxes already in the timing manifest, so it adds nothing to recording time and is fully deterministic. Default factor 1.35.

The attached video is the example tutorial rendered with --zoom. Closes #2.