@vestra-player/sdk 1.5.58 → 1.6.1

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 CHANGED
@@ -1,13 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.6.1
4
+
5
+ **Who must change: nobody.** Docs only: no change to the TypeScript API, the wire or `TestHost`, `apiVersion` stays `vestra/1`, no new capability.
6
+
7
+ - **Player versions corrected to 0.7.1.** Players 0.6.6 to 0.6.8 and 0.7.0 were internal builds that were never published; 0.7.1 is the first published player with the plugin supervisor, on-demand start for everyone and selectable Markdown. Every "player 0.6.6", "0.6.7", "0.6.8" and "planned for player 0.6.x" label now reads player 0.7.1, and `"minPlayerVersion": "0.6.8"` for selectable Markdown is now `"0.7.1"`. A plugin that set `"0.6.8"` keeps loading on 0.7.1 and later. The tables of [How the player runs and talks to plugins](docs/explanation/host-and-node.md), [How a plugin runs](docs/explanation/plugins.md#idle-plugins-are-stopped) and [Reference: limits › Plugin processes](docs/reference/limits.md#plugin-processes) show the 0.7.1 values in one column (start timeout 60 s and no start retry after it, 5 s stop grace, idle time 30 s, process budget, memory pressure). The entries of 1.5.58 and 1.6.0 are aligned the same way. Behaviour that player 0.6.4 already shipped (stop reasons, `InstallOptions.enabled`) is no longer called planned.
8
+ - **Player release version** ([Reference: manifest › Player versions](docs/reference/manifest.md#player-versions), `AppInfo.version`, `minPlayerVersion`). From player 0.7.1, `plugin.app.version` and the version `minPlayerVersion` is compared with are the player release version, which can differ from the host binary version. Plugins compare against the release version.
9
+ - **Windows GPU rendering** ([Reference: platforms](docs/reference/platforms.md), [Offer a GPU rendering switch](docs/how-to/gpu-rendering.md)). Windows has a GPU renderer since player 0.5.14 (Direct3D 12), experimental and off by default: **Video ▸ GPU Rendering (Experimental)** or `VESTRA_RENDER=gpu` turns it on. Software (CPU) mode, the default there, cannot run render options or plugin shaders (`unsupported`); platforms lists plugin shaders in their own row.
10
+ - **The `lut3d` `.cube` format** (`VideoNode`, [Video and audio processing](docs/explanation/processing.md)). FFmpeg's `.cube` reader (`parse_cube` in `libavfilter/vf_lut3d.c`) reads the table: `LUT_3D_SIZE N`, optional `TITLE` and `DOMAIN_MIN`/`DOMAIN_MAX`, then N³ lines `r g b`, red changing fastest. Lines before `LUT_3D_SIZE` are skipped. Any other keyword after it, such as `MIN` or `MAX`, makes FFmpeg refuse the file, and the chain fails with "error running command (-12)". The player checks only the header.
11
+
12
+ ## 1.6.0
13
+
14
+ **Who must change: nobody.** No change to the TypeScript API, the wire or `TestHost`, `apiVersion` stays `vestra/1`, no new capability. New since 1.5.58 for plugin authors: Markdown the user can select and copy (`selectable` on `MarkdownView` and `MarkdownBlocks`, `plain` and `links` on `MarkdownBlock`, in the SDK's copy of the player's `@vestra/markdown.slint`; a plugin that uses them sets `"minPlayerVersion": "0.7.1"`), and, from player 0.7.1, plugins started on demand by default: a plugin starts on its first use and is stopped when idle, so `activate` can run many times and must never wait for its own commands or views (see 1.5.58). This is a minor release because it adds backward-compatible API and marks that milestone; nothing is removed or changed, and a plugin built with 1.5.x keeps working as it is.
15
+
16
+ _Player versions aligned in 1.6.1: players 0.6.6 to 0.6.8 and 0.7.0 were internal builds, never published; 0.7.1 is the first published player with these features._
17
+
18
+ - **Selectable Markdown** (player 0.7.1; the SDK's `slint/vestra/markdown.slint` is synced with the player's). `MarkdownView` and `MarkdownBlocks` take `in property <bool> selectable: false`. With `true`, a paragraph, heading, list item or quote without links is shown as its plain text in a read-only selectable text field (right-click Copy / Select All, where Copy with nothing selected copies the whole block; Ctrl+C / Ctrl+A; each field is a Tab stop); a block with links keeps its clickable links, and its right-click menu copies it as plain text with each address after its link text, `text (url)`, plus Copy Link when it has one link. Code blocks are always selectable, whatever `selectable` says (in player 0.7.1 this applies to every plugin's Markdown). A selection stays inside one block and is lost when its row scrolls out of a `MarkdownView`. `MarkdownBlock` gains `plain: string` (the block as plain text) and `links: [string]` (its link addresses). Older players do not have the property: set `"minPlayerVersion": "0.7.1"` before using it. See [Reference: theme › Markdown](docs/reference/theme.md#markdown).
19
+ - **Start retries corrected** ([How the player runs and talks to plugins](docs/explanation/host-and-node.md#the-start-deadline), [Reference: limits](docs/reference/limits.md#plugin-processes), [How a plugin runs](docs/explanation/plugins.md#restarts-activate-can-run-more-than-once)). 1.5.58 said a start that is not ready within the start timeout is tried up to three more times. That was the internal build 0.6.6; from player 0.7.1 a timed-out start gets one try (60 s) and is not retried, and only a process that cannot be created is tried again (about 250 ms, 500 ms and 1 s apart).
20
+
3
21
  ## 1.5.58
4
22
 
5
- **Who must change: nobody.** Documentation only: no change to the TypeScript API, the wire or `TestHost`, `apiVersion` stays `vestra/1`, no new capability, no player version needed. Plugin authors should read what the player's new plugin supervisor does with their plugin (player 0.6.6, and the defaults planned for 0.6.7), and check that `activate` never waits for its own commands or views.
23
+ **Who must change: nobody.** Documentation only: no change to the TypeScript API, the wire or `TestHost`, `apiVersion` stays `vestra/1`, no new capability, no player version needed. Plugin authors should read what the player's new plugin supervisor does with their plugin (written for the internal build 0.6.6 and the defaults planned for 0.6.7; player 0.7.1 is the first published player with it, with the 0.6.7 defaults), and check that `activate` never waits for its own commands or views.
24
+
25
+ _Player versions aligned in 1.6.1: players 0.6.6 to 0.6.8 and 0.7.0 were internal builds, never published; 0.7.1 is the first published player with these features._
6
26
 
7
- - **The plugin supervisor, as a plugin sees it** ([How the player runs and talks to plugins](docs/explanation/host-and-node.md), [How a plugin runs](docs/explanation/plugins.md)). Player 0.6.6: a failed start is tried up to three more times; a built-in that crashes starts again by itself after 3 s, 6 s…; the fifth crash within 10 minutes stops a plugin until it is reloaded; an own process's JavaScript heap is capped at 512 MB (V8's heap only, not `Buffer` contents or addons), and passing it ends the process with "<name> ran out of memory"; a process that ended by itself gets no `runtime.stop`; without on-demand start a third-party plugin is listed as failed on its first crash, as before; at quit every plugin is stopped at once with 1 s in all; and two processes of one plugin never use its data directory at the same time. With on-demand start (lazy activation, `VESTRA_LAZY=1` in 0.6.6): a crashed plugin keeps its entries, shown disabled with the reason during a pause of 1 s, 2 s, 4 s…, and starts again on its next use (a resident plugin by itself); third-party plugins start a few at a time, clicks first; every start holds its clicks, resolutions, drops and view callbacks until `runtime.ready` (so an `activate` that awaits its own command or view callback waits until the start timeout); at most 64 are held per plugin, and one that cannot be taken is answered ("<name> is busy", "… could not start", "… is disabled") instead of dropped; busy work (a `busy` command, a dialog, a settings page shown, a resolution) keeps a plugin running with no time limit, and its idle time counts from when the work ends. Planned for player 0.6.7: on-demand start for everyone; a process budget for the machine's memory and the system's memory pressure put idle plugins in standby early, with the stop reason `idle`; idle time 30 s, start timeout 60 s, 5 s grace for a disable, uninstall, reload, upgrade or standby, a 5-minute cap on a player request awaiting the plugin's reply (a resolution; declared busy work stays uncapped), and a resident plugin never goes to standby. The source excerpts of host code that no longer exists (the lazy-activation trigger list, the 15-second activation record, the 250 ms idle poll, the stop's 5 ms poll) are replaced by descriptions of the current behaviour.
8
- - **[Reference: limits › Plugin processes](docs/reference/limits.md#plugin-processes)**: heap cap, start timeout, retries, start limit, restart pauses, stop graces, quit, idle time, request timeout, start slots (`min(4, max(2, cores / 2))` plus a reserve for clicks), held triggers (64), waiting starts (256), the process budget (`clamp(physical MiB / 2 × 0.30 / 85, 3, 16)`) and the memory pressure thresholds (both planned for 0.6.7), for player 0.6.6 and as planned for 0.6.7.
9
- - **`StopReason`** lists when each reason comes with the supervisor: `quit` also after a lost GPU device, `disable` clears session storage, `idle` also for the process budget and memory pressure (planned for 0.6.7; never for a busy plugin), `failed` also for a start timeout (possibly while `activate` runs) and, planned for 0.6.7, an unanswered request; a crash or running out of memory is no stop at all. The stop reasons are no longer described as planned: they shipped in player 0.6.4. **`PluginInfo.process`** `"idle"` also covers the pause after a crash before a plugin starts again. `Plugin`, `DisposeInfo` and [Store a plugin's data](docs/how-to/store-data.md) give the new stop graces.
10
- - **`appearance.changed` says what changed** (player 0.6.6): `"changed": ["theme"]` or `["language"]` beside `theme` and `language`. This SDK does not read it yet; a later release uses it so that a plugin woken by a language change no longer hears its unchanged theme once.
27
+ - **The plugin supervisor, as a plugin sees it** ([How the player runs and talks to plugins](docs/explanation/host-and-node.md), [How a plugin runs](docs/explanation/plugins.md)). The internal build 0.6.6: a failed start is tried up to three more times; a built-in that crashes starts again by itself after 3 s, 6 s…; the fifth crash within 10 minutes stops a plugin until it is reloaded; an own process's JavaScript heap is capped at 512 MB (V8's heap only, not `Buffer` contents or addons), and passing it ends the process with "<name> ran out of memory"; a process that ended by itself gets no `runtime.stop`; without on-demand start a third-party plugin is listed as failed on its first crash, as before; at quit every plugin is stopped at once with 1 s in all; and two processes of one plugin never use its data directory at the same time. With on-demand start (lazy activation, `VESTRA_LAZY=1` in 0.6.6): a crashed plugin keeps its entries, shown disabled with the reason during a pause of 1 s, 2 s, 4 s…, and starts again on its next use (a resident plugin by itself); third-party plugins start a few at a time, clicks first; every start holds its clicks, resolutions, drops and view callbacks until `runtime.ready` (so an `activate` that awaits its own command or view callback waits until the start timeout); at most 64 are held per plugin, and one that cannot be taken is answered ("<name> is busy", "… could not start", "… is disabled") instead of dropped; busy work (a `busy` command, a dialog, a settings page shown, a resolution) keeps a plugin running with no time limit, and its idle time counts from when the work ends. Player 0.7.1 (planned as 0.6.7): on-demand start for everyone; a process budget for the machine's memory and the system's memory pressure put idle plugins in standby early, with the stop reason `idle`; idle time 30 s, start timeout 60 s, 5 s grace for a disable, uninstall, reload, upgrade or standby, a 5-minute cap on a player request awaiting the plugin's reply (a resolution; declared busy work stays uncapped), and a resident plugin never goes to standby. The source excerpts of host code that no longer exists (the lazy-activation trigger list, the 15-second activation record, the 250 ms idle poll, the stop's 5 ms poll) are replaced by descriptions of the current behaviour.
28
+ - **[Reference: limits › Plugin processes](docs/reference/limits.md#plugin-processes)**: heap cap, start timeout, retries, start limit, restart pauses, stop graces, quit, idle time, request timeout, start slots (`min(4, max(2, cores / 2))` plus a reserve for clicks), held triggers (64), waiting starts (256), the process budget (`clamp(physical MiB / 2 × 0.30 / 85, 3, 16)`) and the memory pressure thresholds (both from player 0.7.1), for the internal build 0.6.6 and for 0.7.1 (planned as 0.6.7).
29
+ - **`StopReason`** lists when each reason comes with the supervisor: `quit` also after a lost GPU device, `disable` clears session storage, `idle` also for the process budget and memory pressure (player 0.7.1; never for a busy plugin), `failed` also for a start timeout (possibly while `activate` runs) and, from player 0.7.1, an unanswered request; a crash or running out of memory is no stop at all. The stop reasons are no longer described as planned: they shipped in player 0.6.4. **`PluginInfo.process`** `"idle"` also covers the pause after a crash before a plugin starts again. `Plugin`, `DisposeInfo` and [Store a plugin's data](docs/how-to/store-data.md) give the new stop graces.
30
+ - **`appearance.changed` says what changed** (player 0.7.1): `"changed": ["theme"]` or `["language"]` beside `theme` and `language`. This SDK does not read it yet; a later release uses it so that a plugin woken by a language change no longer hears its unchanged theme once.
11
31
 
12
32
  ## 1.5.57
13
33
 
Binary file
@@ -22,7 +22,7 @@ export interface PluginInstance {
22
22
  /**
23
23
  * Stop the plugin: dispose the root scope, then stop the runtime.
24
24
  * `info.reason` (SDK 1.5.54) is why: the runner passes `runtime.stop`'s
25
- * `data.reason` (planned for player 0.6.4), or `"host-lost"` when its input closed
25
+ * `data.reason` (player 0.6.4), or `"host-lost"` when its input closed
26
26
  * without a `runtime.stop`. It reaches `plugin.stopReason` and every
27
27
  * `onDispose` cleanup; a reason the SDK does not know reads as
28
28
  * `undefined`. A second call runs no cleanup again and keeps the first reason.
@@ -2451,7 +2451,7 @@ export declare class TestHost implements Transport {
2451
2451
  * adds or upgrades the entry in {@link TestHost.packages} following the
2452
2452
  * player's source rules: the same source upgrades, another source needs
2453
2453
  * `replace` (`conflict` otherwise), a built-in id is `unsupported`. As
2454
- * player 0.6.4 (planned), the installed entry's `state` follows
2454
+ * player 0.6.4 and later, the installed entry's `state` follows
2455
2455
  * {@link "@vestra-player/sdk"!InstallOptions.enabled}: a first install
2456
2456
  * runs, an update of a `disabled` entry stays `disabled` unless `enabled`
2457
2457
  * is `true`, and `enabled: false` installs it `disabled`. Set
@@ -2475,7 +2475,7 @@ export declare class TestHost implements Transport {
2475
2475
  readonly candidates: Map<string, PackageManifest>;
2476
2476
  /**
2477
2477
  * Which player's install rule `packages.install` plays. `true` (default,
2478
- * SDK 1.5.55): player 0.6.4 (planned), where an update or reinstall
2478
+ * SDK 1.5.55): player 0.6.4 and later, where an update or reinstall
2479
2479
  * keeps a `disabled` plugin disabled unless the plugin passes
2480
2480
  * {@link "@vestra-player/sdk"!InstallOptions.enabled}. `false`: players
2481
2481
  * up to 0.6.3, where every install runs the plugin and `enabled` is
@@ -3100,7 +3100,7 @@ export class TestHost {
3100
3100
  * adds or upgrades the entry in {@link TestHost.packages} following the
3101
3101
  * player's source rules: the same source upgrades, another source needs
3102
3102
  * `replace` (`conflict` otherwise), a built-in id is `unsupported`. As
3103
- * player 0.6.4 (planned), the installed entry's `state` follows
3103
+ * player 0.6.4 and later, the installed entry's `state` follows
3104
3104
  * {@link "@vestra-player/sdk"!InstallOptions.enabled}: a first install
3105
3105
  * runs, an update of a `disabled` entry stays `disabled` unless `enabled`
3106
3106
  * is `true`, and `enabled: false` installs it `disabled`. Set
@@ -3124,7 +3124,7 @@ export class TestHost {
3124
3124
  candidates = new Map();
3125
3125
  /**
3126
3126
  * Which player's install rule `packages.install` plays. `true` (default,
3127
- * SDK 1.5.55): player 0.6.4 (planned), where an update or reinstall
3127
+ * SDK 1.5.55): player 0.6.4 and later, where an update or reinstall
3128
3128
  * keeps a `disabled` plugin disabled unless the plugin passes
3129
3129
  * {@link "@vestra-player/sdk"!InstallOptions.enabled}. `false`: players
3130
3130
  * up to 0.6.3, where every install runs the plugin and `enabled` is
@@ -421,7 +421,7 @@ export interface Scope extends Disposable {
421
421
  * The plugin writes its cache file at once when the player crashed or
422
422
  * was killed, instead of waiting for the next periodic save. The cleanup
423
423
  * may get a {@link DisposeInfo}: `stopping` says the plugin itself stops
424
- * (not a child scope), `reason` why (planned for player 0.6.4). Players
424
+ * (not a child scope), `reason` why (player 0.6.4). Players
425
425
  * whose SDK is older than 1.5.54 call the cleanup without an argument,
426
426
  * so read it with `?.`.
427
427
  * ```ts
@@ -524,9 +524,9 @@ export interface Scope extends Disposable {
524
524
  * (menus, windows, panels, filters, subtitle layers, snapshots, jobs,
525
525
  * resolvers, streams); pending requests then fail with `disabled` and the
526
526
  * root scope is disposed for local cleanup (timers, signals, sockets). A
527
- * process that has not exited after its grace is killed: 300 ms in player
528
- * 0.6.6 (2 s when it goes to standby), planned to be 5 s from player 0.6.7
529
- * (300 ms after a fault); at quit all plugins together get 1 s. Media the
527
+ * process that has not exited after its grace is killed: 5 s from player
528
+ * 0.7.1 (before it 300 ms; 300 ms after a fault); at quit all plugins
529
+ * together get 1 s. Media the
530
530
  * plugin opened with {@link Player.open} keeps playing.
531
531
  *
532
532
  * @example Write a minimal plugin with one menu item
@@ -591,7 +591,7 @@ export interface Plugin extends Scope {
591
591
  *
592
592
  * - `quit`: the player quits (or restarts after a lost GPU device). Every
593
593
  * plugin is stopped at the same time, with 1 s in all (300 ms after a
594
- * lost device; player 0.6.6).
594
+ * lost device; player 0.7.1).
595
595
  * - `disable`: the user, or a plugin with `plugins.manage`, switched the
596
596
  * plugin off. Its session storage is cleared.
597
597
  * - `uninstall`: the user uninstalls it.
@@ -604,25 +604,23 @@ export interface Plugin extends Scope {
604
604
  * its menu items, keys, settings pages, resolvers and drop targets stay,
605
605
  * and so does its session storage
606
606
  * ([Idle plugins](../../docs/explanation/plugins.md#idle-plugins-are-stopped)).
607
- * Only with on-demand start (lazy activation), off by default up to
608
- * player 0.6.6 (`VESTRA_LAZY=1` turns it on) and planned for everyone in
609
- * player 0.6.7. It comes after the idle time without use (60 s; 30 s
610
- * planned for 0.6.7), right after a first run that holds nothing live,
611
- * and when the player's limit on third-party processes or low system
612
- * memory puts an idle plugin in standby early (planned for 0.6.7). A busy
613
- * plugin never gets it; a resident one (it held something live when it
614
- * became ready) only after the idle time, and from player 0.6.7 (planned)
615
- * never.
607
+ * Only with on-demand start (lazy activation): on for everyone from
608
+ * player 0.7.1, off by default before it (`VESTRA_LAZY=1` turns it on).
609
+ * It comes after the idle time without use (30 s; 60 s before 0.7.1),
610
+ * right after a first run that holds nothing live, and, from player
611
+ * 0.7.1, when the player's limit on third-party processes or low system
612
+ * memory puts an idle plugin in standby early. A busy plugin never gets
613
+ * it; a resident one (it held something live when it became ready) never
614
+ * from player 0.7.1 (before it, after the idle time).
616
615
  * - `failed`: the player stops it for a fault it can still tell the plugin
617
616
  * about: a malformed request, an unreadable or oversized line, a start
618
- * that is not ready within the start timeout (15 s; 60 s planned for
619
- * player 0.6.7; the stop may come while `activate` still runs), an
620
- * uncaught exception the shared process attributes to this plugin, and,
621
- * planned for player 0.6.7, a request from the player (a resolution) it
622
- * leaves unanswered for 5 minutes. From player 0.6.6 the player may
623
- * start it again afterwards (a built-in or resident plugin by itself, a
624
- * plugin with on-demand start on its next use), until the fifth fault
625
- * within 10 minutes.
617
+ * that is not ready within the start timeout (60 s from player 0.7.1;
618
+ * the stop may come while `activate` still runs), an uncaught exception
619
+ * the shared process attributes to this plugin, and, from player 0.7.1,
620
+ * a request from the player (a resolution) it leaves unanswered for 5
621
+ * minutes. From player 0.7.1 the player may start it again afterwards (a
622
+ * built-in or resident plugin by itself, a plugin that has run before on
623
+ * its next use), until the fifth fault within 10 minutes.
626
624
  * - `host-lost`: the plugin's runner saw its pipe to the player close
627
625
  * without a stop: the player crashed or was killed. Requests are refused
628
626
  * with `disabled`; clean up locally only. Player 0.6.4 sends `quit` on a
@@ -634,7 +632,7 @@ export interface Plugin extends Scope {
634
632
  * own process is killed because its input queue is full or a write to it
635
633
  * failed, when its own process fails to activate (the runner exits without
636
634
  * cleaning up), when the process ended by itself (it crashed, or its
637
- * JavaScript heap passed the 512 MB cap of player 0.6.6 and V8 ended it:
635
+ * JavaScript heap passed the 512 MB cap of player 0.7.1 and V8 ended it:
638
636
  * "ran out of memory"), and when the shared runner is restarted as a whole
639
637
  * (a hang, a fault it cannot attribute, an unreadable line of its own, a
640
638
  * member's full input queue, the runner exiting): every member is killed
@@ -1773,6 +1771,13 @@ export interface Ui {
1773
1771
  * the list on every streamed chunk keeps the scroll position and costs
1774
1772
  * little.
1775
1773
  *
1774
+ * From player 0.7.1 both views take `selectable: true` (default `false`):
1775
+ * the user can select and copy the text, one block at a time; blocks with
1776
+ * links keep their clickable links and copy as `text (url)`. Code blocks
1777
+ * are always selectable. `MarkdownBlock.plain` (the block as plain text)
1778
+ * and `MarkdownBlock.links` (its link addresses) are readable in Slint.
1779
+ * See [Reference: theme › Markdown](../../docs/reference/theme.md#markdown).
1780
+ *
1776
1781
  * @capability None.
1777
1782
  * @player 0.2.0 (set `"minPlayerVersion": "0.2.0"` in the manifest)
1778
1783
  *
@@ -4398,7 +4403,13 @@ export interface MainWindow {
4398
4403
  * @category Plugin
4399
4404
  */
4400
4405
  export interface AppInfo {
4401
- /** The player's version, e.g. `"1.0.0"`. */
4406
+ /**
4407
+ * The player's version, e.g. `"0.7.1"`. From player 0.7.1 this is the
4408
+ * player's release version (the version of the published release), which
4409
+ * can differ from the version of the host binary inside it. The player
4410
+ * compares `minPlayerVersion` with this value, and plugins compare with
4411
+ * it too.
4412
+ */
4402
4413
  readonly version: string;
4403
4414
  /** Short git hash of the build, `null` when unknown. */
4404
4415
  readonly build: string | null;
@@ -8579,11 +8590,11 @@ export interface InstallOptions {
8579
8590
  * Built-in plugins always run (and cannot be installed over). A value
8580
8591
  * that is not a boolean is `invalid`.
8581
8592
  *
8582
- * Planned for player 0.6.4: older players ignore it and an install there
8593
+ * Player 0.6.4 and later: older players ignore it and an install there
8583
8594
  * always runs the plugin, so after a committed install call
8584
8595
  * {@link Packages.setEnabled} when the state matters.
8585
8596
  *
8586
- * @player 0.6.4 (planned); older players ignore it (an install always
8597
+ * @player 0.6.4; older players ignore it (an install always
8587
8598
  * enables the plugin).
8588
8599
  */
8589
8600
  enabled?: boolean;
@@ -8632,8 +8643,8 @@ export interface PackageCandidate {
8632
8643
  * player). A reserved id (`vestra` or `vestra.*`, which every built-in
8633
8644
  * uses) fails with `reserved` (player 0.4.1; already when validated), as
8634
8645
  * only the player installs built-in plugins. An update keeps a disabled
8635
- * plugin disabled unless `enabled` says otherwise (planned for player
8636
- * 0.6.4; see {@link InstallOptions.enabled}).
8646
+ * plugin disabled unless `enabled` says otherwise (player 0.6.4; see
8647
+ * {@link InstallOptions.enabled}).
8637
8648
  *
8638
8649
  * @capability plugins.manage
8639
8650
  * @throws `invalid` (rejects) An option is malformed: a `source` the
@@ -8838,11 +8849,11 @@ export interface PluginInfo {
8838
8849
  * while the plugin is switched on but has no process. That is standby
8839
8850
  * with on-demand start (lazy activation): nothing used it for the idle
8840
8851
  * time, or the player's limit on third-party processes or low system
8841
- * memory stopped it early (planned for player 0.6.7); it starts again on its next
8852
+ * memory stopped it early (player 0.7.1); it starts again on its next
8842
8853
  * use, and its menu items, keys, settings pages, resolvers and drop
8843
- * targets stay. From player 0.6.6 it is also the pause after a crash: a
8854
+ * targets stay. From player 0.7.1 it is also the pause after a crash: a
8844
8855
  * built-in or resident plugin waiting to start again by itself, or a
8845
- * plugin with on-demand start whose commands are shown disabled with the
8856
+ * plugin that has run before whose commands are shown disabled with the
8846
8857
  * reason until the pause ends. Absent in other states and from players
8847
8858
  * before 0.5.18. A change between the two alone does not fire
8848
8859
  * {@link Packages.onChange}; a plugin stopped by the start limit (five
@@ -194,6 +194,39 @@ export type VideoNode =
194
194
  * 16 MiB) inside the plugin's package directory or data directory
195
195
  * after following links; a path with `[` or `]` is refused. Outside
196
196
  * those: `invalid`; a file that does not exist: `missing`.
197
+ *
198
+ * The player checks only that header; the table itself is read by
199
+ * FFmpeg's `.cube` reader (`parse_cube` in FFmpeg's
200
+ * [`libavfilter/vf_lut3d.c`](https://github.com/FFmpeg/FFmpeg/blob/master/libavfilter/vf_lut3d.c)),
201
+ * which takes a subset of the
202
+ * Adobe/Resolve format:
203
+ *
204
+ * - Every line before `LUT_3D_SIZE N` is skipped (a `TITLE`, comments,
205
+ * and also a `DOMAIN_MIN` or `DOMAIN_MAX` there, which then has no
206
+ * effect).
207
+ * - After it, optional `TITLE`, `DOMAIN_MIN r g b` and
208
+ * `DOMAIN_MAX r g b` lines (default `0 0 0` and `1 1 1`), blank
209
+ * lines and `#` comments, then N³ data lines of three numbers
210
+ * `r g b`, red changing fastest, then green, then blue.
211
+ * - Any other keyword after `LUT_3D_SIZE`, such as `MIN` or `MAX`, is
212
+ * read as a data line, and FFmpeg refuses the file, as it does one
213
+ * with fewer than N³ data lines. The chain then fails and the
214
+ * player reports mpv's "error running command (-12)".
215
+ *
216
+ * ```text
217
+ * TITLE "Identity"
218
+ * LUT_3D_SIZE 2
219
+ * DOMAIN_MIN 0 0 0
220
+ * DOMAIN_MAX 1 1 1
221
+ * 0 0 0
222
+ * 1 0 0
223
+ * 0 1 0
224
+ * 1 1 0
225
+ * 0 0 1
226
+ * 1 0 1
227
+ * 0 1 1
228
+ * 1 1 1
229
+ * ```
197
230
  */
198
231
  file: string;
199
232
  /** How colours between the table's points are computed; `tetrahedral` is FFmpeg's default. */
package/dist/wire.d.ts CHANGED
@@ -733,7 +733,7 @@ export declare function createWire(transport: Transport): {
733
733
  installed: (id: string) => Promise<InstalledPackage>;
734
734
  /**
735
735
  * `source` and `replace` are sent only when given (player 0.2.0);
736
- * `enabled` only when given (planned for player 0.6.4, older players
736
+ * `enabled` only when given (player 0.6.4; older players
737
737
  * ignore it).
738
738
  */
739
739
  install: (token: string, expectedRevision: number, requestId: string, options?: {
package/dist/wire.js CHANGED
@@ -343,7 +343,7 @@ export function createWire(transport) {
343
343
  installed: (id) => transport.request("packages.installed", { id }),
344
344
  /**
345
345
  * `source` and `replace` are sent only when given (player 0.2.0);
346
- * `enabled` only when given (planned for player 0.6.4, older players
346
+ * `enabled` only when given (player 0.6.4; older players
347
347
  * ignore it).
348
348
  */
349
349
  install: (token, expectedRevision, requestId, options = {}) => transport.request("packages.install", {
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vestra Player SDK
2
2
 
3
- > @vestra-player/sdk 1.5.58: 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. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
3
+ > @vestra-player/sdk 1.6.1: 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. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
4
4
 
5
5
  ## Read the documentation
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.5.58",
3
+ "version": "1.6.1",
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": {
@@ -9,7 +9,15 @@
9
9
  // row (a chat message: a `[MarkdownBlock]` field of the row's struct).
10
10
  // `code-copy: true` adds a Copy button to code blocks that reports
11
11
  // `copy-code(code)`; the plugin copies it (`plugin.clipboard`).
12
- import { ListView } from "std-widgets.slint";
12
+ // `selectable: true` lets the user select and copy text with the mouse:
13
+ // paragraphs, headings, list items and quotes without links then show their
14
+ // plain text (`MarkdownBlock.plain`, no inline styles) in a read-only text
15
+ // field, one field per block, with a Copy / Select All menu. A block with
16
+ // links keeps its styled text (links stay clickable); its right-click menu
17
+ // copies the block as plain text with each address after its link text,
18
+ // `text (url)`, and offers Copy Link when the block has one link. Code
19
+ // blocks are always selectable.
20
+ import { ListView, Palette } from "std-widgets.slint";
13
21
  import { Theme } from "theme.slint";
14
22
  import { Icon } from "icons.slint";
15
23
 
@@ -36,21 +44,130 @@ export struct MarkdownBlock {
36
44
  // Task list item: 0 none, 1 open, 2 done (a drawn box, no font glyph).
37
45
  task: int,
38
46
  text: styled-text,
47
+ // The block's text without styles, what `selectable` shows: a code
48
+ // block's code, a table's cells (tab-separated lines), an image's alt.
49
+ plain: string,
39
50
  code: string,
40
51
  image: image,
41
52
  has-image: bool,
42
53
  image-width: int,
43
54
  image-height: int,
44
55
  alt: string,
56
+ // The addresses of the block's links, in order.
57
+ links: [string],
45
58
  rows: [MarkdownRow],
46
59
  // Table column alignment: "left", "center", "right" or "" (left).
47
60
  aligns: [string],
48
61
  }
49
62
 
63
+ // Read-only text the user can select: a right-click menu with Copy and
64
+ // Select All (as Slint's own text fields; Copy without a selection copies
65
+ // the whole block), and Ctrl+C / Ctrl+A (⌘C / ⌘A) while it has focus. No
66
+ // caret, as selectable text on a web page. Each one is a Tab stop, and
67
+ // while one has focus the arrow keys move in it (Slint 1.18's read-only
68
+ // TextInput).
69
+ component SelectableText {
70
+ in property <string> text;
71
+ in property <brush> color: Theme.text;
72
+ in property <length> font-size: Theme.font-tab;
73
+ in property <int> font-weight: 400;
74
+ in property <string> font-family;
75
+ min-height: input.min-height;
76
+ preferred-height: input.preferred-height;
77
+ ContextMenuArea {
78
+ Menu {
79
+ MenuItem {
80
+ title: @tr("Copy");
81
+ enabled: root.text != "";
82
+ activated => {
83
+ if input.anchor-position-byte-offset == input.cursor-position-byte-offset {
84
+ input.select-all();
85
+ input.copy();
86
+ input.clear-selection();
87
+ } else {
88
+ input.copy();
89
+ }
90
+ }
91
+ }
92
+ MenuItem {
93
+ title: @tr("Select All");
94
+ enabled: root.text != "";
95
+ activated => { input.select-all(); }
96
+ }
97
+ }
98
+ input := TextInput {
99
+ width: root.width;
100
+ height: root.height;
101
+ text: root.text;
102
+ read-only: true;
103
+ single-line: false;
104
+ wrap: word-wrap;
105
+ color: root.color;
106
+ font-size: root.font-size;
107
+ font-weight: root.font-weight;
108
+ font-family: root.font-family;
109
+ text-cursor-width: 0px;
110
+ // The std-widgets LineEdit's selection colours, legible in both
111
+ // palettes (as the info plugin's properties).
112
+ selection-background-color: Palette.selection-background;
113
+ selection-foreground-color: Palette.selection-foreground;
114
+ }
115
+ }
116
+ }
117
+
118
+ // A block with links in a selectable view: its styled text (links stay
119
+ // clickable) under a right-click menu that copies `plain` (with the
120
+ // addresses) and, for one link, the link. The menu lies above the text: a
121
+ // left click passes through it to the links.
122
+ component LinkText {
123
+ in property <styled-text> text;
124
+ in property <string> plain;
125
+ in property <[string]> links;
126
+ in property <brush> color: Theme.text;
127
+ in property <length> font-size: Theme.font-tab;
128
+ callback link-clicked(string);
129
+ min-height: styled.min-height;
130
+ preferred-height: styled.preferred-height;
131
+ function put(text: string) {
132
+ clip.text = text;
133
+ clip.select-all();
134
+ clip.copy();
135
+ }
136
+ styled := StyledText {
137
+ width: root.width;
138
+ height: root.height;
139
+ text: root.text;
140
+ default-color: root.color;
141
+ default-font-size: root.font-size;
142
+ link-color: Theme.accent;
143
+ link-clicked(url) => { root.link-clicked(url); }
144
+ }
145
+ ContextMenuArea {
146
+ Menu {
147
+ MenuItem {
148
+ title: @tr("Copy");
149
+ activated => { root.put(root.plain); }
150
+ }
151
+ if root.links.length == 1: MenuItem {
152
+ title: @tr("Copy Link");
153
+ activated => { root.put(root.links[0]); }
154
+ }
155
+ }
156
+ }
157
+ // Puts text on the clipboard (Slint has no clipboard function).
158
+ clip := TextInput {
159
+ visible: false;
160
+ width: 0px;
161
+ height: 0px;
162
+ read-only: true;
163
+ }
164
+ }
165
+
50
166
  component MarkdownBlockView {
51
167
  in property <MarkdownBlock> block;
52
168
  in property <length> available;
53
169
  in property <bool> code-copy;
170
+ in property <bool> selectable;
54
171
  callback link-clicked(string);
55
172
  callback copy-code(string);
56
173
  private property <length> quote-indent: block.quote * 12px;
@@ -61,7 +178,21 @@ component MarkdownBlockView {
61
178
  HorizontalLayout {
62
179
  padding-left: root.quote-indent + root.list-indent;
63
180
  VerticalLayout {
64
- if block.kind == "heading": StyledText {
181
+ if block.kind == "heading" && root.selectable && block.links.length > 0: LinkText {
182
+ width: root.inner;
183
+ text: block.text;
184
+ plain: block.plain;
185
+ links: block.links;
186
+ font-size: root.heading-size;
187
+ link-clicked(url) => { root.link-clicked(url); }
188
+ }
189
+ if block.kind == "heading" && root.selectable && block.links.length == 0: SelectableText {
190
+ width: root.inner;
191
+ text: block.plain;
192
+ font-size: root.heading-size;
193
+ font-weight: 700;
194
+ }
195
+ if block.kind == "heading" && !root.selectable: StyledText {
65
196
  width: root.inner;
66
197
  text: block.text;
67
198
  default-color: Theme.text;
@@ -69,7 +200,20 @@ component MarkdownBlockView {
69
200
  link-color: Theme.accent;
70
201
  link-clicked(url) => { root.link-clicked(url); }
71
202
  }
72
- if block.kind == "paragraph": StyledText {
203
+ if block.kind == "paragraph" && root.selectable && block.links.length > 0: LinkText {
204
+ width: root.inner;
205
+ text: block.text;
206
+ plain: block.plain;
207
+ links: block.links;
208
+ color: block.quote > 0 ? Theme.text-dim : Theme.text;
209
+ link-clicked(url) => { root.link-clicked(url); }
210
+ }
211
+ if block.kind == "paragraph" && root.selectable && block.links.length == 0: SelectableText {
212
+ width: root.inner;
213
+ text: block.plain;
214
+ color: block.quote > 0 ? Theme.text-dim : Theme.text;
215
+ }
216
+ if block.kind == "paragraph" && !root.selectable: StyledText {
73
217
  width: root.inner;
74
218
  text: block.text;
75
219
  default-color: block.quote > 0 ? Theme.text-dim : Theme.text;
@@ -106,7 +250,18 @@ component MarkdownBlockView {
106
250
  }
107
251
  }
108
252
  }
109
- StyledText {
253
+ if root.selectable && block.links.length > 0: LinkText {
254
+ width: root.inner - 20px;
255
+ text: block.text;
256
+ plain: block.plain;
257
+ links: block.links;
258
+ link-clicked(url) => { root.link-clicked(url); }
259
+ }
260
+ if root.selectable && block.links.length == 0: SelectableText {
261
+ width: root.inner - 20px;
262
+ text: block.plain;
263
+ }
264
+ if !root.selectable: StyledText {
110
265
  width: root.inner - 20px;
111
266
  text: block.text;
112
267
  default-color: Theme.text;
@@ -123,12 +278,12 @@ component MarkdownBlockView {
123
278
  VerticalLayout {
124
279
  padding: 6px;
125
280
  // Plain lines in the monospace font; CJK falls back.
126
- StyledText {
281
+ // Always selectable: code is there to be copied.
282
+ SelectableText {
127
283
  width: root.inner - 12px;
128
- text: block.text;
129
- default-color: Theme.text;
130
- default-font-family: Theme.font-mono;
131
- default-font-size: Theme.font-body;
284
+ text: block.code;
285
+ font-family: Theme.font-mono;
286
+ font-size: Theme.font-body;
132
287
  }
133
288
  }
134
289
  if root.code-copy: Rectangle {
@@ -216,6 +371,8 @@ export component MarkdownBlocks inherits Rectangle {
216
371
  in property <[MarkdownBlock]> blocks;
217
372
  // A Copy button on code blocks, reporting `copy-code(code)`.
218
373
  in property <bool> code-copy: false;
374
+ // Text the user can select and copy (see the top of this file).
375
+ in property <bool> selectable: false;
219
376
  callback link-clicked(string);
220
377
  callback copy-code(string);
221
378
  min-height: column.min-height;
@@ -228,6 +385,7 @@ export component MarkdownBlocks inherits Rectangle {
228
385
  block: block;
229
386
  available: root.width;
230
387
  code-copy: root.code-copy;
388
+ selectable: root.selectable;
231
389
  link-clicked(url) => { root.link-clicked(url); }
232
390
  copy-code(code) => { root.copy-code(code); }
233
391
  }
@@ -244,6 +402,10 @@ export component MarkdownView inherits Rectangle {
244
402
  in property <length> inset: 10px;
245
403
  // A Copy button on code blocks, reporting `copy-code(code)`.
246
404
  in property <bool> code-copy: false;
405
+ // Text the user can select and copy (see the top of this file). A
406
+ // selection is lost when its block scrolls out of view (the `ListView`
407
+ // drops the row).
408
+ in property <bool> selectable: false;
247
409
  callback link-clicked(string);
248
410
  callback copy-code(string);
249
411
  list := ListView {
@@ -261,6 +423,7 @@ export component MarkdownView inherits Rectangle {
261
423
  block: block;
262
424
  available: self.width;
263
425
  code-copy: root.code-copy;
426
+ selectable: root.selectable;
264
427
  link-clicked(url) => { root.link-clicked(url); }
265
428
  copy-code(code) => { root.copy-code(code); }
266
429
  }