@vestra-player/sdk 1.5.20 → 1.5.21

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,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.21
4
+
5
+ **Who must change: nobody.** Plugins built on earlier SDKs keep working unchanged; the additions need player 0.5.2. Additive and bug fixes; `apiVersion` stays `vestra/1`.
6
+
7
+ - **`Host.run("fullscreen")`** (player 0.5.2; requested by the playback plugin for a bottom-bar button): toggles the main window's fullscreen exactly like the Toggle Fullscreen command (Enter, F11): in when windowed, out when fullscreen. Read the result from `window.state.fullscreen`. `unavailable` when the player has no window; players before 0.5.2 reject the id with `invalid`, so set `"minPlayerVersion": "0.5.2"`. `HostAction` gains `"fullscreen"`.
8
+ - **Default mouse bindings** (player 0.5.2, user ruling): a left click on the video is Play/Pause (`Mouse:LeftClick`, fired on release) and nothing is bound to a double click; Toggle Fullscreen keeps `Enter`, `Alt+Enter`, `F11`. `BUILTIN_COMMANDS` says so. A plugin that declared `Mouse:LeftClick` itself is shadowed by the player's default, as for any key the player binds; users' own bindings are unchanged.
9
+ - **`ToolTipArea` no longer swallows a press** (`slint/vestra/widgets.slint`, the player's widget library; bug fix): the tip was a `PopupWindow`, so while it showed, the first press anywhere only closed it, and it took the keyboard focus. It is now Slint's input-transparent tooltip: every press reaches the control under the pointer. It shows after about half a second (was 600 ms), and `showing` follows that delay closely. No API change.
10
+ - **Icons `fullscreen` and `exit-fullscreen`** in `slint/vestra/icons.slint` (14 × 12, the V-Line style).
11
+ - **`TestHost`**: `host.run("fullscreen")` flips `host.mainWindow.fullscreen` and emits `window.state` (recorded in `coreRuns`); `host/play-pause` declares `Space` and `Mouse:LeftClick`.
12
+ - Docs: *Commands* (the player's mouse defaults), *Playback* (a fullscreen button), the Theme and widgets example (the tooltip); generated tables and `llms.txt` / `llms-full.txt` regenerated.
13
+
3
14
  ## 1.5.20
4
15
 
5
16
  Republishes 1.5.19, which was tagged but never published: its API documentation build failed (an example title and four undocumented `IMAGE_LIMITS` fields). Documentation only; the code is the same as 1.5.19 below.
package/dist/commands.js CHANGED
@@ -142,7 +142,7 @@ vestra.playback | reopen | Reopen Current/Last File | Ctrl+Y
142
142
  vestra.playback | copy-name | Copy to Clipboard ▸ File Name
143
143
  vestra.playback | copy-folder | Copy to Clipboard ▸ Folder Path
144
144
  vestra.playback | copy-path | Copy to Clipboard ▸ Full Path
145
- host | play-pause | Playback ▸ Play/Pause | Space
145
+ host | play-pause | Playback ▸ Play/Pause | Space, Mouse:LeftClick
146
146
  host | previous | Playback ▸ Previous | PageUp, Mouse:X1
147
147
  host | next | Playback ▸ Next | PageDown, Mouse:X2
148
148
  host | frame-back | Playback ▸ Seek ▸ Previous Frame | D
@@ -372,7 +372,7 @@ vestra.visualizer | audio-cover | Audio ▸ Show Album Cover for Audio Only
372
372
  vestra.nes | nes.open | Emulator ▸ Open NES ROM…
373
373
  vestra.nes | nes.stop | Emulator ▸ Stop NES
374
374
  vestra.nes | nes.window | Emulator ▸ NES Tools…
375
- host | fullscreen | Toggle Fullscreen | Enter, Alt+Enter, F11, Mouse:LeftDoubleClick
375
+ host | fullscreen | Toggle Fullscreen | Enter, Alt+Enter, F11
376
376
  vestra.window | pip | Screen ▸ Picture-in-Picture
377
377
  vestra.window | mini | Screen ▸ Mini Size | \`
378
378
  vestra.window | size.0.5 | Screen ▸ 0.5× | 1
@@ -30,6 +30,7 @@ const ACTIONS = new Set([
30
30
  "volume-up",
31
31
  "volume-down",
32
32
  "mute",
33
+ "fullscreen",
33
34
  ]);
34
35
  export const host = (scope) => ({
35
36
  async run(action, options) {
@@ -1375,7 +1375,7 @@ export class TestHost {
1375
1375
  ["Playback", "Play/Pause"],
1376
1376
  201,
1377
1377
  "domain",
1378
- ["Space"],
1378
+ ["Space", "Mouse:LeftClick"],
1379
1379
  false,
1380
1380
  ],
1381
1381
  [
@@ -2735,6 +2735,7 @@ export class TestHost {
2735
2735
  "volume-up",
2736
2736
  "volume-down",
2737
2737
  "mute",
2738
+ "fullscreen",
2738
2739
  ];
2739
2740
  if (!actions.includes(String(p.action)))
2740
2741
  throw new HostError("invalid", `unknown host action ${p.action}`);
@@ -2747,6 +2748,13 @@ export class TestHost {
2747
2748
  throw new HostError("invalid", "seconds must be 0.1..3600 in tenths");
2748
2749
  seconds = p.seconds;
2749
2750
  }
2751
+ // SDK 1.5.21: the main window's Toggle Fullscreen, like Enter: in when
2752
+ // windowed, out when fullscreen, then `window.state`.
2753
+ if (p.action === "fullscreen") {
2754
+ this.coreRuns.push("fullscreen");
2755
+ this.setMainWindow({ fullscreen: !this.mainWindow.fullscreen });
2756
+ return;
2757
+ }
2750
2758
  this.#runCore(String(p.action), seconds);
2751
2759
  },
2752
2760
  "player.setStepSeek": (p) => {
@@ -3877,22 +3877,28 @@ export interface Player {
3877
3877
  setAudioDevice(id: string | null): Promise<void>;
3878
3878
  }
3879
3879
  /**
3880
- * The core's basic playback actions (menu commands owned by the player).
3880
+ * The core's basic playback actions (menu commands owned by the player),
3881
+ * and the main window's fullscreen toggle.
3881
3882
  *
3882
3883
  * | action | default bindings | behaviour |
3883
3884
  * | --- | --- | --- |
3884
3885
  * | `stop` | F4 | close the source |
3885
- * | `play-pause` | Space | pause / resume, replay after the end |
3886
+ * | `play-pause` | Space, a left click on the video (player 0.5.2) | pause / resume, replay after the end |
3886
3887
  * | `previous` / `next` | PageUp, Mouse:X1 / PageDown, Mouse:X2 | ask the sequence provider |
3887
3888
  * | `frame-back` / `frame-forward` | D / F | `player.step("frame", …)` |
3888
3889
  * | `seek-backward` / `seek-forward` | ← / → | relative seek by `setStepSeek` seconds (or `host.run`'s `seconds`), exact or to keyframes as `setStepSeek` says |
3889
3890
  * | `rate-normal`, `rate-down`, `rate-up` | Z, X, C | 1×, ±0.1 within 0.25–4 |
3890
3891
  * | `volume-up` / `volume-down` | ↑ / ↓, wheel | ±5 (`volume-up` stops at 100) |
3891
3892
  * | `mute` | M | toggle mute |
3893
+ * | `fullscreen` | Enter, Alt+Enter, F11 | toggle the main window's fullscreen: enter it when windowed, leave it when fullscreen, as the Toggle Fullscreen command does (SDK 1.5.21, player 0.5.2; `unavailable` when the player has no window) |
3892
3894
  *
3893
- * In the binding overlay their keys are `host/<action>`.
3895
+ * In the binding overlay their keys are `host/<action>`. `fullscreen`
3896
+ * changes no playback state: read the result from `window.state.fullscreen`
3897
+ * (`plugin.window.onChange`), which also changes when the user toggles it.
3898
+ * Players before 0.5.2 reject it with `invalid`: set
3899
+ * `"minPlayerVersion": "0.5.2"` when you use it.
3894
3900
  */
3895
- export type HostAction = "play-pause" | "stop" | "previous" | "next" | "frame-back" | "frame-forward" | "seek-backward" | "seek-forward" | "rate-down" | "rate-up" | "rate-normal" | "volume-up" | "volume-down" | "mute";
3901
+ export type HostAction = "play-pause" | "stop" | "previous" | "next" | "frame-back" | "frame-forward" | "seek-backward" | "seek-forward" | "rate-down" | "rate-up" | "rate-normal" | "volume-up" | "volume-down" | "mute" | "fullscreen";
3896
3902
  /** `plugin.host`: the player's own core actions. */
3897
3903
  export interface Host {
3898
3904
  /**
package/llms-full.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @vestra-player/sdk 1.5.20: full reference
1
+ # @vestra-player/sdk 1.5.21: 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
 
@@ -445,7 +445,7 @@ Key names describe keys, not characters, with two deliberate exceptions:
445
445
  - **Option combinations match the physical key**, so `Alt+O` fires for ⌥O although that key types `ø`, and dead keys (⌥E, ⌥N…) fire too. On layouts that need Option to type a symbol (German ⌥5 = ``, ⌥L = `@`), a binding for the typed symbol (`"["`, `"@"`) wins over one for the physical key (`Alt+5`).
446
446
  - **`Ctrl` is portable, `Cmd` is not.** On macOS `Ctrl+X` fires for both ⌃X and ⌘X, `Cmd+X` only for ⌘X. On Windows `Cmd` is the Windows logo key, which the system mostly keeps, so write `Ctrl+…` for a binding that works on both systems.
447
447
 
448
- Mouse bindings apply only over the video area. `Mouse:LeftDoubleClick:LeftHalf` and `:RightHalf` win over the plain double click; wheel bindings count accumulated scroll steps. The right button is reserved for the context menu, and a left drag in window mode moves the window instead of clicking. A `Mouse:LeftClick` fires on release, except when the same modifiers also have a double-click binding and the click's command is not checkable: then the player waits the system's double-click interval to tell a click from a double click. (A checkable command such as play/pause runs at once; a double click runs it twice, which restores its state, and the double-click binding still fires.)
448
+ Mouse bindings apply only over the video area. `Mouse:LeftDoubleClick:LeftHalf` and `:RightHalf` win over the plain double click; wheel bindings count accumulated scroll steps. The right button is reserved for the context menu, and a left drag in window mode moves the window instead of clicking. A `Mouse:LeftClick` fires on release, except when the same modifiers also have a double-click binding and the click's command is not checkable: then the player waits the system's double-click interval to tell a click from a double click. (A checkable command such as play/pause runs at once; a double click runs it twice, which restores its state, and the double-click binding still fires.) The player's defaults (player 0.5.2): a left click on the video is Play/Pause and nothing is bound to a double click (up to 0.5.1 a double click was Toggle Fullscreen); a click whose command is disabled does nothing.
449
449
 
450
450
  ##### The user's bindings
451
451
 
@@ -579,7 +579,7 @@ The player's **Previous** and **Next** (menu, keys, the bottom bar) are not tied
579
579
 
580
580
  #### Core actions
581
581
 
582
- Play/pause, stop, previous/next, frame steps, relative seeks, speed, volume and mute are **core actions**: commands the player registers itself, so they work with no plugin running. plugin.host.run(action) runs one exactly as its menu item or key would, with the same checks and the same on-screen text. A plugin's play button should call `host.run("play-pause")` rather than `setPaused`, so that it behaves like the Space key in every case (restart after the end, the "Paused" message, disabled without a source).
582
+ Play/pause, stop, previous/next, frame steps, relative seeks, speed, volume and mute are **core actions**: commands the player registers itself, so they work with no plugin running. plugin.host.run(action) runs one exactly as its menu item or key would, with the same checks and the same on-screen text. A plugin's play button should call `host.run("play-pause")` rather than `setPaused`, so that it behaves like the Space key in every case (restart after the end, the "Paused" message, disabled without a source). A fullscreen button calls `host.run("fullscreen")` (player 0.5.2), which toggles the main window like Enter; show the button's state from `window.state.fullscreen`.
583
583
 
584
584
  The difference from the `Player` methods: a `Player` control is a precise operation the plugin is responsible for (conditional, silent, its own error handling); a core action is the user's action, performed on the plugin's behalf. The action ids, menu places and default keys are in Reference: commands; how keys reach them is in Commands, menus and key bindings.
585
585
 
@@ -2176,6 +2176,7 @@ The table lists the API whose documentation names a player version, newest last:
2176
2176
  | 0.4.2 | Resolvers.fileTypes, Resolvers.onFileTypesChange |
2177
2177
  | 0.4.3 | CommandOptions.path, PropValue, View.set, Appearance.setLanguage, TemplateArgs, Text, ConfirmOptions.args, OsdPanel.set, OsdPanelOptions.maxColumns, Osd.show |
2178
2178
  | 0.5.1 | ImageRef, ImageOptions.maxSide, PropValue, Ui.image, Ui.remoteImage, SlotState.reason, Panel.setInteraction, PlaybackStats.presentedFps, Player.setStepSeek, Host.run, Packages.reload |
2179
+ | 0.5.2 | HostAction |
2179
2180
 
2180
2181
  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).
2181
2182
 
@@ -2410,7 +2411,7 @@ Every command the player (`host`) and its built-in plugins register when the pla
2410
2411
  | Copy to Clipboard ▸ File Name | | `vestra.playback/copy-name` |
2411
2412
  | Copy to Clipboard ▸ Folder Path | | `vestra.playback/copy-folder` |
2412
2413
  | Copy to Clipboard ▸ Full Path | | `vestra.playback/copy-path` |
2413
- | Playback ▸ Play/Pause | `Space` | `host/play-pause` |
2414
+ | Playback ▸ Play/Pause | `Space`, `Mouse:LeftClick` | `host/play-pause` |
2414
2415
  | Playback ▸ Previous | `PageUp`, `Mouse:X1` | `host/previous` |
2415
2416
  | Playback ▸ Next | `PageDown`, `Mouse:X2` | `host/next` |
2416
2417
  | Playback ▸ Seek ▸ Previous Frame | `D` | `host/frame-back` |
@@ -2640,7 +2641,7 @@ Every command the player (`host`) and its built-in plugins register when the pla
2640
2641
  | Emulator ▸ Open NES ROM… | | `vestra.nes/nes.open` |
2641
2642
  | Emulator ▸ Stop NES | | `vestra.nes/nes.stop` |
2642
2643
  | Emulator ▸ NES Tools… | | `vestra.nes/nes.window` |
2643
- | Toggle Fullscreen | `Enter`, `Alt+Enter`, `F11`, `Mouse:LeftDoubleClick` | `host/fullscreen` |
2644
+ | Toggle Fullscreen | `Enter`, `Alt+Enter`, `F11` | `host/fullscreen` |
2644
2645
  | Screen ▸ Picture-in-Picture | | `vestra.window/pip` |
2645
2646
  | Screen ▸ Mini Size | ``` | `vestra.window/mini` |
2646
2647
  | Screen ▸ 0.5× | `1` | `vestra.window/size.0.5` |
@@ -2753,7 +2754,7 @@ The player's own playback commands, which plugin.host.run runs by id (capability
2753
2754
  | Action | Menu item | Default bindings |
2754
2755
  | --- | --- | --- |
2755
2756
  | `stop` | Close | `F4` |
2756
- | `play-pause` | Playback ▸ Play/Pause | `Space` |
2757
+ | `play-pause` | Playback ▸ Play/Pause | `Space`, `Mouse:LeftClick` |
2757
2758
  | `previous` | Playback ▸ Previous | `PageUp`, `Mouse:X1` |
2758
2759
  | `next` | Playback ▸ Next | `PageDown`, `Mouse:X2` |
2759
2760
  | `frame-back` | Playback ▸ Seek ▸ Previous Frame | `D` |
@@ -2766,6 +2767,7 @@ The player's own playback commands, which plugin.host.run runs by id (capability
2766
2767
  | `volume-up` | Audio ▸ Volume + | `ArrowUp`, `Mouse:WheelUp` |
2767
2768
  | `volume-down` | Audio ▸ Volume − | `ArrowDown`, `Mouse:WheelDown` |
2768
2769
  | `mute` | Audio ▸ Mute | `M` |
2770
+ | `fullscreen` | Toggle Fullscreen | `Enter`, `Alt+Enter`, `F11` |
2769
2771
 
2770
2772
  The player also owns **Toggle Fullscreen** (`host/fullscreen`), **Leave Fullscreen** (`host/exit-fullscreen`, `Escape`, only in fullscreen) and **Quit** (`host/quit`); their keys are in Built-in commands.
2771
2773
 
@@ -2940,17 +2942,19 @@ Icon { name: "close"; width: 20px; height: 20px; } // scaled
2940
2942
  | `add` | 10 × 10 | 1.5px |
2941
2943
  | `remove` | 10 × 10 | 1.5px |
2942
2944
  | `sort` | 10 × 10 | 1.5px |
2945
+ | `fullscreen` | 14 × 12 | 1.5px |
2946
+ | `exit-fullscreen` | 14 × 12 | 1.5px |
2943
2947
 
2944
2948
  #### Widgets
2945
2949
 
2946
2950
  `@vestra/widgets.slint`: compact controls drawn with the theme. Other controls come from `std-widgets.slint`.
2947
2951
 
2948
- **`ToolTipArea`**: Hover for 600 ms to show `text` beside the pointer. The tip closes when the pointer leaves or presses; it shows again on the next hover.
2952
+ **`ToolTipArea`**: Hover for about half a second to show `text` beside the pointer. The tip closes when the pointer leaves, or presses on content that does not take the press itself; it shows again on the next hover. It is Slint's input-transparent tooltip: every press reaches the control under the pointer, and the tip does not take the keyboard focus. (Up to SDK 1.5.20 the tip was a PopupWindow, which swallowed the first press after it showed and took the focus while it was open.)
2949
2953
 
2950
2954
  | Member | Kind | Default | Note |
2951
2955
  | --- | --- | --- | --- |
2952
2956
  | `text` | in `string` | | |
2953
- | `showing` | out `bool` | | The tip is showing (read-only). |
2957
+ | `showing` | out `bool` | | The tip is showing (read-only); it follows the tip's delay closely, not exactly. |
2954
2958
 
2955
2959
  **`Button`**: Dialog button: text (21px, or 18px when `small`), or an icon button when `text` is empty and `icon` holds Path data drawn in an icon-width × icon-height viewbox (filled, or 1.5px round-capped lines when `stroked`, as the @vestra/icons.slint set). The caller sizes icon buttons.
2956
2960
 
@@ -5278,23 +5282,29 @@ interface Player {
5278
5282
 
5279
5283
  ### HostAction
5280
5284
 
5281
- The core's basic playback actions (menu commands owned by the player).
5285
+ The core's basic playback actions (menu commands owned by the player),
5286
+ and the main window's fullscreen toggle.
5282
5287
 
5283
5288
  | action | default bindings | behaviour |
5284
5289
  | --- | --- | --- |
5285
5290
  | `stop` | F4 | close the source |
5286
- | `play-pause` | Space | pause / resume, replay after the end |
5291
+ | `play-pause` | Space, a left click on the video (player 0.5.2) | pause / resume, replay after the end |
5287
5292
  | `previous` / `next` | PageUp, Mouse:X1 / PageDown, Mouse:X2 | ask the sequence provider |
5288
5293
  | `frame-back` / `frame-forward` | D / F | `player.step("frame", …)` |
5289
5294
  | `seek-backward` / `seek-forward` | ← / → | relative seek by `setStepSeek` seconds (or `host.run`'s `seconds`), exact or to keyframes as `setStepSeek` says |
5290
5295
  | `rate-normal`, `rate-down`, `rate-up` | Z, X, C | 1×, ±0.1 within 0.25–4 |
5291
5296
  | `volume-up` / `volume-down` | ↑ / ↓, wheel | ±5 (`volume-up` stops at 100) |
5292
5297
  | `mute` | M | toggle mute |
5298
+ | `fullscreen` | Enter, Alt+Enter, F11 | toggle the main window's fullscreen: enter it when windowed, leave it when fullscreen, as the Toggle Fullscreen command does (SDK 1.5.21, player 0.5.2; `unavailable` when the player has no window) |
5293
5299
 
5294
- In the binding overlay their keys are `host/<action>`.
5300
+ In the binding overlay their keys are `host/<action>`. `fullscreen`
5301
+ changes no playback state: read the result from `window.state.fullscreen`
5302
+ (`plugin.window.onChange`), which also changes when the user toggles it.
5303
+ Players before 0.5.2 reject it with `invalid`: set
5304
+ `"minPlayerVersion": "0.5.2"` when you use it.
5295
5305
 
5296
5306
  ```ts
5297
- type HostAction = "play-pause" | "stop" | "previous" | "next" | "frame-back" | "frame-forward" | "seek-backward" | "seek-forward" | "rate-down" | "rate-up" | "rate-normal" | "volume-up" | "volume-down" | "mute";
5307
+ type HostAction = "play-pause" | "stop" | "previous" | "next" | "frame-back" | "frame-forward" | "seek-backward" | "seek-forward" | "rate-down" | "rate-up" | "rate-normal" | "volume-up" | "volume-down" | "mute" | "fullscreen";
5298
5308
  ```
5299
5309
 
5300
5310
  ### Host
@@ -9730,7 +9740,7 @@ From top to bottom:
9730
9740
  - **The window.** A fixed `width`; `Theme.bg-panel` instead of a hard-coded colour, so the background follows the player's palette.
9731
9741
  - **The properties.** `level` is `in-out` because both the plugin and the user change it; `label` is only written by the plugin. `level-changed` reports a drag; `reset` reports the reset buttons.
9732
9742
  - **The layout.** `Theme.dialog-padding` and `Theme.font-body` give the spacing and text size of the player's dialogs.
9733
- - **`ToolTipArea`** shows its `text` after the pointer rests on its content for 600 ms.
9743
+ - **`ToolTipArea`** shows its `text` after the pointer rests on its content for about half a second (600 ms before player 0.5.2). The tip never takes a press or the keyboard focus: a click goes to the control under the pointer even while the tip shows.
9734
9744
  - **The sliders** are bound _two-way_ to `level` with `<=>`, so dragging one moves the other at once, inside Slint. `changed` passes the value to the plugin. A one-way binding (`value: root.level`) would be broken by Slint the first time the user moved the slider. `panel: true` is the panel style; without it, the bar style.
9735
9745
  - **`Button`** with `text` is a dialog button; `small: true` is the 18-pixel one.
9736
9746
  - **Icon buttons** have no `text`; `icon` is SVG-style path data drawn in an `icon-width` × `icon-height` box (14×12 by default). `stroked: true` draws lines instead of filling. `checked` draws the icon in the accent colour.
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vestra Player SDK
2
2
 
3
- > @vestra-player/sdk 1.5.20: 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. Everything a plugin creates (commands, windows, panels, filters, listeners…) is a handle that is released automatically when the plugin stops. L<n> are line numbers in llms-full.txt, next to this file: every page, declaration and example.
3
+ > @vestra-player/sdk 1.5.21: 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. Everything a plugin creates (commands, windows, panels, filters, listeners…) is a handle that is released automatically when the plugin stops. L<n> are line numbers in llms-full.txt, next to this file: 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: L2120–2183.
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: L2120–2184.
37
37
 
38
38
  **main.ts**
39
39
 
@@ -76,9 +76,9 @@ export default async function activate(plugin: Plugin) {
76
76
  - `ui.settings`: Settings.page/show
77
77
  - `ui.theme`: Appearance.set/setLanguage
78
78
  - `secrets`: Secrets.get/set/delete
79
- - No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme and packages.list. Details: L2184–2226.
79
+ - No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme and packages.list. Details: L2185–2227.
80
80
 
81
- **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` is a TestHost (an in-memory player): `host.setSource(…)`, `host.command(id).run()`, `host.press(key)`, `await host.settle()`, then assert on `host.osd`, `host.window(id).props`… Guide: L1606–1771; TestHost: L6845.
81
+ **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` is a TestHost (an in-memory player): `host.setSource(…)`, `host.command(id).run()`, `host.press(key)`, `await host.settle()`, then assert on `host.osd`, `host.window(id).props`… Guide: L1606–1771; TestHost: L6855.
82
82
 
83
83
  **Command line**
84
84
 
@@ -106,7 +106,7 @@ export default async function activate(plugin: Plugin) {
106
106
  - `limit`: A player-wide limit was reached, a property value is over the limits of PropValue
107
107
  - `reserved`: The id is `vestra` or starts with `vestra.`
108
108
  - `versionMismatch`: The package needs a newer player or another `apiVersion`
109
- - Others: PluginError, llms-full.txt L3122.
109
+ - Others: PluginError, llms-full.txt L3126.
110
110
 
111
111
  ## Reference
112
112
 
@@ -115,30 +115,30 @@ export default async function activate(plugin: Plugin) {
115
115
  - Concepts: How a plugin runs L215–364; Commands, menus and key bindings L365–462; Playback state, controls and sources L463–602; Video and audio processing L603–680; Windows, panels, settings pages, dialogs and the OSD L681–968
116
116
  - How-to: Keep a slider responsive while dragging L969–1143; Remember a window's state between runs L1144–1273; Translate a plugin L1274–1457; Migrate a plugin to English source text L1458–1605; Test a plugin L1606–1771; Publish a plugin L1772–1860; Migrating off package rollback (1.5.16, removed in 1.5.17) L1861–1936
117
117
  - Guidelines: Interface guidelines L1937–2021; Menu guidelines L2022–2119
118
- - Reference: Manifest L2120–2183; Capabilities L2184–2226; Limits L2227–2321; Commands, menus and bindings L2322–2789; Platforms L2790–2818; Theme L2819–3023; Command line L3024–3079
119
- - [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L3080–6680
120
- - [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L6681–8045
118
+ - Reference: Manifest L2120–2184; Capabilities L2185–2227; Limits L2228–2322; Commands, menus and bindings L2323–2791; Platforms L2792–2820; Theme L2821–3027; Command line L3028–3083
119
+ - [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L3084–6690
120
+ - [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L6691–8055
121
121
 
122
122
  ## API
123
123
 
124
124
  Where each module of `plugin` (`plugin.<module>`) is declared:
125
125
 
126
- - Plugin L3314, Scope L3183, PluginError L3122, Disposable L3095
127
- - Commands L3526; Windows L3756; Panels L4399; Dialogs L4685; Player L5133; video: VideoFilter L5433, VideoView L5496, RenderOptions L5538; audio: AudioFilter L5457; Subtitles L5636; media: MediaCapture L5796; Resolvers L5901; Packages L6143; Osd L5369; Host L5300; Clipboard L6275; Power L6295; Tools L6328; Storage L6371; System L6394; Secrets L6412; window: MainWindow L4522; App L4603; Bindings L6245; Ui L3631; Timeline L4479; Drops L4055; Appearance L4084; I18n L4226; Settings L4332.
126
+ - Plugin L3318, Scope L3187, PluginError L3126, Disposable L3099
127
+ - Commands L3530; Windows L3760; Panels L4403; Dialogs L4689; Player L5137; video: VideoFilter L5443, VideoView L5506, RenderOptions L5548; audio: AudioFilter L5467; Subtitles L5646; media: MediaCapture L5806; Resolvers L5911; Packages L6153; Osd L5379; Host L5310; Clipboard L6285; Power L6305; Tools L6338; Storage L6381; System L6404; Secrets L6422; window: MainWindow L4526; App L4607; Bindings L6255; Ui L3635; Timeline L4483; Drops L4059; Appearance L4088; I18n L4230; Settings L4336.
128
128
 
129
129
  ## Examples
130
130
 
131
131
  Tested plugins in [examples/](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples):
132
132
 
133
- - hello: The README Quick start: one menu command and an on-screen message. L8469–8558
134
- - blur: A tool window with one slider that blurs the playing video. L8559–8820
135
- - signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L8821–9153
136
- - bookmark: A tool window bound to the playback state: remember the position and jump back to it. L9154–9338
137
- - panel-menu: A sidebar panel with its own right-click menu. L9339–9544
138
- - menus: Radio menu items, separator groups and a menu item whose title changes. L9545–9704
139
- - theme-widgets: A tool window drawn with the player's theme and widgets. L9705–9868
140
- - markdown: A window that shows a Markdown document. L9869–10063
141
- - i18n: A plugin in the player's language: English source text, a Chinese translation. L10064–10231
133
+ - hello: The README Quick start: one menu command and an on-screen message. L8479–8568
134
+ - blur: A tool window with one slider that blurs the playing video. L8569–8830
135
+ - signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L8831–9163
136
+ - bookmark: A tool window bound to the playback state: remember the position and jump back to it. L9164–9348
137
+ - panel-menu: A sidebar panel with its own right-click menu. L9349–9554
138
+ - menus: Radio menu items, separator groups and a menu item whose title changes. L9555–9714
139
+ - theme-widgets: A tool window drawn with the player's theme and widgets. L9715–9878
140
+ - markdown: A window that shows a Markdown document. L9879–10073
141
+ - i18n: A plugin in the player's language: English source text, a Chinese translation. L10074–10241
142
142
 
143
143
  ## Optional
144
144
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.5.20",
3
+ "version": "1.5.21",
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": {
@@ -45,6 +45,9 @@ export global Icons {
45
45
  if name == "add" { return "M 5 1.5 V 8.5 M 1.5 5 H 8.5"; }
46
46
  if name == "remove" { return "M 1.5 5 H 8.5"; }
47
47
  if name == "sort" { return "M 3 1.5 V 8.5 M 1.2 6.7 L 3 8.5 L 4.8 6.7 M 7 8.5 V 1.5 M 5.2 3.3 L 7 1.5 L 8.8 3.3"; }
48
+ // Four corners pointing out (enter) or in (leave), the default 14 × 12 box.
49
+ if name == "fullscreen" { return "M 1.5 4.5 V 1.5 H 4.5 M 9.5 1.5 H 12.5 V 4.5 M 12.5 7.5 V 10.5 H 9.5 M 4.5 10.5 H 1.5 V 7.5"; }
50
+ if name == "exit-fullscreen" { return "M 4.5 1.5 V 4.5 H 1.5 M 9.5 1.5 V 4.5 H 12.5 M 12.5 7.5 H 9.5 V 10.5 M 1.5 7.5 H 4.5 V 10.5"; }
48
51
  return "";
49
52
  }
50
53
  // Line width.
@@ -5,15 +5,20 @@
5
5
  import { TextEdit } from "std-widgets.slint";
6
6
  import { Theme } from "theme.slint";
7
7
 
8
- // Hover for 600 ms to show `text` beside the pointer. The tip closes when
9
- // the pointer leaves or presses; it shows again on the next hover.
8
+ // Hover for about half a second to show `text` beside the pointer. The tip
9
+ // closes when the pointer leaves, or presses on content that does not take
10
+ // the press itself; it shows again on the next hover. It is Slint's
11
+ // input-transparent tooltip: every press reaches the control under the
12
+ // pointer, and the tip does not take the keyboard focus. (Up to SDK 1.5.20 the tip was a
13
+ // PopupWindow, which swallowed the first press after it showed and took the
14
+ // focus while it was open.)
10
15
  export component ToolTipArea {
11
16
  in property <string> text;
12
- // The tip is showing (read-only).
17
+ // The tip is showing (read-only); it follows the tip's delay closely,
18
+ // not exactly.
13
19
  out property <bool> showing;
14
- // Shown (or pressed) during this hover: wait for the pointer to leave.
15
- // (Never assign the timer's `running`: that drops its binding, and the
16
- // tip would show only once.)
20
+ // Pressed during this hover: no tip until the pointer leaves.
21
+ // (Never assign the timer's `running`: that drops its binding.)
17
22
  private property <bool> done;
18
23
  area := TouchArea {
19
24
  width: 100%; height: 100%;
@@ -22,40 +27,24 @@ export component ToolTipArea {
22
27
  if !self.has-hover {
23
28
  root.done = false;
24
29
  root.showing = false;
25
- tip.close();
26
30
  }
27
31
  }
28
32
  pointer-event(event) => {
29
33
  if event.kind == PointerEventKind.down {
30
34
  root.done = true;
31
35
  root.showing = false;
32
- tip.close();
33
36
  }
34
37
  }
35
- }
36
- Timer {
37
- interval: 600ms;
38
- running: area.has-hover && root.text != "" && !root.done;
39
- triggered => {
40
- root.done = true;
41
- root.showing = true;
42
- tip.show();
38
+ // Removing it closes an open tip.
39
+ if root.text != "" && !root.done: Tooltip {
40
+ // Interpolated text is plain: `*` or `_` in it are not markup.
41
+ text: @markdown("\{root.text}");
43
42
  }
44
43
  }
45
- tip := PopupWindow {
46
- x: area.mouse-x + 12px;
47
- y: area.mouse-y + 16px;
48
- close-policy: close-on-click-outside;
49
- Rectangle {
50
- background: Theme.bg-panel;
51
- border-color: Theme.line;
52
- border-width: 1px;
53
- border-radius: Theme.radius-control;
54
- HorizontalLayout {
55
- padding-left: 5px; padding-right: 5px; padding-top: 2px; padding-bottom: 2px;
56
- Text { text: root.text; color: Theme.text; font-size: Theme.font-body; }
57
- }
58
- }
44
+ Timer {
45
+ interval: 500ms;
46
+ running: area.has-hover && root.text != "" && !root.done && !root.showing;
47
+ triggered => { root.showing = true; }
59
48
  }
60
49
  }
61
50