@vestra-player/sdk 1.5.34 → 1.5.35
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.
- package/CHANGELOG.md +31 -19
- package/dist/api-requests.js +3 -0
- package/dist/capabilities.d.ts +2 -2
- package/dist/capabilities.js +2 -2
- package/dist/host-capabilities.js +12 -0
- package/dist/plugin/scope.d.ts +1 -0
- package/dist/plugin/scope.js +2 -1
- package/dist/plugin/services.d.ts +2 -1
- package/dist/plugin/services.js +22 -2
- package/dist/plugin/testing.d.ts +75 -5
- package/dist/plugin/testing.js +137 -4
- package/dist/plugin/types.d.ts +353 -0
- package/dist/wire.d.ts +29 -0
- package/dist/wire.js +11 -0
- package/llms-full.txt +514 -9
- package/llms.txt +22 -21
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.35
|
|
4
|
+
|
|
5
|
+
**Who must change: nobody.** Additions only, `apiVersion` stays `vestra/1`. The new API needs player 0.5.13 (`"minPlayerVersion": "0.5.13"`): older players leave `SystemInfo.renderingReason` out, refuse `rendering.state` as an unknown request (`unsupported`), and do not load a plugin that declares the new capability `rendering.manage`. A plugin that uses none of it sees no difference.
|
|
6
|
+
|
|
7
|
+
- **`plugin.rendering`** (player 0.5.13): the player's GPU or software rendering choice. `state()` (no capability) reports the renderer drawing now and why (`mode`, `reason`), the saved choice (`preference`: `"auto"`, `"gpu"` or `"software"`), what the next start will use and why (`nextMode`, `nextReason`), `restartPending` (the next start's mode differs from the mode this run started with) and whether `"gpu"` can be chosen now (`gpu.available`, `gpu.reason`). `setPreference(preference)` saves the choice at once and resolves with the new state; the running player keeps its renderer and the next start uses the choice whether or not the user restarts (`invalid`, `unsupported` for `"gpu"` without a GPU renderer or user interface, `unavailable` for `"gpu"` with a driver on the denylist or after a GPU crash, `failed`). `restartPlayer()` resolves once the player accepted, then the player quits normally (every plugin stops) and starts again with the same plugins, continuing a local file at its position (`unsupported` headless, `busy` while a restart is under way or within 30 s after one, `failed`). Reasons are `{ code, message }` with stable kebab-case codes (`RenderingReasonCode`: `default`, `pending`, `headless`, `no-gpu-backend`, `env`, `preference`, `denylist`, `previous-crash`, `init-failed`, `window-fallback`; a newer player may add codes). New types `Rendering`, `RenderingState`, `RenderingReason`, `RenderingReasonCode`, `RenderingMode`, `RenderingPreference`.
|
|
8
|
+
- **New capability `rendering.manage`** ("Choose GPU or software rendering and restart the player", highlighted in the store), last in `CAPABILITIES`; needed by `setPreference` and `restartPlayer`, which reject with `capability` without it. `rendering.state` needs none. The player's capability table copy, `CAPABILITY_INFO`, `vestra-plugin check` and the editor plugin know it.
|
|
9
|
+
- **`SystemInfo.renderingReason`** (player 0.5.13, optional): why `rendering` is what it is, the same reason as `RenderingState.reason`.
|
|
10
|
+
- **`TestHost.rendering`** models the player (the next start's reason in the player's order: `headless` or `no-gpu-backend` before the preference): preset `reason`, `preference`, `startedWith` and `gpu` (available, and why not), with `videoRender.gpu` as the mode drawing now; `setPreference` saves the choice and refuses `"gpu"` while it is not available (`unsupported` for `no-gpu-backend` and `headless`, `unavailable` otherwise), `restartPending` follows the choice back and forth, and `restartPlayer` counts in `restarts` (a second one is `busy`). `failVideoRender("unsupported", …)` also sets the reason to `window-fallback`, and `app.system()` reports it as `renderingReason`.
|
|
11
|
+
- New how-to guide _Offer a GPU rendering switch_.
|
|
12
|
+
|
|
3
13
|
## 1.5.34
|
|
4
14
|
|
|
5
15
|
**Who must change: nobody.** Additions only, `apiVersion` stays `vestra/1`. A plugin that reads `SystemInfo.rendering` needs player 0.5.12 (`"minPlayerVersion": "0.5.12"`); older players leave the field out, and a plugin that does not read it sees no difference. Tests that load subtitles past the player's limits now fail with `busy` in `TestHost`, as they would in the player.
|
|
6
16
|
|
|
7
17
|
- **`plugin.app.system()` reports the renderer** (player 0.5.12): `rendering` is `"gpu"` (render options apply) or `"software"` (they fail with `unsupported`), read at each call (an optional field: absent on players before 0.5.12, where the renderer is unknown), so a plugin can hide its render settings without creating `plugin.video.render()` to find out. No capability. It changes at most once while the player runs, from GPU to software when a window cannot show GPU frames. `TestHost` reports it from `videoRender.gpu`, so `failVideoRender("unsupported", …)` switches both.
|
|
8
|
-
- **`SUBTITLE_LIMITS`** (`@vestra-player/sdk/testing`, `./runtime`): the player's limits on loaded subtitle files. Per source, 4 tracks of one plugin at once, 16 tracks of every plugin, 16 MiB of their files, 16 loads of one plugin and 128 of every plugin (removed tracks still count as loads); 2 loads of one plugin and 8 of every plugin in progress; a file of at most 2 MiB. A file over 2 MiB is `invalid`, the others `busy`. `TestHost` now enforces them with these codes (all but `totalBytes` and the all-plugin upload count, which one plugin cannot reach alone); external tracks a test adds to `subtitles.tracks` count as other plugins'. A new source drops the uploads of the old one (`uploads` entries carry `generation`). `Subtitles.load`,
|
|
18
|
+
- **`SUBTITLE_LIMITS`** (`@vestra-player/sdk/testing`, `./runtime`): the player's limits on loaded subtitle files. Per source, 4 tracks of one plugin at once, 16 tracks of every plugin, 16 MiB of their files, 16 loads of one plugin and 128 of every plugin (removed tracks still count as loads); 2 loads of one plugin and 8 of every plugin in progress; a file of at most 2 MiB. A file over 2 MiB is `invalid`, the others `busy`. `TestHost` now enforces them with these codes (all but `totalBytes` and the all-plugin upload count, which one plugin cannot reach alone); external tracks a test adds to `subtitles.tracks` count as other plugins'. A new source drops the uploads of the old one (`uploads` entries carry `generation`). `Subtitles.load`, _Load subtitles from a plugin_ and _Limits_ name them.
|
|
9
19
|
- Fix: a subtitle load the player refused no longer keeps one of the plugin's two upload slots. After a refused commit (`busy` at a track limit, `stale`, `invalid`…) the SDK now discards the upload, as it already did for a failure before the commit; before, two refusals on one source left the plugin unable to load any subtitle there until the source changed.
|
|
10
20
|
|
|
11
21
|
## 1.5.33
|
|
@@ -21,7 +31,7 @@
|
|
|
21
31
|
- **TestHost follows the player more closely.** A core action run from its menu record (`host.coreAction(id).run()`) while it is disabled now rejects with `command`, like the player (it rejected with `unavailable`, which is what `plugin.host.run` gives). When only disabled commands hold a pressed key, `press` now looks only at the first of them, as the player does: without a `reason` the key passes, even if a later one has a reason. Tests that relied on the old behaviour see a different code or `"passed"`.
|
|
22
32
|
- More corrections from the review: built-in plugins cannot be disabled, so another plugin never gets the built-in playlist's drop extensions or the sequence provider (except a development load replacing the playlist); `validateArchive` reports an archive that cannot be unpacked (over 64 MB, too many files, damaged, unsafe paths) as `invalid` and a broken unpacked package as `host`; `install` is `busy` only while the library is being cleaned up or 64 change records are kept, and `revisionConflict` is retried by calling `install` again; the `PluginError` table no longer lists `staleDigest`, and `versionMismatch` means a newer `minPlayerVersion` only.
|
|
23
33
|
- `llms.txt` may now be 10 KB (10,240 bytes, up from 9 KB) to leave room for about 20 more how-to pages in its index; it stays under 2.6k tokens.
|
|
24
|
-
- New how-to guide:
|
|
34
|
+
- New how-to guide: _Load subtitles from a plugin_ (reading tracks, selecting with `stale`, uploading SRT/ASS with the limits, the style layer and which plugin's layer the player shows).
|
|
25
35
|
|
|
26
36
|
## 1.5.31
|
|
27
37
|
|
|
@@ -29,8 +39,8 @@
|
|
|
29
39
|
|
|
30
40
|
- **The Commands, Playback and Services categories reach the full reference standard** (they are now in `STRICT_CATEGORIES`, so CI keeps them complete): `Commands`, `Command`, `Host` and their option types; `Player`, `Media`, `Scrub`, `Stream` and the playback state and stream types; `Storage`, `JsonStore`, `SessionStorage`, `Secrets`, `Clipboard`, `System`, `Tools`, `Power`. Every method has an example (what it does and how it works), `@capability`, `@since` and a `@throws` per error code it can reject with, checked against the SDK and the player's source; every type has `@category`, so the API reference lists them under their category instead of "Other". Methods with an explained example: 75 of 191 → 121.
|
|
31
41
|
- Error codes the reference now names, from the player's source: a control while nothing is open is `invalid` (`close` is not: closing with nothing open just succeeds); `setRate` takes 0.05–10 (the player's own keys stay within 0.25–4); `setOutput`'s volume outside 0–200 is `invalid`; `Stream.stop` rejects only when closing the source fails, and releases the stream either way.
|
|
32
|
-
- **Corrections from the review against the player's source.** `stale` means any change of the source since the state a call is based on: another file or a close, but also any pause, seek or speed change by the user or another plugin. A second sequence provider is refused with `conflict` (`sequence-taken`) without failing activation, and the built-in playlist registers first. `VESTRA_FFMPEG` / `VESTRA_FFPROBE` replace the `PATH` search instead of coming before it. More `@throws`: `capability` for `Secrets` and `Clipboard`; for a generated stream `busy` and `unsupported` for `seek` and `setRate`, `busy` for `setPaused` and `unsupported` for `restart`; `replaced`, `busy` and the resolver failures (`backend`, `unsupported`, `unavailable`) for `open`; `capability` for `Command.ready`; `aborted` for `openStream`; `stale` and `unsupported` for `Scrub.seek`; `disabled` and `unsupported` (players before 0.5.5) for session storage; `failed` and `timeout` (macOS) for `System`.
|
|
33
|
-
- New how-to guides:
|
|
42
|
+
- **Corrections from the review against the player's source.** `stale` means any change of the source since the state a call is based on: another file or a close, but also any pause, seek or speed change by the user or another plugin. A second sequence provider is refused with `conflict` (`sequence-taken`) without failing activation, and the built-in playlist registers first. `VESTRA_FFMPEG` / `VESTRA_FFPROBE` replace the `PATH` search instead of coming before it. More `@throws`: `capability` for `Secrets` and `Clipboard`; for a generated stream `busy` and `unsupported` for `seek` and `setRate`, `busy` for `setPaused` and `unsupported` for `restart`; `replaced`, `busy` and the resolver failures (`backend`, `unsupported`, `unavailable`) for `open`; `capability` for `Command.ready`; `aborted` for `openStream`; `stale` and `unsupported` for `Scrub.seek`; `disabled` and `unsupported` (players before 0.5.5) for session storage; `failed` and `timeout` (macOS) for `System`. _Store a plugin's data_ no longer promises that a quit right after `save` loses nothing (a stopping plugin gets about 300 ms).
|
|
43
|
+
- New how-to guides: _Control playback_ (reading and following the state, `stale` and `ifCurrent`, `Media`, core actions, Next and Previous) and _Store a plugin's data_ (settings files, session values, secrets, your own files).
|
|
34
44
|
- `doc-lint`: `--list explained` lists the methods without an explained example; its summary line no longer says "Not enforced yet" (every example has been enforced since 1.5.30).
|
|
35
45
|
- Comments and docs name the player's host files at their new places (`src/host/src/packages/manifest.rs`, `ui/ui_image.rs`, `ui/ui_values.rs`, `playback/effects.rs`, `ui/theme.rs`).
|
|
36
46
|
|
|
@@ -38,7 +48,7 @@
|
|
|
38
48
|
|
|
39
49
|
**Who must change: nobody.** No API changes, `apiVersion` stays `vestra/1`, no player version needed. Only documentation, its checks and the generated `llms-full.txt` change.
|
|
40
50
|
|
|
41
|
-
- **Every example explains what it does and how it works.** The standard, in
|
|
51
|
+
- **Every example explains what it does and how it works.** The standard, in _Maintaining_ ("Examples in reference comments"): an `@example` has a task title, a lead saying what it does and how it works (checked against the SDK and the player's source), code that compiles on its own with comments on the key lines, and error handling where the user can make a call fail. All 159 examples of the API now meet it (none did before this release), including the 65 of the UI category, the test helpers (`TestHost`, `startPlugin`, `HostError`), the limits and tables (`COMMAND_LIMITS`, `MENU_IDS`, `BUILTIN_*`, `BAR_BUTTON_ICONS`, `CAPABILITY_INFO`, `BINDING_INPUTS`, `OSD_LIMITS`, `SESSION_STORAGE_LIMITS`, `SECRET_LIMITS`, `UI_VALUE_LIMITS`, `THEME_PALETTES`) and the 1.5.29 bar buttons (`CommandButton`, `TestHost.barButtons`). Every example now type checks (61 did not). Examples no longer compose shown sentences in TypeScript: OSD and dialog texts are templates with arguments.
|
|
42
52
|
- **Corrections found on the way.** `Subtitles.setStyle`, `SubtitlesState.style` and `SubtitleStyle`: when several plugins set a style layer, the effective one is the layer of the plugin whose manifest id sorts last, not the latest call. `COMMAND_LIMITS`: `add` never throws `limit`; the player refuses the registration (activation fails, or `Scope.onError`). `startPlugin`'s restart example gives both runs the same `dataDirectory` instead of copying the data before `stop()` (which could miss a pending store write). `TestHost.settle` says the debounced store write is not waited for. `TestHost.dialogAnswers` is one queue for every kind of dialog. `TestHost.openElsewhere`: calls that return nothing (`TimelineLayer.set` with `ifCurrent`) drop the write silently instead of rejecting. Resolver examples (`I18n.error`, `FileResolverOptions`, `TestHost.otherResolvers`) use a made-up `.ch8` format instead of `.nes`, which the store plugin VestraNES owns.
|
|
43
53
|
- **`llms-full.txt` carries the examples and `@remarks`** of every declaration and member (hidden setup lines dropped, `{@includeCode}` inlined), so the plugin workshop's model sees them: 536 KB → 693 KB (about 137k → 177k tokens). `llms.txt` wording trimmed to stay under its 9 KB limit (9,134 bytes).
|
|
44
54
|
- **Hidden setup lines**: in an `@example` code block, lines above `// ---cut---` are type checked but not shown on the site or in `llms-full.txt`.
|
|
@@ -55,7 +65,7 @@
|
|
|
55
65
|
- **`TestHost.barButtons`**: the plugin's button as the player shows it (`command`, `icon`, `image`, `order`, `enabled`, `checked`, `tooltip`) with `click()`, which runs the command and rejects while it is disabled or once it is gone; empty after the command is disposed. The test host checks buttons as the player does.
|
|
56
66
|
- **`vestra-plugin check` warns** when a plugin mounts a bottom bar whose Slint file does not import `BarButtons` from `@vestra/bar-buttons.slint`, itself or through the plugin's own Slint files it imports (comments do not count; a warning, the check still passes).
|
|
57
67
|
- `BUILTIN_COMMANDS` gains the player's **Toggle Sidebar** (`host/sidebar`, outside the menu, no default key).
|
|
58
|
-
- Docs:
|
|
68
|
+
- Docs: _Commands_ (concepts) gains _Bottom-bar buttons_; _Commands, menus and bindings_ (reference) gains _Bottom-bar buttons_; _Interface guidelines_ gains _Writing a bottom bar_; _Limits_ gains _Bottom-bar buttons_; _Theme_ gains _Bar buttons_ and the two icons; _Windows, panels…_ points to buttons for a one-click entry point. `llms.txt` and `llms-full.txt` regenerated.
|
|
59
69
|
|
|
60
70
|
## 1.5.28
|
|
61
71
|
|
|
@@ -64,14 +74,14 @@
|
|
|
64
74
|
- **`BUILTIN_COMMANDS` and `BUILTIN_SUBMENUS` follow the player** (its agreement test failed on them):
|
|
65
75
|
- Subtitles 0.4.6: `vestra.subtitles` **Move Up** and **Move Down** have no default keys any more (they were `Alt+ArrowUp` / `Alt+ArrowDown`, which pan the video).
|
|
66
76
|
- The NES emulator is no longer a built-in plugin (it moved to the plugin repository as VestraNES, under **Games ▸ VestraNES**): its three commands (`nes.open`, `nes.stop`, `nes.window`) and the built-in **Emulator** submenu are gone. The player has no built-in Games item.
|
|
67
|
-
- Docs:
|
|
77
|
+
- Docs: _Built-in commands_ regenerated; _Menu guidelines_ names a domain submenu **Games** for an emulator and no longer lists Emulator at the top level. `TestHost.otherResolvers` examples use a neutral id (`example.emulator`). `llms.txt` and `llms-full.txt` regenerated.
|
|
68
78
|
|
|
69
79
|
## 1.5.27
|
|
70
80
|
|
|
71
81
|
**Who must change: nobody.** No API changes, `apiVersion` stays `vestra/1`, no player version needed. Plugins pick the fix up with the player's next SDK upgrade (the player builds the `plugin` object with its own copy); nothing in a plugin changes.
|
|
72
82
|
|
|
73
83
|
- **Generated streams reach their full frame rate on Windows.** A stream from `openStream` or a file resolver (the NES emulator: one 1/60 s picture per `frame()`) played at 23–26 frames a second on Windows instead of 60. The SDK asked the player how much audio the sound card had played, rendered at most one frame, and waited 5 ms before asking again; Windows rounds that wait up to its 15.6 ms timer tick, and with the round trip one pass took about 40 ms, so one frame per pass could not keep up. macOS timers are about 1 ms, which hid it. Each pass now calls `frame()` as many times in a row as it takes to refill the whole lead (about 50 ms of audio, unchanged, so latency is unchanged), at most 16 times, and stops early when a frame carries no audio; and a pass that rendered frames and already took longer than 5 ms is followed at once instead of after another timer tick. A pass that rendered nothing (paused, lead full) still waits 5 ms after it ends, so an idle or paused stream asks the player no more often than before. Measured against a real-time audio clock with the NES core and its homebrew ROM: with a 15–30 ms round trip 1.5.26 rendered 22–32 frames a second and let the device run dry, 1.5.27 renders 60 and keeps it fed; only a round trip near 50 ms (the whole lead) still lets it run dry now and then. Controls (start, pause, seek) still apply between frames and never while one renders, and `stop()` still ends after the frame being rendered.
|
|
74
|
-
- Docs:
|
|
84
|
+
- Docs: _Example: signal generator_ says `frame()` may be called several times back to back. `llms.txt` and `llms-full.txt` regenerated.
|
|
75
85
|
|
|
76
86
|
## 1.5.26
|
|
77
87
|
|
|
@@ -81,7 +91,7 @@
|
|
|
81
91
|
- **`vestra-plugin check`** runs it after the type check and fails on any finding; `build` and `pack` run `check` first. **`check --json`** prints the findings as JSON.
|
|
82
92
|
- **`@vestra-player/sdk/ts-plugin`**, a TypeScript language-service plugin: the same findings as errors in the editor, with the quick fix "Add '<capability>' to manifest.json". List it in `tsconfig.json` (`"plugins": [{ "name": "@vestra-player/sdk/ts-plugin" }]`) and use the workspace's TypeScript (VS Code: **Use Workspace Version**).
|
|
83
93
|
- **One rule with the player.** The SDK carries a copy of the player's generated capability table (`src/host/capabilities.json`: which request needs which capability, by event name or processing stage where it depends on one) and the requests each member sends. `CAPABILITY_INFO` is now built from them; its contents are unchanged. `HOST_CAPABILITIES` (`@vestra-player/sdk/runtime`, for the player's agreement test) is the copy.
|
|
84
|
-
- Docs:
|
|
94
|
+
- Docs: _Capabilities_ gains _Catching a missing capability_; _Getting started_ adds the `tsconfig.json` with the plugin and the VS Code step; _Command line_ (the rule and `--json`); _Subtitles_ and _Capabilities_: changes also need `subtitles.read` (they read the current selection first; the table now says so). `examples/tsconfig.json` lists the plugin. `llms.txt` and `llms-full.txt` regenerated.
|
|
85
95
|
|
|
86
96
|
## 1.5.25
|
|
87
97
|
|
|
@@ -90,7 +100,7 @@
|
|
|
90
100
|
- **`plugin.storage.session`** (`SessionStorage`, player 0.5.5; user ruling of 2026-10-27, after Chrome's `chrome.storage.session`): `get(key)`, `set(key, value)`, `delete(key)` and `keys()` of JSON values that live as long as the **player**, not the plugin's process. They survive every restart of the plugin within one player session (a reload, also of a development load, an upgrade, a crash, a failed start) and are cleared when the user disables or uninstalls the plugin, and when the player quits. Per plugin, no capability. Keys are 1–1024 bytes of well-formed text; a plugin's keys, values (as compact JSON) and 128 bytes per key are at most 1 MiB together, and a `set` beyond fails with `limit` and changes nothing. One value must also fit in one 1 MiB message. A value `JSON.stringify` cannot write, or one with a lone surrogate in a string, fails with `invalid`; the order of an object's properties is not kept; `get` returns `undefined` for a missing key. Each `set` is one acknowledged request, without debounce. Players before 0.5.5 reject the calls with `unsupported`, so set `"minPlayerVersion": "0.5.5"`.
|
|
91
101
|
- **`SESSION_STORAGE_LIMITS`** (`@vestra-player/sdk/testing` and `/runtime`): `keyBytes` 1024, `bytes` 1 MiB, `entryBytes` 128.
|
|
92
102
|
- **`TestHost.sessionStorage`** (the player's store for the plugin, with the player's key rule and quota, objects stored with sorted properties as the player returns them) and **`startPlugin(..., { sessionStorage })`** to start with the values an earlier run left, to test a restart within one player session. `TestHost` now refuses any request over the 1 MiB message limit with `limit`, as the plugin runner does.
|
|
93
|
-
- Docs:
|
|
103
|
+
- Docs: _How a plugin runs_ gains _Restarts: activate can run more than once_: when the player restarts a plugin's process, and "keep state that must survive a restart in session storage (or persistent storage)", with a table of what survives what. _Test a plugin_ (testing a restart with session values), _Reference: limits_ (`SESSION_STORAGE_LIMITS`). `llms.txt` and `llms-full.txt` regenerated.
|
|
94
104
|
|
|
95
105
|
## 1.5.24
|
|
96
106
|
|
|
@@ -98,11 +108,11 @@
|
|
|
98
108
|
|
|
99
109
|
- **`VideoNode` `{ kind: "deinterlace", when: "auto", output: "field" }`** (player 0.5.4; requested by the video plugin, DEINT-AUTO-1: user ruling of 2026-10-27, Auto deinterlacing must cost nothing on progressive files). The player's own deinterlacer instead of BWDIF in the chain: decided per frame from the decoder's interlaced flag, with the decoder's hardware deinterlacer where frames stay on the GPU, one output frame per field; progressive frames pass untouched. It needs no frames in memory, so a chain with only this node keeps zero-copy hardware decoding, where any other node switches to a copy back to memory. It runs before every plugin's chain wherever it is written, and it is shared: on while any plugin's chain has one, off when the last one is removed or its plugin stops. `output: "frame"` with it, or another `deinterlace` node in the same chain, fails with `invalid`. Processed snapshots and exports deinterlace the same frames with BWDIF at field rate. Players before 0.5.4 refuse `auto` with `invalid`, so set `"minPlayerVersion": "0.5.4"`.
|
|
100
110
|
- **`TestHost`** validates the new value as the player does (`nodeProblem`).
|
|
101
|
-
- Docs:
|
|
111
|
+
- Docs: _Processing_ (ordering and side effects). `llms.txt` and `llms-full.txt` regenerated.
|
|
102
112
|
|
|
103
113
|
## 1.5.23
|
|
104
114
|
|
|
105
|
-
**Who must change: nobody.** Republishes 1.5.22 with a clean API documentation build; 1.5.22 was tagged but never published to npm: TypeDoc treats warnings as errors and failed on two `{@link}` labels in
|
|
115
|
+
**Who must change: nobody.** Republishes 1.5.22 with a clean API documentation build; 1.5.22 was tagged but never published to npm: TypeDoc treats warnings as errors and failed on two `{@link}` labels in _Playback_ that contained a brace (`Scrub.seek(seconds, { keyframes })`). Documentation only; the code is the same as 1.5.22 below.
|
|
106
116
|
|
|
107
117
|
## 1.5.22
|
|
108
118
|
|
|
@@ -110,7 +120,7 @@
|
|
|
110
120
|
|
|
111
121
|
- **`Scrub.seek(seconds, { keyframes?: boolean })`** (player 0.5.3; requested by the playback plugin for the seek bar, user ruling of 2026-10-27: the bar follows the same "Jump to keyframes only" switch as the arrow keys, PotPlayer style, no second exact seek). With `keyframes: true` the seek lands on the last keyframe at or before `seconds` (mpv `absolute+keyframes`) instead of decoding up to the exact frame: the picture appears in tens of milliseconds on any file, up to one keyframe interval before the target, and the reported position is the keyframe's. Omitted or `false`: exact, as before. The option goes on the wire only when true (`control: { kind: "seek", seconds, keyframes: true }`); players before 0.5.3 ignore the field and seek exactly, so set `"minPlayerVersion": "0.5.3"` when you rely on it. The player sends a repeated target of the same gesture only once while that seek is still running (a click is a press and a release), keyed on the target and the mode: an exact seek to the target of a keyframe seek is always sent.
|
|
112
122
|
- **`TestHost.seeks`**: every seek a plugin submitted (`player.seek`, `Scrub.seek`), newest last, as `{ seconds, keyframes }`. The test host lands on the exact target in both modes; a non-boolean `keyframes` is `invalid`.
|
|
113
|
-
- Docs:
|
|
123
|
+
- Docs: _Playback_ (scrubs and the keyframe switch); _Windows, panels, settings pages_ gains a pitfall: an `if` that flips under the pointer (a hover highlight created and destroyed on every hover change) rebuilds the window's accessibility tree and drops the video to 10–15 frames a second while the pointer moves; keep the element and change `visible` or `opacity`. `llms.txt` and `llms-full.txt` regenerated.
|
|
114
124
|
|
|
115
125
|
## 1.5.21
|
|
116
126
|
|
|
@@ -121,7 +131,7 @@
|
|
|
121
131
|
- **`ToolTipArea` no longer swallows a press** (`slint/vestra/widgets.slint`, the player's widget library; bug fix): the tip was a `PopupWindow`, so while it showed, the first press anywhere only closed it, and it took the keyboard focus. It is now Slint's input-transparent tooltip: every press reaches the control under the pointer. It shows after about half a second (was 600 ms), and `showing` follows that delay closely. No API change.
|
|
122
132
|
- **Icons `fullscreen` and `exit-fullscreen`** in `slint/vestra/icons.slint` (14 × 12, the V-Line style).
|
|
123
133
|
- **`TestHost`**: `host.run("fullscreen")` flips `host.mainWindow.fullscreen` and emits `window.state` (recorded in `coreRuns`); `host/play-pause` declares `Space` and `Mouse:LeftClick`.
|
|
124
|
-
- Docs:
|
|
134
|
+
- Docs: _Commands_ (the player's mouse defaults), _Playback_ (a fullscreen button), the Theme and widgets example (the tooltip); generated tables and `llms.txt` / `llms-full.txt` regenerated.
|
|
125
135
|
|
|
126
136
|
## 1.5.20
|
|
127
137
|
|
|
@@ -139,7 +149,7 @@ Images in list rows, restarting a plugin, and keyframe step seeks (player 0.5.1)
|
|
|
139
149
|
- **`Player.setStepSeek({ seconds, keyframes })`** (player 0.5.1): with `keyframes: true` the step seeks land on a keyframe (forward at or after position + step, backward at or before position − step, always moving), presses during a seek add up from the landed position, and the on-screen text shows the landed time. Sources without moving video seek exactly; the seek bar, `player.seek`, scrubs, chapters, loops and `player.step` are unaffected. A non-boolean `keyframes` is `invalid`.
|
|
140
150
|
- **`Host.run("seek-backward" | "seek-forward", { seconds })`** (player 0.5.1): one step seek of another step, with the core's checks, mode, accumulation and text. In keyframe mode it resolves once the seek has landed. `seconds` with another action, or outside 0.1–3600 in tenths, is `invalid`. Older players ignore `seconds`: set `"minPlayerVersion": "0.5.1"`.
|
|
141
151
|
- **`TestHost`**: nested images are recorded in `host.images` (with `maxSide`); `ui.remoteImage` is served from `host.remoteImages`; `packages.reload` is modelled (`starting`, then `running`; `packageCalls` method `"reload"`); `stepSeekKeyframes`, the test data `keyframes` and the log `coreSeeks` model keyframe step seeks, and `host.run` takes `seconds`. The test host lands each press at once (the player completes presses that add up together, with one text).
|
|
142
|
-
- Docs:
|
|
152
|
+
- Docs: _Windows, panels, settings pages_ (images in rows), _Playback_ (Step seeks: exact or keyframe); `llms.txt` and `llms-full.txt` regenerated.
|
|
143
153
|
|
|
144
154
|
## 1.5.18
|
|
145
155
|
|
|
@@ -147,7 +157,7 @@ Fixes for Windows and for a vendored SDK, and built-ins that can no longer be di
|
|
|
147
157
|
|
|
148
158
|
- **`TestHost` streams on Windows**: `openStream`'s default transport listened on a socket file in the temp folder, which Windows refuses (`listen EACCES`), so stream tests failed there. It now listens on a named pipe (`\\.\pipe\vestra-test-host-…-producer-<id>`) on Windows and on a Unix socket elsewhere. `producerTransport = "tcp"` still gives the player's own Windows transport (loopback TCP with a token).
|
|
149
159
|
- **The CLI runs from an SDK outside any `node_modules`** (a copy with TypeScript on `NODE_PATH`, as the player's AI workshop vendors it): `dist/i18n-tools.js` resolved TypeScript with a bare ES module import, which ignores `NODE_PATH`, so `vestra-plugin` stopped at start with `ERR_MODULE_NOT_FOUND`. It now resolves TypeScript as the rest of the CLI does.
|
|
150
|
-
- **Built-in plugins cannot be disabled** (user ruling, 2026-10-26: users manage third-party plugins only). The player and `TestHost` answer `packages.setEnabled(builtin, false)` with `unsupported`; a built-in an older player let the user disable runs again after the upgrade. `PluginInfo.builtin`, `Packages.setEnabled` and
|
|
160
|
+
- **Built-in plugins cannot be disabled** (user ruling, 2026-10-26: users manage third-party plugins only). The player and `TestHost` answer `packages.setEnabled(builtin, false)` with `unsupported`; a built-in an older player let the user disable runs again after the upgrade. `PluginInfo.builtin`, `Packages.setEnabled` and _Migrating off package rollback_ say so; a plugin manager offers no action for a built-in.
|
|
151
161
|
|
|
152
162
|
## 1.5.17
|
|
153
163
|
|
|
@@ -158,7 +168,7 @@ Fixes for Windows and for a vendored SDK, and built-ins that can no longer be di
|
|
|
158
168
|
- **Removed from `CAPABILITY_INFO["plugins.manage"].apis`**: `"Packages.rollback"`; the capability is unchanged otherwise.
|
|
159
169
|
- **`TestHost`**: `host.packages` entries have no `previousVersion` field (delete it from test data), and there is no `packages.rollback` handler.
|
|
160
170
|
- Unchanged: `revisionConflict`, and the `restoring` and `failedDegraded` statuses of `PackageChange`, which belong to recovery from a failed install.
|
|
161
|
-
- Docs: the Capabilities reference loses the deprecation note;
|
|
171
|
+
- Docs: the Capabilities reference loses the deprecation note; _Migrating off package rollback_ says what 1.5.17 removed; `llms.txt` and `llms-full.txt` regenerated.
|
|
162
172
|
|
|
163
173
|
## 1.5.16
|
|
164
174
|
|
|
@@ -169,7 +179,7 @@ Fixes for Windows and for a vendored SDK, and built-ins that can no longer be di
|
|
|
169
179
|
- **Built-in plugins** run in place from the player installation: `PluginInfo.directory` of a built-in is `<install resources>/plugins/<id>` and read-only (plugins write only under `plugin.dataDirectory`); they can be disabled and enabled, not uninstalled (`unsupported`, as before).
|
|
170
180
|
- **`TestHost`** matches the player: `packages.list` and `packages.changed` report `previousVersion: null` for every plugin, an install no longer records the replaced version, and `packages.rollback` rejects with `unsupported` ("unknown package method"). The `previousVersion` field of `host.packages` entries is deprecated and ignored; it stays required (as `PluginInfo.previousVersion` does), so existing test code compiles unchanged.
|
|
171
181
|
- `"Packages.rollback"` stays in `CAPABILITY_INFO["plugins.manage"].apis` until 1.5.17; the Capabilities reference notes the deprecation.
|
|
172
|
-
- Docs: the new how-to
|
|
182
|
+
- Docs: the new how-to _Migrating off package rollback (1.5.16)_; _Publish a plugin_ no longer mentions a roll back button, installing an older version from the store or the user's rollback as a safety net; the data directory is "kept across restarts, disabling and upgrades"; `revisionConflict`, `PackageChange` and `PackageChangeStatus` speak of installs and uninstalls (the statuses are unchanged).
|
|
173
183
|
|
|
174
184
|
## 1.5.15
|
|
175
185
|
|
|
@@ -191,7 +201,7 @@ Fixes for Windows and for a vendored SDK, and built-ins that can no longer be di
|
|
|
191
201
|
- **Seven groups**: Tutorials, Concepts, How-to, Guidelines, Examples, Reference and API. `scripts/doc-structure.mjs` lists every page and why it is a page of its own; `npm run docs:check` fails on a page that is not listed, a page without a `description`, a placeholder ("coming soon"), a nested folder, a stale generated table or a page far over its size limit.
|
|
192
202
|
- **Reference** (new): Manifest (every field and the player version each newer part of the API needs), Capabilities, Limits, Commands, menus and bindings (built-in menus, submenu keys in use, the built-in commands and their default keys, binding syntax and names, core actions, the keys the system takes first), Platforms, Theme (tokens with their current `vestra` values, fonts, icons, widgets) and Command line. Their tables are generated from the SDK's code and Slint files (`npm run docs:tables`), so they cannot drift from it.
|
|
193
203
|
- **Guidelines** (new): interface and menu rules.
|
|
194
|
-
- **New pages**: Concepts
|
|
204
|
+
- **New pages**: Concepts _How a plugin runs_, _Commands, menus and key bindings_, _Playback state, controls and sources_, _Video and audio processing_; How-to _Test a plugin_, _Publish a plugin_; _Translate a plugin_ gains the language list and the machine translation review. Getting started and the README link to the Reference instead of repeating its tables; the old compatibility tables are replaced by the generated player version table.
|
|
195
205
|
- **New exports** for the generated tables and for tests (`@vestra-player/sdk/testing`): `CAPABILITY_INFO` (per capability: the members it opens, the capabilities they also need, the store's wording, whether the store highlights it; `CAPABILITIES` is derived from it), `MESSAGE_LIMIT` (1 MiB per message), `OSD_LIMITS`, `SECRET_LIMITS`, `BINDING_INPUTS` (the key and mouse names a binding is made of), `BUILTIN_SUBMENUS` (the submenu keys the built-in plugins use), `BUILTIN_COMMANDS` (the built-in commands with their menu paths and default keys) and `THEME_PALETTES` (the colours of `vestra` and `vestra-light` by `Theme` token). The player's tests check them against the player.
|
|
196
206
|
- **`TestHost`** enforces the information block's limits like the player: `OsdPanel.set` with more than 32 lines or 4096 bytes (after `maxColumns` is applied) fails with `invalid`.
|
|
197
207
|
- `llms.txt` lists every capability and points at every page of the site in `llms-full.txt`, which now contains all of them.
|
|
@@ -286,6 +296,7 @@ Fixes for Windows and for a vendored SDK, and built-ins that can no longer be di
|
|
|
286
296
|
|
|
287
297
|
- Only the plugins built into the player are official plugins; the ids `vestra` and `vestra.*` (any case) are reserved for them. Every other plugin is a third-party plugin, including those hosted in the official plugin repository, and uses `<GitHub user or organization>.<plugin name>`.
|
|
288
298
|
- `PluginError` lists the code `reserved`: `packages.validate`, `packages.validateArchive`, `PackageCandidate.trial` and `PackageCandidate.install` refuse a package with a reserved id. Development loads (`vestra-player --plugin DIR`) still accept one, with a warning, so built-in plugins can be developed; they are never installed.
|
|
299
|
+
|
|
289
300
|
## 1.5.4
|
|
290
301
|
|
|
291
302
|
**Markdown in list rows** (the player after 0.4.0). Additive; `apiVersion` stays `vestra/1`, and plugins written for 1.5.3 need no change.
|
|
@@ -296,6 +307,7 @@ Fixes for Windows and for a vendored SDK, and built-ins that can no longer be di
|
|
|
296
307
|
- **Code copy, opt in.** With `code-copy: true`, code blocks get a 复制 button that reports `copy-code(code)` with the block's text; the plugin copies it (`plugin.clipboard.writeText`). Off by default: views that do not set it look and behave as before.
|
|
297
308
|
- **Cheaper updates** (player): writing a value again with unchanged documents does not parse them again (a new reference for the same text included), and a value with documents is updated in place where its shape allows: changed rows, rows added or removed at the end, and only the changed blocks of a changed document. Streaming a reply into the last row of a 200-message list therefore keeps the list, its scroll position and the other rows. Nothing to change in plugins.
|
|
298
309
|
- `TestHost` keeps nested documents in the view's properties as they were sent; `host.markdown(value)` reads one back from a field (`host.markdown(rows[0].body)`).
|
|
310
|
+
|
|
299
311
|
## 1.5.3
|
|
300
312
|
|
|
301
313
|
**File resolvers: local files as generated streams** (player 0.4.1). Additive; `apiVersion` stays `vestra/1`. Plugins that register one set `"minPlayerVersion": "0.4.1"`: an older player refuses the declaration (`invalid`).
|
package/dist/api-requests.js
CHANGED
|
@@ -140,4 +140,7 @@ export const API_REQUESTS = Object.freeze({
|
|
|
140
140
|
"Secrets.get": [{ method: "secrets.get" }],
|
|
141
141
|
"Secrets.set": [{ method: "secrets.set" }],
|
|
142
142
|
"Secrets.delete": [{ method: "secrets.delete" }],
|
|
143
|
+
"Rendering.state": [{ method: "rendering.state" }],
|
|
144
|
+
"Rendering.setPreference": [{ method: "rendering.setPreference" }],
|
|
145
|
+
"Rendering.restartPlayer": [{ method: "rendering.restart" }],
|
|
143
146
|
});
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -48,8 +48,8 @@ export interface CapabilityInfo {
|
|
|
48
48
|
*
|
|
49
49
|
* A member not listed under any capability needs none (`plugin.tools`,
|
|
50
50
|
* `plugin.storage`, `plugin.system`, `plugin.app`, `plugin.ui`,
|
|
51
|
-
* `plugin.i18n`, reading `plugin.window.state`, `plugin.appearance.theme
|
|
52
|
-
*
|
|
51
|
+
* `plugin.i18n`, reading `plugin.window.state`, `plugin.appearance.theme`,
|
|
52
|
+
* `plugin.packages.list` and `plugin.rendering.state`).
|
|
53
53
|
*
|
|
54
54
|
* @example Check in a test that the manifest declares every required capability
|
|
55
55
|
* The test fails, naming the missing capability, when the manifest declares
|
package/dist/capabilities.js
CHANGED
|
@@ -10,8 +10,8 @@ import { capabilityInfo } from "./capability-rules.js";
|
|
|
10
10
|
*
|
|
11
11
|
* A member not listed under any capability needs none (`plugin.tools`,
|
|
12
12
|
* `plugin.storage`, `plugin.system`, `plugin.app`, `plugin.ui`,
|
|
13
|
-
* `plugin.i18n`, reading `plugin.window.state`, `plugin.appearance.theme
|
|
14
|
-
*
|
|
13
|
+
* `plugin.i18n`, reading `plugin.window.state`, `plugin.appearance.theme`,
|
|
14
|
+
* `plugin.packages.list` and `plugin.rendering.state`).
|
|
15
15
|
*
|
|
16
16
|
* @example Check in a test that the manifest declares every required capability
|
|
17
17
|
* The test fails, naming the missing capability, when the manifest declares
|
|
@@ -343,6 +343,17 @@ export const HOST_CAPABILITIES = {
|
|
|
343
343
|
residency: "none",
|
|
344
344
|
risky: true,
|
|
345
345
|
},
|
|
346
|
+
{
|
|
347
|
+
label: "Choose GPU or software rendering and restart the player",
|
|
348
|
+
methods: [
|
|
349
|
+
{ name: "rendering.setPreference", needs: "rendering.manage" },
|
|
350
|
+
{ name: "rendering.restart", needs: "rendering.manage" },
|
|
351
|
+
],
|
|
352
|
+
name: "rendering.manage",
|
|
353
|
+
requires: [],
|
|
354
|
+
residency: "none",
|
|
355
|
+
risky: true,
|
|
356
|
+
},
|
|
346
357
|
],
|
|
347
358
|
uncapable: [
|
|
348
359
|
{ name: "packages.list", needs: null },
|
|
@@ -356,5 +367,6 @@ export const HOST_CAPABILITIES = {
|
|
|
356
367
|
{ name: "storage.session.set", needs: null },
|
|
357
368
|
{ name: "storage.session.delete", needs: null },
|
|
358
369
|
{ name: "storage.session.keys", needs: null },
|
|
370
|
+
{ name: "rendering.state", needs: null },
|
|
359
371
|
],
|
|
360
372
|
};
|
package/dist/plugin/scope.d.ts
CHANGED
|
@@ -32,6 +32,7 @@ export declare class ScopeImpl implements Plugin {
|
|
|
32
32
|
readonly storage: import("./types.ts").Storage;
|
|
33
33
|
readonly system: import("./types.ts").System;
|
|
34
34
|
readonly secrets: import("./types.ts").Secrets;
|
|
35
|
+
readonly rendering: import("./types.ts").Rendering;
|
|
35
36
|
readonly window: import("./types.ts").MainWindow;
|
|
36
37
|
readonly bindings: import("./types.ts").Bindings;
|
|
37
38
|
readonly ui: import("./types.ts").Ui;
|
package/dist/plugin/scope.js
CHANGED
|
@@ -17,7 +17,7 @@ import { packages } from "./packages.js";
|
|
|
17
17
|
import { osd } from "./osd.js";
|
|
18
18
|
import { mainWindow } from "./window.js";
|
|
19
19
|
import { bindingsApi } from "./bindings.js";
|
|
20
|
-
import { clipboard, host, power, secrets, storage, system, tools, } from "./services.js";
|
|
20
|
+
import { clipboard, host, power, rendering, secrets, storage, system, tools, } from "./services.js";
|
|
21
21
|
export class ScopeImpl {
|
|
22
22
|
runtime;
|
|
23
23
|
#parent;
|
|
@@ -63,6 +63,7 @@ export class ScopeImpl {
|
|
|
63
63
|
storage = storage(this);
|
|
64
64
|
system = system(this);
|
|
65
65
|
secrets = secrets(this);
|
|
66
|
+
rendering = rendering(this);
|
|
66
67
|
window = mainWindow(this);
|
|
67
68
|
bindings = bindingsApi(this);
|
|
68
69
|
ui = ui(this);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ScopeImpl } from "./scope.ts";
|
|
2
|
-
import type { Clipboard, Host, Power, Secrets, Storage, System, Tools } from "./types.ts";
|
|
2
|
+
import type { Clipboard, Host, Power, Rendering, Secrets, Storage, System, Tools } from "./types.ts";
|
|
3
3
|
export declare const clipboard: (scope: ScopeImpl) => Clipboard;
|
|
4
4
|
export declare const host: (scope: ScopeImpl) => Host;
|
|
5
5
|
export declare const power: (scope: ScopeImpl) => Power;
|
|
@@ -112,3 +112,4 @@ export declare const SECRET_LIMITS: Readonly<{
|
|
|
112
112
|
readonly names: 256;
|
|
113
113
|
}>;
|
|
114
114
|
export declare const secrets: (scope: ScopeImpl) => Secrets;
|
|
115
|
+
export declare const rendering: (scope: ScopeImpl) => Rendering;
|
package/dist/plugin/services.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared services: the clipboard, sleep prevention, showing and opening files
|
|
3
|
-
* and links, secrets
|
|
4
|
-
* settings files run in this plugin's own process.
|
|
3
|
+
* and links, secrets, session storage and the rendering choice go to the
|
|
4
|
+
* host; external tools and settings files run in this plugin's own process.
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync, readFileSync, renameSync } from "node:fs";
|
|
7
7
|
import { mkdir, open, rename, rm, stat } from "node:fs/promises";
|
|
@@ -477,3 +477,23 @@ export const secrets = (scope) => {
|
|
|
477
477
|
delete: async (name) => scope.runtime.host.secrets.delete(named(name)),
|
|
478
478
|
};
|
|
479
479
|
};
|
|
480
|
+
const PREFERENCES = new Set(["auto", "gpu", "software"]);
|
|
481
|
+
export const rendering = (scope) => {
|
|
482
|
+
const manage = () => {
|
|
483
|
+
if (!scope.runtime.can("rendering.manage"))
|
|
484
|
+
throw failure("capability", "rendering.manage must be declared in manifest");
|
|
485
|
+
};
|
|
486
|
+
return {
|
|
487
|
+
state: async () => (await scope.runtime.host.rendering.state()),
|
|
488
|
+
async setPreference(preference) {
|
|
489
|
+
manage();
|
|
490
|
+
if (typeof preference !== "string" || !PREFERENCES.has(preference))
|
|
491
|
+
throw failure("invalid", 'the rendering preference is "auto", "gpu" or "software"');
|
|
492
|
+
return (await scope.runtime.host.rendering.setPreference(preference));
|
|
493
|
+
},
|
|
494
|
+
async restartPlayer() {
|
|
495
|
+
manage();
|
|
496
|
+
await scope.runtime.host.rendering.restart();
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
};
|
package/dist/plugin/testing.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { IMAGE_LIMITS, UI_VALUE_LIMITS } from "./values.ts";
|
|
|
15
15
|
import type { CommandDeclaration } from "../commands.ts";
|
|
16
16
|
import type { SourceState } from "../source.ts";
|
|
17
17
|
import type { SubtitleState } from "../subtitles.ts";
|
|
18
|
-
import type { RenderSettings, ViewSettings } from "./types.ts";
|
|
18
|
+
import type { RenderingMode, RenderingPreference, RenderingReason, RenderSettings, ViewSettings } from "./types.ts";
|
|
19
19
|
import type { MediaJob } from "../media.ts";
|
|
20
20
|
import type { LanguagePreference, Locale, Plugin, TemplateArgs, TextValue, ThemeId } from "./types.ts";
|
|
21
21
|
import { LOCALES, LIST_SEPARATORS, TEXT_LIMITS } from "../i18n.ts";
|
|
@@ -469,7 +469,7 @@ export interface MarkdownWire {
|
|
|
469
469
|
* {@link TestHost.app}, {@link TestHost.systemInfo},
|
|
470
470
|
* {@link TestHost.chapters}, {@link TestHost.audioDevices},
|
|
471
471
|
* {@link TestHost.secrets}, {@link TestHost.clipboard},
|
|
472
|
-
* {@link TestHost.tools}.
|
|
472
|
+
* {@link TestHost.tools}, {@link TestHost.rendering}.
|
|
473
473
|
* - **Assert** on records: {@link TestHost.osd}, {@link TestHost.calls},
|
|
474
474
|
* {@link TestHost.sent}, {@link TestHost.conflicts},
|
|
475
475
|
* {@link TestHost.layout}, {@link TestHost.menus}, and many more.
|
|
@@ -1306,7 +1306,8 @@ export declare class TestHost implements Transport {
|
|
|
1306
1306
|
/**
|
|
1307
1307
|
* What the player pushes when mpv cannot compile a shader (code `failed`)
|
|
1308
1308
|
* or switches to software rendering (`unsupported`, which also sets
|
|
1309
|
-
* `videoRender.gpu` to false
|
|
1309
|
+
* `videoRender.gpu` to false and `rendering.reason` to `window-fallback`
|
|
1310
|
+
* with `message`). Pushed only when this plugin is the owner.
|
|
1310
1311
|
*
|
|
1311
1312
|
* @example Make a shader fail to compile
|
|
1312
1313
|
* The plugin's render options report `failed`, as when mpv rejects a
|
|
@@ -1611,9 +1612,10 @@ export declare class TestHost implements Transport {
|
|
|
1611
1612
|
/**
|
|
1612
1613
|
* What `plugin.app.system()` reports. Its `rendering` follows
|
|
1613
1614
|
* {@link TestHost.videoRender | videoRender.gpu} (`"gpu"` or
|
|
1614
|
-
* `"software"`), so one switch covers the render options and the fact
|
|
1615
|
+
* `"software"`), so one switch covers the render options and the fact;
|
|
1616
|
+
* its `renderingReason` is {@link TestHost.rendering | rendering.reason}.
|
|
1615
1617
|
*/
|
|
1616
|
-
systemInfo: Omit<WireSystemInfo, "rendering">;
|
|
1618
|
+
systemInfo: Omit<WireSystemInfo, "rendering" | "renderingReason">;
|
|
1617
1619
|
/**
|
|
1618
1620
|
* What `plugin.app` reports. Set it before {@link startPlugin}: the plugin
|
|
1619
1621
|
* receives it once at start-up.
|
|
@@ -1633,6 +1635,74 @@ export declare class TestHost implements Transport {
|
|
|
1633
1635
|
slint: string;
|
|
1634
1636
|
};
|
|
1635
1637
|
};
|
|
1638
|
+
/**
|
|
1639
|
+
* The player's rendering choice behind `plugin.rendering` (player
|
|
1640
|
+
* 0.5.13). The mode drawing now is {@link TestHost.videoRender |
|
|
1641
|
+
* videoRender.gpu}, as for `plugin.app.system()`; the rest is preset here
|
|
1642
|
+
* and changed by the plugin's calls, as the player would:
|
|
1643
|
+
*
|
|
1644
|
+
* - `setPreference` saves `preference` and never changes the mode. It
|
|
1645
|
+
* refuses `"gpu"` while `gpu.available` is false: with `unsupported`
|
|
1646
|
+
* when `gpu.reason.code` is `no-gpu-backend` or `headless`, otherwise
|
|
1647
|
+
* with `unavailable`, the reason's message in both cases.
|
|
1648
|
+
* - The next start uses `"software"` for the `"software"` choice, else
|
|
1649
|
+
* software with `gpu.reason` while the GPU is not available, else the
|
|
1650
|
+
* GPU (code `preference` for a `"gpu"` choice, `default` for
|
|
1651
|
+
* `"auto"`). `restartPending` compares that with `startedWith`.
|
|
1652
|
+
* - `restartPlayer` counts in `restarts` and leaves the plugin running
|
|
1653
|
+
* (call `stop()` yourself); a second one rejects with `busy`, as a
|
|
1654
|
+
* restart is then under way.
|
|
1655
|
+
*
|
|
1656
|
+
* Inject other refusals (`failed` for an unwritable settings file or a
|
|
1657
|
+
* new process that could not start) with `host.handle("rendering.setPreference", …)`
|
|
1658
|
+
* or `host.handle("rendering.restart", …)`. The defaults are a macOS
|
|
1659
|
+
* player on the GPU: `"auto"`, available, started on the GPU.
|
|
1660
|
+
*
|
|
1661
|
+
* @example Test a GPU switch on a system without a GPU renderer
|
|
1662
|
+
* The plugin's GPU menu item is greyed out with the player's reason when
|
|
1663
|
+
* the GPU cannot be chosen. The host is preset before the plugin starts,
|
|
1664
|
+
* because the plugin reads the state while activating; software
|
|
1665
|
+
* rendering is `videoRender.gpu = false`, and `gpu` says why the GPU
|
|
1666
|
+
* cannot be chosen.
|
|
1667
|
+
* ```ts
|
|
1668
|
+
* import assert from "node:assert/strict";
|
|
1669
|
+
* import { startPlugin, TestHost } from "@vestra-player/sdk/testing";
|
|
1670
|
+
* import activate from "../main.ts";
|
|
1671
|
+
*
|
|
1672
|
+
* const host = new TestHost();
|
|
1673
|
+
* host.videoRender.gpu = false; // software draws now (Windows)
|
|
1674
|
+
* host.rendering.startedWith = "software";
|
|
1675
|
+
* host.rendering.reason = { code: "no-gpu-backend", message: "No GPU renderer on Windows" };
|
|
1676
|
+
* host.rendering.gpu = { available: false, reason: host.rendering.reason };
|
|
1677
|
+
* const { stop } = await startPlugin(activate, {
|
|
1678
|
+
* directory: new URL("..", import.meta.url),
|
|
1679
|
+
* host,
|
|
1680
|
+
* });
|
|
1681
|
+
* const item = host.command("gpu-rendering");
|
|
1682
|
+
* assert.equal(item.state.enabled, false);
|
|
1683
|
+
* assert.equal(item.state.reason, "No GPU renderer on Windows");
|
|
1684
|
+
* await stop();
|
|
1685
|
+
* ```
|
|
1686
|
+
*
|
|
1687
|
+
* @since 1.5.35
|
|
1688
|
+
*/
|
|
1689
|
+
readonly rendering: {
|
|
1690
|
+
/** Why the mode drawing now is what it is (`RenderingState.reason`, `SystemInfo.renderingReason`). */
|
|
1691
|
+
reason: RenderingReason;
|
|
1692
|
+
/** The saved choice: `"auto"`, `"gpu"` or `"software"`. */
|
|
1693
|
+
preference: RenderingPreference;
|
|
1694
|
+
/** The mode this run started with; `restartPending` compares the next start's mode with it. */
|
|
1695
|
+
startedWith: RenderingMode;
|
|
1696
|
+
/** Whether the plugin may choose `"gpu"`, and why not (`null` while available). */
|
|
1697
|
+
gpu: {
|
|
1698
|
+
/** `false`: `setPreference("gpu")` is refused. */
|
|
1699
|
+
available: boolean;
|
|
1700
|
+
/** Why the GPU cannot be chosen; `null` while it can. */
|
|
1701
|
+
reason: RenderingReason | null;
|
|
1702
|
+
};
|
|
1703
|
+
/** Accepted `restartPlayer` calls. */
|
|
1704
|
+
restarts: number;
|
|
1705
|
+
};
|
|
1636
1706
|
/** What `player.chapters()` lists while media is ready. */
|
|
1637
1707
|
chapters: {
|
|
1638
1708
|
/** The chapter title, `null` without one. */
|