@vestra-player/sdk 1.5.44 → 1.5.46

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/llms-full.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @vestra-player/sdk 1.5.44: full reference
1
+ # @vestra-player/sdk 1.5.46: full reference
2
2
 
3
3
  > Everything needed to write a Vestra Player plugin (plugin API `vestra/1`): the guides, every public declaration of `@vestra-player/sdk` and `@vestra-player/sdk/testing` with its documentation, and the complete source of the example plugins. Generated from the SDK's sources by scripts/llms.mjs; the short index is llms.txt.
4
4
 
@@ -1903,6 +1903,8 @@ host.handle("processing.replace", () => {
1903
1903
 
1904
1904
  `await host.settle()` after every action that makes the plugin talk to the player; `host.settle(400)` also lets debounces and timers due by then fire. Then assert on what the host recorded: `host.window(id).props` (kebab-case names), `host.command(id).state`, `host.osd`, `host.source`.
1905
1905
 
1906
+ `settle` does not wait for the once-a-second full update pass (SDK 1.5.45): after `host.setSource(...)` only the bindings that read a changed field of `plugin.player.state` run again. A value your plugin changed outside a handler (a timer, a promise continuation, a child process) without `plugin.update()` shows in the next full pass, at most about a second later; call `plugin.update()` in the plugin after such a change, or wait `await host.settle(1000)` in the test.
1907
+
1906
1908
  `settle` waits only for calls to the player. Work the plugin does on its own (reading or parsing a file, a child process, a network request) can finish later on a loaded machine; poll the value you assert on instead:
1907
1909
 
1908
1910
  ```ts nocheck
@@ -2282,6 +2284,7 @@ The table lists the API whose documentation names a player version, newest last:
2282
2284
  | 0.5.20 | VideoShaders |
2283
2285
  | 0.5.21 | Scope.when, When, Bindings.onChange |
2284
2286
  | 0.5.25 | UpscalingReason.cause, UpscalingReason.plugins, UpscalingDecodingCause, UpscalingPlugin, UpscalingVideoSize, UpscalingState.video, UpscalingState.area, UpscalingState.gpu, UpscalingState.hwdecCurrent, UpscalingState.inChain, VideoUpscaling.onChange |
2287
+ | 0.6.0 | PropValue |
2285
2288
 
2286
2289
  The Slint library carries no version tags: `Theme.danger` needs player 0.3.0; `@vestra/icons.slint` and the palette `vestra-light` need 0.4.0 (Theme).
2287
2290
 
@@ -2350,7 +2353,7 @@ The check follows types, not names: `plugin.player.seek`, `const { player } = pl
2350
2353
 
2351
2354
  ## Reference: Limits
2352
2355
 
2353
- The player bounds what one request may cost it. The limits are guards against runaway plugins, not sizes to aim for: a plugin that needs to come close to one usually needs a different design (send the rows near the view, not the whole list). The SDK checks the same limits before it sends a request, so a plugin's own tests hit them too (TestHost enforces them). A name in code style is a constant you can import from `@vestra-player/sdk/testing`.
2356
+ The player bounds what one request may cost it. The limits are guards against runaway plugins, not sizes to aim for. Since player 0.6.0 a whole list fits (50,000 playlist rows): bind it and draw it with a Slint `ListView` (a long list should use one: a `for` builds every row). The SDK sends row patches, and an array property larger than one request in parts (SDK 1.5.46); only a property's top-level array without Markdown documents is split, so any other value, and each row, must fit in one request (a row of more than 512 KiB is refused with `limit`). The SDK checks the same limits before it sends a request, so a plugin's own tests hit them too (TestHost enforces them). A name in code style is a constant you can import from `@vestra-player/sdk/testing`.
2354
2357
 
2355
2358
  #### Commands
2356
2359
 
@@ -2378,10 +2381,10 @@ One value written to a window, panel or settings page property (View.set, View.b
2378
2381
  | Limit | Value | Meaning |
2379
2382
  | --- | --- | --- |
2380
2383
  | `UI_VALUE_LIMITS.depth` | 8 | Nesting depth; the value itself is depth 0. |
2381
- | `UI_VALUE_LIMITS.values` | 16384 | Values in all, counting every array, object, string, number, boolean and null, the value itself included; a Markdown document is one. |
2382
- | `UI_VALUE_LIMITS.arrayRows` | 4096 | Rows of one array. |
2384
+ | `UI_VALUE_LIMITS.values` | 1048576 | Values in all, counting every array, object, string, number, boolean and null, the value itself included; a Markdown document is one. |
2385
+ | `UI_VALUE_LIMITS.arrayRows` | 100000 | Rows of one array. |
2383
2386
  | `UI_VALUE_LIMITS.stringBytes` | 65536 (64 KiB) | UTF-8 bytes of one string or object field name. |
2384
- | `UI_VALUE_LIMITS.textBytes` | 1048576 (1 MiB) | UTF-8 bytes of all strings and field names together, the text of the value's Markdown documents included. |
2387
+ | `UI_VALUE_LIMITS.textBytes` | 33554432 (32 MiB) | UTF-8 bytes of all strings and field names together, the text of the value's Markdown documents included. |
2385
2388
 
2386
2389
  #### Text values
2387
2390
 
@@ -3866,14 +3869,18 @@ There is no cleanup function to return from `activate`. Because every resource i
3866
3869
  A plugin's state is plain variables. The SDK keeps the player in step with it through **bindings** and the **update pass**:
3867
3870
 
3868
3871
  - A binding is a function the SDK calls to get a current value: view.bind(() => props) for a window, panel or settings page, and the function form of a command's `enabled`, `checked`, `busy` and `reason` (Bindable).
3869
- - In an update pass the SDK calls every binding of the plugin, compares each result with the value it last sent, and sends only what changed.
3872
+ - In an update pass the SDK calls the bindings of the plugin, compares each result with the value it last sent, and sends only what changed.
3870
3873
 
3871
- Update passes run:
3874
+ A **full pass** calls every binding. It runs:
3872
3875
 
3873
3876
  - after every handler the SDK calls returns (a command's `run`, a view callback, an `onChange`, `onStatus`, `onEnd` or `onStop` listener), and again when the promise it returned settles;
3874
- - after the player, subtitle, panel position, filter or export state changes;
3877
+ - after the subtitle, window, panel position, timeline, appearance, filter or export state changes;
3875
3878
  - when the plugin calls plugin.update().
3876
3879
 
3880
+ A **precise pass** follows a change of the player state (SDK 1.5.45). While media plays the player sends its state about ten times a second, mostly with only `position` changed. The SDK notes which fields of `plugin.player.state` each binding read the last time it ran (each `view.bind` function on its own; a command's four functions together) and calls only the bindings that read a field that changed. A panel whose list reads nothing of the player state is not rebuilt on every position tick; a label that shows the position is. `player.isCurrent` and `Media.current` count as reading the source. A `player.onChange` listener is followed by a full pass, as above, unless it returned synchronously and read only fields that did not change.
3881
+
3882
+ A precise pass cannot see state the plugin changed by itself. So while precise passes leave bindings out, a full pass follows at least once a second: a variable your own timer changed without `plugin.update()` appears within about a second (before SDK 1.5.45 it usually appeared at the next position tick). Call `plugin.update()` after such a change to show it at once.
3883
+
3877
3884
  Several requests within one microtask are merged into one pass, and every target has at most one write in flight: values that change meanwhile are merged into the next write. A binding that changes 60 times a second therefore costs one write per round trip, never a growing queue.
3878
3885
 
3879
3886
  One click, end to end (a bookmark window; the video is at 42 s):
@@ -3893,7 +3900,7 @@ sequenceDiagram
3893
3900
  S->>U: the label changes, Jump to bookmark is enabled
3894
3901
  ```
3895
3902
 
3896
- The rule that follows: **state you change outside a handler, or after an `await` inside one, appears only at the next pass.** Changing a variable from your own `setInterval`, a socket callback or the middle of a long task shows nothing until something triggers a pass; call `plugin.update()` after such a change. Keep a slider responsive while dragging shows the pattern; How Slint and TypeScript talk explains `set` versus `bind` for views.
3903
+ The rule that follows: **state you change outside a handler, or after an `await` inside one, appears only at the next full pass.** Changing a variable from your own `setInterval`, a socket callback or the middle of a long task shows nothing until something triggers one (at most about a second while media plays); call `plugin.update()` after such a change. Keep a slider responsive while dragging shows the pattern; How Slint and TypeScript talk explains `set` versus `bind` for views.
3897
3904
 
3898
3905
  #### Stopping
3899
3906
 
@@ -4009,7 +4016,7 @@ const toggle = plugin.commands.add({
4009
4016
 
4010
4017
  - **The id is the command's identity.** It is unique within the plugin; the player knows the command as `<manifest id>/<id>`. The user's own key bindings are stored under that pair, so changing a title or moving the command to another menu keeps them.
4011
4018
  - **`path` puts it in the main menu.** Leave it out for a command that only has bindings or lives in a panel's context menu (Panel.showMenu); it then needs a `title`.
4012
- - **State is declared, not pushed.** `enabled`, `checked`, `busy` and `reason` are values or functions (Bindable); `enabled` and `checked` may also be conditions from `plugin.when` (below). Functions are evaluated in every update pass and sent only when they change, so the menu's check mark follows the player without a listener you write. Command.set replaces them later.
4019
+ - **State is declared, not pushed.** `enabled`, `checked`, `busy` and `reason` are values or functions (Bindable); `enabled` and `checked` may also be conditions from `plugin.when` (below). Functions are evaluated in every full update pass, and after a player state change only if they read a field that changed; they are sent only when they change, so the menu's check mark follows the player without a listener you write. Command.set replaces them later.
4013
4020
  - **The player calls `run` only when it can run.** While a command is disabled or busy, neither its menu item nor its binding calls `run`; `reason` tells the user why (it appears in the menu and, for a key press, on the video).
4014
4021
  - **Titles and paths are fixed.** They cannot change in place. A menu that follows changing content (tracks, presets, a radio group) lives in a child scope that is disposed and rebuilt when the content changes; `order` keeps its place (Handles and scopes).
4015
4022
  - Disposing the command removes it from the menu and the binding table; the same id can be added again at once. The number of commands per plugin and per player is limited (Reference: limits); the limit stops runaway menus, so cap menus built from media content (tracks, chapters) yourself.
@@ -4266,7 +4273,7 @@ player.openStream (capability `producer.av`) makes a source whose picture and so
4266
4273
 
4267
4274
  #### Next and previous
4268
4275
 
4269
- The player's **Previous** and **Next** (menu, keys, the bottom bar) are not tied to a playlist. One plugin at a time can **provide the sequence** with provideSequence, giving `next()`, `previous()` and two flags, `canNext` and `canPrevious`, which are bindings re-evaluated in every update pass.
4276
+ The player's **Previous** and **Next** (menu, keys, the bottom bar) are not tied to a playlist. One plugin at a time can **provide the sequence** with provideSequence, giving `next()`, `previous()` and two flags, `canNext` and `canPrevious`, which are bindings re-evaluated in every full update pass, and after a player state change only if they read a changed field.
4270
4277
 
4271
4278
  - Every plugin sees the flags as `player.state.can.next` and `can.previous`, and player.next() asks the provider to move; the provider's `next` runs in its own scope.
4272
4279
  - A second provider fails with `conflict` and does not fail activation; it is tried again when its flags change, so it takes over once the first provider goes away.
@@ -4627,12 +4634,12 @@ export component ListPanel inherits Rectangle {
4627
4634
 
4628
4635
  | | `set({ x })` | `bind(() => ({ x }))` |
4629
4636
  | ---------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
4630
- | When it is sent | Immediately (in the next write). | In every update pass, if `x` differs from the last value sent. |
4637
+ | When it is sent | Immediately (in the next write). | In every full update pass, and after a player state change if the function read a changed field; when `x` differs from the last value sent. |
4631
4638
  | Same value again | Sent again. Use this to pull a control back to the model value after the user moved it. | Not sent. A field the user edited stays as the user left it until your value changes. |
4632
4639
  | Write fails | Retried with back-off (0.25 s doubling up to 30 s). | Not repeated until the value changes. |
4633
4640
  | Typical use | One-off values: a document, a reset. | Everything derived from plugin state: labels, enabled flags, lists, status lines. |
4634
4641
 
4635
- Update passes run after every handler the SDK calls, after player and other state changes, and when you call plugin.update(); several requests are merged into one pass, and each view has at most one write in flight (The update pass has the details).
4642
+ Update passes run after every handler the SDK calls, after player and other state changes, and when you call plugin.update(); several requests are merged into one pass, and each view has at most one write in flight. After a player state change only the bind functions that read a changed field of `plugin.player.state` run again (The update pass has the details).
4636
4643
 
4637
4644
  The rule to remember: **state you change after an `await` inside a handler appears only when the handler finishes**, unless something else triggers a pass. Call `plugin.update()` right after changing it (see Keep a slider responsive while dragging).
4638
4645
 
@@ -5810,6 +5817,8 @@ Every SDK release's CHANGELOG entry opens with who must change and what happens
5810
5817
 
5811
5818
  | Release | Who must change | Guide |
5812
5819
  | --- | --- | --- |
5820
+ | [1.5.46](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1546) | **Nobody.** No changes to the TypeScript API, `apiVersion` stays `vestra/1`, no new capability. | — |
5821
+ | [1.5.45](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1545) | **Nobody.** No changes to the TypeScript API, `apiVersion` stays `vestra/1`, no new capability, no player version needed. | — |
5813
5822
  | [1.5.44](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1544) | **Nobody.** No changes to the TypeScript API, `apiVersion` stays `vestra/1`, no new capability. | — |
5814
5823
  | [1.5.43](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1543) | **Nobody.** Additions only, `apiVersion` stays `vestra/1`, no new capability. | — |
5815
5824
  | [1.5.42](https://github.com/vestrasys/vestra-player-sdk/blob/main/CHANGELOG.md#1542) | **Nobody.** Documentation only: no API changes, `apiVersion` stays `vestra/1`, no player version needed. | — |
@@ -6367,8 +6376,10 @@ interface CommandState {
6367
6376
 
6368
6377
  ### Bindable
6369
6378
 
6370
- A value, or a function the SDK re-evaluates on every update pass (see
6371
- Scope.update); only changes are sent to the player.
6379
+ A value, or a function the SDK re-evaluates on every full update pass,
6380
+ and after a player state change only if it read a field of
6381
+ `plugin.player.state` that changed (see Scope.update); only
6382
+ changes are sent to the player.
6372
6383
 
6373
6384
  ```ts
6374
6385
  type Bindable<T> = T | (() => T);
@@ -6377,9 +6388,9 @@ type Bindable<T> = T | (() => T);
6377
6388
  Example: Mix a computed state with a fixed one
6378
6389
 
6379
6390
  The Loop item carries a check mark while a loop is set and is always
6380
- enabled. A function is evaluated again in every update pass (after each
6381
- handler and player state change) and only changes are sent; a plain
6382
- value never changes.
6391
+ enabled. A function is evaluated again in every full update pass (after
6392
+ each handler), and after a player state change only if it read a changed
6393
+ field; only changes are sent, and a plain value never changes.
6383
6394
  ```ts
6384
6395
  plugin.commands.add({
6385
6396
  id: "loop",
@@ -6800,8 +6811,9 @@ from the menu and the binding table; the id can be added again right away.
6800
6811
 
6801
6812
  The handle of one menu item and its keys. Its state (`enabled`, `checked`,
6802
6813
  `busy`, `reason`) is bound: functions given to Commands.add or
6803
- Command.set are evaluated again in every update pass and only
6804
- changes reach the player. Its title, path, bindings and button cannot
6814
+ Command.set are evaluated again in every full update pass (after
6815
+ a player state change only if they read a changed field of
6816
+ `plugin.player.state`) and only changes reach the player. Its title, path, bindings and button cannot
6805
6817
  change in place; dispose the command (or a child Scope) and add it
6806
6818
  again. Disposing it, its scope or stopping the plugin removes the menu
6807
6819
  item, its keys and its bar button.
@@ -6864,7 +6876,7 @@ const exportCommand = plugin.commands.add({
6864
6876
  path: ["@video", "Export…"],
6865
6877
  run: runExport,
6866
6878
  });
6867
- // A function: re-evaluated in every update pass.
6879
+ // A function: re-evaluated in every full pass and when `phase` changes.
6868
6880
  exportCommand.set({ enabled: () => plugin.player.state.phase === "ready" });
6869
6881
  ```
6870
6882
 
@@ -7092,20 +7104,23 @@ ImageOptions.maxSide for small pictures such as logos and
7092
7104
  thumbnails.
7093
7105
 
7094
7106
  Limits per value (`UI_VALUE_LIMITS` in `@vestra-player/sdk/testing`,
7095
- player 0.4.3; older players: 4096 values and 1024 rows): nesting depth 8,
7096
- 16384 values in total (every array, object, string, number, boolean and
7097
- null counts, the value itself included; a document or an image is one), 4096 rows
7098
- per array, 64 KiB per string or field name and 1 MiB of text in total,
7099
- field names and the text of its Markdown documents included (256 KB per
7100
- document). A list of rows with 8 fields is 9 values per row, so about
7101
- 1800 rows fit. The whole request must also fit the player's 1 MiB
7102
- message. A value over a limit is refused with code `limit` and the
7103
- property keeps what it showed (see View.set); `TestHost` refuses
7104
- it the same way.
7105
-
7106
- The limits bound the work of one write, not the size of your data: for
7107
- a list that can grow without bound, send the rows near the view and the
7108
- row count, and move that window as the user scrolls.
7107
+ player 0.6.0; players 0.4.3 to 0.5: 16384 values, 4096 rows, 1 MiB of
7108
+ text): nesting depth 8, 1,048,576 values in total (every array, object,
7109
+ string, number, boolean and null counts, the value itself included; a
7110
+ document or an image is one), 100,000 rows per array, 64 KiB per string
7111
+ or field name and 32 MiB of text in total, field names and the text of
7112
+ its Markdown documents included (256 KB per document). A list of rows
7113
+ with 13 fields is 14 values per row, so 50,000 such rows fit. An array
7114
+ property larger than one 1 MiB request is sent in parts (SDK 1.5.46, a
7115
+ player that takes row patches); only the top-level array is split, so
7116
+ any other value, and each row, must fit in one request (a row of more
7117
+ than 512 KiB is refused with `limit`). A value over a limit is refused
7118
+ with code
7119
+ `limit` and the property keeps what it showed (see View.set);
7120
+ `TestHost` refuses it the same way.
7121
+
7122
+ Bind a whole list and draw it with a Slint `ListView`, which builds only
7123
+ the rows on screen; the SDK sends what changed (row patches).
7109
7124
 
7110
7125
  ```ts
7111
7126
  type PropValue = PropData | ImageRef;
@@ -7415,8 +7430,9 @@ a property back: a value the user changes reaches it only through a
7415
7430
  callback.
7416
7431
 
7417
7432
  `bind` is the usual way: the SDK re-evaluates bound functions in every
7418
- update pass (after each handler it calls, after player state changes, on
7419
- Scope.update) and sends only what changed, at most one write in
7433
+ full update pass (after each handler it calls, on Scope.update),
7434
+ and after a player state change those that read a changed field of
7435
+ `plugin.player.state`, and sends only what changed, at most one write in
7420
7436
  flight per view. `set` sends now, even an unchanged value.
7421
7437
 
7422
7438
  Writes that fail (a name the Slint file does not declare, a value of the
@@ -7519,7 +7535,11 @@ interface View {
7519
7535
  * Keep properties equal to a function of the plugin's state.
7520
7536
  *
7521
7537
  * The SDK calls `props` in every update pass (and right after `bind`) and
7522
- * sends the values that differ from the ones it sent last. Because an
7538
+ * sends the values that differ from the ones it sent last. After a change
7539
+ * of the player state alone it calls `props` only if it read a field of
7540
+ * `plugin.player.state` that changed (SDK 1.5.45); a full pass follows
7541
+ * after each handler, on Scope.update and at least once a second
7542
+ * while the player state keeps changing. Because an
7523
7543
  * unchanged value is not sent again, text the user typed into an `in-out`
7524
7544
  * property stays until the plugin's value for it changes.
7525
7545
  *
@@ -7537,8 +7557,9 @@ interface View {
7537
7557
  * @example Show what is playing and whether it can seek
7538
7558
  * The status window always shows the current file and enables its seek
7539
7559
  * controls only for seekable sources. The SDK calls the function right
7540
- * away and again in every update pass (after player state changes and
7541
- * after each handler) and sends only the values that changed.
7560
+ * away, again in every full update pass (after each handler) and after
7561
+ * each change of `source` or `can`, and sends only the values that
7562
+ * changed.
7542
7563
  * ```ts
7543
7564
  * const status = plugin.windows.create("ui/status.slint");
7544
7565
  * status.bind(() => ({
@@ -10947,8 +10968,8 @@ interface Player {
10947
10968
  * @example Drive the player's Next and Previous from a playlist
10948
10969
  * The player's Next and Previous items and keys play the plugin's list.
10949
10970
  * The player calls `next` and `previous` when the user asks; `canNext`
10950
- * and `canPrevious` are evaluated again after every handler the SDK runs,
10951
- * and while they return false the player disables its items ("No next
10971
+ * and `canPrevious` are evaluated again after every handler the SDK runs
10972
+ * (and after a player state change if they read a changed field), and while they return false the player disables its items ("No next
10952
10973
  * item").
10953
10974
  * ```ts
10954
10975
  * const playAt = async (at: number) => {
@@ -17001,6 +17022,13 @@ class TestHost implements Transport {
17001
17022
  * or parsing a file, a child process, a network request) is not waited
17002
17023
  * for: on a loaded machine it can finish after `settle()` returns. Poll
17003
17024
  * the result you assert on instead (second example).
17025
+ *
17026
+ * `settle()` does not wait for the SDK's once-a-second full update pass
17027
+ * (SDK 1.5.45). After `setSource` (or another player state change) only
17028
+ * the bindings that read a changed field of `plugin.player.state` run
17029
+ * again; state the plugin changed outside a handler without
17030
+ * `plugin.update()` shows only in the next full pass. Have the plugin
17031
+ * call `plugin.update()` after such a change, or wait `settle(1000)`.
17004
17032
  * @param ms - Extra time to wait, for debounces and timers (default 0).
17005
17033
  * @capability None.
17006
17034
  * @throws Never.
@@ -17831,53 +17859,41 @@ keeps what it showed) and reported to `Scope.onError`: a write by
17831
17859
  makes `Window.show` reject; a panel or settings page opens without it and
17832
17860
  the failure goes to the plugin's `onError`.
17833
17861
 
17834
- They bound the work one write costs the player's interface thread; they
17835
- are not meant as list sizes to aim for. A list that can grow without
17836
- bound (a playlist, a history) should send the rows near the view and say
17837
- how many there are, not the whole list.
17862
+ Since player 0.6.0 (SDK 1.5.46) they allow a whole list: a plugin binds
17863
+ all of its rows (a playlist of 50,000 entries fits) and the view draws
17864
+ them with a Slint `ListView`, which builds only the rows on screen. The
17865
+ SDK sends a changed array as row patches, and an array larger than one
17866
+ request (about 512 KiB of rows) as its first rows followed by `insert`
17867
+ patches of the rest; the player converts large writes off its interface
17868
+ thread. Before player 0.6.0 the limits were 4,096 rows, 16,384 values and
17869
+ 1 MiB of text, so a long list had to be sent as a window of rows.
17870
+
17871
+ In a row, `null` where the view declares an `image` shows no picture
17872
+ (player 0.6.0): rows without a thumbnail need no placeholder image.
17838
17873
 
17839
17874
  ```ts
17840
17875
  const UI_VALUE_LIMITS: {
17841
17876
  /** Nesting depth; the value itself is depth 0 (8). */
17842
17877
  readonly depth: 8;
17843
- /** Values in all, counting every array, object, string, number, boolean and null, the value itself included; a Markdown document is one (16384). */
17844
- readonly values: 16384;
17845
- /** Rows of one array (4096). */
17846
- readonly arrayRows: 4096;
17878
+ /**
17879
+ * Values in all, counting every array, object, string, number, boolean and null, the value itself included; a Markdown document is one (1,048,576; 16,384 before player 0.6.0).
17880
+ */
17881
+ readonly values: number;
17882
+ /** Rows of one array (100,000; 4,096 before player 0.6.0). */
17883
+ readonly arrayRows: 100000;
17847
17884
  /** UTF-8 bytes of one string or object field name (64 KiB). */
17848
17885
  readonly stringBytes: number;
17849
- /** UTF-8 bytes of all strings and field names together, the text of the value's Markdown documents included (1 MiB). */
17886
+ /** UTF-8 bytes of all strings and field names together, the text of the value's Markdown documents included (32 MiB; 1 MiB before player 0.6.0). */
17850
17887
  readonly textBytes: number;
17851
17888
  };
17852
17889
  ```
17853
17890
 
17854
- Example: Send a long playlist as a window of rows
17891
+ Example: Bind a whole list
17855
17892
 
17856
- A list view shows any number of entries while each write stays small:
17857
- the plugin sends only the rows around the scroll position plus the total,
17858
- and the view reports where it scrolled to. After the `scrolled` handler
17859
- returns, the SDK evaluates the `bind` function again and sends the new
17860
- window. Each row object and each of its fields count as one value toward
17861
- `values`, and so does the array holding them.
17862
- ```ts
17863
- import { UI_VALUE_LIMITS } from "@vestra-player/sdk/testing";
17893
+ The view draws every row with a `ListView`; the SDK sends what changed.
17864
17894
 
17865
- // 200 rows, or fewer if the limits were smaller: 2 fields per row make
17866
- // 3 values (the row and its fields), plus 1 for the array.
17867
- const size = Math.min(
17868
- 200,
17869
- UI_VALUE_LIMITS.arrayRows,
17870
- Math.floor((UI_VALUE_LIMITS.values - 1) / 3),
17871
- );
17872
- let first = 0;
17873
- list.bind(() => ({
17874
- total: entries.length,
17875
- rows: entries.slice(first, first + size),
17876
- }));
17877
- // The Slint view reports its first visible row; the window follows it.
17878
- list.on("scrolled", (row: number) => {
17879
- first = Math.max(0, row - Math.floor(size / 2));
17880
- });
17895
+ ```ts
17896
+ list.bind(() => ({ rows: entries.map((e) => ({ ...e, selected: false })) }));
17881
17897
  ```
17882
17898
 
17883
17899
  ### IMAGE_LIMITS
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vestra Player SDK
2
2
 
3
- > @vestra-player/sdk 1.5.44: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls; it runs in its own Node.js process (the player's built-in plugins share one). Everything a plugin creates (commands, windows, panels, filters, listeners…) is a handle that is released automatically when the plugin stops. L<n>: a line of llms-full.txt (next to this file), which has every page, declaration and example.
3
+ > @vestra-player/sdk 1.5.46: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls; it runs in its own Node.js process (the player's built-in plugins share one). Everything a plugin creates (commands, windows, panels, filters, listeners…) is a handle that is released automatically when the plugin stops. L<n>: a line of llms-full.txt (next to this file), which has every page, declaration and example.
4
4
 
5
5
  **Rules**
6
6
 
@@ -33,7 +33,7 @@ my-plugin/
33
33
  }
34
34
  ```
35
35
 
36
- Fields: `id`, `version`, `apiVersion`, `entry`, `capabilities`, `name`, `author`, `punchline`, `description`, `tags`, `icon`, `readme`, `changelog`, `sourceUrl`, `license`, `minPlayerVersion`; unknown fields and capabilities are rejected. Rules and limits: L2216–2289.
36
+ Fields: `id`, `version`, `apiVersion`, `entry`, `capabilities`, `name`, `author`, `punchline`, `description`, `tags`, `icon`, `readme`, `changelog`, `sourceUrl`, `license`, `minPlayerVersion`; unknown fields and capabilities are rejected. Rules and limits: L2218–2292.
37
37
 
38
38
  **main.ts**
39
39
 
@@ -78,9 +78,9 @@ export default async function activate(plugin: Plugin) {
78
78
  - `secrets`: Secrets.get/set/delete
79
79
  - `rendering.manage`: Rendering.setPreference/restartPlayer
80
80
  - `video.upscaling`: VideoUpscaling.state/set/onChange
81
- - No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme, packages.list and rendering.state. Details: L2290–2350.
81
+ - No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme, packages.list and rendering.state. Details: L2293–2353.
82
82
 
83
- **Test**: `tests/*.test.ts` with `node:test`, run by `vestra-plugin test`. `startPlugin(activate, { directory: new URL("..", import.meta.url) })` from `@vestra-player/sdk/testing` returns `{ host, stop }`, `host` an in-memory player (TestHost): act (`host.command(id).run()`, `host.press(key)`), `await host.settle()`, assert on `host.osd`, `host.window(id).props`… Guide: L1861–2019; TestHost: L15463.
83
+ **Test**: `tests/*.test.ts` with `node:test`, run by `vestra-plugin test`. `startPlugin(activate, { directory: new URL("..", import.meta.url) })` from `@vestra-player/sdk/testing` returns `{ host, stop }`, `host` an in-memory player (TestHost): act (`host.command(id).run()`, `host.press(key)`), `await host.settle()`, assert on `host.osd`, `host.window(id).props`… Guide: L1861–2021; TestHost: L15484.
84
84
 
85
85
  **Command line**
86
86
 
@@ -108,40 +108,40 @@ export default async function activate(plugin: Plugin) {
108
108
  - `limit`: A player-wide limit was reached, a property value is over the limits of PropValue
109
109
  - `reserved`: The id is `vestra` or starts with `vestra.`
110
110
  - `versionMismatch`: The package needs a newer player
111
- - Others: PluginError, llms-full.txt L5945.
111
+ - Others: PluginError, llms-full.txt L5954.
112
112
 
113
113
  ## Reference
114
114
 
115
115
  - [llms-full.txt](https://vestrasys.github.io/vestra-player-sdk/llms-full.txt), also node_modules/@vestra-player/sdk/llms-full.txt; pages: [site](https://vestrasys.github.io/vestra-player-sdk/).
116
116
  - Tutorials: Getting started L9–134; Add a window L135–213; Add settings and translations L214–367; Test, check, pack and publish L368–424; Write your own blur kernel L425–685
117
- - How-to: Control playback L686–854; Store a plugin's data L855–961; Load subtitles from a plugin L962–1074; Keep a slider responsive while dragging L1075–1262; Remember a window's state between runs L1263–1414; Offer a GPU rendering switch L1415–1540; Keep a command's state without keeping the plugin running L1541–1697; Translate a plugin L1698–1860; Test a plugin L1861–2019; Publish a plugin L2020–2121; Troubleshooting L2122–2215
118
- - Reference: Manifest L2216–2289; Capabilities L2290–2350; Limits L2351–2496; Commands, menus and bindings L2497–3022; Platforms L3023–3053; Theme L3054–3403; Command line L3404–3461; Error codes L3462–3509; Translations L3510–3602; Test host L3603–3789
119
- - Explanation: How a plugin runs L3790–3988; Commands, menus and key bindings L3989–4143; Playback state, controls and sources L4144–4303; Video and audio processing L4304–4424; Windows, panels, settings pages, dialogs and the OSD L4425–4687; GPU and software rendering L4688–4704; The player's Rust core L4705–5046; How the player runs and talks to plugins L5047–5386
120
- - Guidelines: Plugin rules L5387–5398; Interface guidelines L5399–5515; Menu guidelines L5516–5574
121
- - Upgrading: Migrate a plugin to English source text L5575–5724; Migrating off package rollback (1.5.16, removed in 1.5.17) L5725–5802; Who must change, release by release L5803–5867
122
- - [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L5868–15136
123
- - [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L15137–18004
117
+ - How-to: Control playback L686–854; Store a plugin's data L855–961; Load subtitles from a plugin L962–1074; Keep a slider responsive while dragging L1075–1262; Remember a window's state between runs L1263–1414; Offer a GPU rendering switch L1415–1540; Keep a command's state without keeping the plugin running L1541–1697; Translate a plugin L1698–1860; Test a plugin L1861–2021; Publish a plugin L2022–2123; Troubleshooting L2124–2217
118
+ - Reference: Manifest L2218–2292; Capabilities L2293–2353; Limits L2354–2499; Commands, menus and bindings L2500–3025; Platforms L3026–3056; Theme L3057–3406; Command line L3407–3464; Error codes L3465–3512; Translations L3513–3605; Test host L3606–3792
119
+ - Explanation: How a plugin runs L3793–3995; Commands, menus and key bindings L3996–4150; Playback state, controls and sources L4151–4310; Video and audio processing L4311–4431; Windows, panels, settings pages, dialogs and the OSD L4432–4694; GPU and software rendering L4695–4711; The player's Rust core L4712–5053; How the player runs and talks to plugins L5054–5393
120
+ - Guidelines: Plugin rules L5394–5405; Interface guidelines L5406–5522; Menu guidelines L5523–5581
121
+ - Upgrading: Migrate a plugin to English source text L5582–5731; Migrating off package rollback (1.5.16, removed in 1.5.17) L5732–5809; Who must change, release by release L5810–5876
122
+ - [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L5877–15157
123
+ - [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L15158–18020
124
124
 
125
125
  ## API
126
126
 
127
127
  Where each `plugin.<module>` is declared:
128
128
 
129
- - Plugin L6284, Scope L6020, PluginError L5945, Disposable L5883
130
- - Commands L6871; When L6469; Windows L7808; Panels L9254; Dialogs L9924; Player L10665; video: VideoFilter L11545, VideoView L11645, RenderOptions L11732, VideoShaders L11810, VideoUpscaling L12168; audio: AudioFilter L11590; Subtitles L12404; media: MediaCapture L12808; Resolvers L13038; Packages L13440; Osd L11410; Host L11252; Clipboard L13769; Power L13856; Tools L13915; Storage L14067; System L14266; Secrets L14346; window: MainWindow L9571; App L9751; Rendering L14609; Bindings L13668; Ui L7160; Timeline L9483; Drops L8501; Appearance L8582; I18n L8815; Settings L9098.
129
+ - Plugin L6293, Scope L6029, PluginError L5954, Disposable L5892
130
+ - Commands L6883; When L6480; Windows L7829; Panels L9275; Dialogs L9945; Player L10686; video: VideoFilter L11566, VideoView L11666, RenderOptions L11753, VideoShaders L11831, VideoUpscaling L12189; audio: AudioFilter L11611; Subtitles L12425; media: MediaCapture L12829; Resolvers L13059; Packages L13461; Osd L11431; Host L11273; Clipboard L13790; Power L13877; Tools L13936; Storage L14088; System L14287; Secrets L14367; window: MainWindow L9592; App L9772; Rendering L14630; Bindings L13689; Ui L7175; Timeline L9504; Drops L8522; Appearance L8603; I18n L8836; Settings L9119.
131
131
 
132
132
  ## Examples
133
133
 
134
134
  Tested plugins in [examples/](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples):
135
135
 
136
- - hello: The README Quick start: one menu command and an on-screen message. L18439–18534
137
- - blur: A tool window with one slider that blurs the playing video. L18535–18802
138
- - signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L18803–19141
139
- - bookmark: A tool window bound to the playback state: remember the position and jump back to it. L19142–19334
140
- - panel-menu: A sidebar panel with its own right-click menu. L19335–19549
141
- - menus: Radio menu items, separator groups and a menu item whose title changes. L19550–19715
142
- - theme-widgets: A tool window drawn with the player's theme and widgets. L19716–19885
143
- - markdown: A window that shows a Markdown document. L19886–20088
144
- - i18n: A plugin in the player's language: English source text, a Chinese translation. L20089–20262
136
+ - hello: The README Quick start: one menu command and an on-screen message. L18455–18550
137
+ - blur: A tool window with one slider that blurs the playing video. L18551–18818
138
+ - signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L18819–19157
139
+ - bookmark: A tool window bound to the playback state: remember the position and jump back to it. L19158–19350
140
+ - panel-menu: A sidebar panel with its own right-click menu. L19351–19565
141
+ - menus: Radio menu items, separator groups and a menu item whose title changes. L19566–19731
142
+ - theme-widgets: A tool window drawn with the player's theme and widgets. L19732–19901
143
+ - markdown: A window that shows a Markdown document. L19902–20104
144
+ - i18n: A plugin in the player's language: English source text, a Chinese translation. L20105–20278
145
145
 
146
146
  ## Optional
147
147
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.5.44",
3
+ "version": "1.5.46",
4
4
  "description": "Plugin API types, test host and plugin CLI for Vestra Player (apiVersion vestra/1)",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {