@vestra-player/sdk 1.5.28 → 1.5.30
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 +23 -0
- package/bin/vestra-plugin.mjs +103 -0
- package/dist/capabilities.d.ts +13 -3
- package/dist/capabilities.js +13 -3
- package/dist/commands.d.ts +94 -8
- package/dist/commands.js +124 -7
- package/dist/index.d.ts +6 -1
- package/dist/palettes.d.ts +16 -2
- package/dist/palettes.js +16 -2
- package/dist/player-runtime.d.ts +1 -1
- package/dist/player-runtime.js +1 -1
- package/dist/plugin/bindings.d.ts +27 -3
- package/dist/plugin/bindings.js +27 -3
- package/dist/plugin/commands.js +29 -2
- package/dist/plugin/osd.d.ts +13 -1
- package/dist/plugin/osd.js +13 -1
- package/dist/plugin/services.d.ts +46 -6
- package/dist/plugin/services.js +46 -6
- package/dist/plugin/testing.d.ts +403 -61
- package/dist/plugin/testing.js +408 -60
- package/dist/plugin/types.d.ts +860 -162
- package/dist/plugin/values.d.ts +27 -3
- package/dist/plugin/values.js +27 -3
- package/dist/processing.d.ts +16 -4
- package/dist/source-resolvers.d.ts +33 -8
- package/dist/subtitles.d.ts +19 -9
- package/dist/wire.d.ts +12 -3
- package/dist/wire.js +12 -3
- package/llms-full.txt +3503 -25
- package/llms.txt +26 -26
- package/package.json +1 -1
- package/slint/vestra/bar-buttons.slint +138 -0
- package/slint/vestra/icons.slint +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.30
|
|
4
|
+
|
|
5
|
+
**Who must change: nobody.** No API changes, `apiVersion` stays `vestra/1`, no player version needed. Only documentation, its checks and the generated `llms-full.txt` change.
|
|
6
|
+
|
|
7
|
+
- **Every example explains what it does and how it works.** The standard, in *Maintaining* ("Examples in reference comments"): an `@example` has a task title, a lead saying what it does and how it works (checked against the SDK and the player's source), code that compiles on its own with comments on the key lines, and error handling where the user can make a call fail. All 159 examples of the API now meet it (none did before this release), including the 65 of the UI category, the test helpers (`TestHost`, `startPlugin`, `HostError`), the limits and tables (`COMMAND_LIMITS`, `MENU_IDS`, `BUILTIN_*`, `BAR_BUTTON_ICONS`, `CAPABILITY_INFO`, `BINDING_INPUTS`, `OSD_LIMITS`, `SESSION_STORAGE_LIMITS`, `SECRET_LIMITS`, `UI_VALUE_LIMITS`, `THEME_PALETTES`) and the 1.5.29 bar buttons (`CommandButton`, `TestHost.barButtons`). Every example now type checks (61 did not). Examples no longer compose shown sentences in TypeScript: OSD and dialog texts are templates with arguments.
|
|
8
|
+
- **Corrections found on the way.** `Subtitles.setStyle`, `SubtitlesState.style` and `SubtitleStyle`: when several plugins set a style layer, the effective one is the layer of the plugin whose manifest id sorts last, not the latest call. `COMMAND_LIMITS`: `add` never throws `limit`; the player refuses the registration (activation fails, or `Scope.onError`). `startPlugin`'s restart example gives both runs the same `dataDirectory` instead of copying the data before `stop()` (which could miss a pending store write). `TestHost.settle` says the debounced store write is not waited for. `TestHost.dialogAnswers` is one queue for every kind of dialog. `TestHost.openElsewhere`: calls that return nothing (`TimelineLayer.set` with `ifCurrent`) drop the write silently instead of rejecting. Resolver examples (`I18n.error`, `FileResolverOptions`, `TestHost.otherResolvers`) use a made-up `.ch8` format instead of `.nes`, which the store plugin VestraNES owns.
|
|
9
|
+
- **`llms-full.txt` carries the examples and `@remarks`** of every declaration and member (hidden setup lines dropped, `{@includeCode}` inlined), so the plugin workshop's model sees them: 536 KB → 693 KB (about 137k → 177k tokens). `llms.txt` wording trimmed to stay under its 9 KB limit (9,134 bytes).
|
|
10
|
+
- **Hidden setup lines**: in an `@example` code block, lines above `// ---cut---` are type checked but not shown on the site or in `llms-full.txt`.
|
|
11
|
+
- **Checks.** `docs:check` fails on any block that does not compile (it only warned for most examples; `--lenient` keeps the old behaviour for work in progress). `doc-lint` gains `example-lead` (task title, lead, comments, one `// ---cut---` at most; `See` markers for trivial siblings, whose link must resolve, at most 20 % of a declaration's methods) and `stale-refs` (removed or handed-over names such as `vestra.nes`, `.nes` resolvers), enforced for every example and Markdown code block, and prints per category the share of methods with an explained example. `EXAMPLE_BASELINE` keeps known violations per problem when a rule is tightened; it is empty, and `test/doc-baseline.test.ts` keeps it from growing.
|
|
12
|
+
- The parts of `examples/bookmark`, `examples/markdown` and `examples/panel-menu` that the reference includes gained comments on their key lines.
|
|
13
|
+
- Test only: the storage test in `test/round2.test.ts` waits for the debounced file to appear and for `flush()` instead of a fixed 60 ms, and stops its plugins before removing their data directory, so it no longer fails now and then with `ENOENT` on a loaded machine.
|
|
14
|
+
|
|
15
|
+
## 1.5.29
|
|
16
|
+
|
|
17
|
+
**Who must change: nobody.** Additions only, `apiVersion` stays `vestra/1`. A plugin that does not use `button` is unaffected. A plugin that uses it on a player before 0.5.10 keeps working without a button: the player builds the `plugin` object with its own SDK copy, which does not send the field, so the command and its menu entry register as before (declare `"minPlayerVersion": "0.5.10"` when the button matters). A plugin that mounts its own bottom bar should place `BarButtons` (below); until it does, `vestra-plugin check` warns and the player logs the buttons it hides.
|
|
18
|
+
|
|
19
|
+
- **Commands can have a bottom-bar button** (player 0.5.10, user-approved design of 2026-10-27): `button: { icon, order }` on `commands.add` (`CommandOptions.button`, `CommandButton`, `BarButtonIcon`). The icon is a name of the player's icon set or an image from `plugin.ui.image` (drawn in the icon colour); `order` 0–999 places it left to right. The button needs a menu `path`; its tooltip is the command's title (without a trailing "…") and first key; disabled or busy commands show a disabled button with the reason; a click runs the command like its menu item. One button per plugin, eight in the player (`BAR_BUTTON_LIMITS`; past eight the highest orders stay and the lowest go first, ties: the smaller plugin id, then the earlier declaration); a button past them is left out (the command and its menu entry stay) and the player logs it with `limit`. `add` throws `invalid` for an unknown icon name (`BAR_BUTTON_ICONS`), an order outside 0–999, a released image or a button without a path.
|
|
20
|
+
- **`@vestra/bar-buttons.slint`**: `BarButtons`, the strip of command buttons a bottom bar places at its right end with `room`, the width it can spare. It shows the cells that fit (the player's sidebar toggle, **Toggle Sidebar** `host/sidebar`, rightmost and last to go; cells go from the strip's outer (left) end inward, so of the plugins' buttons the lowest order goes first; `order` is position only) and is as wide as the cells it shows (`first-shown`, out, is the index of the leftmost plugin button shown). The player fills it in every window it composes and runs the clicks. Slint library copy synced; the icon set gains `store` (a shopping bag) and `ai` (a sparkle).
|
|
21
|
+
- **`TestHost.barButtons`**: the plugin's button as the player shows it (`command`, `icon`, `image`, `order`, `enabled`, `checked`, `tooltip`) with `click()`, which runs the command and rejects while it is disabled or once it is gone; empty after the command is disposed. The test host checks buttons as the player does.
|
|
22
|
+
- **`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 (comments do not count; a warning, the check still passes).
|
|
23
|
+
- `BUILTIN_COMMANDS` gains the player's **Toggle Sidebar** (`host/sidebar`, outside the menu, no default key).
|
|
24
|
+
- Docs: *Commands* (concepts) gains *Bottom-bar buttons*; *Commands, menus and bindings* (reference) gains *Bottom-bar buttons*; *Interface guidelines* gains *Writing a bottom bar*; *Limits* gains *Bottom-bar buttons*; *Theme* gains *Bar buttons* and the two icons; *Windows, panels…* points to buttons for a one-click entry point. `llms.txt` and `llms-full.txt` regenerated.
|
|
25
|
+
|
|
3
26
|
## 1.5.28
|
|
4
27
|
|
|
5
28
|
**Who must change: nobody.** No API changes, `apiVersion` stays `vestra/1`. Only the SDK's copy of what the player ships changes; a plugin that read `BUILTIN_COMMANDS` or `BUILTIN_SUBMENUS` to avoid a key or a menu path simply finds those entries free now.
|
package/bin/vestra-plugin.mjs
CHANGED
|
@@ -75,6 +75,7 @@ import {
|
|
|
75
75
|
basename,
|
|
76
76
|
dirname,
|
|
77
77
|
extname,
|
|
78
|
+
isAbsolute,
|
|
78
79
|
join,
|
|
79
80
|
relative,
|
|
80
81
|
resolve,
|
|
@@ -356,6 +357,8 @@ async function check(target, report = null) {
|
|
|
356
357
|
}
|
|
357
358
|
say(`${target.name}: capabilities declared for every API use`);
|
|
358
359
|
if (report) return;
|
|
360
|
+
for (const line of await bottomBarWarnings(target))
|
|
361
|
+
console.log(`${target.name}: warning: ${line}`);
|
|
359
362
|
// Translation warnings never fail the check.
|
|
360
363
|
if (present(join(target.directory, "locales"))) {
|
|
361
364
|
try {
|
|
@@ -399,6 +402,106 @@ async function capabilityFindings(target, config) {
|
|
|
399
402
|
.map((finding) => ({ ...finding, file: resolve(finding.file) }));
|
|
400
403
|
}
|
|
401
404
|
|
|
405
|
+
/** Slint source without its comments (string literals kept as they are). */
|
|
406
|
+
function slintCode(text) {
|
|
407
|
+
let out = "";
|
|
408
|
+
for (let i = 0; i < text.length;) {
|
|
409
|
+
if (text[i] === '"') {
|
|
410
|
+
let j = i + 1;
|
|
411
|
+
while (j < text.length && text[j] !== '"') j += text[j] === "\\" ? 2 : 1;
|
|
412
|
+
out += text.slice(i, j + 1);
|
|
413
|
+
i = j + 1;
|
|
414
|
+
} else if (text.startsWith("//", i)) {
|
|
415
|
+
const next = text.indexOf("\n", i);
|
|
416
|
+
i = next < 0 ? text.length : next;
|
|
417
|
+
} else if (text.startsWith("/*", i)) {
|
|
418
|
+
const close = text.indexOf("*/", i + 2);
|
|
419
|
+
i = close < 0 ? text.length : close + 2;
|
|
420
|
+
out += " ";
|
|
421
|
+
} else out += text[i++];
|
|
422
|
+
}
|
|
423
|
+
return out;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/** Whether `path` lies inside `directory` (not a sibling sharing its prefix). */
|
|
427
|
+
function inside(directory, path) {
|
|
428
|
+
const rel = relative(resolve(directory), path);
|
|
429
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Whether the Slint file at `file` (relative to the plugin directory)
|
|
434
|
+
* imports `BarButtons` from `@vestra/bar-buttons.slint`, itself or
|
|
435
|
+
* through the plugin's own Slint files (relative paths, any depth, inside
|
|
436
|
+
* the plugin directory). Local re-exports count as such files too:
|
|
437
|
+
* `export { Row } from "row.slint"` and `export * from "row.slint"` are
|
|
438
|
+
* followed, and `export { BarButtons } from "@vestra/bar-buttons.slint"` (or
|
|
439
|
+
* `export * from` it) in a file the bar imports counts as placing it.
|
|
440
|
+
* Comments do not count.
|
|
441
|
+
*/
|
|
442
|
+
async function importsBarButtons(target, file, seen = new Set()) {
|
|
443
|
+
const path = resolve(target.directory, file);
|
|
444
|
+
if (seen.has(path) || !inside(target.directory, path)) return false;
|
|
445
|
+
seen.add(path);
|
|
446
|
+
const text = await readFile(path, "utf8").catch(() => null);
|
|
447
|
+
if (text === null) return false;
|
|
448
|
+
const code = slintCode(text);
|
|
449
|
+
// { names (or "*"), from }: imports, and re-exports with a source file.
|
|
450
|
+
const imports = [
|
|
451
|
+
...code.matchAll(
|
|
452
|
+
/\b(?:import|export)\s*(?:(\*)\s*from\s*|\{([^}]*)\}\s*from\s*)?"([^"]+)"/g,
|
|
453
|
+
),
|
|
454
|
+
].map(([, star, names, from]) => ({ names: star ?? names ?? "", from }));
|
|
455
|
+
for (const { names, from } of imports)
|
|
456
|
+
if (
|
|
457
|
+
from === "@vestra/bar-buttons.slint" &&
|
|
458
|
+
(names === "*" ||
|
|
459
|
+
names
|
|
460
|
+
.split(",")
|
|
461
|
+
.some((name) => /^BarButtons(\s+as\s+[\w-]+)?$/.test(name.trim())))
|
|
462
|
+
)
|
|
463
|
+
return true;
|
|
464
|
+
for (const { from } of imports) {
|
|
465
|
+
if (
|
|
466
|
+
from.startsWith("@") ||
|
|
467
|
+
!from.endsWith(".slint") ||
|
|
468
|
+
from === "std-widgets.slint"
|
|
469
|
+
)
|
|
470
|
+
continue;
|
|
471
|
+
if (await importsBarButtons(target, join(dirname(file), from), seen))
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* A plugin that mounts a bottom bar (`panels.mount("bottom", "<file>")`)
|
|
479
|
+
* whose Slint file does not import `BarButtons` (`@vestra/bar-buttons.slint`),
|
|
480
|
+
* itself or through its local imports: while it is the bottom bar, no
|
|
481
|
+
* plugin's command button shows (SDK 1.5.29). The mount is read from the
|
|
482
|
+
* source text: a literal file name in the call.
|
|
483
|
+
*/
|
|
484
|
+
async function bottomBarWarnings(target) {
|
|
485
|
+
const { typescript: sources } = await sourceFiles(target);
|
|
486
|
+
const files = new Set();
|
|
487
|
+
for (const file of sources) {
|
|
488
|
+
const text = await readFile(join(target.directory, file), "utf8");
|
|
489
|
+
for (const match of text.matchAll(
|
|
490
|
+
/\.mount\(\s*["'`]bottom["'`]\s*,\s*["'`]([^"'`$]+)["'`]/g,
|
|
491
|
+
))
|
|
492
|
+
files.add(match[1]);
|
|
493
|
+
}
|
|
494
|
+
const warnings = [];
|
|
495
|
+
for (const file of files) {
|
|
496
|
+
if (!present(join(target.directory, file))) continue;
|
|
497
|
+
if (await importsBarButtons(target, file)) continue;
|
|
498
|
+
warnings.push(
|
|
499
|
+
`${file} is a bottom bar without BarButtons (import { BarButtons } from "@vestra/bar-buttons.slint"): while it is mounted, the command buttons of every plugin (and the player's sidebar toggle) show nowhere; see docs/guidelines/interface.md#writing-a-bottom-bar`,
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
return warnings;
|
|
503
|
+
}
|
|
504
|
+
|
|
402
505
|
/** The plugin's own production .ts / .mts and .slint files. */
|
|
403
506
|
async function sourceFiles(target) {
|
|
404
507
|
const all = await files(target.directory);
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -49,13 +49,23 @@ export interface CapabilityInfo {
|
|
|
49
49
|
* `plugin.i18n`, reading `plugin.window.state`, `plugin.appearance.theme`
|
|
50
50
|
* and `plugin.packages.list`).
|
|
51
51
|
*
|
|
52
|
-
* @example
|
|
52
|
+
* @example Check in a test that the manifest declares every required capability
|
|
53
|
+
* The test fails, naming the missing capability, when the manifest declares
|
|
54
|
+
* a capability without one its members also need. It reads the plugin's own
|
|
55
|
+
* `manifest.json` and looks each declared name up in `CAPABILITY_INFO`,
|
|
56
|
+
* whose `requires` lists the capabilities that must be declared with it;
|
|
57
|
+
* at run time the player would refuse such a call with code `capability`.
|
|
53
58
|
* ```ts
|
|
59
|
+
* import assert from "node:assert/strict";
|
|
60
|
+
* import { readFile } from "node:fs/promises";
|
|
54
61
|
* import { CAPABILITY_INFO } from "@vestra-player/sdk/testing";
|
|
55
62
|
*
|
|
56
|
-
* const manifest = JSON.parse(
|
|
63
|
+
* const manifest: { capabilities: string[] } = JSON.parse(
|
|
64
|
+
* await readFile(new URL("../manifest.json", import.meta.url), "utf8"),
|
|
65
|
+
* );
|
|
57
66
|
* for (const capability of manifest.capabilities)
|
|
58
|
-
*
|
|
67
|
+
* // An unknown name has no entry: `vestra-plugin check` reports it.
|
|
68
|
+
* for (const needed of CAPABILITY_INFO[capability]?.requires ?? [])
|
|
59
69
|
* assert.ok(manifest.capabilities.includes(needed), needed);
|
|
60
70
|
* ```
|
|
61
71
|
*
|
package/dist/capabilities.js
CHANGED
|
@@ -13,13 +13,23 @@ import { capabilityInfo } from "./capability-rules.js";
|
|
|
13
13
|
* `plugin.i18n`, reading `plugin.window.state`, `plugin.appearance.theme`
|
|
14
14
|
* and `plugin.packages.list`).
|
|
15
15
|
*
|
|
16
|
-
* @example
|
|
16
|
+
* @example Check in a test that the manifest declares every required capability
|
|
17
|
+
* The test fails, naming the missing capability, when the manifest declares
|
|
18
|
+
* a capability without one its members also need. It reads the plugin's own
|
|
19
|
+
* `manifest.json` and looks each declared name up in `CAPABILITY_INFO`,
|
|
20
|
+
* whose `requires` lists the capabilities that must be declared with it;
|
|
21
|
+
* at run time the player would refuse such a call with code `capability`.
|
|
17
22
|
* ```ts
|
|
23
|
+
* import assert from "node:assert/strict";
|
|
24
|
+
* import { readFile } from "node:fs/promises";
|
|
18
25
|
* import { CAPABILITY_INFO } from "@vestra-player/sdk/testing";
|
|
19
26
|
*
|
|
20
|
-
* const manifest = JSON.parse(
|
|
27
|
+
* const manifest: { capabilities: string[] } = JSON.parse(
|
|
28
|
+
* await readFile(new URL("../manifest.json", import.meta.url), "utf8"),
|
|
29
|
+
* );
|
|
21
30
|
* for (const capability of manifest.capabilities)
|
|
22
|
-
*
|
|
31
|
+
* // An unknown name has no entry: `vestra-plugin check` reports it.
|
|
32
|
+
* for (const needed of CAPABILITY_INFO[capability]?.requires ?? [])
|
|
23
33
|
* assert.ok(manifest.capabilities.includes(needed), needed);
|
|
24
34
|
* ```
|
|
25
35
|
*
|
package/dist/commands.d.ts
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
/** Finite candidate command registry. Host authenticates owner; no plugin supplies an owner id. */
|
|
2
|
-
import type { Transport } from "./wire.ts";
|
|
2
|
+
import type { Json, Transport } from "./wire.ts";
|
|
3
3
|
import type { ListJson, TextJson } from "./i18n.ts";
|
|
4
4
|
/**
|
|
5
5
|
* The player's command registry caps, the same values as the player's own
|
|
6
6
|
* table (the player's tests check that they agree). They are runaway guards,
|
|
7
|
-
* not product limits: a command added past them
|
|
8
|
-
* (its `ready` rejects; during activation, activation
|
|
7
|
+
* not product limits: a command added past them is refused by the player
|
|
8
|
+
* with code `limit` (its `ready` rejects; during activation, activation
|
|
9
|
+
* fails; later the failure goes to `Scope.onError`), commands
|
|
9
10
|
* already registered are unaffected, and disposing a command frees its slot
|
|
10
11
|
* at once. {@link TestHost} enforces the same caps.
|
|
11
12
|
*
|
|
12
13
|
* For menus built from media content (tracks, chapters, presets), set your
|
|
13
14
|
* own sensible display cap well below `perPlugin`.
|
|
14
15
|
*
|
|
15
|
-
* @example
|
|
16
|
+
* @example Cap a chapter menu well below the command limit
|
|
17
|
+
* The Chapters submenu lists at most 50 chapters, however many the file
|
|
18
|
+
* has. Every menu item is a command, and the player counts them per plugin.
|
|
19
|
+
* `add` itself returns at once and never throws `limit`: past `perPlugin`
|
|
20
|
+
* the player refuses the registration, which fails the activation or, later,
|
|
21
|
+
* reaches `Scope.onError`. A try/catch around `add` cannot catch that, so a
|
|
22
|
+
* menu built from media content keeps its own cap and leaves room for the
|
|
23
|
+
* plugin's fixed commands.
|
|
16
24
|
* ```ts
|
|
25
|
+
* declare const chapters: { title: string; time: number }[];
|
|
26
|
+
* // ---cut---
|
|
17
27
|
* import { COMMAND_LIMITS } from "@vestra-player/sdk/testing";
|
|
18
28
|
*
|
|
29
|
+
* // 20 slots stay free for the plugin's own commands.
|
|
19
30
|
* const shown = chapters.slice(0, Math.min(50, COMMAND_LIMITS.perPlugin - 20));
|
|
20
31
|
* ```
|
|
21
32
|
*/
|
|
@@ -36,14 +47,65 @@ export declare const COMMAND_LIMITS: {
|
|
|
36
47
|
* `@video` as a menu of its own. The player's tests check that this list
|
|
37
48
|
* agrees with the player's.
|
|
38
49
|
*
|
|
39
|
-
* @example
|
|
50
|
+
* @example Put a command into the built-in Video menu
|
|
51
|
+
* "Blur" appears in the player's Video menu in every interface language.
|
|
52
|
+
* The player replaces a leading `@video` with that menu's key ("Video")
|
|
53
|
+
* before it merges paths, so the item joins the built-in menu; an id that
|
|
54
|
+
* is not in `MENU_IDS` makes `add` fail with `invalid`, which a test can
|
|
55
|
+
* rule out ahead of time.
|
|
40
56
|
* ```ts
|
|
57
|
+
* import type { Plugin } from "@vestra-player/sdk";
|
|
58
|
+
* declare const plugin: Plugin;
|
|
59
|
+
* // ---cut---
|
|
60
|
+
* import assert from "node:assert/strict";
|
|
41
61
|
* import { MENU_IDS } from "@vestra-player/sdk/testing";
|
|
42
62
|
*
|
|
43
|
-
*
|
|
63
|
+
* plugin.commands.add({ id: "blur", path: ["@video", "Blur"], run() {} });
|
|
64
|
+
* assert.ok(MENU_IDS.includes("video")); // a menu id, not a title
|
|
44
65
|
* ```
|
|
45
66
|
*/
|
|
46
67
|
export declare const MENU_IDS: readonly ["open", "playback", "subtitles", "video", "audio", "screen", "settings"];
|
|
68
|
+
/**
|
|
69
|
+
* The icons a bottom-bar button may name (`CommandOptions.button.icon`):
|
|
70
|
+
* every icon of the player's V-Line set, `@vestra/icons.slint` (draw one
|
|
71
|
+
* in your own Slint with `Icon { name: "store"; }`). The player's tests check
|
|
72
|
+
* that this list is its icon set.
|
|
73
|
+
*
|
|
74
|
+
* @example Check in a test that the button's icon exists
|
|
75
|
+
* The test fails before release if the plugin's bar button names an icon
|
|
76
|
+
* the player does not have. `commands.add` would refuse an unknown name
|
|
77
|
+
* with `invalid`; the player's tests keep `BAR_BUTTON_ICONS` equal to its
|
|
78
|
+
* icon set, so checking against it is the same check without a player.
|
|
79
|
+
* ```ts
|
|
80
|
+
* import assert from "node:assert/strict";
|
|
81
|
+
* import { BAR_BUTTON_ICONS } from "@vestra-player/sdk/testing";
|
|
82
|
+
*
|
|
83
|
+
* assert.ok(BAR_BUTTON_ICONS.includes("store")); // the icon main.ts uses
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @since 1.5.29
|
|
87
|
+
*/
|
|
88
|
+
export declare 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"];
|
|
89
|
+
/**
|
|
90
|
+
* The player's limits on bottom-bar buttons (`CommandOptions.button`), the
|
|
91
|
+
* same values as the player's (its tests check that they agree). A button
|
|
92
|
+
* past `perPlugin` or `total` is left out: its command still registers and
|
|
93
|
+
* its menu entry stays, and the player logs the button it left out
|
|
94
|
+
* (`limit`). Which ones stay: each plugin's first declared button, then the
|
|
95
|
+
* `total` highest by order, plugin id and declaration order: as in a narrow
|
|
96
|
+
* strip, the lowest order goes first (equal orders: the smaller plugin id,
|
|
97
|
+
* then the earlier declaration, goes first).
|
|
98
|
+
*
|
|
99
|
+
* @since 1.5.29
|
|
100
|
+
*/
|
|
101
|
+
export declare const BAR_BUTTON_LIMITS: {
|
|
102
|
+
/** Buttons one plugin shows (1). */
|
|
103
|
+
readonly perPlugin: 1;
|
|
104
|
+
/** Buttons the player shows, every plugin together, besides its own sidebar toggle (8). */
|
|
105
|
+
readonly total: 8;
|
|
106
|
+
/** The highest `order` (999; the lowest is 0). */
|
|
107
|
+
readonly maxOrder: 999;
|
|
108
|
+
};
|
|
47
109
|
/**
|
|
48
110
|
* A submenu that one of the player's built-in plugins declares: an entry of
|
|
49
111
|
* {@link BUILTIN_SUBMENUS}.
|
|
@@ -71,13 +133,23 @@ export interface BuiltinSubmenu {
|
|
|
71
133
|
* listed. The player's tests check that this list agrees with what its
|
|
72
134
|
* built-in plugins declare.
|
|
73
135
|
*
|
|
74
|
-
* @example
|
|
136
|
+
* @example Add an item to the built-in Snapshot submenu
|
|
137
|
+
* "Copy Snapshot" appears in Video ▸ Snapshot next to the built-in capture
|
|
138
|
+
* plugin's items. Paths merge by their English keys, so a command whose
|
|
139
|
+
* path continues with the submenu's keys (`["Video", "Snapshot"]`, the menu
|
|
140
|
+
* written as `@video`) lands in the same submenu whatever the interface
|
|
141
|
+
* language.
|
|
75
142
|
* ```ts
|
|
143
|
+
* import type { Plugin } from "@vestra-player/sdk";
|
|
144
|
+
* declare const plugin: Plugin;
|
|
145
|
+
* declare function copySnapshot(): Promise<void>;
|
|
146
|
+
* // ---cut---
|
|
76
147
|
* import { BUILTIN_SUBMENUS } from "@vestra-player/sdk/testing";
|
|
77
148
|
*
|
|
78
149
|
* const snapshot = BUILTIN_SUBMENUS.find((menu) => menu.path.at(-1) === "Snapshot")!;
|
|
79
150
|
* plugin.commands.add({
|
|
80
151
|
* id: "snapshot-to-clipboard",
|
|
152
|
+
* // ["@video", "Snapshot", "Copy Snapshot"]; @video stands for "Video".
|
|
81
153
|
* path: ["@video", ...snapshot.path.slice(1), "Copy Snapshot"],
|
|
82
154
|
* run: copySnapshot,
|
|
83
155
|
* });
|
|
@@ -120,7 +192,12 @@ export interface BuiltinCommand {
|
|
|
120
192
|
* placeholders (`No Audio Tracks`) are. The player's tests check that this
|
|
121
193
|
* list agrees with what the player and its built-in plugins register.
|
|
122
194
|
*
|
|
123
|
-
* @example
|
|
195
|
+
* @example Check in a test that a default key is still free
|
|
196
|
+
* The test fails if the player or a built-in plugin already binds Ctrl+K
|
|
197
|
+
* globally, before users ever see a conflict. It collects the default
|
|
198
|
+
* bindings of every built-in command that is not limited to its panel's
|
|
199
|
+
* focus (`focus: null`) and looks for the key the plugin wants; bindings
|
|
200
|
+
* are canonical, so compare them as the player writes them.
|
|
124
201
|
* ```ts
|
|
125
202
|
* import assert from "node:assert/strict";
|
|
126
203
|
* import { BUILTIN_COMMANDS } from "@vestra-player/sdk/testing";
|
|
@@ -157,6 +234,15 @@ export type CommandDeclaration = {
|
|
|
157
234
|
focus: "panel" | null;
|
|
158
235
|
/** Fire again on keyboard auto-repeat while held. */
|
|
159
236
|
repeat: boolean;
|
|
237
|
+
/**
|
|
238
|
+
* A bottom-bar button (player 0.5.10): an icon name, or an image in the
|
|
239
|
+
* `plugin.ui.image` wire form. Sent only when set: older players refuse
|
|
240
|
+
* unknown declaration fields.
|
|
241
|
+
*/
|
|
242
|
+
button?: {
|
|
243
|
+
icon: string | Json;
|
|
244
|
+
order: number;
|
|
245
|
+
};
|
|
160
246
|
};
|
|
161
247
|
export declare function commandClient(transport: Transport): {
|
|
162
248
|
register: (id: string, revision: number, declaration: CommandDeclaration, state: CommandState) => Promise<{
|
package/dist/commands.js
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The player's command registry caps, the same values as the player's own
|
|
3
3
|
* table (the player's tests check that they agree). They are runaway guards,
|
|
4
|
-
* not product limits: a command added past them
|
|
5
|
-
* (its `ready` rejects; during activation, activation
|
|
4
|
+
* not product limits: a command added past them is refused by the player
|
|
5
|
+
* with code `limit` (its `ready` rejects; during activation, activation
|
|
6
|
+
* fails; later the failure goes to `Scope.onError`), commands
|
|
6
7
|
* already registered are unaffected, and disposing a command frees its slot
|
|
7
8
|
* at once. {@link TestHost} enforces the same caps.
|
|
8
9
|
*
|
|
9
10
|
* For menus built from media content (tracks, chapters, presets), set your
|
|
10
11
|
* own sensible display cap well below `perPlugin`.
|
|
11
12
|
*
|
|
12
|
-
* @example
|
|
13
|
+
* @example Cap a chapter menu well below the command limit
|
|
14
|
+
* The Chapters submenu lists at most 50 chapters, however many the file
|
|
15
|
+
* has. Every menu item is a command, and the player counts them per plugin.
|
|
16
|
+
* `add` itself returns at once and never throws `limit`: past `perPlugin`
|
|
17
|
+
* the player refuses the registration, which fails the activation or, later,
|
|
18
|
+
* reaches `Scope.onError`. A try/catch around `add` cannot catch that, so a
|
|
19
|
+
* menu built from media content keeps its own cap and leaves room for the
|
|
20
|
+
* plugin's fixed commands.
|
|
13
21
|
* ```ts
|
|
22
|
+
* declare const chapters: { title: string; time: number }[];
|
|
23
|
+
* // ---cut---
|
|
14
24
|
* import { COMMAND_LIMITS } from "@vestra-player/sdk/testing";
|
|
15
25
|
*
|
|
26
|
+
* // 20 slots stay free for the plugin's own commands.
|
|
16
27
|
* const shown = chapters.slice(0, Math.min(50, COMMAND_LIMITS.perPlugin - 20));
|
|
17
28
|
* ```
|
|
18
29
|
*/
|
|
@@ -33,11 +44,21 @@ export const COMMAND_LIMITS = {
|
|
|
33
44
|
* `@video` as a menu of its own. The player's tests check that this list
|
|
34
45
|
* agrees with the player's.
|
|
35
46
|
*
|
|
36
|
-
* @example
|
|
47
|
+
* @example Put a command into the built-in Video menu
|
|
48
|
+
* "Blur" appears in the player's Video menu in every interface language.
|
|
49
|
+
* The player replaces a leading `@video` with that menu's key ("Video")
|
|
50
|
+
* before it merges paths, so the item joins the built-in menu; an id that
|
|
51
|
+
* is not in `MENU_IDS` makes `add` fail with `invalid`, which a test can
|
|
52
|
+
* rule out ahead of time.
|
|
37
53
|
* ```ts
|
|
54
|
+
* import type { Plugin } from "@vestra-player/sdk";
|
|
55
|
+
* declare const plugin: Plugin;
|
|
56
|
+
* // ---cut---
|
|
57
|
+
* import assert from "node:assert/strict";
|
|
38
58
|
* import { MENU_IDS } from "@vestra-player/sdk/testing";
|
|
39
59
|
*
|
|
40
|
-
*
|
|
60
|
+
* plugin.commands.add({ id: "blur", path: ["@video", "Blur"], run() {} });
|
|
61
|
+
* assert.ok(MENU_IDS.includes("video")); // a menu id, not a title
|
|
41
62
|
* ```
|
|
42
63
|
*/
|
|
43
64
|
export const MENU_IDS = [
|
|
@@ -49,6 +70,86 @@ export const MENU_IDS = [
|
|
|
49
70
|
"screen",
|
|
50
71
|
"settings",
|
|
51
72
|
];
|
|
73
|
+
/**
|
|
74
|
+
* The icons a bottom-bar button may name (`CommandOptions.button.icon`):
|
|
75
|
+
* every icon of the player's V-Line set, `@vestra/icons.slint` (draw one
|
|
76
|
+
* in your own Slint with `Icon { name: "store"; }`). The player's tests check
|
|
77
|
+
* that this list is its icon set.
|
|
78
|
+
*
|
|
79
|
+
* @example Check in a test that the button's icon exists
|
|
80
|
+
* The test fails before release if the plugin's bar button names an icon
|
|
81
|
+
* the player does not have. `commands.add` would refuse an unknown name
|
|
82
|
+
* with `invalid`; the player's tests keep `BAR_BUTTON_ICONS` equal to its
|
|
83
|
+
* icon set, so checking against it is the same check without a player.
|
|
84
|
+
* ```ts
|
|
85
|
+
* import assert from "node:assert/strict";
|
|
86
|
+
* import { BAR_BUTTON_ICONS } from "@vestra-player/sdk/testing";
|
|
87
|
+
*
|
|
88
|
+
* assert.ok(BAR_BUTTON_ICONS.includes("store")); // the icon main.ts uses
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* @since 1.5.29
|
|
92
|
+
*/
|
|
93
|
+
export const BAR_BUTTON_ICONS = [
|
|
94
|
+
"play",
|
|
95
|
+
"pause",
|
|
96
|
+
"stop",
|
|
97
|
+
"back",
|
|
98
|
+
"forward",
|
|
99
|
+
"open",
|
|
100
|
+
"volume",
|
|
101
|
+
"mute",
|
|
102
|
+
"playlist",
|
|
103
|
+
"detach",
|
|
104
|
+
"attach",
|
|
105
|
+
"close",
|
|
106
|
+
"failed",
|
|
107
|
+
"move-top",
|
|
108
|
+
"move-up",
|
|
109
|
+
"move-down",
|
|
110
|
+
"move-bottom",
|
|
111
|
+
"search",
|
|
112
|
+
"chapter",
|
|
113
|
+
"bookmark",
|
|
114
|
+
"check-task",
|
|
115
|
+
"check-box",
|
|
116
|
+
"menu-check",
|
|
117
|
+
"submenu",
|
|
118
|
+
"scroll-up",
|
|
119
|
+
"scroll-down",
|
|
120
|
+
"caret-down",
|
|
121
|
+
"sort-asc",
|
|
122
|
+
"sort-desc",
|
|
123
|
+
"tag-remove",
|
|
124
|
+
"preview-play",
|
|
125
|
+
"add",
|
|
126
|
+
"remove",
|
|
127
|
+
"sort",
|
|
128
|
+
"fullscreen",
|
|
129
|
+
"exit-fullscreen",
|
|
130
|
+
"store",
|
|
131
|
+
"ai",
|
|
132
|
+
];
|
|
133
|
+
/**
|
|
134
|
+
* The player's limits on bottom-bar buttons (`CommandOptions.button`), the
|
|
135
|
+
* same values as the player's (its tests check that they agree). A button
|
|
136
|
+
* past `perPlugin` or `total` is left out: its command still registers and
|
|
137
|
+
* its menu entry stays, and the player logs the button it left out
|
|
138
|
+
* (`limit`). Which ones stay: each plugin's first declared button, then the
|
|
139
|
+
* `total` highest by order, plugin id and declaration order: as in a narrow
|
|
140
|
+
* strip, the lowest order goes first (equal orders: the smaller plugin id,
|
|
141
|
+
* then the earlier declaration, goes first).
|
|
142
|
+
*
|
|
143
|
+
* @since 1.5.29
|
|
144
|
+
*/
|
|
145
|
+
export const BAR_BUTTON_LIMITS = {
|
|
146
|
+
/** Buttons one plugin shows (1). */
|
|
147
|
+
perPlugin: 1,
|
|
148
|
+
/** Buttons the player shows, every plugin together, besides its own sidebar toggle (8). */
|
|
149
|
+
total: 8,
|
|
150
|
+
/** The highest `order` (999; the lowest is 0). */
|
|
151
|
+
maxOrder: 999,
|
|
152
|
+
};
|
|
52
153
|
/**
|
|
53
154
|
* The submenus the player's built-in plugins declare, by key path, in menu
|
|
54
155
|
* order (built-in menus as in {@link MENU_IDS}, then top-level submenus).
|
|
@@ -59,13 +160,23 @@ export const MENU_IDS = [
|
|
|
59
160
|
* listed. The player's tests check that this list agrees with what its
|
|
60
161
|
* built-in plugins declare.
|
|
61
162
|
*
|
|
62
|
-
* @example
|
|
163
|
+
* @example Add an item to the built-in Snapshot submenu
|
|
164
|
+
* "Copy Snapshot" appears in Video ▸ Snapshot next to the built-in capture
|
|
165
|
+
* plugin's items. Paths merge by their English keys, so a command whose
|
|
166
|
+
* path continues with the submenu's keys (`["Video", "Snapshot"]`, the menu
|
|
167
|
+
* written as `@video`) lands in the same submenu whatever the interface
|
|
168
|
+
* language.
|
|
63
169
|
* ```ts
|
|
170
|
+
* import type { Plugin } from "@vestra-player/sdk";
|
|
171
|
+
* declare const plugin: Plugin;
|
|
172
|
+
* declare function copySnapshot(): Promise<void>;
|
|
173
|
+
* // ---cut---
|
|
64
174
|
* import { BUILTIN_SUBMENUS } from "@vestra-player/sdk/testing";
|
|
65
175
|
*
|
|
66
176
|
* const snapshot = BUILTIN_SUBMENUS.find((menu) => menu.path.at(-1) === "Snapshot")!;
|
|
67
177
|
* plugin.commands.add({
|
|
68
178
|
* id: "snapshot-to-clipboard",
|
|
179
|
+
* // ["@video", "Snapshot", "Copy Snapshot"]; @video stands for "Video".
|
|
69
180
|
* path: ["@video", ...snapshot.path.slice(1), "Copy Snapshot"],
|
|
70
181
|
* run: copySnapshot,
|
|
71
182
|
* });
|
|
@@ -413,6 +524,7 @@ vestra.preferences | language.es | Settings ▸ Language ▸ Español
|
|
|
413
524
|
vestra.info | info.about | About Vestra Player… | F1
|
|
414
525
|
host | quit | Quit | Cmd+Q
|
|
415
526
|
host | exit-fullscreen | (Leave Fullscreen) | Escape
|
|
527
|
+
host | sidebar | (Toggle Sidebar)
|
|
416
528
|
vestra.info | info.brief | (Brief Info) | Shift+Tab
|
|
417
529
|
vestra.playlist | playlist.play | (Play) | Enter, P | panel
|
|
418
530
|
vestra.playlist | playlist.list-add | (Add Files…) | Ctrl+I | panel
|
|
@@ -456,7 +568,12 @@ vestra.playlist | playlist.settings | (Playlist Settings…) | F5 | panel
|
|
|
456
568
|
* placeholders (`No Audio Tracks`) are. The player's tests check that this
|
|
457
569
|
* list agrees with what the player and its built-in plugins register.
|
|
458
570
|
*
|
|
459
|
-
* @example
|
|
571
|
+
* @example Check in a test that a default key is still free
|
|
572
|
+
* The test fails if the player or a built-in plugin already binds Ctrl+K
|
|
573
|
+
* globally, before users ever see a conflict. It collects the default
|
|
574
|
+
* bindings of every built-in command that is not limited to its panel's
|
|
575
|
+
* focus (`focus: null`) and looks for the key the plugin wants; bindings
|
|
576
|
+
* are canonical, so compare them as the player writes them.
|
|
460
577
|
* ```ts
|
|
461
578
|
* import assert from "node:assert/strict";
|
|
462
579
|
* import { BUILTIN_COMMANDS } from "@vestra-player/sdk/testing";
|
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,12 @@
|
|
|
12
12
|
* documents the manifest capability it needs. Errors are described at
|
|
13
13
|
* {@link PluginError}. Tests use `@vestra-player/sdk/testing`.
|
|
14
14
|
*
|
|
15
|
-
* @example
|
|
15
|
+
* @example Add a menu item that shows a message
|
|
16
|
+
* The player gets a Tools ▸ Hello menu item; picking it shows "Hello" on
|
|
17
|
+
* the video. The player imports the entry module and calls `activate` with
|
|
18
|
+
* the plugin's `Plugin` object; `commands.add` registers the item (manifest
|
|
19
|
+
* capability `commands`), and the player calls `run` when the user picks it
|
|
20
|
+
* or presses a key bound to it. `osd.show` needs `ui.osd`.
|
|
16
21
|
* ```ts
|
|
17
22
|
* import type { Plugin } from "@vestra-player/sdk";
|
|
18
23
|
*
|
package/dist/palettes.d.ts
CHANGED
|
@@ -11,11 +11,25 @@
|
|
|
11
11
|
* the palette (`slint/palette.slint`); the player's tests check both
|
|
12
12
|
* palettes against its own.
|
|
13
13
|
*
|
|
14
|
-
* @example
|
|
14
|
+
* @example Check in a test that a colour stays readable in both themes
|
|
15
|
+
* The test fails if the plugin's own highlight colour gets too close to
|
|
16
|
+
* the panel background of either palette. Plugins draw with `Theme` tokens
|
|
17
|
+
* in Slint and the player switches palettes; a colour the plugin picks
|
|
18
|
+
* itself does not switch, so a test compares it with each palette's values
|
|
19
|
+
* (hex strings, `#rrggbb`).
|
|
15
20
|
* ```ts
|
|
21
|
+
* import assert from "node:assert/strict";
|
|
16
22
|
* import { THEME_PALETTES } from "@vestra-player/sdk/testing";
|
|
17
23
|
*
|
|
18
|
-
* const
|
|
24
|
+
* const highlight = "#e07000"; // the plugin's own orange
|
|
25
|
+
* const luminance = (hex: string) => {
|
|
26
|
+
* const [r, g, b] = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255);
|
|
27
|
+
* return 0.2126 * r! + 0.7152 * g! + 0.0722 * b!; // rough, without gamma
|
|
28
|
+
* };
|
|
29
|
+
* for (const [name, palette] of Object.entries(THEME_PALETTES)) {
|
|
30
|
+
* const gap = Math.abs(luminance(highlight) - luminance(palette["bg-panel"]!));
|
|
31
|
+
* assert.ok(gap > 0.3, `${name}: highlight too close to bg-panel`);
|
|
32
|
+
* }
|
|
19
33
|
* ```
|
|
20
34
|
*
|
|
21
35
|
* @since 1.5.14
|
package/dist/palettes.js
CHANGED
|
@@ -11,11 +11,25 @@
|
|
|
11
11
|
* the palette (`slint/palette.slint`); the player's tests check both
|
|
12
12
|
* palettes against its own.
|
|
13
13
|
*
|
|
14
|
-
* @example
|
|
14
|
+
* @example Check in a test that a colour stays readable in both themes
|
|
15
|
+
* The test fails if the plugin's own highlight colour gets too close to
|
|
16
|
+
* the panel background of either palette. Plugins draw with `Theme` tokens
|
|
17
|
+
* in Slint and the player switches palettes; a colour the plugin picks
|
|
18
|
+
* itself does not switch, so a test compares it with each palette's values
|
|
19
|
+
* (hex strings, `#rrggbb`).
|
|
15
20
|
* ```ts
|
|
21
|
+
* import assert from "node:assert/strict";
|
|
16
22
|
* import { THEME_PALETTES } from "@vestra-player/sdk/testing";
|
|
17
23
|
*
|
|
18
|
-
* const
|
|
24
|
+
* const highlight = "#e07000"; // the plugin's own orange
|
|
25
|
+
* const luminance = (hex: string) => {
|
|
26
|
+
* const [r, g, b] = [1, 3, 5].map((i) => parseInt(hex.slice(i, i + 2), 16) / 255);
|
|
27
|
+
* return 0.2126 * r! + 0.7152 * g! + 0.0722 * b!; // rough, without gamma
|
|
28
|
+
* };
|
|
29
|
+
* for (const [name, palette] of Object.entries(THEME_PALETTES)) {
|
|
30
|
+
* const gap = Math.abs(luminance(highlight) - luminance(palette["bg-panel"]!));
|
|
31
|
+
* assert.ok(gap > 0.3, `${name}: highlight too close to bg-panel`);
|
|
32
|
+
* }
|
|
19
33
|
* ```
|
|
20
34
|
*
|
|
21
35
|
* @since 1.5.14
|
package/dist/player-runtime.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { HostError } from "./wire.ts";
|
|
|
13
13
|
export type { Json, Transport } from "./wire.ts";
|
|
14
14
|
export { API_VERSION, CAPABILITIES, CAPABILITY_INFO, MESSAGE_LIMIT, } from "./capabilities.ts";
|
|
15
15
|
export { HOST_CAPABILITIES } from "./host-capabilities.ts";
|
|
16
|
-
export { BUILTIN_COMMANDS, BUILTIN_SUBMENUS, COMMAND_LIMITS, MENU_IDS, } from "./commands.ts";
|
|
16
|
+
export { BAR_BUTTON_ICONS, BAR_BUTTON_LIMITS, BUILTIN_COMMANDS, BUILTIN_SUBMENUS, COMMAND_LIMITS, MENU_IDS, } from "./commands.ts";
|
|
17
17
|
export { BINDING_INPUTS } from "./plugin/bindings.ts";
|
|
18
18
|
export { OSD_LIMITS } from "./plugin/osd.ts";
|
|
19
19
|
export { SECRET_LIMITS, SESSION_STORAGE_LIMITS } from "./plugin/services.ts";
|
package/dist/player-runtime.js
CHANGED
|
@@ -11,7 +11,7 @@ export { createPlugin } from "./plugin/index.js";
|
|
|
11
11
|
export { HostError } from "./wire.js";
|
|
12
12
|
export { API_VERSION, CAPABILITIES, CAPABILITY_INFO, MESSAGE_LIMIT, } from "./capabilities.js";
|
|
13
13
|
export { HOST_CAPABILITIES } from "./host-capabilities.js";
|
|
14
|
-
export { BUILTIN_COMMANDS, BUILTIN_SUBMENUS, COMMAND_LIMITS, MENU_IDS, } from "./commands.js";
|
|
14
|
+
export { BAR_BUTTON_ICONS, BAR_BUTTON_LIMITS, BUILTIN_COMMANDS, BUILTIN_SUBMENUS, COMMAND_LIMITS, MENU_IDS, } from "./commands.js";
|
|
15
15
|
export { BINDING_INPUTS } from "./plugin/bindings.js";
|
|
16
16
|
export { OSD_LIMITS } from "./plugin/osd.js";
|
|
17
17
|
export { SECRET_LIMITS, SESSION_STORAGE_LIMITS } from "./plugin/services.js";
|