From e85a3919250943659068a7a0d1c6c760d9787393 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Mon, 19 Oct 2026 00:18:32 +1100 Subject: [PATCH] Narrate a film for every docs page, and fix the bugs filming found Every docs page with a feature to show now opens on a narrated walkthrough of the real extension, captioned in English, Japanese and Chinese: getting started, editor setup, configuration, F# language support, completions, hover, navigation, diagnostics, refactoring, context menus, NuGet, the profiler (a C# tour), memory leaks (now narrated, on the layered retention graph), architecture and contributing. The language films run on demos/Shop.slnx, C# and F# in one solution, and show F# first. Bugs the cameras found, each fixed test-first: - F#: hand-built project options put every source file on the FCS command line as well as in SourceFiles, so FCS compiled each file twice and every F# project showed FS3551 warnings. - Sort Members kept each member's own blank lines and added its own, leaving two or three blank lines between members. - NuGet: the version picker beside Install showed the newest listed version, often a prerelease, not the version Install sends. The film kit gains language acts (completion, quick fixes, rename, navigation, the Solution Explorer), a gliding pointer, retried takes, logs kept from a failed shoot, and a neutral environment: staging, temp files and a cloned .NET install under /tmp, with the login shell's environment off, so no home path reaches a frame. --- docs/specs/DESIGN-SYSTEM.md | 6 +- src/editors/vscode/src/nuget-browser/html.ts | 5 +- .../src/test/suite/nuget-browser.test.ts | 27 ++ src/sharplsp/src/sort_members.rs | 128 ++++--- .../tests/e2e_modules/sort_members_extra.rs | 35 ++ .../FSharpNavigationEdgeTests.fs | 33 ++ .../FSharpProjectCrackingTests.fs | 24 ++ .../FSharpProjectLoading.fs | 12 +- src/website/src/_data/videos.json | 319 +++++++++++++++++- .../assets/videos/architecture-poster.webp | Bin 0 -> 139610 bytes .../src/assets/videos/architecture.en.vtt | 8 + .../src/assets/videos/architecture.ja.vtt | 8 + .../src/assets/videos/architecture.mp4 | 3 + .../src/assets/videos/architecture.webm | 3 + .../src/assets/videos/architecture.zh.vtt | 8 + .../videos/code-completions-poster.webp | Bin 0 -> 123910 bytes .../src/assets/videos/code-completions.en.vtt | 11 + .../src/assets/videos/code-completions.ja.vtt | 11 + .../src/assets/videos/code-completions.mp4 | 3 + .../src/assets/videos/code-completions.webm | 3 + .../src/assets/videos/code-completions.zh.vtt | 11 + .../assets/videos/configuration-poster.webp | Bin 0 -> 49716 bytes .../src/assets/videos/configuration.en.vtt | 14 + .../src/assets/videos/configuration.ja.vtt | 14 + .../src/assets/videos/configuration.mp4 | 3 + .../src/assets/videos/configuration.webm | 3 + .../src/assets/videos/configuration.zh.vtt | 14 + .../assets/videos/context-menus-poster.webp | Bin 0 -> 57848 bytes .../src/assets/videos/context-menus.en.vtt | 14 + .../src/assets/videos/context-menus.ja.vtt | 14 + .../src/assets/videos/context-menus.mp4 | 3 + .../src/assets/videos/context-menus.webm | 3 + .../src/assets/videos/context-menus.zh.vtt | 14 + .../assets/videos/contributing-poster.webp | Bin 0 -> 38010 bytes .../src/assets/videos/contributing.en.vtt | 11 + .../src/assets/videos/contributing.ja.vtt | 11 + .../src/assets/videos/contributing.mp4 | 3 + .../src/assets/videos/contributing.webm | 3 + .../src/assets/videos/contributing.zh.vtt | 11 + .../src/assets/videos/diagnostics-poster.webp | Bin 0 -> 123032 bytes .../src/assets/videos/diagnostics.en.vtt | 14 + .../src/assets/videos/diagnostics.ja.vtt | 14 + src/website/src/assets/videos/diagnostics.mp4 | 3 + .../src/assets/videos/diagnostics.webm | 3 + .../src/assets/videos/diagnostics.zh.vtt | 14 + .../assets/videos/editor-setup-poster.webp | Bin 0 -> 70870 bytes .../src/assets/videos/editor-setup.en.vtt | 11 + .../src/assets/videos/editor-setup.ja.vtt | 11 + .../src/assets/videos/editor-setup.mp4 | 3 + .../src/assets/videos/editor-setup.webm | 3 + .../src/assets/videos/editor-setup.zh.vtt | 11 + .../assets/videos/fsharp-language-poster.webp | Bin 0 -> 129968 bytes .../src/assets/videos/fsharp-language.en.vtt | 23 ++ .../src/assets/videos/fsharp-language.ja.vtt | 23 ++ .../src/assets/videos/fsharp-language.mp4 | 3 + .../src/assets/videos/fsharp-language.webm | 3 + .../src/assets/videos/fsharp-language.zh.vtt | 23 ++ .../assets/videos/getting-started-poster.webp | Bin 0 -> 138188 bytes .../src/assets/videos/getting-started.en.vtt | 14 + .../src/assets/videos/getting-started.ja.vtt | 14 + .../src/assets/videos/getting-started.mp4 | 3 + .../src/assets/videos/getting-started.webm | 3 + .../src/assets/videos/getting-started.zh.vtt | 14 + .../src/assets/videos/hover-poster.webp | Bin 0 -> 69184 bytes src/website/src/assets/videos/hover.en.vtt | 14 + src/website/src/assets/videos/hover.ja.vtt | 14 + src/website/src/assets/videos/hover.mp4 | 3 + src/website/src/assets/videos/hover.webm | 3 + src/website/src/assets/videos/hover.zh.vtt | 14 + .../src/assets/videos/navigation-poster.webp | Bin 0 -> 151056 bytes .../src/assets/videos/navigation.en.vtt | 14 + .../src/assets/videos/navigation.ja.vtt | 14 + src/website/src/assets/videos/navigation.mp4 | 3 + src/website/src/assets/videos/navigation.webm | 3 + .../src/assets/videos/navigation.zh.vtt | 14 + .../src/assets/videos/nuget-poster.webp | Bin 0 -> 99164 bytes src/website/src/assets/videos/nuget.en.vtt | 14 + src/website/src/assets/videos/nuget.ja.vtt | 14 + src/website/src/assets/videos/nuget.mp4 | 3 + src/website/src/assets/videos/nuget.webm | 3 + src/website/src/assets/videos/nuget.zh.vtt | 14 + .../assets/videos/profiler-csharp-poster.webp | Bin 0 -> 223336 bytes .../src/assets/videos/profiler-csharp.en.vtt | 32 ++ .../src/assets/videos/profiler-csharp.ja.vtt | 32 ++ .../src/assets/videos/profiler-csharp.mp4 | 3 + .../src/assets/videos/profiler-csharp.webm | 3 + .../src/assets/videos/profiler-csharp.zh.vtt | 32 ++ .../profiler-fix-memory-leak-poster.webp | Bin 160582 -> 75592 bytes .../videos/profiler-fix-memory-leak.en.vtt | 26 ++ .../videos/profiler-fix-memory-leak.ja.vtt | 26 ++ .../videos/profiler-fix-memory-leak.mp4 | 4 +- .../videos/profiler-fix-memory-leak.webm | 4 +- .../videos/profiler-fix-memory-leak.zh.vtt | 26 ++ .../src/assets/videos/refactoring-poster.webp | Bin 0 -> 122442 bytes .../src/assets/videos/refactoring.en.vtt | 14 + .../src/assets/videos/refactoring.ja.vtt | 14 + src/website/src/assets/videos/refactoring.mp4 | 3 + .../src/assets/videos/refactoring.webm | 3 + .../src/assets/videos/refactoring.zh.vtt | 14 + src/website/src/docs/architecture.md | 4 +- src/website/src/docs/completions.md | 2 + src/website/src/docs/configuration.md | 2 + src/website/src/docs/context-menus.md | 2 + src/website/src/docs/contributing.md | 2 + src/website/src/docs/diagnostics.md | 2 + src/website/src/docs/editors.md | 2 + src/website/src/docs/fsharp.md | 2 + src/website/src/docs/go-to-definition.md | 2 + src/website/src/docs/hover.md | 2 + src/website/src/docs/index.md | 2 +- src/website/src/docs/nuget.md | 2 + src/website/src/docs/profiler.md | 2 +- src/website/src/docs/refactoring.md | 2 + src/website/src/ja/docs/architecture.md | 4 +- src/website/src/ja/docs/completions.md | 2 + src/website/src/ja/docs/configuration.md | 2 + src/website/src/ja/docs/context-menus.md | 2 + src/website/src/ja/docs/contributing.md | 2 + src/website/src/ja/docs/diagnostics.md | 2 + src/website/src/ja/docs/editors.md | 2 + src/website/src/ja/docs/fsharp.md | 2 + src/website/src/ja/docs/go-to-definition.md | 2 + src/website/src/ja/docs/hover.md | 2 + src/website/src/ja/docs/index.md | 2 +- src/website/src/ja/docs/nuget.md | 2 + src/website/src/ja/docs/profiler.md | 2 +- src/website/src/ja/docs/refactoring.md | 2 + src/website/src/zh/docs/architecture.md | 4 +- src/website/src/zh/docs/completions.md | 2 + src/website/src/zh/docs/configuration.md | 2 + src/website/src/zh/docs/context-menus.md | 2 + src/website/src/zh/docs/contributing.md | 2 + src/website/src/zh/docs/diagnostics.md | 2 + src/website/src/zh/docs/editors.md | 2 + src/website/src/zh/docs/fsharp.md | 2 + src/website/src/zh/docs/go-to-definition.md | 2 + src/website/src/zh/docs/hover.md | 2 + src/website/src/zh/docs/index.md | 2 +- src/website/src/zh/docs/nuget.md | 2 + src/website/src/zh/docs/profiler.md | 2 +- src/website/src/zh/docs/refactoring.md | 2 + src/website/tests/profiler-media.spec.js | 4 +- src/website/tests/site-videos.spec.js | 55 +-- src/website/tools/profiler-film/README.md | 61 ++-- .../tools/profiler-film/demos/Shop.slnx | 4 + .../profiler-film/demos/storefront/Catalog.cs | 2 + src/website/tools/profiler-film/film.mjs | 2 +- .../profiler-film/films/architecture.mjs | 53 +++ .../films/architecture.narration.json | 19 ++ .../tools/profiler-film/films/completions.mjs | 41 +++ .../films/completions.narration.json | 25 ++ .../profiler-film/films/configuration.mjs | 86 +++++ .../films/configuration.narration.json | 31 ++ .../profiler-film/films/context-menus.mjs | 44 +++ .../films/context-menus.narration.json | 31 ++ .../profiler-film/films/contributing.mjs | 80 +++++ .../films/contributing.narration.json | 37 ++ .../tools/profiler-film/films/csharp.mjs | 27 ++ .../profiler-film/films/csharp.narration.json | 76 +++++ .../tools/profiler-film/films/diagnostics.mjs | 58 ++++ .../films/diagnostics.narration.json | 39 +++ .../profiler-film/films/editor-setup.mjs | 53 +++ .../films/editor-setup.narration.json | 25 ++ .../profiler-film/films/fsharp-language.mjs | 46 +++ .../films/fsharp-language.narration.json | 49 +++ .../tools/profiler-film/films/fsharp.mjs | 72 ++-- .../profiler-film/films/getting-started.mjs | 49 +++ .../films/getting-started.narration.json | 31 ++ .../tools/profiler-film/films/hover.mjs | 40 +++ .../profiler-film/films/hover.narration.json | 41 +++ .../tools/profiler-film/films/leak.mjs | 64 ++-- .../profiler-film/films/leak.narration.json | 55 +++ .../tools/profiler-film/films/navigation.mjs | 34 ++ .../films/navigation.narration.json | 41 +++ .../tools/profiler-film/films/nuget.mjs | 62 ++++ .../profiler-film/films/nuget.narration.json | 31 ++ .../tools/profiler-film/films/refactoring.mjs | 36 ++ .../films/refactoring.narration.json | 31 ++ .../tools/profiler-film/kit/acts/explorer.mjs | 33 ++ .../tools/profiler-film/kit/acts/language.mjs | 210 ++++++++++++ .../tools/profiler-film/kit/acts/shop.mjs | 62 ++++ .../tools/profiler-film/kit/acts/tour.mjs | 16 + .../tools/profiler-film/kit/camera.mjs | 11 +- src/website/tools/profiler-film/kit/code.mjs | 76 +++++ .../tools/profiler-film/kit/editor.mjs | 52 ++- .../tools/profiler-film/kit/overlay.mjs | 36 +- .../tools/profiler-film/kit/scenes.mjs | 10 +- src/website/tools/profiler-film/kit/set.mjs | 62 +++- .../tools/profiler-film/kit/sound/voice.mjs | 3 +- .../tools/profiler-film/kit/workbench.mjs | 13 +- 190 files changed, 3362 insertions(+), 230 deletions(-) create mode 100644 src/website/src/assets/videos/architecture-poster.webp create mode 100644 src/website/src/assets/videos/architecture.en.vtt create mode 100644 src/website/src/assets/videos/architecture.ja.vtt create mode 100644 src/website/src/assets/videos/architecture.mp4 create mode 100644 src/website/src/assets/videos/architecture.webm create mode 100644 src/website/src/assets/videos/architecture.zh.vtt create mode 100644 src/website/src/assets/videos/code-completions-poster.webp create mode 100644 src/website/src/assets/videos/code-completions.en.vtt create mode 100644 src/website/src/assets/videos/code-completions.ja.vtt create mode 100644 src/website/src/assets/videos/code-completions.mp4 create mode 100644 src/website/src/assets/videos/code-completions.webm create mode 100644 src/website/src/assets/videos/code-completions.zh.vtt create mode 100644 src/website/src/assets/videos/configuration-poster.webp create mode 100644 src/website/src/assets/videos/configuration.en.vtt create mode 100644 src/website/src/assets/videos/configuration.ja.vtt create mode 100644 src/website/src/assets/videos/configuration.mp4 create mode 100644 src/website/src/assets/videos/configuration.webm create mode 100644 src/website/src/assets/videos/configuration.zh.vtt create mode 100644 src/website/src/assets/videos/context-menus-poster.webp create mode 100644 src/website/src/assets/videos/context-menus.en.vtt create mode 100644 src/website/src/assets/videos/context-menus.ja.vtt create mode 100644 src/website/src/assets/videos/context-menus.mp4 create mode 100644 src/website/src/assets/videos/context-menus.webm create mode 100644 src/website/src/assets/videos/context-menus.zh.vtt create mode 100644 src/website/src/assets/videos/contributing-poster.webp create mode 100644 src/website/src/assets/videos/contributing.en.vtt create mode 100644 src/website/src/assets/videos/contributing.ja.vtt create mode 100644 src/website/src/assets/videos/contributing.mp4 create mode 100644 src/website/src/assets/videos/contributing.webm create mode 100644 src/website/src/assets/videos/contributing.zh.vtt create mode 100644 src/website/src/assets/videos/diagnostics-poster.webp create mode 100644 src/website/src/assets/videos/diagnostics.en.vtt create mode 100644 src/website/src/assets/videos/diagnostics.ja.vtt create mode 100644 src/website/src/assets/videos/diagnostics.mp4 create mode 100644 src/website/src/assets/videos/diagnostics.webm create mode 100644 src/website/src/assets/videos/diagnostics.zh.vtt create mode 100644 src/website/src/assets/videos/editor-setup-poster.webp create mode 100644 src/website/src/assets/videos/editor-setup.en.vtt create mode 100644 src/website/src/assets/videos/editor-setup.ja.vtt create mode 100644 src/website/src/assets/videos/editor-setup.mp4 create mode 100644 src/website/src/assets/videos/editor-setup.webm create mode 100644 src/website/src/assets/videos/editor-setup.zh.vtt create mode 100644 src/website/src/assets/videos/fsharp-language-poster.webp create mode 100644 src/website/src/assets/videos/fsharp-language.en.vtt create mode 100644 src/website/src/assets/videos/fsharp-language.ja.vtt create mode 100644 src/website/src/assets/videos/fsharp-language.mp4 create mode 100644 src/website/src/assets/videos/fsharp-language.webm create mode 100644 src/website/src/assets/videos/fsharp-language.zh.vtt create mode 100644 src/website/src/assets/videos/getting-started-poster.webp create mode 100644 src/website/src/assets/videos/getting-started.en.vtt create mode 100644 src/website/src/assets/videos/getting-started.ja.vtt create mode 100644 src/website/src/assets/videos/getting-started.mp4 create mode 100644 src/website/src/assets/videos/getting-started.webm create mode 100644 src/website/src/assets/videos/getting-started.zh.vtt create mode 100644 src/website/src/assets/videos/hover-poster.webp create mode 100644 src/website/src/assets/videos/hover.en.vtt create mode 100644 src/website/src/assets/videos/hover.ja.vtt create mode 100644 src/website/src/assets/videos/hover.mp4 create mode 100644 src/website/src/assets/videos/hover.webm create mode 100644 src/website/src/assets/videos/hover.zh.vtt create mode 100644 src/website/src/assets/videos/navigation-poster.webp create mode 100644 src/website/src/assets/videos/navigation.en.vtt create mode 100644 src/website/src/assets/videos/navigation.ja.vtt create mode 100644 src/website/src/assets/videos/navigation.mp4 create mode 100644 src/website/src/assets/videos/navigation.webm create mode 100644 src/website/src/assets/videos/navigation.zh.vtt create mode 100644 src/website/src/assets/videos/nuget-poster.webp create mode 100644 src/website/src/assets/videos/nuget.en.vtt create mode 100644 src/website/src/assets/videos/nuget.ja.vtt create mode 100644 src/website/src/assets/videos/nuget.mp4 create mode 100644 src/website/src/assets/videos/nuget.webm create mode 100644 src/website/src/assets/videos/nuget.zh.vtt create mode 100644 src/website/src/assets/videos/profiler-csharp-poster.webp create mode 100644 src/website/src/assets/videos/profiler-csharp.en.vtt create mode 100644 src/website/src/assets/videos/profiler-csharp.ja.vtt create mode 100644 src/website/src/assets/videos/profiler-csharp.mp4 create mode 100644 src/website/src/assets/videos/profiler-csharp.webm create mode 100644 src/website/src/assets/videos/profiler-csharp.zh.vtt create mode 100644 src/website/src/assets/videos/profiler-fix-memory-leak.en.vtt create mode 100644 src/website/src/assets/videos/profiler-fix-memory-leak.ja.vtt create mode 100644 src/website/src/assets/videos/profiler-fix-memory-leak.zh.vtt create mode 100644 src/website/src/assets/videos/refactoring-poster.webp create mode 100644 src/website/src/assets/videos/refactoring.en.vtt create mode 100644 src/website/src/assets/videos/refactoring.ja.vtt create mode 100644 src/website/src/assets/videos/refactoring.mp4 create mode 100644 src/website/src/assets/videos/refactoring.webm create mode 100644 src/website/src/assets/videos/refactoring.zh.vtt create mode 100644 src/website/tools/profiler-film/demos/Shop.slnx create mode 100644 src/website/tools/profiler-film/films/architecture.mjs create mode 100644 src/website/tools/profiler-film/films/architecture.narration.json create mode 100644 src/website/tools/profiler-film/films/completions.mjs create mode 100644 src/website/tools/profiler-film/films/completions.narration.json create mode 100644 src/website/tools/profiler-film/films/configuration.mjs create mode 100644 src/website/tools/profiler-film/films/configuration.narration.json create mode 100644 src/website/tools/profiler-film/films/context-menus.mjs create mode 100644 src/website/tools/profiler-film/films/context-menus.narration.json create mode 100644 src/website/tools/profiler-film/films/contributing.mjs create mode 100644 src/website/tools/profiler-film/films/contributing.narration.json create mode 100644 src/website/tools/profiler-film/films/csharp.mjs create mode 100644 src/website/tools/profiler-film/films/csharp.narration.json create mode 100644 src/website/tools/profiler-film/films/diagnostics.mjs create mode 100644 src/website/tools/profiler-film/films/diagnostics.narration.json create mode 100644 src/website/tools/profiler-film/films/editor-setup.mjs create mode 100644 src/website/tools/profiler-film/films/editor-setup.narration.json create mode 100644 src/website/tools/profiler-film/films/fsharp-language.mjs create mode 100644 src/website/tools/profiler-film/films/fsharp-language.narration.json create mode 100644 src/website/tools/profiler-film/films/getting-started.mjs create mode 100644 src/website/tools/profiler-film/films/getting-started.narration.json create mode 100644 src/website/tools/profiler-film/films/hover.mjs create mode 100644 src/website/tools/profiler-film/films/hover.narration.json create mode 100644 src/website/tools/profiler-film/films/leak.narration.json create mode 100644 src/website/tools/profiler-film/films/navigation.mjs create mode 100644 src/website/tools/profiler-film/films/navigation.narration.json create mode 100644 src/website/tools/profiler-film/films/nuget.mjs create mode 100644 src/website/tools/profiler-film/films/nuget.narration.json create mode 100644 src/website/tools/profiler-film/films/refactoring.mjs create mode 100644 src/website/tools/profiler-film/films/refactoring.narration.json create mode 100644 src/website/tools/profiler-film/kit/acts/explorer.mjs create mode 100644 src/website/tools/profiler-film/kit/acts/language.mjs create mode 100644 src/website/tools/profiler-film/kit/acts/shop.mjs create mode 100644 src/website/tools/profiler-film/kit/acts/tour.mjs create mode 100644 src/website/tools/profiler-film/kit/code.mjs diff --git a/docs/specs/DESIGN-SYSTEM.md b/docs/specs/DESIGN-SYSTEM.md index 37697c05..843b1b03 100644 --- a/docs/specs/DESIGN-SYSTEM.md +++ b/docs/specs/DESIGN-SYSTEM.md @@ -126,9 +126,9 @@ Page-level grids and marketing card styles must not leak into `.prose`. Do not r Screenshots and video of a SharpLsp feature show the real extension, never a mock-up. -- **Capture.** The profiler media is shot by `src/website/tools/profiler-film/` (its README says how). Every shot runs on `kit/set.mjs`'s `onSet`: an isolated VS Code (its own user-data and extensions directories, never the user's editor) running the packaged VSIX against a real .NET demo app from `demos/`. A walkthrough is one file in `films/`, a list of scenes that `film.mjs` shoots as real-time clips and publishes; `kit/acts/` tells the CPU and memory stories, so each film supplies only its words, its demo and its fix. `films/leak.mjs` films the C# Storefront demo and `films/fsharp.mjs` its F# twin, FsStorefront, so every F# claim the docs make about the profiler is shown on F# code. The composited one-minute film is `films/profiler/`: `capture.mjs` records stills and screencasts, and `render.mjs` turns them into the site's assets. +- **Capture.** Product films are shot by `src/website/tools/profiler-film/` (its README says how). Every shot runs on `kit/set.mjs`'s `onSet`: an isolated VS Code (its own user-data and extensions directories, never the user's editor) running the packaged VSIX against a real .NET demo from `demos/`. A walkthrough is one file in `films/`, a list of scenes that `film.mjs` shoots as real-time clips and publishes; `kit/acts/` tells the shared stories (the CPU and memory loops, the language features, the Solution Explorer), so each film supplies only its words, its demo and its edits. The profiler films run on the C# Storefront demo and its F# twin, FsStorefront; the language films run on `Shop.slnx`, both in one solution, and show F# before C#. Every F# claim the docs make is shown on F# code. The composited one-minute film is `films/profiler/`: `capture.mjs` records stills and screencasts, and `render.mjs` turns them into the site's assets. - **Honesty.** Footage plays in real time unless the frame says otherwise: every fast-forwarded stretch carries an on-screen speed marker, and captions name time-lapses. Motion graphics (title cards, camera moves, highlights, the cursor) frame the footage but never alter what the editor shows. -- **Privacy.** No frame may show a user name, home directory, hostname or terminal. The demo app runs from a neutral temporary path and the terminal is never opened. +- **Privacy.** No frame may show a user name, home directory or hostname. The demo runs from a neutral temporary path, and the editor runs on a copy-on-write clone of the .NET install under that path, with its login-shell environment off, so every path it prints is neutral. A terminal appears only where the feature runs in one (F# Interactive, a build task, a contributor's command line), and a shell terminal starts with no profile and a bare `$ ` prompt. - **Formats.** - Films ship as H.264 MP4 and VP9 WebM with a WebP poster. - Stills ship as WebP, 2400px wide, with `width` and `height` set. @@ -137,7 +137,7 @@ Screenshots and video of a SharpLsp feature show the real extension, never a moc - Every video ships a WebP poster, the films and the recordings alike. - **Narration.** A walkthrough may be narrated (`kit/sound/`). Its reviewed script (`films/.narration.json`) is spoken by Kokoro-82M through `kokoro-js` on the capturing machine, never by a speech service or the operating system's voices, whose licences forbid publishing. Each line starts just after its scene's cut, and a scene holds its last frame until its line ends. The music is an original score synthesised from code (`kit/sound/score.mjs`), so no film carries licensed or sampled music, and it ducks under the voice. A narrated film ships AAC in its MP4 and Opus in its WebM, plays unmuted with controls, and carries WebVTT captions in every site language (`..vtt`, the page's own language shown by default), listed under `captions` in the catalogue. - **One catalogue.** Every video on the site is declared once in `src/website/src/_data/videos.json`: encodings, size, length, upload date, and its label and caption in each language. Pages place a video with the `{% video "" %}` shortcode (`src/website/video-figure.js`) and never repeat `