@vestra-player/sdk 1.6.2 → 1.6.4
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 +18 -0
- package/dist/commands.d.ts +17 -0
- package/dist/commands.js +8 -0
- package/dist/docs-tree.json.gz +0 -0
- package/dist/plugin/bindings.js +1 -0
- package/dist/plugin/commands.js +15 -2
- package/dist/plugin/testing.d.ts +13 -5
- package/dist/plugin/testing.js +29 -8
- package/dist/plugin/types.d.ts +114 -23
- package/dist/wire.d.ts +2 -0
- package/llms.txt +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.4
|
|
4
|
+
|
|
5
|
+
**Who must change: nobody, but check tests that compare whole `bindings.list()` entries.** Additions only: two optional `CommandOptions` fields, one optional `BindingInfo` field, a `COMMAND_LIMITS` entry and a new built-in command. `apiVersion` stays `vestra/1`, no new capability. A plugin that does not set the new fields sends exactly what it sent before; one that sets them needs player 0.7.6 (`"minPlayerVersion": "0.7.6"`), because older players refuse a declaration with fields they do not know (`invalid`).
|
|
6
|
+
|
|
7
|
+
- **`CommandOptions.category`** (player 0.7.6): what a command is about, in a word or two (`"Subtitle Sync"`), English source text translated like the title, or a `Text`. The player's new command palette (Ctrl+Shift+P, ⌘⇧P on macOS) and lists of commands name a command `Category: Title`, so a title such as "Earlier 0.5 s" or "Off" still says what it changes outside its menu. Without one the player uses the menu path above the item, then the plugin's name. 1–80 bytes (`COMMAND_LIMITS.categoryBytes`), checked like the other command texts. See [The command palette and categories](docs/explanation/commands.md#the-command-palette-and-categories).
|
|
8
|
+
- **`CommandOptions.home`** (player 0.7.6): the local id of the plugin's command that opens the window or panel where this command's control lives; the palette shows that place. Checked like a command id.
|
|
9
|
+
- **`BindingInfo.category`** (player 0.7.6): the shown category from `bindings.list`, `null` without one, for a keyboard shortcuts page that names commands like the palette.
|
|
10
|
+
- **`BUILTIN_COMMANDS`** lists the player's **Command Palette** (`host/palette`, `Ctrl+Shift+P`, outside the menu). From player 0.7.6 a plugin that declares `Ctrl+Shift+P` as a default key loses it to this command (the player's own commands win conflicts); no built-in plugin declares it.
|
|
11
|
+
- **`bindings.list()` entries now carry `category`** (`null` when the command declares none), in the player and in `TestHost`. A test that compares a whole entry with `deepEqual` now needs `category` in the expected value.
|
|
12
|
+
- **The palette lists a disabled command only when it gives a `reason`.** A command that is disabled without one is left out, whatever its keys (as VS Code hides a command whose `when` is false), so give a `reason` to a command that should stay findable while it is disabled.
|
|
13
|
+
- `TestHost` checks `category` and `home` as the player does, shows the translated category in `FakeCommand.display.category` (not enumerable, like `reason`) and in its `bindings.list` entries.
|
|
14
|
+
|
|
15
|
+
## 1.6.3
|
|
16
|
+
|
|
17
|
+
**Who must change: nobody.** Documentation only: no change to the TypeScript API, the wire or `TestHost`, `apiVersion` stays `vestra/1`, no new capability, no player version needed.
|
|
18
|
+
|
|
19
|
+
- **Examples name window handles after the window they name (its Slint file where there is one).** The API examples, guides, tutorials and example plugins no longer call a window handle `window`: `readmeWindow` for `ui/readme.slint`, `presetsWindow`, `thumbnailWindow`, `bookmarkWindow`, `blurWindow` and so on, in `plugin.windows.create(…)` and `host.window(…)` alike.
|
|
20
|
+
|
|
3
21
|
## 1.6.2
|
|
4
22
|
|
|
5
23
|
**Who must change: nobody, but check `TestHost` tests of a full sidebar or of `keepsRunning` with a background tab.** No change to the TypeScript API or the wire, `apiVersion` stays `vestra/1`, no new capability, no player version needed. `TestHost` follows player 0.7.3: it no longer refuses a ninth sidebar tab, and `keepsRunning` lists a sidebar panel only while its tab is the current tab, as `"its sidebar tab is the current tab"`. A test that expected `conflict` for a ninth tab, or `"a mounted panel"` for a sidebar panel, now fails.
|
package/dist/commands.d.ts
CHANGED
|
@@ -78,6 +78,13 @@ export declare const COMMAND_LIMITS: {
|
|
|
78
78
|
* @since 1.5.48
|
|
79
79
|
*/
|
|
80
80
|
readonly groupBytes: 80;
|
|
81
|
+
/**
|
|
82
|
+
* UTF-8 bytes of a category (`CommandOptions.category`), a text value
|
|
83
|
+
* counted by its English rendering.
|
|
84
|
+
*
|
|
85
|
+
* @since 1.6.4
|
|
86
|
+
*/
|
|
87
|
+
readonly categoryBytes: 80;
|
|
81
88
|
/**
|
|
82
89
|
* UTF-8 bytes of a state's reason, as shown in the player's
|
|
83
90
|
* language.
|
|
@@ -327,6 +334,16 @@ export type CommandDeclaration = {
|
|
|
327
334
|
icon: string | Json;
|
|
328
335
|
order: number;
|
|
329
336
|
};
|
|
337
|
+
/**
|
|
338
|
+
* The command's category, source text or a text value (player 0.7.6).
|
|
339
|
+
* Sent only when set: older players refuse unknown declaration fields.
|
|
340
|
+
*/
|
|
341
|
+
category?: string | TextJson;
|
|
342
|
+
/**
|
|
343
|
+
* The local id of the command that opens where this one lives (player
|
|
344
|
+
* 0.7.6). Sent only when set, like `category`.
|
|
345
|
+
*/
|
|
346
|
+
home?: string;
|
|
330
347
|
};
|
|
331
348
|
export declare function commandClient(transport: Transport): {
|
|
332
349
|
register: (id: string, revision: number, declaration: CommandDeclaration, state: CommandState, when?: CommandWhen) => Promise<{
|
package/dist/commands.js
CHANGED
|
@@ -74,6 +74,13 @@ export const COMMAND_LIMITS = {
|
|
|
74
74
|
* @since 1.5.48
|
|
75
75
|
*/
|
|
76
76
|
groupBytes: 80,
|
|
77
|
+
/**
|
|
78
|
+
* UTF-8 bytes of a category (`CommandOptions.category`), a text value
|
|
79
|
+
* counted by its English rendering.
|
|
80
|
+
*
|
|
81
|
+
* @since 1.6.4
|
|
82
|
+
*/
|
|
83
|
+
categoryBytes: 80,
|
|
77
84
|
/**
|
|
78
85
|
* UTF-8 bytes of a state's reason, as shown in the player's
|
|
79
86
|
* language.
|
|
@@ -594,6 +601,7 @@ host | quit | Quit | Cmd+Q
|
|
|
594
601
|
host | enter-fullscreen | (Enter Fullscreen)
|
|
595
602
|
host | exit-fullscreen | (Leave Fullscreen)
|
|
596
603
|
host | sidebar | (Toggle Sidebar)
|
|
604
|
+
host | palette | (Command Palette) | Ctrl+Shift+P
|
|
597
605
|
vestra.info | info.brief | (Brief Info) | Shift+Tab
|
|
598
606
|
vestra.playlist | playlist.play | (Play) | Enter, P | panel
|
|
599
607
|
vestra.playlist | playlist.list-add | (Add Files…) | Ctrl+I | panel
|
package/dist/docs-tree.json.gz
CHANGED
|
Binary file
|
package/dist/plugin/bindings.js
CHANGED
package/dist/plugin/commands.js
CHANGED
|
@@ -26,13 +26,18 @@ function declare(options) {
|
|
|
26
26
|
throw failure("invalid", `command ${options.id}: title or path required`);
|
|
27
27
|
if (path.length > 8)
|
|
28
28
|
throw failure("invalid", `command ${options.id}: path has 1–8 segments`);
|
|
29
|
-
|
|
29
|
+
const category = options.category;
|
|
30
|
+
for (const segment of [
|
|
31
|
+
title,
|
|
32
|
+
...path,
|
|
33
|
+
...(category === undefined ? [] : [category]),
|
|
34
|
+
])
|
|
30
35
|
if (typeof segment !== "string") {
|
|
31
36
|
const problem = isTextValue(segment)
|
|
32
37
|
? "$vestraList" in segment
|
|
33
38
|
? "a menu text is a string or a text, not a list"
|
|
34
39
|
: textProblem(segment)
|
|
35
|
-
: "title
|
|
40
|
+
: "title, path segments and category are strings or texts";
|
|
36
41
|
if (problem)
|
|
37
42
|
throw failure("invalid", `command ${options.id}: ${problem}`);
|
|
38
43
|
}
|
|
@@ -59,6 +64,14 @@ function declare(options) {
|
|
|
59
64
|
// Only when set: players before 0.5.10 refuse the field.
|
|
60
65
|
if (options.button !== undefined)
|
|
61
66
|
declaration.button = button(options.id, options.button, path.length > 0);
|
|
67
|
+
// Only when set: players before 0.7.6 refuse the fields.
|
|
68
|
+
if (category !== undefined)
|
|
69
|
+
declaration.category = category;
|
|
70
|
+
if (options.home !== undefined) {
|
|
71
|
+
if (typeof options.home !== "string")
|
|
72
|
+
throw failure("invalid", `command ${options.id}: home is a command id`);
|
|
73
|
+
declaration.home = options.home;
|
|
74
|
+
}
|
|
62
75
|
return declaration;
|
|
63
76
|
}
|
|
64
77
|
/** `CommandOptions.button`, checked as the player checks it, in wire form. */
|
package/dist/plugin/testing.d.ts
CHANGED
|
@@ -115,7 +115,7 @@ type Call = {
|
|
|
115
115
|
* import assert from "node:assert/strict";
|
|
116
116
|
* declare const host: TestHost;
|
|
117
117
|
* // ---cut---
|
|
118
|
-
* await host.command("open-picture").run(); // the plugin calls
|
|
118
|
+
* await host.command("open-picture").run(); // the plugin calls pictureWindow.show()
|
|
119
119
|
* const picture = host.window("picture");
|
|
120
120
|
* assert.equal(picture.visible, true);
|
|
121
121
|
* picture.emit("adjust", "brightness", 40); // fire a Slint callback
|
|
@@ -270,6 +270,14 @@ export interface FakeCommand {
|
|
|
270
270
|
* @since 1.5.11
|
|
271
271
|
*/
|
|
272
272
|
reason?: string | null;
|
|
273
|
+
/**
|
|
274
|
+
* The category as shown (`CommandOptions.category`, translated with
|
|
275
|
+
* the plugin's catalog), `null` without one. Not enumerable, like
|
|
276
|
+
* `reason`.
|
|
277
|
+
*
|
|
278
|
+
* @since 1.6.4
|
|
279
|
+
*/
|
|
280
|
+
category?: string | null;
|
|
273
281
|
};
|
|
274
282
|
/**
|
|
275
283
|
* The conditions the plugin sent for `enabled` and `checked`
|
|
@@ -2746,11 +2754,11 @@ export declare class TestHost implements Transport {
|
|
|
2746
2754
|
* declare const host: TestHost;
|
|
2747
2755
|
* declare const expected: string;
|
|
2748
2756
|
* // ---cut---
|
|
2749
|
-
* const
|
|
2750
|
-
*
|
|
2751
|
-
* for (let i = 0; i < 100 &&
|
|
2757
|
+
* const importWindow = host.window("import");
|
|
2758
|
+
* importWindow.emit("load-file"); // the plugin reads and parses the file itself
|
|
2759
|
+
* for (let i = 0; i < 100 && importWindow.props["status-text"] !== expected; i++)
|
|
2752
2760
|
* await host.settle(20);
|
|
2753
|
-
* assert.equal(
|
|
2761
|
+
* assert.equal(importWindow.props["status-text"], expected);
|
|
2754
2762
|
* ```
|
|
2755
2763
|
*
|
|
2756
2764
|
* @capability None.
|
package/dist/plugin/testing.js
CHANGED
|
@@ -354,7 +354,7 @@ function menuTextKey(value) {
|
|
|
354
354
|
* The declaration checks of the player's `Registry::register`, in its
|
|
355
355
|
* order, on the keyed title and path (`@<id>` already replaced).
|
|
356
356
|
*/
|
|
357
|
-
function checkDeclaration(id, title, path, declaration) {
|
|
357
|
+
function checkDeclaration(id, title, path, declaration, category = null) {
|
|
358
358
|
commandText(id, "localId", COMMAND_LIMITS.idBytes);
|
|
359
359
|
if (!/^[A-Za-z0-9._-]+$/.test(id))
|
|
360
360
|
throw commandInvalid("localId permits ASCII letters, digits, '.', '_' and '-'");
|
|
@@ -367,6 +367,17 @@ function checkDeclaration(id, title, path, declaration) {
|
|
|
367
367
|
if (typeof group !== "string")
|
|
368
368
|
throw new HostError("invalid", "group is a string");
|
|
369
369
|
commandText(group, "group", COMMAND_LIMITS.groupBytes);
|
|
370
|
+
// Player 0.7.6: the category and the home command's local id.
|
|
371
|
+
if (category !== null)
|
|
372
|
+
commandText(category, "category", COMMAND_LIMITS.categoryBytes);
|
|
373
|
+
const home = declaration.home;
|
|
374
|
+
if (home !== undefined && home !== null) {
|
|
375
|
+
if (typeof home !== "string")
|
|
376
|
+
throw new HostError("invalid", "home is a command id");
|
|
377
|
+
commandText(home, "home", COMMAND_LIMITS.idBytes);
|
|
378
|
+
if (!/^[A-Za-z0-9._-]+$/.test(home))
|
|
379
|
+
throw commandInvalid("home permits ASCII letters, digits, '.', '_' and '-'");
|
|
380
|
+
}
|
|
370
381
|
const order = declaration.order ?? 0;
|
|
371
382
|
if (!Number.isInteger(order) ||
|
|
372
383
|
Math.abs(order) > COMMAND_LIMITS.order)
|
|
@@ -669,7 +680,7 @@ export class TestHost {
|
|
|
669
680
|
: formatTemplate(shown, args);
|
|
670
681
|
}
|
|
671
682
|
/** A declared menu path and title as the player shows them. */
|
|
672
|
-
#display(path, title, reason = null) {
|
|
683
|
+
#display(path, title, reason = null, category = null) {
|
|
673
684
|
const keys = menuKeys(path.map((segment) => typeof segment === "string" ? segment : textKey(segment)));
|
|
674
685
|
const display = {
|
|
675
686
|
title: this.translate(title),
|
|
@@ -684,6 +695,10 @@ export class TestHost {
|
|
|
684
695
|
value: reason === null ? null : this.translate(reason),
|
|
685
696
|
enumerable: false,
|
|
686
697
|
});
|
|
698
|
+
Object.defineProperty(display, "category", {
|
|
699
|
+
value: category === null ? null : this.translate(category),
|
|
700
|
+
enumerable: false,
|
|
701
|
+
});
|
|
687
702
|
return display;
|
|
688
703
|
}
|
|
689
704
|
/**
|
|
@@ -3230,6 +3245,8 @@ export class TestHost {
|
|
|
3230
3245
|
key: declaration.path?.length
|
|
3231
3246
|
? menuKeys(declaration.path.map((segment) => typeof segment === "string" ? segment : textKey(segment)))
|
|
3232
3247
|
: null,
|
|
3248
|
+
// Player 0.7.6: the category as shown, `null` without one.
|
|
3249
|
+
category: shown?.display.category ?? null,
|
|
3233
3250
|
scope: scope,
|
|
3234
3251
|
defaults: declaration.bindings,
|
|
3235
3252
|
current,
|
|
@@ -3479,11 +3496,11 @@ export class TestHost {
|
|
|
3479
3496
|
* declare const host: TestHost;
|
|
3480
3497
|
* declare const expected: string;
|
|
3481
3498
|
* // ---cut---
|
|
3482
|
-
* const
|
|
3483
|
-
*
|
|
3484
|
-
* for (let i = 0; i < 100 &&
|
|
3499
|
+
* const importWindow = host.window("import");
|
|
3500
|
+
* importWindow.emit("load-file"); // the plugin reads and parses the file itself
|
|
3501
|
+
* for (let i = 0; i < 100 && importWindow.props["status-text"] !== expected; i++)
|
|
3485
3502
|
* await host.settle(20);
|
|
3486
|
-
* assert.equal(
|
|
3503
|
+
* assert.equal(importWindow.props["status-text"], expected);
|
|
3487
3504
|
* ```
|
|
3488
3505
|
*
|
|
3489
3506
|
* @capability None.
|
|
@@ -4086,7 +4103,11 @@ export class TestHost {
|
|
|
4086
4103
|
if (menu !== null && keys.length < 2)
|
|
4087
4104
|
throw commandInvalid(`menu id @${menu} needs an item after it`);
|
|
4088
4105
|
const path = menuKeys(keys);
|
|
4089
|
-
|
|
4106
|
+
// Player 0.7.6: the category, keyed like a menu text.
|
|
4107
|
+
const category = declaration.category === undefined || declaration.category === null
|
|
4108
|
+
? null
|
|
4109
|
+
: menuTextKey(declaration.category);
|
|
4110
|
+
checkDeclaration(id, title, path, declaration, category);
|
|
4090
4111
|
if (button !== undefined && button !== null) {
|
|
4091
4112
|
if (path.length === 0)
|
|
4092
4113
|
throw commandInvalid("button needs a menu path: a bar button is also a menu item");
|
|
@@ -4144,7 +4165,7 @@ export class TestHost {
|
|
|
4144
4165
|
this.commands.set(id, {
|
|
4145
4166
|
declaration,
|
|
4146
4167
|
get display() {
|
|
4147
|
-
return host.#display(declaration.path ?? [], declaration.title, this.state.reason);
|
|
4168
|
+
return host.#display(declaration.path ?? [], declaration.title, this.state.reason, declaration.category ?? null);
|
|
4148
4169
|
},
|
|
4149
4170
|
...(p.when ? { when: p.when } : {}),
|
|
4150
4171
|
get state() {
|
package/dist/plugin/types.d.ts
CHANGED
|
@@ -367,10 +367,10 @@ export interface Scope extends Disposable {
|
|
|
367
367
|
* return;
|
|
368
368
|
* }
|
|
369
369
|
* live = plugin.scope();
|
|
370
|
-
* const
|
|
370
|
+
* const statsWindow = live.windows.create("ui/stats.slint");
|
|
371
371
|
* const timer = setInterval(() => live?.update(), 1000);
|
|
372
372
|
* live.onDispose(() => clearInterval(timer));
|
|
373
|
-
* await
|
|
373
|
+
* await statsWindow.show();
|
|
374
374
|
* },
|
|
375
375
|
* });
|
|
376
376
|
* ```
|
|
@@ -487,12 +487,12 @@ export interface Scope extends Disposable {
|
|
|
487
487
|
* promise settles; state changed between the awaits of a long handler
|
|
488
488
|
* shows only after an explicit `update()`.
|
|
489
489
|
* ```ts
|
|
490
|
-
* declare const
|
|
490
|
+
* declare const downloadWindow: Window;
|
|
491
491
|
* declare function downloadPart(part: number): Promise<void>;
|
|
492
492
|
* // ---cut---
|
|
493
493
|
* let done = 0;
|
|
494
|
-
*
|
|
495
|
-
*
|
|
494
|
+
* downloadWindow.bind(() => ({ progress: done }));
|
|
495
|
+
* downloadWindow.on("start", async () => {
|
|
496
496
|
* for (let part = 0; part < 10; part++) {
|
|
497
497
|
* await downloadPart(part);
|
|
498
498
|
* done = part + 1;
|
|
@@ -1112,6 +1112,87 @@ export interface CommandOptions {
|
|
|
1112
1112
|
* defaults to the last path segment.
|
|
1113
1113
|
*/
|
|
1114
1114
|
title?: string | Text;
|
|
1115
|
+
/**
|
|
1116
|
+
* What the command is about, in a word or two (`"Subtitle Sync"`,
|
|
1117
|
+
* `"Picture"`): English source text, shown translated like `title`, or a
|
|
1118
|
+
* {@link Text}. The player's command palette (Ctrl+Shift+P, ⌘⇧P on macOS)
|
|
1119
|
+
* and lists of commands name a command `Category: Title`, so a short title
|
|
1120
|
+
* such as "Earlier 0.5 s", "Off" or "90°" still says what it changes once
|
|
1121
|
+
* it is out of the menu. Without it the player uses the menu path above
|
|
1122
|
+
* the item, then the plugin's name.
|
|
1123
|
+
*
|
|
1124
|
+
* 1–80 UTF-8 bytes (a text value counted by its English rendering), no
|
|
1125
|
+
* control characters, no whitespace at either end
|
|
1126
|
+
* (`COMMAND_LIMITS.categoryBytes`); the player refuses anything else with
|
|
1127
|
+
* `invalid`. Sent only when set: players before 0.7.6 refuse the field
|
|
1128
|
+
* with `invalid`, so declare `"minPlayerVersion": "0.7.6"` when you set
|
|
1129
|
+
* it.
|
|
1130
|
+
*
|
|
1131
|
+
* @example Name commands that have no menu path
|
|
1132
|
+
* The palette lists "Subtitle Sync: Earlier 0.5 s" and "Subtitle Sync:
|
|
1133
|
+
* Later 0.5 s"; without a category both would read as a bare "Earlier
|
|
1134
|
+
* 0.5 s", indistinguishable from an audio delay command of the same name.
|
|
1135
|
+
* ```ts
|
|
1136
|
+
* declare function shift(seconds: number): void;
|
|
1137
|
+
* // ---cut---
|
|
1138
|
+
* for (const [id, title, seconds] of [
|
|
1139
|
+
* ["sync-earlier", "Earlier 0.5 s", -0.5],
|
|
1140
|
+
* ["sync-later", "Later 0.5 s", 0.5],
|
|
1141
|
+
* ] as const)
|
|
1142
|
+
* plugin.commands.add({
|
|
1143
|
+
* id,
|
|
1144
|
+
* title,
|
|
1145
|
+
* category: "Subtitle Sync", // the palette reads "Subtitle Sync: Earlier 0.5 s"
|
|
1146
|
+
* bindings: [id === "sync-earlier" ? "." : ","],
|
|
1147
|
+
* run: () => shift(seconds),
|
|
1148
|
+
* });
|
|
1149
|
+
* ```
|
|
1150
|
+
*
|
|
1151
|
+
* @player 0.7.6 (declare `"minPlayerVersion": "0.7.6"`; older players
|
|
1152
|
+
* refuse the declaration with `invalid`).
|
|
1153
|
+
* @since 1.6.4
|
|
1154
|
+
*/
|
|
1155
|
+
category?: string | Text;
|
|
1156
|
+
/**
|
|
1157
|
+
* The id of this plugin's command that opens the window or panel where
|
|
1158
|
+
* this command's control lives (`"tune"` for a Subtitle Tuning… command
|
|
1159
|
+
* whose window has the sync slider). The player's command palette shows
|
|
1160
|
+
* that place next to the command. A local id, like `id`: ASCII letters,
|
|
1161
|
+
* digits, `.`, `_` and `-`, 1–128 bytes; the player refuses anything else
|
|
1162
|
+
* with `invalid`. The command it names need not be registered yet; while
|
|
1163
|
+
* it is not, the palette shows the place it would show without `home`:
|
|
1164
|
+
* the menu path above the item, "Menu" for a top-level item, nothing for
|
|
1165
|
+
* a command outside the menu. Sent only when set: players
|
|
1166
|
+
* before 0.7.6 refuse the field with `invalid`, so declare
|
|
1167
|
+
* `"minPlayerVersion": "0.7.6"` when you set it.
|
|
1168
|
+
*
|
|
1169
|
+
* @example Point a key command at its window
|
|
1170
|
+
* The palette row for "Subtitle Sync: Earlier 0.5 s" also says where the
|
|
1171
|
+
* same setting can be changed by hand: the Subtitle Tuning window that the
|
|
1172
|
+
* `tune` command opens.
|
|
1173
|
+
* ```ts
|
|
1174
|
+
* declare const tuning: { show(): void };
|
|
1175
|
+
* declare function shift(seconds: number): void;
|
|
1176
|
+
* // ---cut---
|
|
1177
|
+
* plugin.commands.add({
|
|
1178
|
+
* id: "tune",
|
|
1179
|
+
* path: ["@subtitles", "Subtitle Tuning…"],
|
|
1180
|
+
* run: () => tuning.show(),
|
|
1181
|
+
* });
|
|
1182
|
+
* plugin.commands.add({
|
|
1183
|
+
* id: "sync-earlier",
|
|
1184
|
+
* title: "Earlier 0.5 s",
|
|
1185
|
+
* category: "Subtitle Sync",
|
|
1186
|
+
* home: "tune", // the palette shows "Subtitle Tuning" as its place
|
|
1187
|
+
* run: () => shift(-0.5),
|
|
1188
|
+
* });
|
|
1189
|
+
* ```
|
|
1190
|
+
*
|
|
1191
|
+
* @player 0.7.6 (declare `"minPlayerVersion": "0.7.6"`; older players
|
|
1192
|
+
* refuse the declaration with `invalid`).
|
|
1193
|
+
* @since 1.6.4
|
|
1194
|
+
*/
|
|
1195
|
+
home?: string;
|
|
1115
1196
|
/**
|
|
1116
1197
|
* Sibling order in the menu, ascending (integer, default 0). A submenu sits
|
|
1117
1198
|
* where its first command sits, except a built-in menu, which keeps its
|
|
@@ -1682,11 +1763,11 @@ export interface Ui {
|
|
|
1682
1763
|
* calls `image()` again after rewriting it, because the SDK does not send
|
|
1683
1764
|
* a value that did not change.
|
|
1684
1765
|
* ```ts
|
|
1685
|
-
* const
|
|
1766
|
+
* const thumbnailWindow = plugin.windows.create("ui/thumbnail.slint");
|
|
1686
1767
|
* const file = `${plugin.dataDirectory}/thumbnail.png`;
|
|
1687
1768
|
* // … write the PNG to `file` (for example with plugin.tools.run("ffmpeg", …)) …
|
|
1688
|
-
*
|
|
1689
|
-
* await
|
|
1769
|
+
* thumbnailWindow.set({ thumbnail: plugin.ui.image(file) });
|
|
1770
|
+
* await thumbnailWindow.show();
|
|
1690
1771
|
* ```
|
|
1691
1772
|
*
|
|
1692
1773
|
* @since 1.0.0
|
|
@@ -1850,11 +1931,11 @@ export interface Ui {
|
|
|
1850
1931
|
* const response = await fetch(readmeUrl);
|
|
1851
1932
|
* if (!response.ok) throw new Error(`README: HTTP ${response.status}`);
|
|
1852
1933
|
* const text = await response.text();
|
|
1853
|
-
* const
|
|
1934
|
+
* const readmeWindow = plugin.windows.create("ui/readme.slint");
|
|
1854
1935
|
* // Bound before the first `show`, as a tool window requires.
|
|
1855
|
-
*
|
|
1856
|
-
*
|
|
1857
|
-
* await
|
|
1936
|
+
* readmeWindow.on("openLink", (url: string) => plugin.system.openUrl(url));
|
|
1937
|
+
* readmeWindow.set({ doc: await plugin.ui.markdown(text, { base: readmeUrl }) });
|
|
1938
|
+
* await readmeWindow.show();
|
|
1858
1939
|
* ```
|
|
1859
1940
|
*
|
|
1860
1941
|
* @since 1.1.0
|
|
@@ -1999,10 +2080,10 @@ export interface View {
|
|
|
1999
2080
|
* sends the value even though it equals the one sent before, which is
|
|
2000
2081
|
* what pulls a control the user moved back to the plugin's value.
|
|
2001
2082
|
* ```ts
|
|
2002
|
-
* const
|
|
2083
|
+
* const presetsWindow = plugin.windows.create("ui/presets.slint");
|
|
2003
2084
|
* let presetIndex = 0;
|
|
2004
|
-
*
|
|
2005
|
-
* if (index > 2)
|
|
2085
|
+
* presetsWindow.on("presetChosen", (index: number) => {
|
|
2086
|
+
* if (index > 2) presetsWindow.set({ presetIndex }); // "Custom" is not a preset
|
|
2006
2087
|
* else presetIndex = index;
|
|
2007
2088
|
* });
|
|
2008
2089
|
* ```
|
|
@@ -2165,9 +2246,9 @@ export interface Window extends View, Disposable {
|
|
|
2165
2246
|
* window; it rejects with a code, such as `host` for a Slint file that
|
|
2166
2247
|
* does not compile or `unavailable` without a user interface.
|
|
2167
2248
|
* ```ts
|
|
2168
|
-
* const
|
|
2249
|
+
* const inspectorWindow = plugin.windows.create("ui/inspector.slint");
|
|
2169
2250
|
* try {
|
|
2170
|
-
* await
|
|
2251
|
+
* await inspectorWindow.show();
|
|
2171
2252
|
* } catch (error) {
|
|
2172
2253
|
* // The code is data; `message` has the details for the log.
|
|
2173
2254
|
* await plugin.osd.show("Cannot open the inspector ({})", [(error as PluginError).code]);
|
|
@@ -2276,8 +2357,8 @@ export interface Window extends View, Disposable {
|
|
|
2276
2357
|
* store writes the change to `window.json` shortly after.
|
|
2277
2358
|
* ```ts
|
|
2278
2359
|
* const store = plugin.storage.json("window", { defaults: { open: false } });
|
|
2279
|
-
* const
|
|
2280
|
-
*
|
|
2360
|
+
* const notesWindow = plugin.windows.create("ui/notes.slint");
|
|
2361
|
+
* notesWindow.onClose(() => store.save({ open: false }));
|
|
2281
2362
|
* ```
|
|
2282
2363
|
*
|
|
2283
2364
|
* @see [Remember a window's state between runs](../../docs/how-to/window-state.md)
|
|
@@ -5390,9 +5471,9 @@ export interface Stream extends Disposable {
|
|
|
5390
5471
|
* connection; it resolves once both are closed.
|
|
5391
5472
|
* ```ts
|
|
5392
5473
|
* declare const stream: Stream;
|
|
5393
|
-
* declare const
|
|
5474
|
+
* declare const generatorWindow: Window;
|
|
5394
5475
|
* // ---cut---
|
|
5395
|
-
*
|
|
5476
|
+
* generatorWindow.onClose(() => stream.stop());
|
|
5396
5477
|
* ```
|
|
5397
5478
|
*
|
|
5398
5479
|
* @since 1.0.0
|
|
@@ -9201,6 +9282,16 @@ export interface BindingInfo extends BindingRef {
|
|
|
9201
9282
|
* @since 1.5.11
|
|
9202
9283
|
*/
|
|
9203
9284
|
key?: string[] | null;
|
|
9285
|
+
/**
|
|
9286
|
+
* The command's category as shown ({@link CommandOptions.category},
|
|
9287
|
+
* translated), `null` when it declares none (and always from players
|
|
9288
|
+
* before 0.7.6). Always set by `list()`; optional so that test fakes
|
|
9289
|
+
* written for older SDKs still type-check. A list of commands names one `Category: Title`; without a
|
|
9290
|
+
* category, use the menu path (`path`) as before.
|
|
9291
|
+
*
|
|
9292
|
+
* @since 1.6.4
|
|
9293
|
+
*/
|
|
9294
|
+
category?: string | null;
|
|
9204
9295
|
/** Global, or only while the plugin's panel has focus. */
|
|
9205
9296
|
scope: "global" | "panel";
|
|
9206
9297
|
/** The command's own bindings. */
|
|
@@ -9660,10 +9751,10 @@ export interface JsonStore<T> extends Disposable {
|
|
|
9660
9751
|
* writes the file only once the user stops for 300 ms. Each `save`
|
|
9661
9752
|
* replaces the value and restarts the debounce timer.
|
|
9662
9753
|
* ```ts
|
|
9663
|
-
* declare const
|
|
9754
|
+
* declare const volumeWindow: Window;
|
|
9664
9755
|
* // ---cut---
|
|
9665
9756
|
* const store = plugin.storage.json("prefs", { defaults: { volume: 100 } });
|
|
9666
|
-
*
|
|
9757
|
+
* volumeWindow.on("volumeChanged", (volume: number) => store.save({ ...store.value, volume }));
|
|
9667
9758
|
* ```
|
|
9668
9759
|
*
|
|
9669
9760
|
* @since 1.0.0
|
package/dist/wire.d.ts
CHANGED
|
@@ -209,6 +209,8 @@ export interface BindingTable {
|
|
|
209
209
|
path: string[] | null;
|
|
210
210
|
/** The path's keys (player 0.4.3). */
|
|
211
211
|
key?: string[] | null;
|
|
212
|
+
/** The shown category (player 0.7.6). */
|
|
213
|
+
category?: string | null;
|
|
212
214
|
scope: "global" | "panel";
|
|
213
215
|
defaults: string[];
|
|
214
216
|
current: string[];
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vestra Player SDK
|
|
2
2
|
|
|
3
|
-
> @vestra-player/sdk 1.6.
|
|
3
|
+
> @vestra-player/sdk 1.6.4: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
|
|
4
4
|
|
|
5
5
|
## Read the documentation
|
|
6
6
|
|