@vestra-player/sdk 1.5.27 → 1.5.29
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 +20 -0
- package/bin/vestra-plugin.mjs +103 -0
- package/dist/commands.d.ts +47 -1
- package/dist/commands.js +79 -6
- package/dist/player-runtime.d.ts +1 -1
- package/dist/player-runtime.js +1 -1
- package/dist/plugin/commands.js +29 -2
- package/dist/plugin/testing.d.ts +54 -3
- package/dist/plugin/testing.js +69 -3
- package/dist/plugin/types.d.ts +81 -0
- package/llms-full.txt +194 -13
- package/llms.txt +22 -22
- package/package.json +1 -1
- package/slint/vestra/bar-buttons.slint +138 -0
- package/slint/vestra/icons.slint +4 -0
package/dist/plugin/types.d.ts
CHANGED
|
@@ -386,6 +386,77 @@ export type Bindable<T> = T | (() => T);
|
|
|
386
386
|
* `MENU_IDS` in `@vestra-player/sdk/testing`.
|
|
387
387
|
*/
|
|
388
388
|
export type MenuId = "open" | "playback" | "subtitles" | "video" | "audio" | "screen" | "settings";
|
|
389
|
+
/**
|
|
390
|
+
* An icon of the player's V-Line set (`@vestra/icons.slint`), by name: what
|
|
391
|
+
* a bottom-bar button may show. The list is `BAR_BUTTON_ICONS` in
|
|
392
|
+
* `@vestra-player/sdk/testing`.
|
|
393
|
+
*
|
|
394
|
+
* @since 1.5.29
|
|
395
|
+
*/
|
|
396
|
+
export type BarButtonIcon = "play" | "pause" | "stop" | "back" | "forward" | "open" | "volume" | "mute" | "playlist" | "detach" | "attach" | "close" | "failed" | "move-top" | "move-up" | "move-down" | "move-bottom" | "search" | "chapter" | "bookmark" | "check-task" | "check-box" | "menu-check" | "submenu" | "scroll-up" | "scroll-down" | "caret-down" | "sort-asc" | "sort-desc" | "tag-remove" | "preview-play" | "add" | "remove" | "sort" | "fullscreen" | "exit-fullscreen" | "store" | "ai";
|
|
397
|
+
/**
|
|
398
|
+
* A command's button on the bottom bar: {@link CommandOptions.button}.
|
|
399
|
+
*
|
|
400
|
+
* @remarks
|
|
401
|
+
* **Where it shows.** The bottom bar places the player's strip of command
|
|
402
|
+
* buttons, `BarButtons` from `@vestra/bar-buttons.slint`, at its right end;
|
|
403
|
+
* the player fills it with the buttons of every plugin, left to right by
|
|
404
|
+
* `order`, and its own sidebar toggle rightmost. When the window is narrow
|
|
405
|
+
* the strip loses cells from its outer (left) end inward: the lowest order
|
|
406
|
+
* goes first, the sidebar toggle last. A
|
|
407
|
+
* bottom bar that does not place `BarButtons` shows no buttons: the
|
|
408
|
+
* command's menu entry stays, and the player logs the buttons it cannot
|
|
409
|
+
* show and whose bar hides them. The built-in bar (`vestra.playback`) places
|
|
410
|
+
* it; see
|
|
411
|
+
* [Writing a bottom bar](../../docs/guidelines/interface.md#writing-a-bottom-bar).
|
|
412
|
+
*
|
|
413
|
+
* **What it shows.** The icon, the command's title (translated, a trailing
|
|
414
|
+
* "…" removed) and its first key as the tooltip ("Plugin Store (F9)"),
|
|
415
|
+
* `enabled`, `busy` and `reason` as the menu item has them: a disabled or
|
|
416
|
+
* busy command's button is drawn disabled, its tooltip adds the reason, and
|
|
417
|
+
* a click runs nothing. A command with `checked` shows its icon in the
|
|
418
|
+
* accent colour while checked. A click runs the command exactly like a
|
|
419
|
+
* click on its menu item.
|
|
420
|
+
*
|
|
421
|
+
* **Limits.** One button per plugin and eight in the player
|
|
422
|
+
* (`BAR_BUTTON_LIMITS`): a button past them is left out (its command and
|
|
423
|
+
* menu entry stay) and the player logs it with `limit`. The button goes
|
|
424
|
+
* with its command: disposing the command, or disabling, stopping or
|
|
425
|
+
* uninstalling the plugin, removes it.
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```ts
|
|
429
|
+
* const store = plugin.windows.create("ui/store.slint");
|
|
430
|
+
* plugin.commands.add({
|
|
431
|
+
* id: "open",
|
|
432
|
+
* path: ["Plugin Store…"],
|
|
433
|
+
* button: { icon: "store", order: 200 },
|
|
434
|
+
* run: () => store.show(),
|
|
435
|
+
* });
|
|
436
|
+
* ```
|
|
437
|
+
*
|
|
438
|
+
* @player 0.5.10; older players show the menu entry without a button
|
|
439
|
+
* (declare `"minPlayerVersion": "0.5.10"` when the button matters).
|
|
440
|
+
* @since 1.5.29
|
|
441
|
+
*/
|
|
442
|
+
export interface CommandButton {
|
|
443
|
+
/**
|
|
444
|
+
* A name of the player's icon set ({@link BarButtonIcon}), or an image
|
|
445
|
+
* made with {@link Ui.image} (a PNG or JPEG in your package or data
|
|
446
|
+
* directory), drawn in the 14×12 icon box, aspect kept, coloured like the
|
|
447
|
+
* player's line icons: only its alpha counts, so draw it as a dark shape
|
|
448
|
+
* on a transparent background. An unknown name fails with `invalid`.
|
|
449
|
+
*/
|
|
450
|
+
icon: BarButtonIcon | ImageRef;
|
|
451
|
+
/**
|
|
452
|
+
* Place in the strip, left to right ascending: an integer 0–999. It is
|
|
453
|
+
* position only: a lower order sits further left, and in a narrow window
|
|
454
|
+
* (or past the limit of eight) it is the first to go. Equal orders sort
|
|
455
|
+
* by plugin id, then declaration order. The player's own buttons use AI
|
|
456
|
+
* 100, Plugin Store 200.
|
|
457
|
+
*/
|
|
458
|
+
order: number;
|
|
459
|
+
}
|
|
389
460
|
/**
|
|
390
461
|
* The declaration passed to {@link Commands.add}.
|
|
391
462
|
*
|
|
@@ -500,6 +571,16 @@ export interface CommandOptions {
|
|
|
500
571
|
focus?: "panel";
|
|
501
572
|
/** Fire again on keyboard auto-repeat while the key is held. Default false. */
|
|
502
573
|
repeat?: boolean;
|
|
574
|
+
/**
|
|
575
|
+
* A button for this command on the bottom bar (SDK 1.5.29, player 0.5.10):
|
|
576
|
+
* an icon and a place, `{ icon: "store", order: 200 }`. Needs a `path`: the
|
|
577
|
+
* menu entry is where the command stays reachable when the bar shows no
|
|
578
|
+
* button (a bar without `BarButtons`, a narrow window, a limit). See
|
|
579
|
+
* {@link CommandButton} for where it shows, what it shows and the limits.
|
|
580
|
+
* Fails with `invalid` for an unknown icon name, an order outside 0–999 or
|
|
581
|
+
* a button without a path.
|
|
582
|
+
*/
|
|
583
|
+
button?: CommandButton;
|
|
503
584
|
/** Whether the command can run (default true). */
|
|
504
585
|
enabled?: Bindable<boolean>;
|
|
505
586
|
/** Check mark (or radio dot with `radio`); `null` (default) draws none. */
|
package/llms-full.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @vestra-player/sdk 1.5.
|
|
1
|
+
# @vestra-player/sdk 1.5.29: 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
|
|
|
@@ -498,11 +498,25 @@ The user can rebind any command, the player's own included. Those **overrides**
|
|
|
498
498
|
- `list()` returns every declared command with its default and current inputs, which of them another command shadows, and the overrides whose command does not exist right now (its plugin is stopped); those are kept and apply again when it returns.
|
|
499
499
|
- To find a command by its place in the menu, compare BindingInfo.key (the English menu keys, the same in every language), not `path`, which is the translated text.
|
|
500
500
|
|
|
501
|
+
#### Bottom-bar buttons
|
|
502
|
+
|
|
503
|
+
A command can also be a button on the bottom bar: give it `button: { icon, order }` (CommandButton, player 0.5.10) besides its menu `path`. The player shows the buttons of all plugins in one strip at the bar's right end, left to right by `order`, its own **Toggle Sidebar** rightmost; in a narrow window the lowest order (leftmost) goes first and the toggle last; a click runs the command like its menu item, the tooltip is the item's title and first key, and a disabled or busy command's button is drawn disabled with the reason in its tooltip. One button per plugin and eight in the player (`BAR_BUTTON_LIMITS`). The strip lives in the bottom bar's own layout (`BarButtons`, [Writing a bottom bar): a bar that does not place it shows no buttons, which is why a button always has a menu entry too.
|
|
504
|
+
|
|
505
|
+
```ts
|
|
506
|
+
const store = plugin.windows.create("ui/store.slint");
|
|
507
|
+
plugin.commands.add({
|
|
508
|
+
id: "open",
|
|
509
|
+
path: ["Plugin Store…"],
|
|
510
|
+
button: { icon: "store", order: 200 },
|
|
511
|
+
run: () => store.show(),
|
|
512
|
+
});
|
|
513
|
+
```
|
|
514
|
+
|
|
501
515
|
#### The player's own commands
|
|
502
516
|
|
|
503
|
-
The player registers some commands itself, with the owner `host`: **Toggle Fullscreen**, **Leave Fullscreen**, **Quit** and the core playback actions (play/pause, stop, previous/next, frame step, relative seek, speed, volume, mute). They exist whether or not any plugin runs, and the user rebinds them like any other command.
|
|
517
|
+
The player registers some commands itself, with the owner `host`: **Toggle Fullscreen**, **Leave Fullscreen**, **Quit**, **Toggle Sidebar** (the bottom bar's sidebar button, player 0.5.10) and the core playback actions (play/pause, stop, previous/next, frame step, relative seek, speed, volume, mute). They exist whether or not any plugin runs, and the user rebinds them like any other command.
|
|
504
518
|
|
|
505
|
-
A plugin button that does what a core action does should run that action with plugin.host.run (capability `commands`): it applies the same checks and shows the same on-screen text as the menu item and the key, so a bottom bar's play button and the Space key behave identically. The action ids, their menu places and default keys are in
|
|
519
|
+
A plugin button that does what a core action does should run that action with plugin.host.run (capability `commands`): it applies the same checks and shows the same on-screen text as the menu item and the key, so a bottom bar's play button and the Space key behave identically. The action ids, their menu places and default keys are in Reference: commands; what they do to playback is in Playback.
|
|
506
520
|
|
|
507
521
|
## Concept: Playback state, controls and sources
|
|
508
522
|
|
|
@@ -779,7 +793,7 @@ It assumes you have written a first plugin (Getting started). For every method's
|
|
|
779
793
|
Two rules of thumb:
|
|
780
794
|
|
|
781
795
|
- **Prefer the smallest surface that works.** An OSD message before a dialog, a settings page before a tool window. Every window the user has to close is a cost.
|
|
782
|
-
- **Panels are shared space.** There is one sidebar, one bottom bar and one backdrop for all plugins. The sidebar holds several panels as tabs (the player draws the tab strip while there are two or more; showing, hiding and detaching apply to all of them). The bottom bar and the backdrop hold one: while one plugin's panel is there, another plugin's mount rejects with `conflict`, which does not fail its activation. Mount a panel only when the plugin's main purpose is to live there.
|
|
796
|
+
- **Panels are shared space.** There is one sidebar, one bottom bar and one backdrop for all plugins. The sidebar holds several panels as tabs (the player draws the tab strip while there are two or more; showing, hiding and detaching apply to all of them). The bottom bar and the backdrop hold one: while one plugin's panel is there, another plugin's mount rejects with `conflict`, which does not fail its activation. Mount a panel only when the plugin's main purpose is to live there. A command that needs a one-click entry point does not need a panel: give it a bottom-bar button (`button` on commands.add), which the player shows in the strip of command buttons every bottom bar places (Writing a bottom bar).
|
|
783
797
|
|
|
784
798
|
#### Life cycles
|
|
785
799
|
|
|
@@ -2081,6 +2095,29 @@ The player's picture, audio and subtitle tools share one layout; a window of the
|
|
|
2081
2095
|
- **Commands of a panel go into its right-click menu**, made of the plugin's commands (panel.showMenu); see Menus.
|
|
2082
2096
|
- **The bottom bar is icons only**: no text buttons, no source path, no status text (the source name is in the window title, status is an OSD message).
|
|
2083
2097
|
|
|
2098
|
+
##### Writing a bottom bar
|
|
2099
|
+
|
|
2100
|
+
The bottom bar holds one panel, and every plugin's command buttons (CommandOptions.button) and the player's sidebar toggle show inside it, in a strip the player fills. A bar that replaces the built-in one (`vestra.playback`) must place that strip, or those buttons show nowhere while it is mounted (their menu entries stay):
|
|
2101
|
+
|
|
2102
|
+
- **Place `BarButtons`** from `@vestra/bar-buttons.slint` at the right end of the button row, and give it `room`: the width left after your own controls. It shows as many 41×40 cells as fit and loses them from its outer (left) end inward: of the plugins' buttons the lowest order goes first, and the sidebar toggle goes last. The strip is as wide as the cells it shows, so lay your controls out against its `width`. Do not put it under an `if`: it must exist for the player to see it placed. `first-shown` (out) is the index of the leftmost plugin button shown, if your layout needs it.
|
|
2103
|
+
- **Decide what goes first when the window narrows.** The built-in bar gives the strip only the room its own buttons and the time leave, so the strip shrinks first, then its Fullscreen button hides, and the time is never cut.
|
|
2104
|
+
- **Do not draw a sidebar toggle of your own**: the strip has the player's (**Toggle Sidebar**, `host/sidebar`), with its state.
|
|
2105
|
+
- `vestra-plugin check` warns when a plugin mounts a bottom bar whose Slint file does not import `BarButtons` from `@vestra/bar-buttons.slint`, itself or through the plugin's own Slint files it imports, and the player logs the buttons a mounted bar hides.
|
|
2106
|
+
|
|
2107
|
+
```slint
|
|
2108
|
+
import { BarButtons } from "@vestra/bar-buttons.slint";
|
|
2109
|
+
|
|
2110
|
+
export component MyBar inherits Rectangle {
|
|
2111
|
+
height: 40px;
|
|
2112
|
+
// Your controls on the left, 3 × 41px wide …
|
|
2113
|
+
strip := BarButtons {
|
|
2114
|
+
x: root.width - self.width - 4px;
|
|
2115
|
+
y: 0px;
|
|
2116
|
+
room: max(0px, root.width - 3 * 41px - 8px);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
```
|
|
2120
|
+
|
|
2084
2121
|
#### Text
|
|
2085
2122
|
|
|
2086
2123
|
- **Every visible text is English source text** that the player translates (Translate a plugin). Write short, plain sentences; one sentence per status or error.
|
|
@@ -2102,7 +2139,7 @@ How paths merge, how `order` and `group` sort items and how a key finds its comm
|
|
|
2102
2139
|
|
|
2103
2140
|
#### Organize by what the user does
|
|
2104
2141
|
|
|
2105
|
-
- **Put a command where its subject is, not under your plugin's name.** A video filter goes into Video, a subtitle tool into Subtitles, an open-something command into Open. A plugin that brings a domain of its own gets a submenu named after the domain, not after the plugin (the
|
|
2142
|
+
- **Put a command where its subject is, not under your plugin's name.** A video filter goes into Video, a subtitle tool into Subtitles, an open-something command into Open. A plugin that brings a domain of its own gets a submenu named after the domain, not after the plugin (Games for an emulator, not the emulator's name).
|
|
2106
2143
|
- **Before adding an item, look for an existing one.** Every command the player and its built-in plugins add, with its menu path and default key, is listed in Built-in commands. Do not add a second entry for something the player already does, and do not scatter one feature over several menus.
|
|
2107
2144
|
- **Top level is for frequent, direct commands**: opening files, closing, fullscreen and the entries that show a window or panel. Everything else goes into a submenu of its domain.
|
|
2108
2145
|
- **No "File" or "View" menu.** Commands that open something go into Open ▸; commands that show a window or panel sit directly at the top level.
|
|
@@ -2122,7 +2159,6 @@ Playback ▸ group domain
|
|
|
2122
2159
|
Subtitles ▸
|
|
2123
2160
|
Video ▸
|
|
2124
2161
|
Audio ▸
|
|
2125
|
-
Emulator ▸
|
|
2126
2162
|
────────────────────────────────────────
|
|
2127
2163
|
Toggle Fullscreen Enter group screen
|
|
2128
2164
|
Screen ▸
|
|
@@ -2254,6 +2290,7 @@ The table lists the API whose documentation names a player version, newest last:
|
|
|
2254
2290
|
| 0.5.2 | HostAction |
|
|
2255
2291
|
| 0.5.3 | Scrub.seek |
|
|
2256
2292
|
| 0.5.5 | SessionStorage |
|
|
2293
|
+
| 0.5.10 | CommandButton, CommandOptions.button |
|
|
2257
2294
|
|
|
2258
2295
|
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).
|
|
2259
2296
|
|
|
@@ -2331,6 +2368,16 @@ A command added past a limit fails with code `limit` (its `ready` rejects; durin
|
|
|
2331
2368
|
| `COMMAND_LIMITS.perPlugin` | 160 | Commands one plugin instance may have at the same time. |
|
|
2332
2369
|
| `COMMAND_LIMITS.total` | 1024 | Commands of every plugin plus the player's own, together. |
|
|
2333
2370
|
|
|
2371
|
+
#### Bottom-bar buttons
|
|
2372
|
+
|
|
2373
|
+
A command's button (CommandOptions.button, player 0.5.10) past a limit is left out, not refused: the command registers and its menu entry stays, and the player logs the button with `limit`. Each plugin keeps its first declared button; of those, the player keeps the eight highest orders, so the lowest go first, as in a narrow strip (equal orders: the smaller plugin id, then the earlier declaration, goes first). The player's own sidebar toggle is extra.
|
|
2374
|
+
|
|
2375
|
+
| Limit | Value | Meaning |
|
|
2376
|
+
| --- | --- | --- |
|
|
2377
|
+
| `BAR_BUTTON_LIMITS.perPlugin` | 1 | Buttons one plugin shows. |
|
|
2378
|
+
| `BAR_BUTTON_LIMITS.total` | 8 | Buttons the player shows, every plugin together, besides its own sidebar toggle. |
|
|
2379
|
+
| `BAR_BUTTON_LIMITS.maxOrder` | 999 | The highest `order`. |
|
|
2380
|
+
|
|
2334
2381
|
#### Property values
|
|
2335
2382
|
|
|
2336
2383
|
One value written to a window, panel or settings page property (View.set, View.bind or an initial property). A value over a limit is refused whole with code `limit`; the property keeps what it showed.
|
|
@@ -2491,7 +2538,6 @@ The submenus that exist already, by key path. Write the same keys to add a comma
|
|
|
2491
2538
|
| Settings ▸ Appearance | `vestra.preferences`, `vestra.visualizer` |
|
|
2492
2539
|
| Settings ▸ Language | `vestra.preferences` |
|
|
2493
2540
|
| Copy to Clipboard | `vestra.playback` |
|
|
2494
|
-
| Emulator | `vestra.nes` |
|
|
2495
2541
|
|
|
2496
2542
|
#### Built-in commands
|
|
2497
2543
|
|
|
@@ -2600,8 +2646,8 @@ Every command the player (`host`) and its built-in plugins register when the pla
|
|
|
2600
2646
|
| Subtitles ▸ Subtitle Style ▸ Bold | `Alt+B` | `vestra.subtitles/bold` |
|
|
2601
2647
|
| Subtitles ▸ Subtitle Style ▸ Size + | `Alt+PageUp` | `vestra.subtitles/size-up` |
|
|
2602
2648
|
| Subtitles ▸ Subtitle Style ▸ Size − | `Alt+PageDown` | `vestra.subtitles/size-down` |
|
|
2603
|
-
| Subtitles ▸ Subtitle Style ▸ Move Up |
|
|
2604
|
-
| Subtitles ▸ Subtitle Style ▸ Move Down |
|
|
2649
|
+
| Subtitles ▸ Subtitle Style ▸ Move Up | | `vestra.subtitles/move-up` |
|
|
2650
|
+
| Subtitles ▸ Subtitle Style ▸ Move Down | | `vestra.subtitles/move-down` |
|
|
2605
2651
|
| Subtitles ▸ Subtitle Style ▸ Reset Position | `Alt+Home` | `vestra.subtitles/move-reset` |
|
|
2606
2652
|
| Subtitles ▸ Subtitle Style ▸ Shadow | | `vestra.subtitles/shadow` |
|
|
2607
2653
|
| Subtitles ▸ Subtitle Style ▸ Opaque Box | | `vestra.subtitles/box` |
|
|
@@ -2741,9 +2787,6 @@ Every command the player (`host`) and its built-in plugins register when the pla
|
|
|
2741
2787
|
| Audio ▸ Processing ▸ Crystalizer | `Shift+C` | `vestra.audio/audio.clarity` |
|
|
2742
2788
|
| Audio ▸ Reset | | `vestra.audio/audio.reset` |
|
|
2743
2789
|
| Audio ▸ Show Album Cover for Audio Only | | `vestra.visualizer/audio-cover` |
|
|
2744
|
-
| Emulator ▸ Open NES ROM… | | `vestra.nes/nes.open` |
|
|
2745
|
-
| Emulator ▸ Stop NES | | `vestra.nes/nes.stop` |
|
|
2746
|
-
| Emulator ▸ NES Tools… | | `vestra.nes/nes.window` |
|
|
2747
2790
|
| Toggle Fullscreen | `Enter`, `Alt+Enter`, `F11` | `host/fullscreen` |
|
|
2748
2791
|
| Screen ▸ Picture-in-Picture | | `vestra.window/pip` |
|
|
2749
2792
|
| Screen ▸ Mini Size | ``` | `vestra.window/mini` |
|
|
@@ -2789,6 +2832,7 @@ Every command the player (`host`) and its built-in plugins register when the pla
|
|
|
2789
2832
|
| About Vestra Player… | `F1` | `vestra.info/info.about` |
|
|
2790
2833
|
| Quit | `Cmd+Q` | `host/quit` |
|
|
2791
2834
|
| Leave Fullscreen (not in the menu) | `Escape` | `host/exit-fullscreen` |
|
|
2835
|
+
| Toggle Sidebar (not in the menu) | | `host/sidebar` |
|
|
2792
2836
|
| Brief Info (not in the menu) | `Shift+Tab` | `vestra.info/info.brief` |
|
|
2793
2837
|
| Play (not in the menu) | `Enter`, `P` (panel focus) | `vestra.playlist/playlist.play` |
|
|
2794
2838
|
| Add Files… (not in the menu) | `Ctrl+I` (panel focus) | `vestra.playlist/playlist.list-add` |
|
|
@@ -2822,6 +2866,23 @@ Every command the player (`host`) and its built-in plugins register when the pla
|
|
|
2822
2866
|
| Close List (not in the menu) | `F6` (panel focus) | `vestra.playlist/playlist.close` |
|
|
2823
2867
|
| Playlist Settings… (not in the menu) | `F5` (panel focus) | `vestra.playlist/playlist.settings` |
|
|
2824
2868
|
|
|
2869
|
+
#### Bottom-bar buttons
|
|
2870
|
+
|
|
2871
|
+
`button: { icon, order }` on commands.add (CommandButton, SDK 1.5.29, player 0.5.10) gives a command a button in the bottom bar's strip of command buttons (`BarButtons`, Writing a bottom bar).
|
|
2872
|
+
|
|
2873
|
+
| Field | Rule |
|
|
2874
|
+
| --- | --- |
|
|
2875
|
+
| `icon` | A name of the player's icon set (the names in Theme: Icons, `BAR_BUTTON_ICONS`), or an image from plugin.ui.image drawn in the 14×12 icon box in the icon colour (only its alpha counts). Unknown name: `invalid`. |
|
|
2876
|
+
| `order` | Integer 0–999, left to right ascending; equal orders by plugin id, then declaration order. The built-in plugins use 100 (Vestra Player AI) and 200 (Plugin Store). |
|
|
2877
|
+
| `path` | Required: a button without a menu path is `invalid`. |
|
|
2878
|
+
|
|
2879
|
+
- **Shown**: the icon; the tooltip is the command's shown title without a trailing "…" and its first active key ("Plugin Store (F9)"), plus the reason on a second line while the command is disabled or busy. Disabled or busy: drawn disabled, a click runs nothing. `checked: true`: the icon in the accent colour. Accessibility: a button named by the title, with its enabled and checked state and a default action.
|
|
2880
|
+
- **Click**: runs the command exactly as a click on its menu item does.
|
|
2881
|
+
- **Order and room**: left to right by `order`, the player's **Toggle Sidebar** rightmost; in a narrow window the strip loses cells from its outer (left) end inward: the lowest order goes first, the sidebar toggle last. `order` is position only: low is further left and goes first.
|
|
2882
|
+
- **Limits** (Limits): one button per plugin (its first declared), eight in the player (the highest orders; the lowest go first, ties: the smaller plugin id, then the earlier declaration). A button past them is left out and logged with `limit`; the command and its menu entry stay.
|
|
2883
|
+
- **Life cycle**: the button belongs to its command. Disposing the command, or disabling, stopping or uninstalling the plugin, removes it.
|
|
2884
|
+
- **Older players** (before 0.5.10) build the declaration with their own copy of the SDK, which does not send `button`: the command and its menu entry work, without a button. Declare `"minPlayerVersion": "0.5.10"` when the button matters.
|
|
2885
|
+
|
|
2825
2886
|
#### Binding syntax
|
|
2826
2887
|
|
|
2827
2888
|
```text
|
|
@@ -2872,7 +2933,7 @@ The player's own playback commands, which plugin.host.run runs by id (capability
|
|
|
2872
2933
|
| `mute` | Audio ▸ Mute | `M` |
|
|
2873
2934
|
| `fullscreen` | Toggle Fullscreen | `Enter`, `Alt+Enter`, `F11` |
|
|
2874
2935
|
|
|
2875
|
-
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.
|
|
2936
|
+
The player also owns **Toggle Fullscreen** (`host/fullscreen`), **Leave Fullscreen** (`host/exit-fullscreen`, `Escape`, only in fullscreen) and **Quit** (`host/quit`), and **Toggle Sidebar** (`host/sidebar`, player 0.5.10: the bottom bar's sidebar button, outside the menu, no default key); their keys are in Built-in commands.
|
|
2876
2937
|
|
|
2877
2938
|
#### Keys the system takes first
|
|
2878
2939
|
|
|
@@ -3047,6 +3108,8 @@ Icon { name: "close"; width: 20px; height: 20px; } // scaled
|
|
|
3047
3108
|
| `sort` | 10 × 10 | 1.5px |
|
|
3048
3109
|
| `fullscreen` | 14 × 12 | 1.5px |
|
|
3049
3110
|
| `exit-fullscreen` | 14 × 12 | 1.5px |
|
|
3111
|
+
| `store` | 14 × 12 | 1.5px |
|
|
3112
|
+
| `ai` | 14 × 12 | 1.5px |
|
|
3050
3113
|
|
|
3051
3114
|
#### Widgets
|
|
3052
3115
|
|
|
@@ -3128,6 +3191,19 @@ Struct **`MarkdownRow`**: One table row (cells are plain text).
|
|
|
3128
3191
|
|
|
3129
3192
|
Struct **`MarkdownBlock`**: One displayed block. Plugins only pass these through; the fields are filled by the player.
|
|
3130
3193
|
|
|
3194
|
+
#### Bar buttons
|
|
3195
|
+
|
|
3196
|
+
`@vestra/bar-buttons.slint` (player 0.5.10) is the strip of command buttons a bottom bar places at its right end: the buttons plugins give their commands (CommandOptions.button) and the player's sidebar toggle. The player fills and runs it; the bar only places it and gives it `room`, the width it can spare. See Writing a bottom bar.
|
|
3197
|
+
|
|
3198
|
+
**`BarButtons`**: The strip: place it at the bar's right end and give it the width you can spare; it is as wide as the cells it shows (read `width` to lay out the rest).
|
|
3199
|
+
|
|
3200
|
+
| Member | Kind | Default | Note |
|
|
3201
|
+
| --- | --- | --- | --- |
|
|
3202
|
+
| `room` | in `length` | | The width the bar can give the strip; the cells that fit show. |
|
|
3203
|
+
| `first-shown` | out `int` | `BarButtonsHost.buttons.length - root.count` | The index (in the player's list, ascending order) of the leftmost plugin button shown; the ones before it are hidden for want of room. |
|
|
3204
|
+
|
|
3205
|
+
Struct **`BarButtonItem`**: One button as the player shows it.
|
|
3206
|
+
|
|
3131
3207
|
## Reference: Command line
|
|
3132
3208
|
|
|
3133
3209
|
`vestra-plugin` comes with `@vestra-player/sdk` and works in any project that depends on it (`npx vestra-plugin …`, or an npm script). A plugin directory is a directory with `manifest.json`; the default is the current directory, and several can be given at once.
|
|
@@ -3499,6 +3575,35 @@ Playback, Subtitles, Video, Audio, Screen, Settings. The list is
|
|
|
3499
3575
|
type MenuId = "open" | "playback" | "subtitles" | "video" | "audio" | "screen" | "settings";
|
|
3500
3576
|
```
|
|
3501
3577
|
|
|
3578
|
+
### BarButtonIcon
|
|
3579
|
+
|
|
3580
|
+
An icon of the player's V-Line set (`@vestra/icons.slint`), by name: what
|
|
3581
|
+
a bottom-bar button may show. The list is `BAR_BUTTON_ICONS` in
|
|
3582
|
+
`@vestra-player/sdk/testing`.
|
|
3583
|
+
|
|
3584
|
+
```ts
|
|
3585
|
+
type BarButtonIcon = "play" | "pause" | "stop" | "back" | "forward" | "open" | "volume" | "mute" | "playlist" | "detach" | "attach" | "close" | "failed" | "move-top" | "move-up" | "move-down" | "move-bottom" | "search" | "chapter" | "bookmark" | "check-task" | "check-box" | "menu-check" | "submenu" | "scroll-up" | "scroll-down" | "caret-down" | "sort-asc" | "sort-desc" | "tag-remove" | "preview-play" | "add" | "remove" | "sort" | "fullscreen" | "exit-fullscreen" | "store" | "ai";
|
|
3586
|
+
```
|
|
3587
|
+
|
|
3588
|
+
### CommandButton
|
|
3589
|
+
|
|
3590
|
+
A command's button on the bottom bar: CommandOptions.button.
|
|
3591
|
+
|
|
3592
|
+
- @player 0.5.10; older players show the menu entry without a button (declare `"minPlayerVersion": "0.5.10"` when the button matters).
|
|
3593
|
+
|
|
3594
|
+
```ts
|
|
3595
|
+
interface CommandButton {
|
|
3596
|
+
/**
|
|
3597
|
+
* A name of the player's icon set (BarButtonIcon), or an image made with Ui.image (a PNG or JPEG in your package or data directory), drawn in the 14×12 icon box, aspect kept, coloured like the player's line icons: only its alpha counts, so draw it as a dark shape on a transparent background. An unknown name fails with `invalid`.
|
|
3598
|
+
*/
|
|
3599
|
+
icon: BarButtonIcon | ImageRef;
|
|
3600
|
+
/**
|
|
3601
|
+
* Place in the strip, left to right ascending: an integer 0–999. It is position only: a lower order sits further left, and in a narrow window (or past the limit of eight) it is the first to go. Equal orders sort by plugin id, then declaration order. The player's own buttons use AI 100, Plugin Store 200.
|
|
3602
|
+
*/
|
|
3603
|
+
order: number;
|
|
3604
|
+
}
|
|
3605
|
+
```
|
|
3606
|
+
|
|
3502
3607
|
### CommandOptions
|
|
3503
3608
|
|
|
3504
3609
|
The declaration passed to Commands.add.
|
|
@@ -3597,6 +3702,10 @@ interface CommandOptions {
|
|
|
3597
3702
|
focus?: "panel";
|
|
3598
3703
|
/** Fire again on keyboard auto-repeat while the key is held. Default false. */
|
|
3599
3704
|
repeat?: boolean;
|
|
3705
|
+
/**
|
|
3706
|
+
* A button for this command on the bottom bar (SDK 1.5.29, player 0.5.10): an icon and a place, `{ icon: "store", order: 200 }`. Needs a `path`: the menu entry is where the command stays reachable when the bar shows no button (a bar without `BarButtons`, a narrow window, a limit). See CommandButton for where it shows, what it shows and the limits. Fails with `invalid` for an unknown icon name, an order outside 0–999 or a button without a path.
|
|
3707
|
+
*/
|
|
3708
|
+
button?: CommandButton;
|
|
3600
3709
|
/** Whether the command can run (default true). */
|
|
3601
3710
|
enabled?: Bindable<boolean>;
|
|
3602
3711
|
/** Check mark (or radio dot with `radio`); `null` (default) draws none. */
|
|
@@ -7016,6 +7125,34 @@ interface FakeCommand {
|
|
|
7016
7125
|
}
|
|
7017
7126
|
```
|
|
7018
7127
|
|
|
7128
|
+
### FakeBarButton
|
|
7129
|
+
|
|
7130
|
+
A command's bottom-bar button (`CommandOptions.button`) as the player shows
|
|
7131
|
+
it: an entry of TestHost.barButtons.
|
|
7132
|
+
|
|
7133
|
+
```ts
|
|
7134
|
+
interface FakeBarButton {
|
|
7135
|
+
/** The command's local id. */
|
|
7136
|
+
readonly command: string;
|
|
7137
|
+
/** The icon name, or the image's path for a `plugin.ui.image` icon. */
|
|
7138
|
+
readonly icon: string;
|
|
7139
|
+
/** `true` when the icon is the plugin's image. */
|
|
7140
|
+
readonly image: boolean;
|
|
7141
|
+
/** The declared `order`. */
|
|
7142
|
+
readonly order: number;
|
|
7143
|
+
/** Clickable: the command is enabled and not busy. */
|
|
7144
|
+
readonly enabled: boolean;
|
|
7145
|
+
/** Drawn in the accent colour: the command is checked. */
|
|
7146
|
+
readonly checked: boolean;
|
|
7147
|
+
/** The tooltip without the key: the command's shown title (trailing "…" removed), then, while it cannot run, its reason on a second line. */
|
|
7148
|
+
readonly tooltip: string;
|
|
7149
|
+
/**
|
|
7150
|
+
* Click the button, then wait until the host is idle: runs the command as its menu item would. Rejects (with a HostError) while the button is disabled, and once the command is gone.
|
|
7151
|
+
*/
|
|
7152
|
+
click(): Promise<void>;
|
|
7153
|
+
}
|
|
7154
|
+
```
|
|
7155
|
+
|
|
7019
7156
|
### FakeProducer
|
|
7020
7157
|
|
|
7021
7158
|
A generated stream (`plugin.player.openStream`) as the test host sees it:
|
|
@@ -7815,6 +7952,10 @@ class TestHost implements Transport {
|
|
|
7815
7952
|
step(direction: "next" | "previous"): Promise<unknown>;
|
|
7816
7953
|
/** The plugin's command with this local id. Throws when it is not registered (never added, or disposed). */
|
|
7817
7954
|
command(id: string): FakeCommand;
|
|
7955
|
+
/**
|
|
7956
|
+
* The plugin's bottom-bar buttons (`CommandOptions.button`) as the player shows them: at most one, the first declared (`BAR_BUTTON_LIMITS`; a later one stays a menu item only). Empty once the command is disposed. Whether the bar places them is the bar's business: the list is what the player hands to `BarButtons`.
|
|
7957
|
+
*/
|
|
7958
|
+
get barButtons(): readonly FakeBarButton[];
|
|
7818
7959
|
/**
|
|
7819
7960
|
* The plugin's tool window with this id (by default the file name without extension: `ui/picture.slint` → `picture`). Throws when it was never opened with `show()`.
|
|
7820
7961
|
*/
|
|
@@ -7996,6 +8137,39 @@ agrees with the player's.
|
|
|
7996
8137
|
const MENU_IDS: readonly ["open", "playback", "subtitles", "video", "audio", "screen", "settings"];
|
|
7997
8138
|
```
|
|
7998
8139
|
|
|
8140
|
+
### BAR_BUTTON_ICONS
|
|
8141
|
+
|
|
8142
|
+
The icons a bottom-bar button may name (`CommandOptions.button.icon`):
|
|
8143
|
+
every icon of the player's V-Line set, `@vestra/icons.slint` (draw one
|
|
8144
|
+
in your own Slint with `Icon { name: "store"; }`). The player's tests check
|
|
8145
|
+
that this list is its icon set.
|
|
8146
|
+
|
|
8147
|
+
```ts
|
|
8148
|
+
const BAR_BUTTON_ICONS: readonly ["play", "pause", "stop", "back", "forward", "open", "volume", "mute", "playlist", "detach", "attach", "close", "failed", "move-top", "move-up", "move-down", "move-bottom", "search", "chapter", "bookmark", "check-task", "check-box", "menu-check", "submenu", "scroll-up", "scroll-down", "caret-down", "sort-asc", "sort-desc", "tag-remove", "preview-play", "add", "remove", "sort", "fullscreen", "exit-fullscreen", "store", "ai"];
|
|
8149
|
+
```
|
|
8150
|
+
|
|
8151
|
+
### BAR_BUTTON_LIMITS
|
|
8152
|
+
|
|
8153
|
+
The player's limits on bottom-bar buttons (`CommandOptions.button`), the
|
|
8154
|
+
same values as the player's (its tests check that they agree). A button
|
|
8155
|
+
past `perPlugin` or `total` is left out: its command still registers and
|
|
8156
|
+
its menu entry stays, and the player logs the button it left out
|
|
8157
|
+
(`limit`). Which ones stay: each plugin's first declared button, then the
|
|
8158
|
+
`total` highest by order, plugin id and declaration order: as in a narrow
|
|
8159
|
+
strip, the lowest order goes first (equal orders: the smaller plugin id,
|
|
8160
|
+
then the earlier declaration, goes first).
|
|
8161
|
+
|
|
8162
|
+
```ts
|
|
8163
|
+
const BAR_BUTTON_LIMITS: {
|
|
8164
|
+
/** Buttons one plugin shows (1). */
|
|
8165
|
+
readonly perPlugin: 1;
|
|
8166
|
+
/** Buttons the player shows, every plugin together, besides its own sidebar toggle (8). */
|
|
8167
|
+
readonly total: 8;
|
|
8168
|
+
/** The highest `order` (999; the lowest is 0). */
|
|
8169
|
+
readonly maxOrder: 999;
|
|
8170
|
+
};
|
|
8171
|
+
```
|
|
8172
|
+
|
|
7999
8173
|
### BuiltinSubmenu
|
|
8000
8174
|
|
|
8001
8175
|
A submenu that one of the player's built-in plugins declares: an entry of
|
|
@@ -8688,6 +8862,13 @@ type CommandDeclaration = {
|
|
|
8688
8862
|
focus: "panel" | null;
|
|
8689
8863
|
/** Fire again on keyboard auto-repeat while held. */
|
|
8690
8864
|
repeat: boolean;
|
|
8865
|
+
/**
|
|
8866
|
+
* A bottom-bar button (player 0.5.10): an icon name, or an image in the `plugin.ui.image` wire form. Sent only when set: older players refuse unknown declaration fields.
|
|
8867
|
+
*/
|
|
8868
|
+
button?: {
|
|
8869
|
+
icon: string | Json;
|
|
8870
|
+
order: number;
|
|
8871
|
+
};
|
|
8691
8872
|
};
|
|
8692
8873
|
```
|
|
8693
8874
|
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vestra Player SDK
|
|
2
2
|
|
|
3
|
-
> @vestra-player/sdk 1.5.
|
|
3
|
+
> @vestra-player/sdk 1.5.29: 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:
|
|
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: L2231–2298.
|
|
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:
|
|
79
|
+
- No capability: `tools`, `storage`, `system`, `app`, `ui` (images and Markdown), `i18n`, reading window.state, appearance.theme and packages.list. Details: L2299–2357.
|
|
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:
|
|
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: L1672–1860; TestHost: L7198.
|
|
82
82
|
|
|
83
83
|
**Command line**
|
|
84
84
|
|
|
@@ -106,39 +106,39 @@ 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
|
|
109
|
+
- Others: PluginError, llms-full.txt L3307.
|
|
110
110
|
|
|
111
111
|
## Reference
|
|
112
112
|
|
|
113
113
|
- [llms-full.txt](https://vestrasys.github.io/vestra-player-sdk/llms-full.txt), also node_modules/@vestra-player/sdk/llms-full.txt; the pages are on the [site](https://vestrasys.github.io/vestra-player-sdk/).
|
|
114
114
|
- Tutorials: Getting started L9–237
|
|
115
|
-
- Concepts: How a plugin runs L238–408; Commands, menus and key bindings L409–
|
|
116
|
-
- How-to: Keep a slider responsive while dragging
|
|
117
|
-
- Guidelines: Interface guidelines
|
|
118
|
-
- Reference: Manifest
|
|
119
|
-
- [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html):
|
|
120
|
-
- [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html):
|
|
115
|
+
- Concepts: How a plugin runs L238–408; Commands, menus and key bindings L409–520; Playback state, controls and sources L521–666; Video and audio processing L667–745; Windows, panels, settings pages, dialogs and the OSD L746–1034
|
|
116
|
+
- How-to: Keep a slider responsive while dragging L1035–1209; Remember a window's state between runs L1210–1339; Translate a plugin L1340–1523; Migrate a plugin to English source text L1524–1671; Test a plugin L1672–1860; Publish a plugin L1861–1949; Migrating off package rollback (1.5.16, removed in 1.5.17) L1950–2025
|
|
117
|
+
- Guidelines: Interface guidelines L2026–2133; Menu guidelines L2134–2230
|
|
118
|
+
- Reference: Manifest L2231–2298; Capabilities L2299–2357; Limits L2358–2472; Commands, menus and bindings L2473–2955; Platforms L2956–2984; Theme L2985–3206; Command line L3207–3264
|
|
119
|
+
- [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L3265–7005
|
|
120
|
+
- [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html): L7006–8476
|
|
121
121
|
|
|
122
122
|
## API
|
|
123
123
|
|
|
124
124
|
Where each module of `plugin` (`plugin.<module>`) is declared:
|
|
125
125
|
|
|
126
|
-
- Plugin
|
|
127
|
-
- Commands
|
|
126
|
+
- Plugin L3499, Scope L3368, PluginError L3307, Disposable L3280
|
|
127
|
+
- Commands L3744; Windows L3974; Panels L4617; Dialogs L4903; Player L5370; video: VideoFilter L5677, VideoView L5740, RenderOptions L5782; audio: AudioFilter L5701; Subtitles L5880; media: MediaCapture L6041; Resolvers L6146; Packages L6388; Osd L5613; Host L5544; Clipboard L6520; Power L6540; Tools L6573; Storage L6616; System L6703; Secrets L6721; window: MainWindow L4740; App L4821; Bindings L6490; Ui L3849; Timeline L4697; Drops L4273; Appearance L4302; I18n L4444; Settings L4550.
|
|
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.
|
|
134
|
-
- blur: A tool window with one slider that blurs the playing video.
|
|
135
|
-
- signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone.
|
|
136
|
-
- bookmark: A tool window bound to the playback state: remember the position and jump back to it.
|
|
137
|
-
- panel-menu: A sidebar panel with its own right-click menu.
|
|
138
|
-
- menus: Radio menu items, separator groups and a menu item whose title changes.
|
|
139
|
-
- theme-widgets: A tool window drawn with the player's theme and widgets.
|
|
140
|
-
- markdown: A window that shows a Markdown document.
|
|
141
|
-
- i18n: A plugin in the player's language: English source text, a Chinese translation.
|
|
133
|
+
- hello: The README Quick start: one menu command and an on-screen message. L8907–8996
|
|
134
|
+
- blur: A tool window with one slider that blurs the playing video. L8997–9258
|
|
135
|
+
- signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L9259–9591
|
|
136
|
+
- bookmark: A tool window bound to the playback state: remember the position and jump back to it. L9592–9776
|
|
137
|
+
- panel-menu: A sidebar panel with its own right-click menu. L9777–9982
|
|
138
|
+
- menus: Radio menu items, separator groups and a menu item whose title changes. L9983–10142
|
|
139
|
+
- theme-widgets: A tool window drawn with the player's theme and widgets. L10143–10306
|
|
140
|
+
- markdown: A window that shows a Markdown document. L10307–10501
|
|
141
|
+
- i18n: A plugin in the player's language: English source text, a Chinese translation. L10502–10669
|
|
142
142
|
|
|
143
143
|
## Optional
|
|
144
144
|
|