@vestra-player/sdk 1.6.16 → 1.6.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/commands.d.ts +5 -5
  3. package/dist/commands.js +117 -139
  4. package/dist/docs-tree.json.gz +0 -0
  5. package/dist/plugin/runtime.d.ts +26 -0
  6. package/dist/plugin/runtime.js +16 -0
  7. package/dist/plugin/testing/commands.d.ts +65 -0
  8. package/dist/plugin/testing/commands.js +412 -0
  9. package/dist/plugin/testing/core-actions.d.ts +39 -0
  10. package/dist/plugin/testing/core-actions.js +371 -0
  11. package/dist/plugin/testing/declarations.d.ts +37 -0
  12. package/dist/plugin/testing/declarations.js +80 -0
  13. package/dist/plugin/testing/i18n.d.ts +28 -0
  14. package/dist/plugin/testing/i18n.js +107 -0
  15. package/dist/plugin/testing/index.d.ts +3140 -0
  16. package/dist/plugin/testing/index.js +2796 -0
  17. package/dist/plugin/testing/internals.d.ts +76 -0
  18. package/dist/plugin/testing/internals.js +60 -0
  19. package/dist/plugin/testing/jobs.d.ts +9 -0
  20. package/dist/plugin/testing/jobs.js +304 -0
  21. package/dist/plugin/testing/layout.d.ts +85 -0
  22. package/dist/plugin/testing/layout.js +622 -0
  23. package/dist/plugin/testing/media.d.ts +55 -0
  24. package/dist/plugin/testing/media.js +615 -0
  25. package/dist/plugin/testing/osd.d.ts +4 -0
  26. package/dist/plugin/testing/osd.js +57 -0
  27. package/dist/plugin/testing/processing.d.ts +56 -0
  28. package/dist/plugin/testing/processing.js +491 -0
  29. package/dist/plugin/testing/services.d.ts +50 -0
  30. package/dist/plugin/testing/services.js +466 -0
  31. package/dist/plugin/testing/transport.d.ts +29 -0
  32. package/dist/plugin/testing/transport.js +140 -0
  33. package/dist/plugin/testing.d.ts +1 -3140
  34. package/dist/plugin/testing.js +1 -6259
  35. package/llms.txt +1 -1
  36. package/package.json +5 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.6.18
4
+
5
+ **Who must change: nobody.** Characterisation tests for the SDK refactor (step 0 of the plan, `doc/specs/sdk-refactor.md` §4.0 in the player repository); no API change, no wire change, `apiVersion` stays `vestra/1`.
6
+
7
+ - **Golden wire traces** (`test/golden-traces.test.ts`): the ten examples, and the six built-ins named by the plan when `VESTRA_PLAYER` points at a player checkout, run one fixed script against `TestHost` (start, open a source, three state changes, one command, dispose); the `(method, params)` sequence, every `events.subscribe` line with its `mirror`/`why` marks (line numbers stripped) and the update-pass counts per phase are compared with `test/fixtures/traces/*.json`. Eight host scenarios (`host-*.json`) pin the test host's own edges: `disconnect()` with requests in flight, the `commands.keys` push after `commands.register` against `settle()`, `press`, `handle()`/`reset()`, a stream producer's temporary files, `startPlugin` with a `taken` conflict, the `VESTRA_PLUGIN_RESIDENCY=1` line and a host never started. `UPDATE_TRACES=1` rewrites them.
8
+ - **API surface snapshot** (`scripts/api-surface.mjs`, `npm run api-surface -- --check|--update`, `test/api-surface.test.ts`): every exported declaration and member of the `.` and `./testing` entries (the declaration files package.json `exports` names), sorted, in `test/fixtures/api-surface.txt`.
9
+ - **Pass counters**: `Runtime.counts` (internal, read-only: passes, full passes, safety passes, binding evaluations, view bind functions evaluated and skipped) for the SDK's own tests and benchmark; no behaviour change, nothing new on `Plugin` or `TestHost`.
10
+ - **Benchmark** (`npm run bench`, `scripts/bench.mjs`): a 20,000-row keyed list with 1 % and 10 % row changes through the diff engine, 1,000 passes over 50 bound views and 100 commands, and the state-change-to-send latency; medians of five runs, baseline in `docs/maintaining.md`.
11
+ - **`@vestra-player/sdk/testing` is split into modules** (step 1 of the refactor, batch A). **Who must change: nobody**: no API, behaviour or wire change. The single 7,600-line `src/plugin/testing.ts` is now the directory `src/plugin/testing/`: `index.ts` keeps the module page, every export and the one `TestHost` class with all its public members and doc comments; the class keeps its private state in one internal object (`internals.ts`) and delegates its wire handlers and longer methods to area modules (`transport`, `commands`, `declarations`, `core-actions`, `i18n`, `layout`, `osd`, `jobs`, `processing`, `media`, `services`). Error codes, messages and handler semantics are unchanged: every golden trace and host scenario, the API surface snapshot and the pass counts are identical, and the declarations (`dist/plugin/testing/index.d.ts`) equal the former `dist/plugin/testing.d.ts` apart from relative import paths. The `./testing` export now points at `dist/plugin/testing/index.js`; `dist/plugin/testing.js` stays as a re-export. The documentation tree (`dist/docs-tree.json.gz`) is unchanged.
12
+
13
+ ## 1.6.17
14
+
15
+ **Who must change: nobody.** Tables follow the slim context menu (B5): a data refresh of `BUILTIN_COMMANDS`, `BUILTIN_SUBMENUS` and the test host's core commands for the player's right-click menu (player 0.7.6); no API change, `apiVersion` stays `vestra/1`. A plugin whose tests compare a path or a key against the tables sees the player's current ones.
16
+
17
+ - **`BUILTIN_COMMANDS`**: the top level is now Open File…, Open ▸, Close — **Play/Pause** (`host/play-pause`, from Playback ▸), Audio Track ▸, Subtitle Track ▸, **Snapshot ▸** (`vestra.capture`, from Video ▸) — Picture…, Audio…, Subtitles…, Playback…, Playlist, **Chapter and Bookmark List…** (`vestra.marks`, from Playback ▸ Chapters and Bookmarks ▸) — Toggle Fullscreen — Vestra Player AI…, Plugin Store… — **Command Palette…** (`host/palette`, in the menu now, with an ellipsis; `Ctrl+Shift+P` unchanged), **Options…** (`vestra.preferences`, from Settings ▸), About Vestra Player… — Quit. Open ▸ gains **Search and Download Subtitles…** (`vestra.subtitle-online`, from Subtitles ▸ Online Subtitles ▸). Everything else left the menu for the command palette, with ids, keys and categories unchanged: the player's Previous and Next (PageUp, PageDown, the side mouse buttons), Volume +, Volume − and Mute (the Audio panel has their controls); the playback plugin's Reopen, Copy to Clipboard ▸, Disable Screen Saver and the seek-bar hover items; the window plugin's Hide Pointer and the whole Screen ▸ menu; Playback ▸ Playlist ▸; Resume Playback ▸; the marks plugin's bookmark, chapter and A-B Loop items; Online Subtitle Settings…; the visualizer's Show Album Cover, Show Logo When Idle, Choose Logo Image… and Reset ▸ Idle Logo; Keyboard Shortcuts…, Mouse… and Ignore Default Shortcuts; Appearance ▸ and Language ▸; Playback Info, Playback Properties… and Check for Updates…. The table in [Reference: commands](docs/reference/commands.md) follows, with the new top level, its groups and orders (Toggle Fullscreen 810, Command Palette… 829).
18
+ - **`BUILTIN_SUBMENUS`**: Open ▸ Recently Played, Audio Track, Subtitle Track and Snapshot (a top-level submenu now) remain; the Playback, Subtitles, Screen and Settings submenus and Copy to Clipboard are gone. The built-in menus (`@playback`, `@subtitles`, `@video`, `@audio`, `@screen`, `@settings`, `@reset`) stay valid for plugins at their fixed orders and are shown only while a command is in them.
19
+ - **`TestHost`**: the core commands carry the player's menu places: Play/Pause at the top level (`["Play/Pause"]`, order 201), Previous, Next, the volume steps and Mute outside the menu, and the seek, frame and speed actions outside the menu as the player has had them since 1.6.15 (the test host kept their Playback ▸ Seek and Speed paths until now). A plugin item at `["Playback", "Seek", …]`, `["@playback", "Speed"]` or `["Audio", …]` is therefore accepted, as the player accepts it (1.5.48 listed `Playback ▸ Speed` as an item and `Audio ▸ Mute ▸ …` as conflicts: they were, while the player's items were there); `["Play/Pause"]`, `["Close"]` and an item below either are conflicts.
20
+
3
21
  ## 1.6.16
4
22
 
5
23
  **Who must change: nobody.** A data refresh for the player's four built-in panels (player 0.7.6) and one additive read; no API is removed, `apiVersion` stays `vestra/1`.
@@ -17,11 +35,13 @@
17
35
  - **Wire** (additive): `ui.setMount`, `ui.interaction`, `ui.menu` and `ui.unmount` take an optional `slot` naming the panel (without it: the plugin's only panel; `ui.unmount` without it removes every panel); `ui.callback` and `ui.preparing` carry the panel's `slot`, and `ui.callback` keeps `view: "$mount"`. Older players ignore `slot`.
18
36
  - **`BUILTIN_COMMANDS`**: the player's seek, frame and speed actions (`host/seek-backward`, `seek-forward`, `frame-back`, `frame-forward`, `rate-down`, `rate-up`, `rate-normal`) leave Playback ▸ Seek and Speed for the command palette (player 0.7.6, with the playback plugin's panel); their keys and categories stay.
19
37
  - **`TestHost`**: `panels` (by slot, in mount order) and `mounted(slot?)`; without a slot `mounted()` returns the only panel and throws when there are several. `panel` stays, as the last panel mounted. The handlers take `slot` as the player does; `ui.interaction` stores the locks without it.
38
+
20
39
  ## 1.6.14
21
40
 
22
41
  **Who must change: nobody.** A data refresh: `BUILTIN_COMMANDS` lists the built-in capture plugin's commands as the player registers them since its capture tab; no API change, `apiVersion` stays `vestra/1`.
23
42
 
24
43
  - **`BUILTIN_COMMANDS`**: `vestra.capture` has **Video ▸ Snapshot ▸ Capture…** (Alt+S), which opens its sidebar tab; Save/Copy Rendered Frame, Save Current Frame As…, Export Tasks…, the capture formats, Snapshot Settings…, Snapshot Folder… and Open Snapshot Folder left the menu for the command palette, and new palette commands came: Export Clip Range (Ctrl+Shift+E), Save and Copy Video Frame Only, Show Last Capture in Folder, Copy Last Snapshot, Set Clip Start, Set Clip End and Export Last 30 Seconds (Ctrl+Shift+R). A plugin that checks a key or menu path against the table (`vestra-plugin check`, `TestHost`) sees the player's current ones.
44
+
25
45
  ## 1.6.13
26
46
 
27
47
  **Who must change: plugin tests that toggle a column in fullscreen.** In fullscreen `toggle()` now only switches the column on or off (user ruling 2026-11-08); a test that asserted `effective: true` after toggling a column on in fullscreen now sees `effective: false`, `reason: "fullscreen"` (on, revealed by the right edge). Plugin code needs no change; `apiVersion` stays `vestra/1`, and the wire gains one optional field.
@@ -37,6 +57,7 @@
37
57
  - **`Panel.toggle` and `PanelSlot.toggle` slide a collapsed column out.** In fullscreen a sidebar or adjust column the user wants shown is collapsed at the right edge (`reason: "fullscreen"`). Toggling it then turned it off; it now shows it, which player 0.7.6 answers by sliding the column out, as its own Adjust button and Alt+1…4 do. Toggling a column that is on screen still turns it off. With players before 0.7.6 a show of a collapsed column changes nothing on screen, as before.
38
58
  - **[adjust-panel](docs/examples/adjust-panel.md)** declares `events`: without it `plugin.player.state` changes only after the plugin's own calls, so a panel activated before a file was opened stayed disabled at 1×. A new test opens the file after activation. Its presets are four (0.5×, 1×, 1.5×, 2×), which fit the adjust column at its least width. No other example reads `player.state` without `events`.
39
59
  - **A development warning for state followed without `events`.** A plugin that registers an `onChange` or a `bind` whose function reads changing state (`player.state`, a panel position, and the like) without the `events` capability gets one line in its log, once per plugin: changes made elsewhere never reach it. Only a warning; behaviour is unchanged. `TestHost` already behaved like the player here (it delivers `source.state` and the other state events only to a plugin that subscribed, which needs `events`): a new test pins that down. The example's old tests passed because they opened the file before starting the plugin or did not check `enabled`. No SDK example or test relied on the opposite, so nobody must change.
60
+
40
61
  ## 1.6.11
41
62
 
42
63
  **Who must change: nobody.** Additions only: a mount slot, a panel position, a button icon and `TestHost` support; `apiVersion` stays `vestra/1`, no new capability. A plugin that mounts `"adjust"` sets `minPlayerVersion` to `0.7.6`: older players refuse the slot (`invalid`), which fails the activation like any refused mount.
@@ -208,10 +208,10 @@ export interface BuiltinSubmenu {
208
208
  * built-in plugins declare.
209
209
  *
210
210
  * @example Add an item to the built-in Snapshot submenu
211
- * "Copy Snapshot" appears in Video ▸ Snapshot next to the built-in capture
211
+ * "Copy Snapshot" appears in Snapshot ▸ next to the built-in capture
212
212
  * plugin's items. Paths merge by their English keys, so a command whose
213
- * path continues with the submenu's keys (`["Video", "Snapshot"]`, the menu
214
- * written as `@video`) lands in the same submenu whatever the interface
213
+ * path starts with the submenu's keys (`["Snapshot"]`, a top-level submenu
214
+ * since player 0.7.6) lands in the same submenu whatever the interface
215
215
  * language.
216
216
  * ```ts
217
217
  * import type { Plugin } from "@vestra-player/sdk";
@@ -223,8 +223,8 @@ export interface BuiltinSubmenu {
223
223
  * const snapshot = BUILTIN_SUBMENUS.find((menu) => menu.path.at(-1) === "Snapshot")!;
224
224
  * plugin.commands.add({
225
225
  * id: "snapshot-to-clipboard",
226
- * // ["@video", "Snapshot", "Copy Snapshot"]; @video stands for "Video".
227
- * path: ["@video", ...snapshot.path.slice(1), "Copy Snapshot"],
226
+ * // ["Snapshot", "Copy Snapshot"]
227
+ * path: [...snapshot.path, "Copy Snapshot"],
228
228
  * run: copySnapshot,
229
229
  * });
230
230
  * ```
package/dist/commands.js CHANGED
@@ -234,10 +234,10 @@ export const BAR_BUTTON_LIMITS = {
234
234
  * built-in plugins declare.
235
235
  *
236
236
  * @example Add an item to the built-in Snapshot submenu
237
- * "Copy Snapshot" appears in Video ▸ Snapshot next to the built-in capture
237
+ * "Copy Snapshot" appears in Snapshot ▸ next to the built-in capture
238
238
  * plugin's items. Paths merge by their English keys, so a command whose
239
- * path continues with the submenu's keys (`["Video", "Snapshot"]`, the menu
240
- * written as `@video`) lands in the same submenu whatever the interface
239
+ * path starts with the submenu's keys (`["Snapshot"]`, a top-level submenu
240
+ * since player 0.7.6) lands in the same submenu whatever the interface
241
241
  * language.
242
242
  * ```ts
243
243
  * import type { Plugin } from "@vestra-player/sdk";
@@ -249,8 +249,8 @@ export const BAR_BUTTON_LIMITS = {
249
249
  * const snapshot = BUILTIN_SUBMENUS.find((menu) => menu.path.at(-1) === "Snapshot")!;
250
250
  * plugin.commands.add({
251
251
  * id: "snapshot-to-clipboard",
252
- * // ["@video", "Snapshot", "Copy Snapshot"]; @video stands for "Video".
253
- * path: ["@video", ...snapshot.path.slice(1), "Copy Snapshot"],
252
+ * // ["Snapshot", "Copy Snapshot"]
253
+ * path: [...snapshot.path, "Copy Snapshot"],
254
254
  * run: copySnapshot,
255
255
  * });
256
256
  * ```
@@ -261,31 +261,9 @@ export const BAR_BUTTON_LIMITS = {
261
261
  */
262
262
  export const BUILTIN_SUBMENUS = Object.freeze([
263
263
  { path: ["Open", "Recently Played"], plugins: ["vestra.history"] },
264
- { path: ["Playback", "A-B Loop"], plugins: ["vestra.marks"] },
265
- { path: ["Playback", "A-B Loop", "Loop Count"], plugins: ["vestra.marks"] },
266
- { path: ["Playback", "Chapters and Bookmarks"], plugins: ["vestra.marks"] },
267
- { path: ["Playback", "Playlist"], plugins: ["vestra.playlist"] },
268
- { path: ["Playback", "Playlist", "Repeat"], plugins: ["vestra.playlist"] },
269
- {
270
- path: ["Playback", "Playlist", "When Opening a File"],
271
- plugins: ["vestra.playlist"],
272
- },
273
- { path: ["Playback", "Resume Playback"], plugins: ["vestra.history"] },
274
- {
275
- path: ["Subtitles", "Online Subtitles"],
276
- plugins: ["vestra.subtitle-online"],
277
- },
278
- { path: ["Video", "Snapshot"], plugins: ["vestra.capture"] },
279
- { path: ["Screen", "Always on Top"], plugins: ["vestra.window"] },
280
- { path: ["Screen", "When Playback Starts"], plugins: ["vestra.window"] },
281
- {
282
- path: ["Settings", "Appearance"],
283
- plugins: ["vestra.preferences", "vestra.visualizer"],
284
- },
285
- { path: ["Settings", "Language"], plugins: ["vestra.preferences"] },
286
- { path: ["Copy to Clipboard"], plugins: ["vestra.playback"] },
287
- { path: ["Subtitle Track"], plugins: ["vestra.subtitles"] },
288
264
  { path: ["Audio Track"], plugins: ["vestra.audio"] },
265
+ { path: ["Subtitle Track"], plugins: ["vestra.subtitles"] },
266
+ { path: ["Snapshot"], plugins: ["vestra.capture"] },
289
267
  ]);
290
268
  /** owner | id | key path, or (title) outside the menu | bindings | panel */
291
269
  const BUILTIN_COMMAND_TABLE = `
@@ -293,136 +271,44 @@ vestra.playback | open | Open File… | Ctrl+O, F3
293
271
  vestra.playback | open-url | Open ▸ Open URL… | Ctrl+U
294
272
  vestra.playlist | playlist.open-folder | Open ▸ Open Folder… | F2
295
273
  vestra.subtitles | load | Open ▸ Open Subtitle… | Alt+O
274
+ vestra.subtitles | load-secondary | Open ▸ Add Secondary Subtitle…
296
275
  vestra.audio | audio.load | Open ▸ Load External Audio…
276
+ vestra.subtitle-online | search | Open ▸ Search and Download Subtitles…
297
277
  vestra.history | recent.empty | Open ▸ Recently Played ▸ (No Entries)
298
278
  vestra.history | recent.prune | Open ▸ Recently Played ▸ Remove Invalid Entries
299
279
  vestra.history | recent.clear | Open ▸ Recently Played ▸ Clear Entries
300
280
  vestra.history | recent.enabled | Open ▸ Recently Played ▸ Record Playback History
301
- vestra.subtitles | load-secondary | Open ▸ Add Secondary Subtitle…
302
281
  host | stop | Close | F4
303
- vestra.playback | reopen | Reopen Current/Last File | Ctrl+Y
304
- vestra.playback | copy-name | Copy to Clipboard ▸ File Name
305
- vestra.playback | copy-folder | Copy to Clipboard ▸ Folder Path
306
- vestra.playback | copy-path | Copy to Clipboard ▸ Full Path
307
- host | play-pause | Playback ▸ Play/Pause | Space, Mouse:LeftClick
308
- host | previous | Playback ▸ Previous | PageUp, Mouse:X2
309
- host | next | Playback ▸ Next | PageDown, Mouse:X1
310
- vestra.playlist | playlist.add | Playback ▸ Playlist ▸ Add Files…
311
- vestra.playlist | playlist.link | Playback ▸ Playlist ▸ Add Link…
312
- vestra.playlist | playlist.add-folder | Playback ▸ Playlist ▸ Add Folder…
313
- vestra.playlist | playlist.repeat-none | Playback ▸ Playlist ▸ Repeat ▸ Off
314
- vestra.playlist | playlist.repeat-all | Playback ▸ Playlist ▸ Repeat ▸ All
315
- vestra.playlist | playlist.repeat-one | Playback ▸ Playlist ▸ Repeat ▸ One
316
- vestra.playlist | playlist.shuffle | Playback ▸ Playlist ▸ Shuffle
317
- vestra.playlist | playlist.open-list | Playback ▸ Playlist ▸ Open List File…
318
- vestra.playlist | playlist.save-list | Playback ▸ Playlist ▸ Save List…
319
- vestra.playlist | playlist.open-mode-only | Playback ▸ Playlist ▸ When Opening a File ▸ Open Only That File
320
- vestra.playlist | playlist.open-mode-similar | Playback ▸ Playlist ▸ When Opening a File ▸ Add Similar Files
321
- vestra.playlist | playlist.open-mode-folder | Playback ▸ Playlist ▸ When Opening a File ▸ Add All Files in the Folder
322
- vestra.playlist | playlist.fast-advance | Playback ▸ Playlist ▸ Fast Advance to Next
323
- vestra.playlist | playlist.remember | Playback ▸ Playlist ▸ Remember List
324
- vestra.playlist | playlist.confirm-clear | Playback ▸ Playlist ▸ Confirm Before Removing All
325
- vestra.playback | prevent-sleep | Playback ▸ Disable Screen Saver While Playing
326
- vestra.window | hide-cursor | Playback ▸ Hide Pointer While Playing
327
- vestra.playback | hover-time | Playback ▸ Show Time on Seek Bar Hover
328
- vestra.playback | hover-thumbnail | Playback ▸ Show Thumbnail on Seek Bar Hover
329
- vestra.history | resume.off | Playback ▸ Resume Playback ▸ Don't Resume
330
- vestra.history | resume.auto | Playback ▸ Resume Playback ▸ Resume Automatically
331
- vestra.history | resume.ask | Playback ▸ Resume Playback ▸ Ask Every Time
332
- vestra.history | resume.audio | Playback ▸ Resume Playback ▸ Also Remember Audio Files
333
- vestra.history | resume.clear | Playback ▸ Resume Playback ▸ Clear All Saved Positions
334
- vestra.marks | marks.list | Playback ▸ Chapters and Bookmarks ▸ Chapter and Bookmark List… | H
335
- vestra.marks | marks.add | Playback ▸ Chapters and Bookmarks ▸ Add Bookmark | P
336
- vestra.marks | marks.previous | Playback ▸ Chapters and Bookmarks ▸ Previous Bookmark or Chapter | Shift+PageUp
337
- vestra.marks | marks.next | Playback ▸ Chapters and Bookmarks ▸ Next Bookmark or Chapter | Shift+PageDown
338
- vestra.marks | marks.previous-chapter | Playback ▸ Chapters and Bookmarks ▸ Previous Chapter | Ctrl+PageUp
339
- vestra.marks | marks.next-chapter | Playback ▸ Chapters and Bookmarks ▸ Next Chapter | Ctrl+PageDown
340
- vestra.marks | marks.seekbar | Playback ▸ Chapters and Bookmarks ▸ Show Marks on Seek Bar
341
- vestra.marks | ab.set-a | Playback ▸ A-B Loop ▸ Set Start Point | [
342
- vestra.marks | ab.clear-a | Playback ▸ A-B Loop ▸ Clear Start Point | {
343
- vestra.marks | ab.a-back | Playback ▸ A-B Loop ▸ Start Point Back 0.1 s | Ctrl+[
344
- vestra.marks | ab.a-forward | Playback ▸ A-B Loop ▸ Start Point Forward 0.1 s | Alt+[
345
- vestra.marks | ab.set-b | Playback ▸ A-B Loop ▸ Set End Point | ]
346
- vestra.marks | ab.clear-b | Playback ▸ A-B Loop ▸ Clear End Point | }
347
- vestra.marks | ab.b-back | Playback ▸ A-B Loop ▸ End Point Back 0.1 s | Ctrl+]
348
- vestra.marks | ab.b-forward | Playback ▸ A-B Loop ▸ End Point Forward 0.1 s | Alt+]
349
- vestra.marks | ab.back | Playback ▸ A-B Loop ▸ Move Range Back 0.1 s | Ctrl+Alt+[
350
- vestra.marks | ab.forward | Playback ▸ A-B Loop ▸ Move Range Forward 0.1 s | Ctrl+Alt+]
351
- vestra.marks | ab.segment | Playback ▸ A-B Loop ▸ Loop Current Chapter or Bookmark Range | Alt+\\
352
- vestra.marks | ab.toggle | Playback ▸ A-B Loop ▸ A-B Loop On/Off | \\
353
- vestra.marks | ab.count.endless | Playback ▸ A-B Loop ▸ Loop Count ▸ Endless
354
- vestra.marks | ab.count.1 | Playback ▸ A-B Loop ▸ Loop Count ▸ Once
355
- vestra.marks | ab.count.2 | Playback ▸ A-B Loop ▸ Loop Count ▸ 2 Times
356
- vestra.marks | ab.count.3 | Playback ▸ A-B Loop ▸ Loop Count ▸ 3 Times
357
- vestra.marks | ab.count.5 | Playback ▸ A-B Loop ▸ Loop Count ▸ 5 Times
358
- vestra.marks | ab.count.10 | Playback ▸ A-B Loop ▸ Loop Count ▸ 10 Times
359
- vestra.subtitle-online | search | Subtitles ▸ Online Subtitles ▸ Search and Download Subtitles…
360
- vestra.subtitle-online | settings | Subtitles ▸ Online Subtitles ▸ Online Subtitle Settings…
361
- vestra.subtitles | track-off | Subtitle Track ▸ Off
362
- vestra.capture | capture.save | Video ▸ Snapshot ▸ Save Current Frame | Ctrl+E
363
- vestra.capture | capture.panel | Video ▸ Snapshot ▸ Capture… | Alt+S
364
- vestra.capture | capture.copy | Video ▸ Snapshot ▸ Copy Current Frame | Ctrl+C
365
- vestra.capture | capture.burst | Video ▸ Snapshot ▸ Burst… | Ctrl+G
366
- vestra.capture | capture.clip | Video ▸ Snapshot ▸ Export Clip…
367
- host | volume-up | Audio ▸ Volume + | ArrowUp, Mouse:WheelUp
368
- host | volume-down | Audio ▸ Volume − | ArrowDown, Mouse:WheelDown
369
- host | mute | Audio ▸ Mute | M
370
- vestra.visualizer | audio-cover | Audio ▸ Show Album Cover for Audio Only
282
+ host | play-pause | Play/Pause | Space, Mouse:LeftClick
371
283
  vestra.audio | audio.track.none | Audio Track ▸ No Audio Tracks
372
- host | fullscreen | Toggle Fullscreen | Escape
373
- vestra.window | pip | Screen ▸ Picture-in-Picture
374
- vestra.window | mini | Screen ▸ Mini Size | \`
375
- vestra.window | size.0.5 | Screen ▸ 0.5× | 1
376
- vestra.window | size.1 | Screen ▸ 1× | 2
377
- vestra.window | size.1.5 | Screen ▸ 1.5× | 3
378
- vestra.window | size.2 | Screen ▸ 2× | 4
379
- vestra.window | fit | Screen ▸ Fit to Video Aspect Ratio | 8
380
- vestra.window | auto.off | Screen ▸ When Playback Starts ▸ Don't Resize
381
- vestra.window | auto.0.5 | Screen ▸ When Playback Starts ▸ 0.5×
382
- vestra.window | auto.1 | Screen ▸ When Playback Starts ▸ 1×
383
- vestra.window | auto.1.5 | Screen ▸ When Playback Starts ▸ 1.5×
384
- vestra.window | auto.2 | Screen ▸ When Playback Starts ▸ 2×
385
- vestra.window | auto.fit | Screen ▸ When Playback Starts ▸ Fit to Video Aspect Ratio
386
- vestra.window | auto.once | Screen ▸ When Playback Starts ▸ Only Once
387
- vestra.window | top.cycle | Screen ▸ Always on Top ▸ Cycle Mode | Ctrl+T
388
- vestra.window | top.never | Screen ▸ Always on Top ▸ Never
389
- vestra.window | top.always | Screen ▸ Always on Top ▸ Always
390
- vestra.window | top.playing | Screen ▸ Always on Top ▸ While Playing
391
- vestra.window | top.fullscreen | Screen ▸ Always on Top ▸ While Playing in Fullscreen
284
+ vestra.subtitles | track-off | Subtitle Track ▸ Off
285
+ vestra.capture | capture.save | Snapshot ▸ Save Current Frame | Ctrl+E
286
+ vestra.capture | capture.copy | Snapshot ▸ Copy Current Frame | Ctrl+C
287
+ vestra.capture | capture.burst | Snapshot ▸ Burst… | Ctrl+G
288
+ vestra.capture | capture.clip | Snapshot ▸ Export Clip…
289
+ vestra.capture | capture.panel | Snapshot ▸ Capture… | Alt+S
392
290
  vestra.video | video.picture | Picture… | Alt+1
393
291
  vestra.audio | audio.workbench | Audio… | Alt+2
394
292
  vestra.subtitles | tune | Subtitles… | Alt+3
395
293
  vestra.playback | panel | Playback… | Alt+4
396
294
  vestra.playlist | playlist.visibility | Playlist | F6
397
- vestra.info | info.toggle | Playback Info | Tab
398
- vestra.info | info.properties | Playback Properties… | Ctrl+F1
295
+ vestra.marks | marks.list | Chapter and Bookmark List… | H
296
+ host | fullscreen | Toggle Fullscreen | Escape
399
297
  vestra.workshop | workshop.open | Vestra Player AI…
400
298
  vestra.store | open | Plugin Store…
401
- vestra.preferences | options | Settings ▸ Options… | F5
402
- vestra.hotkeys | keys | Settings ▸ Keyboard Shortcuts…
403
- vestra.hotkeys | mouse | Settings ▸ Mouse…
404
- vestra.hotkeys | ignore-defaults | Settings ▸ Ignore Default Shortcuts
405
- vestra.preferences | theme.vestra | Settings ▸ Appearance ▸ Vestra Dark
406
- vestra.preferences | theme.vestra-light | Settings ▸ Appearance ▸ Vestra Light
407
- vestra.visualizer | idle-logo | Settings ▸ Appearance ▸ Show Logo When Idle
408
- vestra.visualizer | choose-logo | Settings ▸ Appearance ▸ Choose Logo Image…
409
- vestra.preferences | language.system | Settings ▸ Language ▸ Follow System
410
- vestra.preferences | language.en | Settings ▸ Language ▸ English
411
- vestra.preferences | language.zh-Hans | Settings ▸ Language ▸ 简体中文
412
- vestra.preferences | language.ja | Settings ▸ Language ▸ 日本語
413
- vestra.preferences | language.ko | Settings ▸ Language ▸ 한국어
414
- vestra.preferences | language.de | Settings ▸ Language ▸ Deutsch
415
- vestra.preferences | language.fr | Settings ▸ Language ▸ Français
416
- vestra.preferences | language.es | Settings ▸ Language ▸ Español
417
- vestra.visualizer | reset-logo | Reset ▸ Idle Logo
299
+ host | palette | Command Palette… | Ctrl+Shift+P
300
+ vestra.preferences | options | Options… | F5
418
301
  vestra.info | info.about | About Vestra Player… | F1
419
- vestra.info | info.update | Check for Updates…
420
302
  host | quit | Quit | Cmd+Q
303
+ host | previous | (Previous) | PageUp, Mouse:X2
304
+ host | next | (Next) | PageDown, Mouse:X1
421
305
  host | enter-fullscreen | (Enter Fullscreen)
422
306
  host | exit-fullscreen | (Leave Fullscreen)
423
307
  host | sidebar | (Toggle Sidebar)
424
- host | palette | (Command Palette) | Ctrl+Shift+P
425
308
  host | adjust | (Adjust)
309
+ host | volume-up | (Volume +) | ArrowUp, Mouse:WheelUp
310
+ host | volume-down | (Volume −) | ArrowDown, Mouse:WheelDown
311
+ host | mute | (Mute) | M
426
312
  host | frame-back | (Previous Frame) | D
427
313
  host | frame-forward | (Next Frame) | F
428
314
  host | seek-backward | (Back 5 s) | ArrowLeft
@@ -430,6 +316,98 @@ host | seek-forward | (Forward 5 s) | ArrowRight
430
316
  host | rate-normal | (Normal) | Z
431
317
  host | rate-down | (Slower 0.1) | X
432
318
  host | rate-up | (Faster 0.1) | C
319
+ vestra.playback | reopen | (Reopen Current/Last File) | Ctrl+Y
320
+ vestra.playback | copy-name | (File Name)
321
+ vestra.playback | copy-folder | (Folder Path)
322
+ vestra.playback | copy-path | (Full Path)
323
+ vestra.playback | prevent-sleep | (Disable Screen Saver While Playing)
324
+ vestra.playback | hover-time | (Show Time on Seek Bar Hover)
325
+ vestra.playback | hover-thumbnail | (Show Thumbnail on Seek Bar Hover)
326
+ vestra.window | hide-cursor | (Hide Pointer While Playing)
327
+ vestra.window | pip | (Picture-in-Picture)
328
+ vestra.window | mini | (Mini Size) | \`
329
+ vestra.window | size.0.5 | (0.5×) | 1
330
+ vestra.window | size.1 | (1×) | 2
331
+ vestra.window | size.1.5 | (1.5×) | 3
332
+ vestra.window | size.2 | (2×) | 4
333
+ vestra.window | fit | (Fit to Video Aspect Ratio) | 8
334
+ vestra.window | auto.off | (Don't Resize)
335
+ vestra.window | auto.0.5 | (0.5×)
336
+ vestra.window | auto.1 | (1×)
337
+ vestra.window | auto.1.5 | (1.5×)
338
+ vestra.window | auto.2 | (2×)
339
+ vestra.window | auto.fit | (Fit to Video Aspect Ratio)
340
+ vestra.window | auto.once | (Only Once)
341
+ vestra.window | top.cycle | (Cycle Mode) | Ctrl+T
342
+ vestra.window | top.never | (Never)
343
+ vestra.window | top.always | (Always)
344
+ vestra.window | top.playing | (While Playing)
345
+ vestra.window | top.fullscreen | (While Playing in Fullscreen)
346
+ vestra.playlist | playlist.add | (Add Files…)
347
+ vestra.playlist | playlist.link | (Add Link…)
348
+ vestra.playlist | playlist.add-folder | (Add Folder…)
349
+ vestra.playlist | playlist.repeat-none | (Off)
350
+ vestra.playlist | playlist.repeat-all | (All)
351
+ vestra.playlist | playlist.repeat-one | (One)
352
+ vestra.playlist | playlist.shuffle | (Shuffle)
353
+ vestra.playlist | playlist.open-list | (Open List File…)
354
+ vestra.playlist | playlist.save-list | (Save List…)
355
+ vestra.playlist | playlist.open-mode-only | (Open Only That File)
356
+ vestra.playlist | playlist.open-mode-similar | (Add Similar Files)
357
+ vestra.playlist | playlist.open-mode-folder | (Add All Files in the Folder)
358
+ vestra.playlist | playlist.fast-advance | (Fast Advance to Next)
359
+ vestra.playlist | playlist.remember | (Remember List)
360
+ vestra.playlist | playlist.confirm-clear | (Confirm Before Removing All)
361
+ vestra.history | resume.off | (Don't Resume)
362
+ vestra.history | resume.auto | (Resume Automatically)
363
+ vestra.history | resume.ask | (Ask Every Time)
364
+ vestra.history | resume.audio | (Also Remember Audio Files)
365
+ vestra.history | resume.clear | (Clear All Saved Positions)
366
+ vestra.marks | marks.add | (Add Bookmark) | P
367
+ vestra.marks | marks.previous | (Previous Bookmark or Chapter) | Shift+PageUp
368
+ vestra.marks | marks.next | (Next Bookmark or Chapter) | Shift+PageDown
369
+ vestra.marks | marks.previous-chapter | (Previous Chapter) | Ctrl+PageUp
370
+ vestra.marks | marks.next-chapter | (Next Chapter) | Ctrl+PageDown
371
+ vestra.marks | marks.seekbar | (Show Marks on Seek Bar)
372
+ vestra.marks | ab.set-a | (Set Start Point) | [
373
+ vestra.marks | ab.clear-a | (Clear Start Point) | {
374
+ vestra.marks | ab.a-back | (Start Point Back 0.1 s) | Ctrl+[
375
+ vestra.marks | ab.a-forward | (Start Point Forward 0.1 s) | Alt+[
376
+ vestra.marks | ab.set-b | (Set End Point) | ]
377
+ vestra.marks | ab.clear-b | (Clear End Point) | }
378
+ vestra.marks | ab.b-back | (End Point Back 0.1 s) | Ctrl+]
379
+ vestra.marks | ab.b-forward | (End Point Forward 0.1 s) | Alt+]
380
+ vestra.marks | ab.back | (Move Range Back 0.1 s) | Ctrl+Alt+[
381
+ vestra.marks | ab.forward | (Move Range Forward 0.1 s) | Ctrl+Alt+]
382
+ vestra.marks | ab.segment | (Loop Current Chapter or Bookmark Range) | Alt+\\
383
+ vestra.marks | ab.toggle | (A-B Loop On/Off) | \\
384
+ vestra.marks | ab.count.endless | (Endless)
385
+ vestra.marks | ab.count.1 | (Once)
386
+ vestra.marks | ab.count.2 | (2 Times)
387
+ vestra.marks | ab.count.3 | (3 Times)
388
+ vestra.marks | ab.count.5 | (5 Times)
389
+ vestra.marks | ab.count.10 | (10 Times)
390
+ vestra.subtitle-online | settings | (Online Subtitle Settings…)
391
+ vestra.visualizer | audio-cover | (Show Album Cover for Audio Only)
392
+ vestra.visualizer | idle-logo | (Show Logo When Idle)
393
+ vestra.visualizer | choose-logo | (Choose Logo Image…)
394
+ vestra.visualizer | reset-logo | (Idle Logo)
395
+ vestra.hotkeys | keys | (Keyboard Shortcuts…)
396
+ vestra.hotkeys | mouse | (Mouse…)
397
+ vestra.hotkeys | ignore-defaults | (Ignore Default Shortcuts)
398
+ vestra.preferences | theme.vestra | (Vestra Dark)
399
+ vestra.preferences | theme.vestra-light | (Vestra Light)
400
+ vestra.preferences | language.system | (Follow System)
401
+ vestra.preferences | language.en | (English)
402
+ vestra.preferences | language.zh-Hans | (简体中文)
403
+ vestra.preferences | language.ja | (日本語)
404
+ vestra.preferences | language.ko | (한국어)
405
+ vestra.preferences | language.de | (Deutsch)
406
+ vestra.preferences | language.fr | (Français)
407
+ vestra.preferences | language.es | (Español)
408
+ vestra.info | info.toggle | (Playback Info) | Tab
409
+ vestra.info | info.properties | (Playback Properties…) | Ctrl+F1
410
+ vestra.info | info.update | (Check for Updates…)
433
411
  vestra.audio | audio.track.cycle | (Cycle Tracks) | Alt+A
434
412
  vestra.audio | audio.channels.stereo | (Stereo)
435
413
  vestra.audio | audio.channels.left | (Left Channel)
Binary file
@@ -37,6 +37,30 @@ export type Change = ReadonlyMap<string, ReadonlySet<string>>;
37
37
  * without `update()`) still shows within about this long.
38
38
  */
39
39
  export declare const SAFETY_PASS_MS = 1000;
40
+ /**
41
+ * Read-only counters of the update passes, for the SDK's own
42
+ * characterisation tests and benchmark (`test/golden-traces.test.ts`,
43
+ * `scripts/bench.mjs`): how many passes ran, how many were full, how many
44
+ * bindings and view bind functions (parts) were evaluated or skipped. Not
45
+ * part of the plugin API; counting changes no behaviour.
46
+ */
47
+ export interface PassCounts {
48
+ /** Passes run (`Runtime.#run`), precise and full. */
49
+ passes: number;
50
+ /** Of them, full passes (every binding evaluated). */
51
+ fullPasses: number;
52
+ /** Bindings evaluated (`Runtime.#evaluate`). */
53
+ evaluations: number;
54
+ /** View bind functions evaluated inside a binding (`Runtime.part`). */
55
+ parts: number;
56
+ /** View bind functions a precise pass skipped (`Runtime.part`). */
57
+ skippedParts: number;
58
+ /**
59
+ * Full passes the safety timer requested (`SAFETY_PASS_MS` after a
60
+ * precise pass skipped something), counted when the timer fires.
61
+ */
62
+ safetyPasses: number;
63
+ }
40
64
  /** How the plugin is run: the player's runner, or `TestHost`. */
41
65
  export interface RuntimeOptions {
42
66
  /**
@@ -72,6 +96,8 @@ export declare class Runtime {
72
96
  readonly local: LocalServices | undefined;
73
97
  /** Registrations that activation must wait for before reporting ready. */
74
98
  readonly startup: Set<Promise<unknown>>;
99
+ /** Pass counters, read by the SDK's own tests and benchmark ({@link PassCounts}). */
100
+ readonly counts: PassCounts;
75
101
  readonly views: Map<string, (callback: string, args: Json[]) => void>;
76
102
  readonly commands: Map<string, () => void>;
77
103
  readonly services: Map<string, unknown>;
@@ -209,6 +209,15 @@ export class Runtime {
209
209
  #skipped = false;
210
210
  /** The plugin was disposed: no safety pass is armed any more. */
211
211
  #stopped = false;
212
+ /** Pass counters, read by the SDK's own tests and benchmark ({@link PassCounts}). */
213
+ counts = {
214
+ passes: 0,
215
+ fullPasses: 0,
216
+ evaluations: 0,
217
+ parts: 0,
218
+ skippedParts: 0,
219
+ safetyPasses: 0,
220
+ };
212
221
  #events = new Map();
213
222
  /** The missing `events` warning was given (once per plugin). */
214
223
  #warnedEvents = false;
@@ -389,6 +398,7 @@ export class Runtime {
389
398
  const outerDeps = this.#deps;
390
399
  if (last && this.#pass && !affected(last.deps, this.#pass)) {
391
400
  this.#skipped = true;
401
+ this.counts.skippedParts++;
392
402
  if (outerReads)
393
403
  for (const [event, why] of last.reads)
394
404
  if (!outerReads.has(event))
@@ -397,6 +407,7 @@ export class Runtime {
397
407
  merge(outerDeps, last.deps);
398
408
  return undefined;
399
409
  }
410
+ this.counts.parts++;
400
411
  const reads = new Map();
401
412
  const deps = new Map();
402
413
  this.#reads = outerReads && reads;
@@ -512,6 +523,7 @@ export class Runtime {
512
523
  this.#reads = reads;
513
524
  this.#deps = deps;
514
525
  this.#reader = undefined;
526
+ this.counts.evaluations++;
515
527
  try {
516
528
  evaluate();
517
529
  }
@@ -570,6 +582,9 @@ export class Runtime {
570
582
  const change = this.#pending;
571
583
  this.#full = false;
572
584
  this.#pending = new Map();
585
+ this.counts.passes++;
586
+ if (full)
587
+ this.counts.fullPasses++;
573
588
  if (full) {
574
589
  clearTimeout(this.#safety);
575
590
  this.#safety = undefined;
@@ -596,6 +611,7 @@ export class Runtime {
596
611
  if (this.#skipped && !this.#safety && !this.#stopped) {
597
612
  this.#safety = setTimeout(() => {
598
613
  this.#safety = undefined;
614
+ this.counts.safetyPasses++;
599
615
  this.invalidate();
600
616
  }, SAFETY_PASS_MS);
601
617
  this.#safety.unref?.();
@@ -0,0 +1,65 @@
1
+ import type { Json } from "../../wire.ts";
2
+ import type { CommandWhen } from "../../commands.ts";
3
+ import type { FakeCommand, FakeBarButton, TestHost } from "./index.ts";
4
+ import type { Handler, Internals } from "./internals.ts";
5
+ /** A command state's reason, refused as the player refuses it. */
6
+ export declare function checkReason(host: TestHost, inner: Internals, state: Json | undefined): string | null;
7
+ export declare function bindingClaims(host: TestHost, inner: Internals): Map<string, string[]>;
8
+ /** `host/<id>` for core commands, `<manifest id>/<id>` for the plugin's. */
9
+ export declare function bindingKey(host: TestHost, _inner: Internals, id: string): string;
10
+ /**
11
+ * `commands.keys` as the player answers it: each of the plugin's own
12
+ * commands, and the player's core commands as `host/<id>` (SDK 1.6.16),
13
+ * with the labels of the keys it holds now (the overlay, else its
14
+ * defaults; not a key an earlier command holds), as menus write them on
15
+ * `systemInfo.os`.
16
+ */
17
+ export declare function commandKeys(host: TestHost, inner: Internals): {
18
+ keys: Record<string, string[]>;
19
+ revision: number;
20
+ };
21
+ /** Push `commands.keys` when the labels changed and the plugin listens. */
22
+ export declare function keysChanged(host: TestHost, inner: Internals): void;
23
+ export declare function splitCommandKey(_host: TestHost, _inner: Internals, key: string): {
24
+ plugin: string;
25
+ command: string;
26
+ };
27
+ export declare function bindingTable(host: TestHost, inner: Internals): {
28
+ commands: {
29
+ title: string;
30
+ path: string[] | null;
31
+ key: string[] | null;
32
+ category: string | null;
33
+ scope: "panel" | "global";
34
+ defaults: string[];
35
+ current: string[];
36
+ overridden: boolean;
37
+ shadowed: {
38
+ input: string;
39
+ by: {
40
+ plugin: string;
41
+ command: string;
42
+ };
43
+ }[];
44
+ labels: string[];
45
+ plugin: string;
46
+ command: string;
47
+ }[];
48
+ orphans: {
49
+ ref: {
50
+ plugin: string;
51
+ command: string;
52
+ };
53
+ inputs: string[];
54
+ }[];
55
+ };
56
+ /** A command's state as the player shows it: its conditions evaluated. */
57
+ export declare function commandShown(host: TestHost, _inner: Internals, sent: FakeCommand["state"], when: CommandWhen | undefined): FakeCommand["state"];
58
+ /** Implements `TestHost.press`. */
59
+ export declare function press(host: TestHost, inner: Internals, binding: string, options?: {
60
+ panel?: boolean;
61
+ }): Promise<"ran" | "reason" | "passed">;
62
+ /** Implements `TestHost.barButtons`. */
63
+ export declare function barButtons(host: TestHost, _inner: Internals): readonly FakeBarButton[];
64
+ /** The default handlers of this area's wire methods, by method name. */
65
+ export declare function commandsHandlers(host: TestHost, inner: Internals): Record<string, Handler>;