@vestra-player/sdk 1.6.3 → 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 +12 -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 +8 -0
- package/dist/plugin/testing.js +25 -4
- package/dist/plugin/types.d.ts +91 -0
- package/dist/wire.d.ts +2 -0
- package/llms.txt +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
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
|
+
|
|
3
15
|
## 1.6.3
|
|
4
16
|
|
|
5
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.
|
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
|
@@ -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`
|
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,
|
|
@@ -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
|
@@ -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
|
|
@@ -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. */
|
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
|
|