@vestra-player/sdk 1.5.12 → 1.5.14
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 +19 -0
- package/README.md +12 -20
- package/dist/capabilities.d.ts +73 -1
- package/dist/capabilities.js +263 -28
- package/dist/commands.d.ts +89 -0
- package/dist/commands.js +438 -0
- package/dist/i18n-tools.js +24 -7
- package/dist/i18n.d.ts +9 -2
- package/dist/i18n.js +22 -6
- package/dist/palettes.d.ts +23 -0
- package/dist/palettes.js +72 -0
- package/dist/player-runtime.d.ts +6 -2
- package/dist/player-runtime.js +6 -2
- package/dist/plugin/bindings.d.ts +45 -0
- package/dist/plugin/bindings.js +91 -42
- package/dist/plugin/osd.d.ts +32 -0
- package/dist/plugin/osd.js +36 -2
- package/dist/plugin/services.d.ts +25 -0
- package/dist/plugin/services.js +29 -4
- package/dist/plugin/testing.d.ts +24 -1
- package/dist/plugin/testing.js +36 -7
- package/dist/plugin/types.d.ts +93 -25
- package/llms-full.txt +2645 -373
- package/llms.txt +72 -136
- package/package.json +2 -1
package/dist/palettes.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The colours of the player's palettes, by `Theme` colour token (without the
|
|
3
|
+
* `Theme.` prefix): `vestra`, the dark default, and `vestra-light` (player
|
|
4
|
+
* 0.4.0). The player sets one of them on every view; a plugin reads the
|
|
5
|
+
* colours through `Theme` in its Slint files and never needs these values.
|
|
6
|
+
* They are here for the documentation (Reference: theme) and for tests that
|
|
7
|
+
* check a design against both palettes.
|
|
8
|
+
*
|
|
9
|
+
* Current values: a later player may adjust them, a token keeps its
|
|
10
|
+
* meaning. The `vestra` values are also the defaults of the SDK's copy of
|
|
11
|
+
* the palette (`slint/palette.slint`); the player's tests check both
|
|
12
|
+
* palettes against its own.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { THEME_PALETTES } from "@vestra-player/sdk/testing";
|
|
17
|
+
*
|
|
18
|
+
* const accent = THEME_PALETTES["vestra-light"].accent; // "#d4128a"
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @since 1.5.14
|
|
22
|
+
*/
|
|
23
|
+
export const THEME_PALETTES = Object.freeze({
|
|
24
|
+
vestra: Object.freeze({
|
|
25
|
+
"bg-bar": "#16141c",
|
|
26
|
+
"bg-video": "#0a090e",
|
|
27
|
+
"bg-list": "#1a1821",
|
|
28
|
+
"bg-panel": "#201d28",
|
|
29
|
+
line: "#2b2734",
|
|
30
|
+
"hover-bg": "#28232f",
|
|
31
|
+
"press-bg": "#110f16",
|
|
32
|
+
"selected-bg": "#2f2840",
|
|
33
|
+
text: "#f5f3fa",
|
|
34
|
+
"text-dim": "#8e88a0",
|
|
35
|
+
"text-list": "#bdb8cb",
|
|
36
|
+
"text-played": "#6e6980",
|
|
37
|
+
accent: "#f72aa6",
|
|
38
|
+
"accent-press": "#c41a83",
|
|
39
|
+
"accent-disabled": "#5a1d48",
|
|
40
|
+
"icon-disabled": "#3b3646",
|
|
41
|
+
playing: "#2bb6ff",
|
|
42
|
+
"button-bg": "#2f2a3b",
|
|
43
|
+
"button-hover": "#3a3448",
|
|
44
|
+
"button-press": "#25202e",
|
|
45
|
+
"button-disabled": "#1b1922",
|
|
46
|
+
danger: "#ff5a5f",
|
|
47
|
+
}),
|
|
48
|
+
"vestra-light": Object.freeze({
|
|
49
|
+
"bg-bar": "#f7f6fa",
|
|
50
|
+
"bg-video": "#0e0d12",
|
|
51
|
+
"bg-list": "#ffffff",
|
|
52
|
+
"bg-panel": "#f1eff5",
|
|
53
|
+
line: "#dcd8e4",
|
|
54
|
+
"hover-bg": "#e9e6f0",
|
|
55
|
+
"press-bg": "#dcd8e6",
|
|
56
|
+
"selected-bg": "#efe4fb",
|
|
57
|
+
text: "#1c1924",
|
|
58
|
+
"text-dim": "#6f6a7f",
|
|
59
|
+
"text-list": "#3b3748",
|
|
60
|
+
"text-played": "#9a95a8",
|
|
61
|
+
accent: "#d4128a",
|
|
62
|
+
"accent-press": "#a80d6c",
|
|
63
|
+
"accent-disabled": "#ecbcd9",
|
|
64
|
+
"icon-disabled": "#c9c5d2",
|
|
65
|
+
playing: "#0773ba",
|
|
66
|
+
"button-bg": "#e6e3ec",
|
|
67
|
+
"button-hover": "#dcd8e4",
|
|
68
|
+
"button-press": "#cfcadb",
|
|
69
|
+
"button-disabled": "#efedf3",
|
|
70
|
+
danger: "#c42b2b",
|
|
71
|
+
}),
|
|
72
|
+
});
|
package/dist/player-runtime.d.ts
CHANGED
|
@@ -11,8 +11,12 @@ export { createPlugin } from "./plugin/index.ts";
|
|
|
11
11
|
export type { PluginInstance, PluginManifest } from "./plugin/index.ts";
|
|
12
12
|
export { HostError } from "./wire.ts";
|
|
13
13
|
export type { Json, Transport } from "./wire.ts";
|
|
14
|
-
export { API_VERSION, CAPABILITIES } from "./capabilities.ts";
|
|
15
|
-
export { COMMAND_LIMITS, MENU_IDS } from "./commands.ts";
|
|
14
|
+
export { API_VERSION, CAPABILITIES, CAPABILITY_INFO, MESSAGE_LIMIT, } from "./capabilities.ts";
|
|
15
|
+
export { BUILTIN_COMMANDS, BUILTIN_SUBMENUS, COMMAND_LIMITS, MENU_IDS, } from "./commands.ts";
|
|
16
|
+
export { BINDING_INPUTS } from "./plugin/bindings.ts";
|
|
17
|
+
export { OSD_LIMITS } from "./plugin/osd.ts";
|
|
18
|
+
export { SECRET_LIMITS } from "./plugin/services.ts";
|
|
19
|
+
export { THEME_PALETTES } from "./palettes.ts";
|
|
16
20
|
export { Catalog, HOST_ZH_HANS, LIST_SEPARATORS, LOCALES, MENU_KEYS, matchLocale, parsePo, rewriteSlint, renderText, scanSlint, TEXT_LIMITS, textKey, textProblem, } from "./i18n.ts";
|
|
17
21
|
export { MANIFEST_FIELDS, MANIFEST_LIMITS } from "./manifest.ts";
|
|
18
22
|
export { MARKDOWN_LIMITS } from "./plugin/markdown.ts";
|
package/dist/player-runtime.js
CHANGED
|
@@ -9,8 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { createPlugin } from "./plugin/index.js";
|
|
11
11
|
export { HostError } from "./wire.js";
|
|
12
|
-
export { API_VERSION, CAPABILITIES } from "./capabilities.js";
|
|
13
|
-
export { COMMAND_LIMITS, MENU_IDS } from "./commands.js";
|
|
12
|
+
export { API_VERSION, CAPABILITIES, CAPABILITY_INFO, MESSAGE_LIMIT, } from "./capabilities.js";
|
|
13
|
+
export { BUILTIN_COMMANDS, BUILTIN_SUBMENUS, COMMAND_LIMITS, MENU_IDS, } from "./commands.js";
|
|
14
|
+
export { BINDING_INPUTS } from "./plugin/bindings.js";
|
|
15
|
+
export { OSD_LIMITS } from "./plugin/osd.js";
|
|
16
|
+
export { SECRET_LIMITS } from "./plugin/services.js";
|
|
17
|
+
export { THEME_PALETTES } from "./palettes.js";
|
|
14
18
|
export { Catalog, HOST_ZH_HANS, LIST_SEPARATORS, LOCALES, MENU_KEYS, matchLocale, parsePo, rewriteSlint, renderText, scanSlint, TEXT_LIMITS, textKey, textProblem, } from "./i18n.js";
|
|
15
19
|
export { MANIFEST_FIELDS, MANIFEST_LIMITS } from "./manifest.js";
|
|
16
20
|
export { MARKDOWN_LIMITS } from "./plugin/markdown.js";
|
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
import type { ScopeImpl } from "./scope.ts";
|
|
2
2
|
import type { Bindings } from "./types.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The names a binding (`CommandOptions.bindings`, `Bindings.set`) is made
|
|
5
|
+
* of, as the player's binding parser accepts them. A binding is
|
|
6
|
+
* `{ modifier "+" } input`: modifiers in the order of `modifiers`, each at
|
|
7
|
+
* most once, then one input. An input is a name of `letters`, `digits`,
|
|
8
|
+
* `functionKeys`, `named` or `mouse`, or one other printable character that
|
|
9
|
+
* is not a letter, digit, space or `+` (`">"`, `"["`, `"/"`), written as
|
|
10
|
+
* typed, never with `Shift` (`">"`, not `"Shift+."`; `"!"`, not
|
|
11
|
+
* `"Shift+1"`). Anything else fails with code `invalid`. The player's tests
|
|
12
|
+
* check that these agree with the player.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { BINDING_INPUTS } from "@vestra-player/sdk/testing";
|
|
17
|
+
*
|
|
18
|
+
* assert.ok(BINDING_INPUTS.named.includes("PageUp"));
|
|
19
|
+
* assert.ok(BINDING_INPUTS.mouse.includes("Mouse:WheelUp"));
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @since 1.5.14
|
|
23
|
+
*/
|
|
24
|
+
export declare const BINDING_INPUTS: Readonly<{
|
|
25
|
+
/**
|
|
26
|
+
* Modifiers, in the order a binding writes them. `Cmd` is ⌘ on macOS and
|
|
27
|
+
* the Windows logo key on Windows; on macOS `Ctrl` also matches ⌘.
|
|
28
|
+
*/
|
|
29
|
+
readonly modifiers: readonly ["Ctrl", "Alt", "Shift", "Cmd"];
|
|
30
|
+
/** Letter keys, by key name whatever the case: `A` to `Z`. */
|
|
31
|
+
readonly letters: readonly string[];
|
|
32
|
+
/** Digit keys of the main keyboard: `0` to `9`. */
|
|
33
|
+
readonly digits: readonly string[];
|
|
34
|
+
/** Function keys: `F1` to `F24`. */
|
|
35
|
+
readonly functionKeys: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Other named keys, matched by the physical key. `Plus` is the `+` key
|
|
38
|
+
* however it is typed; the `Numpad…` names are the numeric keypad only,
|
|
39
|
+
* never the main keyboard.
|
|
40
|
+
*/
|
|
41
|
+
readonly named: readonly ["Space", "Enter", "Tab", "Escape", "Backspace", "Delete", "Insert", "Home", "End", "PageUp", "PageDown", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Plus", ...string[], "NumpadPlus", "NumpadMinus", "NumpadMultiply", "NumpadDivide", "NumpadEnter", "NumpadDecimal"];
|
|
42
|
+
/**
|
|
43
|
+
* Mouse inputs on the video area. `:LeftHalf` and `:RightHalf` tell the
|
|
44
|
+
* halves of the video apart for a double click.
|
|
45
|
+
*/
|
|
46
|
+
readonly mouse: readonly ["Mouse:LeftClick", "Mouse:LeftDoubleClick", "Mouse:LeftDoubleClick:LeftHalf", "Mouse:LeftDoubleClick:RightHalf", "Mouse:MiddleClick", "Mouse:X1", "Mouse:X2", "Mouse:WheelUp", "Mouse:WheelDown", "Mouse:WheelLeft", "Mouse:WheelRight"];
|
|
47
|
+
}>;
|
|
3
48
|
/** Throws `invalid` unless `text` is a canonical binding; returns it unchanged. */
|
|
4
49
|
export declare function canonical(text: string): string;
|
|
5
50
|
export declare function checkBindings(bindings: readonly string[]): string[];
|
package/dist/plugin/bindings.js
CHANGED
|
@@ -7,54 +7,103 @@
|
|
|
7
7
|
* input = key | "Mouse:" mouse
|
|
8
8
|
*/
|
|
9
9
|
import { failure } from "./runtime.js";
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The names a binding (`CommandOptions.bindings`, `Bindings.set`) is made
|
|
12
|
+
* of, as the player's binding parser accepts them. A binding is
|
|
13
|
+
* `{ modifier "+" } input`: modifiers in the order of `modifiers`, each at
|
|
14
|
+
* most once, then one input. An input is a name of `letters`, `digits`,
|
|
15
|
+
* `functionKeys`, `named` or `mouse`, or one other printable character that
|
|
16
|
+
* is not a letter, digit, space or `+` (`">"`, `"["`, `"/"`), written as
|
|
17
|
+
* typed, never with `Shift` (`">"`, not `"Shift+."`; `"!"`, not
|
|
18
|
+
* `"Shift+1"`). Anything else fails with code `invalid`. The player's tests
|
|
19
|
+
* check that these agree with the player.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { BINDING_INPUTS } from "@vestra-player/sdk/testing";
|
|
24
|
+
*
|
|
25
|
+
* assert.ok(BINDING_INPUTS.named.includes("PageUp"));
|
|
26
|
+
* assert.ok(BINDING_INPUTS.mouse.includes("Mouse:WheelUp"));
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @since 1.5.14
|
|
30
|
+
*/
|
|
31
|
+
export const BINDING_INPUTS = Object.freeze({
|
|
32
|
+
/**
|
|
33
|
+
* Modifiers, in the order a binding writes them. `Cmd` is ⌘ on macOS and
|
|
34
|
+
* the Windows logo key on Windows; on macOS `Ctrl` also matches ⌘.
|
|
35
|
+
*/
|
|
36
|
+
modifiers: ["Ctrl", "Alt", "Shift", "Cmd"],
|
|
37
|
+
/** Letter keys, by key name whatever the case: `A` to `Z`. */
|
|
38
|
+
letters: [..."ABCDEFGHIJKLMNOPQRSTUVWXYZ"],
|
|
39
|
+
/** Digit keys of the main keyboard: `0` to `9`. */
|
|
40
|
+
digits: [..."0123456789"],
|
|
41
|
+
/** Function keys: `F1` to `F24`. */
|
|
42
|
+
functionKeys: Array.from({ length: 24 }, (_, index) => `F${index + 1}`),
|
|
43
|
+
/**
|
|
44
|
+
* Other named keys, matched by the physical key. `Plus` is the `+` key
|
|
45
|
+
* however it is typed; the `Numpad…` names are the numeric keypad only,
|
|
46
|
+
* never the main keyboard.
|
|
47
|
+
*/
|
|
48
|
+
named: [
|
|
49
|
+
"Space",
|
|
50
|
+
"Enter",
|
|
51
|
+
"Tab",
|
|
52
|
+
"Escape",
|
|
53
|
+
"Backspace",
|
|
54
|
+
"Delete",
|
|
55
|
+
"Insert",
|
|
56
|
+
"Home",
|
|
57
|
+
"End",
|
|
58
|
+
"PageUp",
|
|
59
|
+
"PageDown",
|
|
60
|
+
"ArrowLeft",
|
|
61
|
+
"ArrowRight",
|
|
62
|
+
"ArrowUp",
|
|
63
|
+
"ArrowDown",
|
|
64
|
+
"Plus",
|
|
65
|
+
...Array.from({ length: 10 }, (_, digit) => `Numpad${digit}`),
|
|
66
|
+
"NumpadPlus",
|
|
67
|
+
"NumpadMinus",
|
|
68
|
+
"NumpadMultiply",
|
|
69
|
+
"NumpadDivide",
|
|
70
|
+
"NumpadEnter",
|
|
71
|
+
"NumpadDecimal",
|
|
72
|
+
],
|
|
73
|
+
/**
|
|
74
|
+
* Mouse inputs on the video area. `:LeftHalf` and `:RightHalf` tell the
|
|
75
|
+
* halves of the video apart for a double click.
|
|
76
|
+
*/
|
|
77
|
+
mouse: [
|
|
78
|
+
"Mouse:LeftClick",
|
|
79
|
+
"Mouse:LeftDoubleClick",
|
|
80
|
+
"Mouse:LeftDoubleClick:LeftHalf",
|
|
81
|
+
"Mouse:LeftDoubleClick:RightHalf",
|
|
82
|
+
"Mouse:MiddleClick",
|
|
83
|
+
"Mouse:X1",
|
|
84
|
+
"Mouse:X2",
|
|
85
|
+
"Mouse:WheelUp",
|
|
86
|
+
"Mouse:WheelDown",
|
|
87
|
+
"Mouse:WheelLeft",
|
|
88
|
+
"Mouse:WheelRight",
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
const MODIFIERS = BINDING_INPUTS.modifiers;
|
|
11
92
|
const NAMED = new Set([
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"Tab",
|
|
15
|
-
"Escape",
|
|
16
|
-
"Backspace",
|
|
17
|
-
"Delete",
|
|
18
|
-
"Insert",
|
|
19
|
-
"Home",
|
|
20
|
-
"End",
|
|
21
|
-
"PageUp",
|
|
22
|
-
"PageDown",
|
|
23
|
-
"ArrowLeft",
|
|
24
|
-
"ArrowRight",
|
|
25
|
-
"ArrowUp",
|
|
26
|
-
"ArrowDown",
|
|
27
|
-
"Plus",
|
|
28
|
-
// Numeric keypad: never matched by the main keyboard keys, nor they by these.
|
|
29
|
-
..."0123456789".split("").map((digit) => `Numpad${digit}`),
|
|
30
|
-
"NumpadPlus",
|
|
31
|
-
"NumpadMinus",
|
|
32
|
-
"NumpadMultiply",
|
|
33
|
-
"NumpadDivide",
|
|
34
|
-
"NumpadEnter",
|
|
35
|
-
"NumpadDecimal",
|
|
36
|
-
]);
|
|
37
|
-
const MOUSE = new Set([
|
|
38
|
-
"LeftClick",
|
|
39
|
-
"LeftDoubleClick",
|
|
40
|
-
"LeftDoubleClick:LeftHalf",
|
|
41
|
-
"LeftDoubleClick:RightHalf",
|
|
42
|
-
"MiddleClick",
|
|
43
|
-
"X1",
|
|
44
|
-
"X2",
|
|
45
|
-
"WheelUp",
|
|
46
|
-
"WheelDown",
|
|
47
|
-
"WheelLeft",
|
|
48
|
-
"WheelRight",
|
|
93
|
+
...BINDING_INPUTS.functionKeys,
|
|
94
|
+
...BINDING_INPUTS.named,
|
|
49
95
|
]);
|
|
96
|
+
const MOUSE = new Set(BINDING_INPUTS.mouse);
|
|
97
|
+
const LETTERS = new Set(BINDING_INPUTS.letters);
|
|
98
|
+
const DIGITS = new Set(BINDING_INPUTS.digits);
|
|
50
99
|
function kind(input) {
|
|
51
100
|
if (input.startsWith("Mouse:"))
|
|
52
|
-
return MOUSE.has(input
|
|
53
|
-
if (
|
|
101
|
+
return MOUSE.has(input) ? "mouse" : null;
|
|
102
|
+
if (LETTERS.has(input))
|
|
54
103
|
return "letter";
|
|
55
|
-
if (
|
|
104
|
+
if (DIGITS.has(input))
|
|
56
105
|
return "digit";
|
|
57
|
-
if (
|
|
106
|
+
if (NAMED.has(input))
|
|
58
107
|
return "named";
|
|
59
108
|
// One printable, non-alphanumeric, non-space character; "+" is written Plus.
|
|
60
109
|
if ([...input].length === 1 && /^[^\p{L}\p{N}\s+]$/u.test(input))
|
package/dist/plugin/osd.d.ts
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
import type { ScopeImpl } from "./scope.ts";
|
|
2
2
|
import type { Osd } from "./types.ts";
|
|
3
3
|
import type { TemplateArgsJson, TextValueJson } from "../wire.ts";
|
|
4
|
+
/**
|
|
5
|
+
* The player's limits on the on-screen display ({@link Osd}), counted on
|
|
6
|
+
* the text as shown: after the player translated it and filled in its
|
|
7
|
+
* arguments. A text over them fails with code `invalid`: `osd.show`
|
|
8
|
+
* rejects, `OsdPanel.set` reports to `Scope.onError`. The player's tests
|
|
9
|
+
* check that these agree with the player.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { OSD_LIMITS } from "@vestra-player/sdk/testing";
|
|
14
|
+
*
|
|
15
|
+
* const lines = rows.slice(0, OSD_LIMITS.panelLines);
|
|
16
|
+
* panel.set(lines.join("\n"), null, { maxColumns: 60 });
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @since 1.5.14
|
|
20
|
+
*/
|
|
21
|
+
export declare const OSD_LIMITS: Readonly<{
|
|
22
|
+
/**
|
|
23
|
+
* Characters of one `osd.show` message (200; at least 1 that is not
|
|
24
|
+
* white space). Line breaks are shown as spaces.
|
|
25
|
+
*/
|
|
26
|
+
readonly messageChars: 200;
|
|
27
|
+
/** Lines of the information block (`OsdPanel.set`) (32). */
|
|
28
|
+
readonly panelLines: 32;
|
|
29
|
+
/** UTF-8 bytes of the information block (4096). */
|
|
30
|
+
readonly panelBytes: 4096;
|
|
31
|
+
/** The smallest `OsdPanelOptions.maxColumns` (8). */
|
|
32
|
+
readonly minColumns: 8;
|
|
33
|
+
/** The largest `OsdPanelOptions.maxColumns` (1000). */
|
|
34
|
+
readonly maxColumns: 1000;
|
|
35
|
+
}>;
|
|
4
36
|
/**
|
|
5
37
|
* Copy and check template arguments: an array or object of at most 16
|
|
6
38
|
* strings, finite numbers, texts and lists (`TEXT_LIMITS`).
|
package/dist/plugin/osd.js
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
import { Latest } from "./runtime.js";
|
|
3
3
|
import { failure } from "./runtime.js";
|
|
4
4
|
import { isTextValue, textProblem } from "../i18n.js";
|
|
5
|
+
/**
|
|
6
|
+
* The player's limits on the on-screen display ({@link Osd}), counted on
|
|
7
|
+
* the text as shown: after the player translated it and filled in its
|
|
8
|
+
* arguments. A text over them fails with code `invalid`: `osd.show`
|
|
9
|
+
* rejects, `OsdPanel.set` reports to `Scope.onError`. The player's tests
|
|
10
|
+
* check that these agree with the player.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { OSD_LIMITS } from "@vestra-player/sdk/testing";
|
|
15
|
+
*
|
|
16
|
+
* const lines = rows.slice(0, OSD_LIMITS.panelLines);
|
|
17
|
+
* panel.set(lines.join("\n"), null, { maxColumns: 60 });
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @since 1.5.14
|
|
21
|
+
*/
|
|
22
|
+
export const OSD_LIMITS = Object.freeze({
|
|
23
|
+
/**
|
|
24
|
+
* Characters of one `osd.show` message (200; at least 1 that is not
|
|
25
|
+
* white space). Line breaks are shown as spaces.
|
|
26
|
+
*/
|
|
27
|
+
messageChars: 200,
|
|
28
|
+
/** Lines of the information block (`OsdPanel.set`) (32). */
|
|
29
|
+
panelLines: 32,
|
|
30
|
+
/** UTF-8 bytes of the information block (4096). */
|
|
31
|
+
panelBytes: 4096,
|
|
32
|
+
/** The smallest `OsdPanelOptions.maxColumns` (8). */
|
|
33
|
+
minColumns: 8,
|
|
34
|
+
/** The largest `OsdPanelOptions.maxColumns` (1000). */
|
|
35
|
+
maxColumns: 1000,
|
|
36
|
+
});
|
|
5
37
|
/**
|
|
6
38
|
* Copy and check template arguments: an array or object of at most 16
|
|
7
39
|
* strings, finite numbers, texts and lists (`TEXT_LIMITS`).
|
|
@@ -54,8 +86,10 @@ class PanelHandle {
|
|
|
54
86
|
try {
|
|
55
87
|
const max = options?.maxColumns;
|
|
56
88
|
if (max !== undefined &&
|
|
57
|
-
!(Number.isInteger(max) &&
|
|
58
|
-
|
|
89
|
+
!(Number.isInteger(max) &&
|
|
90
|
+
max >= OSD_LIMITS.minColumns &&
|
|
91
|
+
max <= OSD_LIMITS.maxColumns))
|
|
92
|
+
throw failure("invalid", `maxColumns is an integer from ${OSD_LIMITS.minColumns} to ${OSD_LIMITS.maxColumns}`);
|
|
59
93
|
value = {
|
|
60
94
|
text: text === null ? null : shownText(text, args),
|
|
61
95
|
...(text === null || args === undefined || args === null
|
|
@@ -6,4 +6,29 @@ export declare const power: (scope: ScopeImpl) => Power;
|
|
|
6
6
|
export declare function tools(scope: ScopeImpl): Tools;
|
|
7
7
|
export declare const storage: (scope: ScopeImpl) => Storage;
|
|
8
8
|
export declare function system(scope: ScopeImpl): System;
|
|
9
|
+
/**
|
|
10
|
+
* The player's limits on {@link Secrets}. A name or value outside them
|
|
11
|
+
* fails with code `invalid`; a new name beyond `names` fails with `limit`.
|
|
12
|
+
* The player's tests check that these agree with the player.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { SECRET_LIMITS } from "@vestra-player/sdk/testing";
|
|
17
|
+
*
|
|
18
|
+
* assert.ok(Buffer.byteLength(token) <= SECRET_LIMITS.valueBytes);
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @since 1.5.14
|
|
22
|
+
*/
|
|
23
|
+
export declare const SECRET_LIMITS: Readonly<{
|
|
24
|
+
/**
|
|
25
|
+
* Characters of a name (64, at least 1), each one of `a-z`, `0-9`, `.`,
|
|
26
|
+
* `_` and `-`.
|
|
27
|
+
*/
|
|
28
|
+
readonly nameChars: 64;
|
|
29
|
+
/** UTF-8 bytes of a value (1024, at least 1; no NUL character). */
|
|
30
|
+
readonly valueBytes: 1024;
|
|
31
|
+
/** Different names one plugin may have stored at the same time (256). */
|
|
32
|
+
readonly names: 256;
|
|
33
|
+
}>;
|
|
9
34
|
export declare const secrets: (scope: ScopeImpl) => Secrets;
|
package/dist/plugin/services.js
CHANGED
|
@@ -292,13 +292,38 @@ export function system(scope) {
|
|
|
292
292
|
},
|
|
293
293
|
};
|
|
294
294
|
}
|
|
295
|
-
|
|
295
|
+
/**
|
|
296
|
+
* The player's limits on {@link Secrets}. A name or value outside them
|
|
297
|
+
* fails with code `invalid`; a new name beyond `names` fails with `limit`.
|
|
298
|
+
* The player's tests check that these agree with the player.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```ts
|
|
302
|
+
* import { SECRET_LIMITS } from "@vestra-player/sdk/testing";
|
|
303
|
+
*
|
|
304
|
+
* assert.ok(Buffer.byteLength(token) <= SECRET_LIMITS.valueBytes);
|
|
305
|
+
* ```
|
|
306
|
+
*
|
|
307
|
+
* @since 1.5.14
|
|
308
|
+
*/
|
|
309
|
+
export const SECRET_LIMITS = Object.freeze({
|
|
310
|
+
/**
|
|
311
|
+
* Characters of a name (64, at least 1), each one of `a-z`, `0-9`, `.`,
|
|
312
|
+
* `_` and `-`.
|
|
313
|
+
*/
|
|
314
|
+
nameChars: 64,
|
|
315
|
+
/** UTF-8 bytes of a value (1024, at least 1; no NUL character). */
|
|
316
|
+
valueBytes: 1024,
|
|
317
|
+
/** Different names one plugin may have stored at the same time (256). */
|
|
318
|
+
names: 256,
|
|
319
|
+
});
|
|
320
|
+
const SECRET = new RegExp(`^[a-z0-9._-]{1,${SECRET_LIMITS.nameChars}}$`);
|
|
296
321
|
export const secrets = (scope) => {
|
|
297
322
|
const named = (name) => {
|
|
298
323
|
if (!scope.runtime.can("secrets"))
|
|
299
324
|
throw failure("capability", "secrets must be declared in manifest");
|
|
300
325
|
if (typeof name !== "string" || !SECRET.test(name))
|
|
301
|
-
throw failure("invalid",
|
|
326
|
+
throw failure("invalid", `secret names are 1–${SECRET_LIMITS.nameChars} of a-z, 0-9, ., _ and -`);
|
|
302
327
|
return name;
|
|
303
328
|
};
|
|
304
329
|
return {
|
|
@@ -307,8 +332,8 @@ export const secrets = (scope) => {
|
|
|
307
332
|
if (typeof value !== "string" ||
|
|
308
333
|
!value ||
|
|
309
334
|
value.includes("\0") ||
|
|
310
|
-
Buffer.byteLength(value) >
|
|
311
|
-
throw failure("invalid",
|
|
335
|
+
Buffer.byteLength(value) > SECRET_LIMITS.valueBytes)
|
|
336
|
+
throw failure("invalid", `secret values are 1–${SECRET_LIMITS.valueBytes} bytes of text; delete() removes one`);
|
|
312
337
|
await scope.runtime.host.secrets.set(named(name), value);
|
|
313
338
|
},
|
|
314
339
|
delete: async (name) => scope.runtime.host.secrets.delete(named(name)),
|
package/dist/plugin/testing.d.ts
CHANGED
|
@@ -2,7 +2,14 @@ import type { Server } from "node:net";
|
|
|
2
2
|
import { HostError } from "../wire.ts";
|
|
3
3
|
import type { PackageManifest, PackageSource } from "./types.ts";
|
|
4
4
|
import type { Json, LayoutSlot, LocalServices, PlaybackStats, Transport, WireSystemInfo } from "../wire.ts";
|
|
5
|
-
import { COMMAND_LIMITS, MENU_IDS } from "../commands.ts";
|
|
5
|
+
import { BUILTIN_COMMANDS, BUILTIN_SUBMENUS, COMMAND_LIMITS, MENU_IDS } from "../commands.ts";
|
|
6
|
+
import type { BuiltinCommand, BuiltinSubmenu } from "../commands.ts";
|
|
7
|
+
import { CAPABILITY_INFO, MESSAGE_LIMIT } from "../capabilities.ts";
|
|
8
|
+
import type { CapabilityInfo } from "../capabilities.ts";
|
|
9
|
+
import { OSD_LIMITS } from "./osd.ts";
|
|
10
|
+
import { SECRET_LIMITS } from "./services.ts";
|
|
11
|
+
import { BINDING_INPUTS } from "./bindings.ts";
|
|
12
|
+
import { THEME_PALETTES } from "../palettes.ts";
|
|
6
13
|
import { UI_VALUE_LIMITS } from "./values.ts";
|
|
7
14
|
import type { CommandDeclaration } from "../commands.ts";
|
|
8
15
|
import type { SourceState } from "../source.ts";
|
|
@@ -15,6 +22,22 @@ import { LOCALES, LIST_SEPARATORS, TEXT_LIMITS } from "../i18n.ts";
|
|
|
15
22
|
export { HostError };
|
|
16
23
|
/** The player's command caps, which the test host enforces as well. */
|
|
17
24
|
export { COMMAND_LIMITS, MENU_IDS };
|
|
25
|
+
/** The submenus of the player's built-in plugins, by key path. */
|
|
26
|
+
export { BUILTIN_COMMANDS, BUILTIN_SUBMENUS };
|
|
27
|
+
export type { BuiltinCommand, BuiltinSubmenu };
|
|
28
|
+
/** The manifest capabilities: what each opens and how the store shows it. */
|
|
29
|
+
export { CAPABILITY_INFO };
|
|
30
|
+
export type { CapabilityInfo };
|
|
31
|
+
/** The longest message a plugin may send the player. */
|
|
32
|
+
export { MESSAGE_LIMIT };
|
|
33
|
+
/** The player's limits on the on-screen display. */
|
|
34
|
+
export { OSD_LIMITS };
|
|
35
|
+
/** The player's limits on stored secrets. */
|
|
36
|
+
export { SECRET_LIMITS };
|
|
37
|
+
/** The names bindings are made of. */
|
|
38
|
+
export { BINDING_INPUTS };
|
|
39
|
+
/** The colours of the player's palettes, by `Theme` token. */
|
|
40
|
+
export { THEME_PALETTES };
|
|
18
41
|
/** The player's property value limits, which the test host enforces as well. */
|
|
19
42
|
export { UI_VALUE_LIMITS };
|
|
20
43
|
/**
|
package/dist/plugin/testing.js
CHANGED
|
@@ -65,7 +65,12 @@ import { tmpdir } from "node:os";
|
|
|
65
65
|
import { extname, isAbsolute, join } from "node:path";
|
|
66
66
|
import { fileURLToPath } from "node:url";
|
|
67
67
|
import { HostError } from "../wire.js";
|
|
68
|
-
import { COMMAND_LIMITS, MENU_IDS } from "../commands.js";
|
|
68
|
+
import { BUILTIN_COMMANDS, BUILTIN_SUBMENUS, COMMAND_LIMITS, MENU_IDS, } from "../commands.js";
|
|
69
|
+
import { CAPABILITY_INFO, MESSAGE_LIMIT } from "../capabilities.js";
|
|
70
|
+
import { OSD_LIMITS } from "./osd.js";
|
|
71
|
+
import { SECRET_LIMITS } from "./services.js";
|
|
72
|
+
import { BINDING_INPUTS } from "./bindings.js";
|
|
73
|
+
import { THEME_PALETTES } from "../palettes.js";
|
|
69
74
|
import { UI_VALUE_LIMITS, valueLimitProblem } from "./values.js";
|
|
70
75
|
import { nodeProblem } from "../processing.js";
|
|
71
76
|
import { RENDER_DEFAULTS, renderProblem, VIEW_DEFAULTS, viewProblem, } from "./view.js";
|
|
@@ -76,6 +81,20 @@ import { Catalog, format as formatTemplate, HOST_GROUPS, HOST_ZH_HANS, LOCALES,
|
|
|
76
81
|
export { HostError };
|
|
77
82
|
/** The player's command caps, which the test host enforces as well. */
|
|
78
83
|
export { COMMAND_LIMITS, MENU_IDS };
|
|
84
|
+
/** The submenus of the player's built-in plugins, by key path. */
|
|
85
|
+
export { BUILTIN_COMMANDS, BUILTIN_SUBMENUS };
|
|
86
|
+
/** The manifest capabilities: what each opens and how the store shows it. */
|
|
87
|
+
export { CAPABILITY_INFO };
|
|
88
|
+
/** The longest message a plugin may send the player. */
|
|
89
|
+
export { MESSAGE_LIMIT };
|
|
90
|
+
/** The player's limits on the on-screen display. */
|
|
91
|
+
export { OSD_LIMITS };
|
|
92
|
+
/** The player's limits on stored secrets. */
|
|
93
|
+
export { SECRET_LIMITS };
|
|
94
|
+
/** The names bindings are made of. */
|
|
95
|
+
export { BINDING_INPUTS };
|
|
96
|
+
/** The colours of the player's palettes, by `Theme` token. */
|
|
97
|
+
export { THEME_PALETTES };
|
|
79
98
|
/** The player's property value limits, which the test host enforces as well. */
|
|
80
99
|
export { UI_VALUE_LIMITS };
|
|
81
100
|
/**
|
|
@@ -2545,8 +2564,8 @@ export class TestHost {
|
|
|
2545
2564
|
},
|
|
2546
2565
|
"osd.show": (p) => {
|
|
2547
2566
|
const text = this.#shown(p.text, p.args, "text").replace(/[\n\r]/g, " ");
|
|
2548
|
-
if (!text.trim() || [...text].length >
|
|
2549
|
-
throw new HostError("invalid",
|
|
2567
|
+
if (!text.trim() || [...text].length > OSD_LIMITS.messageChars)
|
|
2568
|
+
throw new HostError("invalid", `osd text needs 1..${OSD_LIMITS.messageChars} characters`);
|
|
2550
2569
|
this.osd.push(text);
|
|
2551
2570
|
},
|
|
2552
2571
|
"osd.panel": (p) => {
|
|
@@ -2554,10 +2573,20 @@ export class TestHost {
|
|
|
2554
2573
|
return void (this.osdPanel = null);
|
|
2555
2574
|
const max = p.maxColumns;
|
|
2556
2575
|
if (max !== undefined &&
|
|
2557
|
-
!(Number.isInteger(max) &&
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2576
|
+
!(Number.isInteger(max) &&
|
|
2577
|
+
Number(max) >= OSD_LIMITS.minColumns &&
|
|
2578
|
+
Number(max) <= OSD_LIMITS.maxColumns))
|
|
2579
|
+
throw new HostError("invalid", `maxColumns is an integer from ${OSD_LIMITS.minColumns} to ${OSD_LIMITS.maxColumns}`);
|
|
2580
|
+
const shown = this.#shown(p.text, p.args, "text");
|
|
2581
|
+
const text = max === undefined ? shown : fitColumns(shown, Number(max));
|
|
2582
|
+
// As the player: \n separates lines, a trailing one ends the last.
|
|
2583
|
+
const lines = text.split("\n");
|
|
2584
|
+
if (text.endsWith("\n"))
|
|
2585
|
+
lines.pop();
|
|
2586
|
+
if (Buffer.byteLength(text) > OSD_LIMITS.panelBytes ||
|
|
2587
|
+
lines.length > OSD_LIMITS.panelLines)
|
|
2588
|
+
throw new HostError("invalid", `osd panel allows ${OSD_LIMITS.panelLines} lines, ${OSD_LIMITS.panelBytes} bytes`);
|
|
2589
|
+
this.osdPanel = text;
|
|
2561
2590
|
},
|
|
2562
2591
|
"playback.stats": () => this.stats,
|
|
2563
2592
|
"audio.addTrack": (p) => {
|