@vestra-player/sdk 1.5.13 → 1.5.15

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.
@@ -81,6 +81,19 @@ export interface Disposable {
81
81
  * | `host` | Any other failure inside the player; `message` is the player's reason. |
82
82
  * | `disabled` | The plugin is being stopped; pending requests fail with it. |
83
83
  * | `unknown`, `rejected`, `expired` | After a transport problem the SDK could not confirm the outcome (does not happen in normal operation). |
84
+ * | `activation` | The plugin opened a tool window while its activation was still pending and the player could not track it. |
85
+ * | `command` | The player could not run or update a command it no longer knows for this plugin (removed meanwhile, or another plugin's). |
86
+ * | `input-overflow` | `openStream` input: the player's queue of physical key transitions was full; the input was revoked and its keys released. |
87
+ * | `internal` | An internal step of the player failed (keeping an operation's result, a subtitle or media job); `message` has the reason. |
88
+ * | `javascript`, `slint` | {@link Packages.validate}: the package's entry module does not pass `node --check` / one of its Slint files does not compile; `message` has the diagnostics. |
89
+ * | `owner` | A resolver answered a request that belongs to another plugin's resolver. |
90
+ * | `pending` | An operation with the same token is still running in the player. The SDK waits and asks again, so it rarely reaches a plugin. |
91
+ * | `producer` | `openStream`: the player refused or lost the generated stream; `message` has the reason. |
92
+ * | `resolution` | A file or web resolver's result could not be opened; `message` has the reason. |
93
+ * | `revisionConflict` | {@link Packages}: the installed revision changed since it was read (another install, rollback or uninstall ran). Read again and retry; the SDK passes the revision it read, so this reaches plugins that act on old data. |
94
+ * | `source-geometry`, `source-geometry-unsupported` | The source's picture size or rotation could not be applied: invalid decoder dimensions / a rotation that is not a multiple of 90°. |
95
+ * | `source-unavailable` | The source is not ready (still loading, or its time or native binding is not available yet). |
96
+ * | `unknown-expired` | The player no longer knows the operation or snapshot (it expired or was released). The SDK maps it to `unknownExpired` results where it can; do not resubmit the operation blindly. |
84
97
  *
85
98
  * Errors thrown by handlers the SDK invokes go to {@link Scope.onError}. A
86
99
  * rejected promise you forgot to `await` is only logged; it never crashes
@@ -320,16 +333,19 @@ export interface Plugin extends Scope {
320
333
  /** The manifest id, e.g. `"community.bookmark"`. */
321
334
  readonly id: string;
322
335
  /**
323
- * A writable directory for this plugin's own files (settings, remembered
324
- * folders). Kept across restarts, disabling and upgrades. Write files atomically: to a temporary name, then rename.
325
- * Throws `unavailable` when the player provided none.
336
+ * A writable directory for this plugin's own files (settings, caches,
337
+ * remembered folders), created by the player before it starts the plugin.
338
+ * Each installation has its own directory (player 0.2.0): it is kept across
339
+ * restarts, disabling, upgrades and rollbacks, and a reinstall of the same
340
+ * id from a different source gets a new, empty one. It is deleted only by
341
+ * `packages.uninstall(id, { removeData: true })`. No capability.
326
342
  *
327
- * Location: `~/Library/Application Support/Vestra/plugin-data/<id>/` on
328
- * macOS, `%APPDATA%\Vestra\plugin-data\<id>\` on Windows (the environment
329
- * variable `VESTRA_PLUGIN_DATA` moves the root). Another plugin's data
330
- * directory is `join(dirname(plugin.dataDirectory), id)`. It is deleted
331
- * only by `packages.uninstall(id, { removeData: true })`. No capability.
332
- * For JSON settings prefer {@link Scope.storage}.
343
+ * The path's format is not part of the API: do not derive another
344
+ * plugin's directory from it (a plugin manager reads
345
+ * {@link PluginInfo.dataDirectory}). Write files atomically (to a
346
+ * temporary name, then rename); for JSON settings prefer
347
+ * {@link Scope.storage}. Throws `unavailable` when the player provided
348
+ * none. See [How a plugin runs](../../docs/concepts/plugins.md#the-data-directory).
333
349
  */
334
350
  readonly dataDirectory: string;
335
351
  }
@@ -388,9 +404,10 @@ export type MenuId = "open" | "playback" | "subtitles" | "video" | "audio" | "sc
388
404
  * item never moves the player's menu. Directly inside a built-in menu,
389
405
  * `order` 0 (the default) sorts after every other order: an item without
390
406
  * `order` goes at the end, after the player's own items (which all use 100
391
- * or more). Give a positive order to sit between them (the player's orders
392
- * are listed in its menu table, doc/product/menus.md), or a negative one to
393
- * come first. Deeper levels (a plugin's own submenu) have no such rule.
407
+ * or more). Give a positive order to sit between them (the player's items
408
+ * in menu order are in
409
+ * [Built-in commands](../../docs/reference/commands.md#built-in-commands)),
410
+ * or a negative one to come first. Deeper levels (a plugin's own submenu) have no such rule.
394
411
  * Players before 0.3.0 place every submenu at its smallest order, so there
395
412
  * an item without `order` moves the whole menu to the top; to avoid that,
396
413
  * give it an order in the menu's range (Video: 400–499).
@@ -471,8 +488,8 @@ export interface CommandOptions {
471
488
  *
472
489
  * Platforms: on macOS `Ctrl+X` fires for both ⌃X and ⌘X while `Cmd+X`
473
490
  * means ⌘X only; on Windows `Cmd` is the Windows logo key, so write
474
- * `Ctrl+…` for bindings that work on both. Avoid keys the system takes
475
- * first (⌃F1–⌃F8, ⌃Arrow keys, ⌘Tab on macOS).
491
+ * `Ctrl+…` for bindings that work on both. Avoid the
492
+ * [keys the system takes first](../../docs/reference/commands.md#keys-the-system-takes-first).
476
493
  *
477
494
  * Conflicts are not errors: the user's overrides win, then the player's
478
495
  * own commands, then the plugin loaded first; a shadowed binding takes
@@ -812,6 +829,15 @@ export interface Ui {
812
829
  * The text is at most 256 KB; very long documents are cut off after 4000
813
830
  * blocks.
814
831
  *
832
+ * `MarkdownView` scrolls one document. `MarkdownBlocks` (SDK 1.5.4, players
833
+ * after 0.4.0) draws one without scrolling, as tall as its content, for a
834
+ * layout of your own or a `ListView` row. For a list of documents, write
835
+ * the whole list with one document per row: the player parses only the
836
+ * documents that changed and updates the list in place (rows added or
837
+ * removed at the end, a growing document's changed blocks), so re-sending
838
+ * the list on every streamed chunk keeps the scroll position and costs
839
+ * little.
840
+ *
815
841
  * @capability None.
816
842
  * @player 0.2.0 (set `"minPlayerVersion": "0.2.0"` in the manifest)
817
843
  *
@@ -826,6 +852,44 @@ export interface Ui {
826
852
  * `base` is neither an `http(s):` address nor an absolute path.
827
853
  * @throws `unsupported` (rejects) The player is older than 0.2.0.
828
854
  *
855
+ * @example A chat: one Markdown document per row, with Copy buttons
856
+ * `ui/chat.slint`:
857
+ *
858
+ * ```slint
859
+ * import { ListView } from "std-widgets.slint";
860
+ * import { MarkdownBlocks, MarkdownBlock } from "@vestra/markdown.slint";
861
+ * export struct Message { id: string, role: string, body: [MarkdownBlock] }
862
+ * export component Chat inherits Window {
863
+ * in property <[Message]> messages;
864
+ * callback copy-code(string);
865
+ * ListView {
866
+ * for message in root.messages: VerticalLayout {
867
+ * padding: 8px;
868
+ * MarkdownBlocks {
869
+ * blocks: message.body;
870
+ * code-copy: true;
871
+ * copy-code(code) => { root.copy-code(code); }
872
+ * }
873
+ * }
874
+ * }
875
+ * }
876
+ * ```
877
+ *
878
+ * ```ts
879
+ * const chat = plugin.windows.create("ui/chat.slint");
880
+ * const history: { id: string; role: string; text: string }[] = [];
881
+ * chat.on("copyCode", (code: string) => plugin.clipboard.writeText(code));
882
+ * async function render() {
883
+ * const messages = [];
884
+ * for (const { id, role, text } of history)
885
+ * messages.push({ id, role, body: await plugin.ui.markdown(text) });
886
+ * chat.set({ messages });
887
+ * }
888
+ * history.push({ id: "1", role: "user", text: "Show me `ls`." });
889
+ * await render();
890
+ * await chat.show();
891
+ * ```
892
+ *
829
893
  * @example Show a README from the web
830
894
  * ```ts
831
895
  * const readmeUrl = "https://example.com/plugin/README.md";
@@ -1029,8 +1093,8 @@ export interface View {
1029
1093
  * {@link View.bind} or {@link View.set}.
1030
1094
  * 3. {@link Window.show} opens the window with those properties.
1031
1095
  * {@link Window.hide} hides it and keeps its state.
1032
- * 4. {@link Window.close}, or the user (title-bar button; ⌘W on macOS,
1033
- * Ctrl+W or Ctrl+F4 on Windows), destroys the window instance; only the
1096
+ * 4. {@link Window.close}, or the user (title-bar button or the
1097
+ * [standard close keys](../../docs/reference/commands.md#keys-the-system-takes-first)), destroys the window instance; only the
1034
1098
  * user's close calls {@link Window.onClose}. The handle stays usable: the
1035
1099
  * next `show()` opens a fresh instance with the current properties.
1036
1100
  * 5. `dispose()`, disposing the owning scope or the plugin stopping closes
@@ -1184,8 +1248,8 @@ export interface Window extends View, Disposable {
1184
1248
  * Run code when the user closes the window.
1185
1249
  *
1186
1250
  * @remarks
1187
- * Called for the title-bar close button and the standard close keys (⌘W
1188
- * on macOS, Ctrl+W or Ctrl+F4 on Windows). When it runs, the window
1251
+ * Called for the title-bar close button and the
1252
+ * [standard close keys](../../docs/reference/commands.md#keys-the-system-takes-first). When it runs, the window
1189
1253
  * instance is gone and {@link Window.opened} is already `false`; a later
1190
1254
  * `show()` opens a fresh one. It is not called for the plugin's own
1191
1255
  * {@link Window.close} or `dispose()`, nor when the plugin stops. Errors
@@ -1294,8 +1358,10 @@ export interface SlotState {
1294
1358
  * - `fullscreen`: the sidebar's fullscreen overlay is collapsed and pops
1295
1359
  * out when the pointer reaches the right edge; treat it as "on" for a
1296
1360
  * toggle button;
1297
- * - `idle`: the bottom bar in fullscreen, hidden after 3 seconds without
1298
- * activity.
1361
+ * - `idle`: the bottom bar in fullscreen while the pointer is outside the
1362
+ * bar's own band at the bottom of the screen (player 0.5.1: keys and
1363
+ * pointer motion elsewhere do not bring it back; before 0.5.1 it hid after
1364
+ * 3 seconds without activity).
1299
1365
  */
1300
1366
  reason: "userHidden" | "space" | "fullscreen" | "idle" | null;
1301
1367
  /** The sidebar floats in its own window; always `false` for the bottom bar. */
@@ -1514,8 +1580,8 @@ export interface PanelSlot {
1514
1580
  * Detached, the sidebar panel is shown in a separate window (300×408 by
1515
1581
  * default, at least 300×250) with the panel's `title`. The plugin's
1516
1582
  * properties and callbacks stay the same, but the window holds a new
1517
- * instance of the component. Closing the floating window (its close button,
1518
- * ⌘W, Ctrl+W, Ctrl+F4) is the same as `setVisible(false)`.
1583
+ * instance of the component. Closing the floating window (its close button
1584
+ * or the [standard close keys](../../docs/reference/commands.md#keys-the-system-takes-first)) is the same as `setVisible(false)`.
1519
1585
  *
1520
1586
  * @capability ui.mount
1521
1587
  *
@@ -1732,8 +1798,11 @@ export interface Panel extends View, Disposable {
1732
1798
  * Keep the panel on screen while the user works in it.
1733
1799
  *
1734
1800
  * @remarks
1735
- * The bottom bar hides after 3 seconds without activity in fullscreen, and
1736
- * the fullscreen sidebar overlay collapses when the pointer leaves it. Set a
1801
+ * In fullscreen the bottom bar shows only while the pointer is in the bar's
1802
+ * own band at the bottom of the screen: at once when the pointer enters it,
1803
+ * hidden 500 ms after it leaves (player 0.5.1; keys and pointer motion
1804
+ * elsewhere do not reveal it), and the fullscreen sidebar overlay collapses
1805
+ * when the pointer leaves it. A lock keeps the bar shown. Set a
1737
1806
  * lock while a text field has focus, a drag is in progress or a popup is
1738
1807
  * open, and clear it afterwards. Locks are merged with the earlier ones;
1739
1808
  * the last value of each wins. The player already locks a panel while the
@@ -2645,7 +2714,9 @@ export interface Panels {
2645
2714
  * - `"bottom"`: under the video, as wide as the video column, as tall as the
2646
2715
  * component's preferred height. It may draw above its slot into the video
2647
2716
  * area (negative `y`); the player does not clip it. In fullscreen it
2648
- * floats over the video and hides after 3 seconds without activity.
2717
+ * floats over the video and shows only while the pointer is in its band
2718
+ * at the bottom of the screen (hidden 500 ms after the pointer leaves;
2719
+ * {@link Panel.setInteraction} locks keep it).
2649
2720
  * - `"sidebar"`: right of the video, the window's full height, as wide as the
2650
2721
  * user drags it (at least 317). The user can detach it into a floating
2651
2722
  * window titled `title`. Several plugins may mount here (player 0.4.x
@@ -3440,10 +3511,27 @@ export interface PlaybackStats {
3440
3511
  backend: string;
3441
3512
  /** The playback session number. */
3442
3513
  session: number;
3443
- /** Frames the host has shown. */
3514
+ /**
3515
+ * Frames the video renderer produced. Not all of them reach the screen: a
3516
+ * busy window shows only the newest one, so compare
3517
+ * {@link PlaybackStats.presentedFps | presentedFps}.
3518
+ */
3444
3519
  framesShown: number;
3445
- /** Frames dropped, `null` when unknown. */
3520
+ /** Frames dropped by the backend (mpv), `null` when unknown. */
3446
3521
  framesDropped: number | null;
3522
+ /**
3523
+ * New video frames the player window showed per second over the last
3524
+ * second, measured by the player itself: draws of the main window that put
3525
+ * a new picture on screen. Lower than `video.fps` when frames are lost
3526
+ * after decoding and rendering (the window could not keep up with the
3527
+ * source, or the display refreshes slower); `0` while paused. `null` when
3528
+ * the player cannot measure it (no window, the first second, players
3529
+ * before 0.5.1).
3530
+ *
3531
+ * @player 0.5.1
3532
+ * @since 1.5.15
3533
+ */
3534
+ presentedFps: number | null;
3447
3535
  /** Every field is the backend's own value, `null` when it has none. */
3448
3536
  video: {
3449
3537
  /** Codec name. */
@@ -3468,8 +3556,15 @@ export interface PlaybackStats {
3468
3556
  displayFps: number | null;
3469
3557
  /** Bits per second, updated per keyframe. */
3470
3558
  bitrate: number | null;
3471
- /** Hardware decoder in use, e.g. "videotoolbox"; "no" = software decoding. */
3559
+ /** Hardware decoder in use, e.g. "videotoolbox", "d3d11va-copy"; "no" = software decoding. */
3472
3560
  hwdec: string | null;
3561
+ /**
3562
+ * How the video is decoded, from `hwdec`: `"hardware"` (the method is in
3563
+ * `hwdec`) or `"software"`; `null` before a decoder runs.
3564
+ *
3565
+ * @since 1.5.15 (with older players the SDK derives it from `hwdec`)
3566
+ */
3567
+ decoding: "hardware" | "software" | null;
3473
3568
  /** Transfer characteristic as mpv names it, e.g. "bt.1886", "pq", "hlg". */
3474
3569
  transfer: string | null;
3475
3570
  } | null;
@@ -3714,8 +3809,8 @@ export interface Host {
3714
3809
  * {@link Osd.panel}.
3715
3810
  *
3716
3811
  * @remarks
3717
- * The block sits under the OSD message line, on a translucent black
3718
- * background, and takes no input. Its font scales with the video area. When
3812
+ * The block sits under the OSD message line, on a translucent dark plate
3813
+ * (see {@link Osd} for sizes), and takes no input. When
3719
3814
  * several plugins have one, they are stacked from top to bottom. `dispose()`,
3720
3815
  * disposing the scope or stopping the plugin removes it.
3721
3816
  *
@@ -3789,8 +3884,25 @@ export interface OsdPanelOptions {
3789
3884
  * ({@link Osd.show}) is one line at the top left of the video that
3790
3885
  * disappears after 2 seconds; a newer message, from any plugin, replaces it.
3791
3886
  * The information block ({@link Osd.panel}) stays until the plugin changes
3792
- * or removes it. Both are white text with a dark outline whose size follows
3793
- * the height of the video area.
3887
+ * or removes it.
3888
+ *
3889
+ * How the player draws them (a player before 0.4.0 drew bold white text
3890
+ * with a black outline instead):
3891
+ *
3892
+ * - **Message**: white text on a translucent dark plate with rounded
3893
+ * corners and a gradient line along its bottom edge, 10px from the
3894
+ * video area's left and top edges.
3895
+ * - **Font size** follows the video area: 3 % of its height, rounded and
3896
+ * clamped to 12–48px (about 18px in the default window, 32px in
3897
+ * fullscreen at 1080 pixels high). The plate's padding and corners scale
3898
+ * with it.
3899
+ * - **Information block**: 4px below the message plate, on the same kind of
3900
+ * plate; its font scales in proportion (11px when the message is 16px),
3901
+ * clamped to 9–28px.
3902
+ *
3903
+ * How long a text may be is in
3904
+ * [Limits: on-screen display](../../docs/reference/limits.md#on-screen-display)
3905
+ * ({@link "@vestra-player/sdk/testing"!OSD_LIMITS | OSD_LIMITS}).
3794
3906
  *
3795
3907
  * @capability ui.osd
3796
3908
  *
@@ -45,7 +45,11 @@ export type SubtitleStyle = {
45
45
  outline: number;
46
46
  /** Bottom margin 2–15 % of the video height. */
47
47
  marginPercent: number;
48
- /** Timing offset −600000–600000 ms. */
48
+ /**
49
+ * Timing offset in milliseconds, −600000…600000 (±10 minutes); positive
50
+ * shows the subtitles later. Outside the range the player refuses the
51
+ * style with `invalid`.
52
+ */
49
53
  offsetMs: number;
50
54
  /** Bold text; default false. */
51
55
  bold?: boolean;
package/dist/wire.d.ts CHANGED
@@ -222,6 +222,8 @@ export interface PlaybackStats {
222
222
  session: number;
223
223
  framesShown: number;
224
224
  framesDropped: number | null;
225
+ /** Player 0.5.1 and newer; the SDK fills in `null`. */
226
+ presentedFps?: number | null;
225
227
  video: {
226
228
  codec: string | null;
227
229
  width: number | null;
@@ -235,6 +237,7 @@ export interface PlaybackStats {
235
237
  displayFps?: number | null;
236
238
  bitrate?: number | null;
237
239
  hwdec?: string | null;
240
+ decoding?: "hardware" | "software" | null;
238
241
  transfer?: string | null;
239
242
  } | null;
240
243
  audio: {