@vestra-player/sdk 1.6.10 → 1.6.12
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 +4 -2
- package/dist/commands.js +5 -1
- package/dist/docs-tree.json.gz +0 -0
- package/dist/i18n.js +1 -0
- package/dist/plugin/runtime.js +21 -0
- package/dist/plugin/testing.d.ts +9 -3
- package/dist/plugin/testing.js +29 -9
- package/dist/plugin/types.d.ts +91 -8
- package/dist/plugin/ui.js +18 -4
- package/dist/wire.d.ts +10 -3
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/slint/vestra/bar-buttons.slint +8 -4
- package/slint/vestra/icons.slint +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.12
|
|
4
|
+
|
|
5
|
+
**Who must change: nobody.** A behaviour fix of `toggle` in fullscreen, an example fix and a development warning; no new API, `apiVersion` stays `vestra/1`.
|
|
6
|
+
|
|
7
|
+
- **`Panel.toggle` and `PanelSlot.toggle` slide a collapsed column out.** In fullscreen a sidebar or adjust column the user wants shown is collapsed at the right edge (`reason: "fullscreen"`). Toggling it then turned it off; it now shows it, which player 0.7.6 answers by sliding the column out, as its own Adjust button and Alt+1…4 do. Toggling a column that is on screen still turns it off. With players before 0.7.6 a show of a collapsed column changes nothing on screen, as before.
|
|
8
|
+
- **[adjust-panel](docs/examples/adjust-panel.md)** declares `events`: without it `plugin.player.state` changes only after the plugin's own calls, so a panel activated before a file was opened stayed disabled at 1×. A new test opens the file after activation. Its presets are four (0.5×, 1×, 1.5×, 2×), which fit the adjust column at its least width. No other example reads `player.state` without `events`.
|
|
9
|
+
- **A development warning for state followed without `events`.** A plugin that registers an `onChange` or a `bind` whose function reads changing state (`player.state`, a panel position, and the like) without the `events` capability gets one line in its log, once per plugin: changes made elsewhere never reach it. Only a warning; behaviour is unchanged. `TestHost` already behaved like the player here (it delivers `source.state` and the other state events only to a plugin that subscribed, which needs `events`): a new test pins that down. The example's old tests passed because they opened the file before starting the plugin or did not check `enabled`. No SDK example or test relied on the opposite, so nobody must change.
|
|
10
|
+
## 1.6.11
|
|
11
|
+
|
|
12
|
+
**Who must change: nobody.** Additions only: a mount slot, a panel position, a button icon and `TestHost` support; `apiVersion` stays `vestra/1`, no new capability. A plugin that mounts `"adjust"` sets `minPlayerVersion` to `0.7.6`: older players refuse the slot (`invalid`), which fails the activation like any refused mount.
|
|
13
|
+
|
|
14
|
+
- **The adjust slot** (player 0.7.6, menu redesign step 3b): `plugin.panels.mount("adjust", file, { title })` puts a panel in the adjust column, for controls that change what is playing (picture, sound, subtitles, speed). Any number of plugins mount there, one tab each. The slot is a contract about purpose; the player decides how it looks and may change that. The rule, in [Slots](docs/explanation/ui.md#slots-what-each-one-is-for) and on `Panels.mount`: if the user still looks at the panel after closing it, it is `"sidebar"`; otherwise it is `"adjust"`. Today the player draws a second column between the video and the sidebar, with text tabs and the opening key under each title (the key of the command the panel's commands name as their `home`), no close or detach button, floating over the video in a narrow window and sliding out from the right edge in fullscreen; it starts closed and is compiled the first time it opens.
|
|
15
|
+
- **`plugin.panels.adjust`**: the position, the same shape as `panels.sidebar` without `setDetached` (`PanelSlot`: `state`, `panels`, `onChange`, `setVisible`, `toggle`, `activate`). `Panel.show` and `Panel.toggle` work on an adjust panel as on a sidebar tab. Players before 0.7.6 build the `plugin` object with their own SDK, which has no `adjust`: there `plugin.panels.adjust` is `undefined` and reading its `state` throws a `TypeError` (as for `commands.keys`), so declare `minPlayerVersion` 0.7.6 or test it first.
|
|
16
|
+
- **Wire**: `ui.mount` and `layout.set` take `slot: "adjust"`; `layout.state` reports `adjust` (a `LayoutSlot`, never `detached`); `LayoutSlot` names the adjust column.
|
|
17
|
+
- **`BAR_BUTTON_ICONS`** gains `"sliders"` (the player's Adjust button), and `BUILTIN_COMMANDS` the player's `host/adjust`. `@vestra/bar-buttons.slint` (synced): `BarButtonsHost.adjust`, counted in `fixed`; `@vestra/icons.slint`: `sliders`.
|
|
18
|
+
- **`TestHost`**: `layout.adjust` (starts hidden), `mountOther("adjust", …)`, `selectTab` for adjust tabs, `layout.set` refuses detaching the adjust column, `keepsRunning` names an open adjust column's current tab; an unknown mount slot is refused with `invalid`, as by the player.
|
|
19
|
+
- **Example** [adjust-panel](docs/examples/adjust-panel.md): a speed slider in the adjust column, opened and closed by its own key.
|
|
20
|
+
|
|
3
21
|
## 1.6.10
|
|
4
22
|
|
|
5
23
|
**Who must change: nobody.** Additions only: a clip format, optional export fields, two optional `JobState` fields, an error code and a `TestHost` field. `apiVersion` stays `vestra/1`, no new capability (`media.control` covers them). A plugin that does not use them sends exactly what it sent before; one that uses them needs player 0.7.6: declare `"minPlayerVersion": "0.7.6"`, or compare `plugin.app.version` with 0.7.6 to offer them only where they exist (do not probe by trying: on an older player `plugin` comes from that player's own, older SDK, which rejects `format: "copy"` with `invalid` but builds a re-encode request without the `encoding` it does not know, so the clip is encoded with the fixed settings and no error), and leave `progress`, `start` and `end` out.
|
package/dist/commands.d.ts
CHANGED
|
@@ -136,7 +136,9 @@ export declare const MENU_IDS: readonly ["open", "playback", "subtitles", "video
|
|
|
136
136
|
* The icons a bottom-bar button may name (`CommandOptions.button.icon`):
|
|
137
137
|
* every icon of the player's V-Line set, `@vestra/icons.slint` (draw one
|
|
138
138
|
* in your own Slint with `Icon { name: "store"; }`). The player's tests check
|
|
139
|
-
* that this list is its icon set.
|
|
139
|
+
* that this list is its icon set. `"sliders"` (three sliders, the player's
|
|
140
|
+
* Adjust button) is new in player 0.7.6 (SDK 1.6.11); older players refuse
|
|
141
|
+
* it as a button icon with `invalid`.
|
|
140
142
|
*
|
|
141
143
|
* @example Check in a test that the button's icon exists
|
|
142
144
|
* The test fails before release if the plugin's bar button names an icon
|
|
@@ -154,7 +156,7 @@ export declare const MENU_IDS: readonly ["open", "playback", "subtitles", "video
|
|
|
154
156
|
*
|
|
155
157
|
* @category Testing
|
|
156
158
|
*/
|
|
157
|
-
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"];
|
|
159
|
+
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", "sliders"];
|
|
158
160
|
/**
|
|
159
161
|
* The player's limits on bottom-bar buttons (`CommandOptions.button`), the
|
|
160
162
|
* same values as the player's (its tests check that they agree). A button
|
package/dist/commands.js
CHANGED
|
@@ -141,7 +141,9 @@ export const MENU_IDS = [
|
|
|
141
141
|
* The icons a bottom-bar button may name (`CommandOptions.button.icon`):
|
|
142
142
|
* every icon of the player's V-Line set, `@vestra/icons.slint` (draw one
|
|
143
143
|
* in your own Slint with `Icon { name: "store"; }`). The player's tests check
|
|
144
|
-
* that this list is its icon set.
|
|
144
|
+
* that this list is its icon set. `"sliders"` (three sliders, the player's
|
|
145
|
+
* Adjust button) is new in player 0.7.6 (SDK 1.6.11); older players refuse
|
|
146
|
+
* it as a button icon with `invalid`.
|
|
145
147
|
*
|
|
146
148
|
* @example Check in a test that the button's icon exists
|
|
147
149
|
* The test fails before release if the plugin's bar button names an icon
|
|
@@ -198,6 +200,7 @@ export const BAR_BUTTON_ICONS = [
|
|
|
198
200
|
"exit-fullscreen",
|
|
199
201
|
"store",
|
|
200
202
|
"ai",
|
|
203
|
+
"sliders",
|
|
201
204
|
];
|
|
202
205
|
/**
|
|
203
206
|
* The player's limits on bottom-bar buttons (`CommandOptions.button`), the
|
|
@@ -601,6 +604,7 @@ host | enter-fullscreen | (Enter Fullscreen)
|
|
|
601
604
|
host | exit-fullscreen | (Leave Fullscreen)
|
|
602
605
|
host | sidebar | (Toggle Sidebar)
|
|
603
606
|
host | palette | (Command Palette) | Ctrl+Shift+P
|
|
607
|
+
host | adjust | (Adjust)
|
|
604
608
|
vestra.info | info.brief | (Brief Info) | Shift+Tab
|
|
605
609
|
vestra.playlist | playlist.play | (Play) | Enter, P | panel
|
|
606
610
|
vestra.playlist | playlist.list-add | (Add Files…) | Ctrl+I | panel
|
package/dist/docs-tree.json.gz
CHANGED
|
Binary file
|
package/dist/i18n.js
CHANGED
package/dist/plugin/runtime.js
CHANGED
|
@@ -208,6 +208,8 @@ export class Runtime {
|
|
|
208
208
|
/** The plugin was disposed: no safety pass is armed any more. */
|
|
209
209
|
#stopped = false;
|
|
210
210
|
#events = new Map();
|
|
211
|
+
/** The missing `events` warning was given (once per plugin). */
|
|
212
|
+
#warnedEvents = false;
|
|
211
213
|
views = new Map();
|
|
212
214
|
commands = new Map();
|
|
213
215
|
services = new Map();
|
|
@@ -617,6 +619,10 @@ export class Runtime {
|
|
|
617
619
|
group.active++;
|
|
618
620
|
group.whys.set(token, listenerWhy(this.callSite()));
|
|
619
621
|
}
|
|
622
|
+
if (created && needs === "events" && !this.can(needs))
|
|
623
|
+
group.unheard = true;
|
|
624
|
+
if (!mirror && group.unheard)
|
|
625
|
+
this.#warnUnheard(event);
|
|
620
626
|
if (created && (needs === null || this.can(needs))) {
|
|
621
627
|
group.told = group.active > 0;
|
|
622
628
|
const whys = group.told ? allWhys(group) : undefined;
|
|
@@ -658,6 +664,8 @@ export class Runtime {
|
|
|
658
664
|
const group = this.#events.get(event);
|
|
659
665
|
if (!group)
|
|
660
666
|
return { dispose: async () => { } };
|
|
667
|
+
if (group.unheard)
|
|
668
|
+
this.#warnUnheard(event);
|
|
661
669
|
group.active++;
|
|
662
670
|
const token = {};
|
|
663
671
|
group.whys.set(token, why ?? listenerWhy(this.callSite()));
|
|
@@ -675,6 +683,19 @@ export class Runtime {
|
|
|
675
683
|
},
|
|
676
684
|
};
|
|
677
685
|
}
|
|
686
|
+
/**
|
|
687
|
+
* A development warning, once per plugin, no behaviour change (SDK
|
|
688
|
+
* 1.6.12): the plugin follows `event` (an `onChange`, a `bind` that reads
|
|
689
|
+
* the state) without the `events` capability, so changes made elsewhere
|
|
690
|
+
* (the user, the player, another plugin) never reach it; the state moves
|
|
691
|
+
* only after its own calls.
|
|
692
|
+
*/
|
|
693
|
+
#warnUnheard(event) {
|
|
694
|
+
if (this.#warnedEvents)
|
|
695
|
+
return;
|
|
696
|
+
this.#warnedEvents = true;
|
|
697
|
+
console.warn(`[${this.manifest.id}] follows ${event} (onChange, or a bind() that reads the state) without the "events" capability: changes made elsewhere will not reach it, only its own calls refresh the state. Declare "events" in manifest.json.`);
|
|
698
|
+
}
|
|
678
699
|
/**
|
|
679
700
|
* Tell the player when `group` changes between mirror and listener, and
|
|
680
701
|
* why it listens when that changed.
|
package/dist/plugin/testing.d.ts
CHANGED
|
@@ -601,7 +601,7 @@ export declare class TestHost implements Transport {
|
|
|
601
601
|
* See {@link TestHost.mounted}.
|
|
602
602
|
*/
|
|
603
603
|
panel: (Omit<FakeWindow, "callbacks" | "userClose"> & {
|
|
604
|
-
/** `"bottom"`, `"sidebar"` or `"backdrop"`. */
|
|
604
|
+
/** `"bottom"`, `"sidebar"`, `"adjust"` or `"backdrop"`. */
|
|
605
605
|
slot: string;
|
|
606
606
|
/** The exported component the SDK found in the file. */
|
|
607
607
|
component: string;
|
|
@@ -643,6 +643,12 @@ export declare class TestHost implements Transport {
|
|
|
643
643
|
* the current tab's (the plugin under test is owner 1).
|
|
644
644
|
*/
|
|
645
645
|
sidebar: LayoutSlot | null;
|
|
646
|
+
/**
|
|
647
|
+
* The adjust column (SDK 1.6.11, player 0.7.6), like the sidebar but
|
|
648
|
+
* never `detached`. It starts hidden (`preferred: false`, `reason:
|
|
649
|
+
* "userHidden"`), as the player's does until the user opens it.
|
|
650
|
+
*/
|
|
651
|
+
adjust: LayoutSlot | null;
|
|
646
652
|
};
|
|
647
653
|
/**
|
|
648
654
|
* Every `osd.show` text as the player shows it (translated with the
|
|
@@ -1331,7 +1337,7 @@ export declare class TestHost implements Transport {
|
|
|
1331
1337
|
*
|
|
1332
1338
|
* @capability None.
|
|
1333
1339
|
*/
|
|
1334
|
-
mountOther(slot: "bottom" | "sidebar" | "backdrop", plugin: string, options?: {
|
|
1340
|
+
mountOther(slot: "bottom" | "sidebar" | "adjust" | "backdrop", plugin: string, options?: {
|
|
1335
1341
|
title?: string;
|
|
1336
1342
|
}): void;
|
|
1337
1343
|
/**
|
|
@@ -2829,7 +2835,7 @@ export declare class TestHost implements Transport {
|
|
|
2829
2835
|
* @since 1.0.0
|
|
2830
2836
|
*/
|
|
2831
2837
|
mounted(): Omit<FakeWindow, "callbacks" | "userClose"> & {
|
|
2832
|
-
/** `"bottom"`, `"sidebar"` or `"backdrop"`. */
|
|
2838
|
+
/** `"bottom"`, `"sidebar"`, `"adjust"` or `"backdrop"`. */
|
|
2833
2839
|
slot: string;
|
|
2834
2840
|
/** The exported component the SDK found in the file. */
|
|
2835
2841
|
component: string;
|
package/dist/plugin/testing.js
CHANGED
|
@@ -328,6 +328,7 @@ const SLOT_LABELS = {
|
|
|
328
328
|
bottom: "bottom bar",
|
|
329
329
|
sidebar: "sidebar",
|
|
330
330
|
backdrop: "video backdrop",
|
|
331
|
+
adjust: "adjust column",
|
|
331
332
|
};
|
|
332
333
|
/** Texts only the test host uses, with their Chinese of SDK 1.5.8. */
|
|
333
334
|
const TEST_ZH_HANS = {
|
|
@@ -550,6 +551,7 @@ export class TestHost {
|
|
|
550
551
|
layout = {
|
|
551
552
|
bottom: null,
|
|
552
553
|
sidebar: null,
|
|
554
|
+
adjust: null,
|
|
553
555
|
};
|
|
554
556
|
/**
|
|
555
557
|
* Every `osd.show` text as the player shows it (translated with the
|
|
@@ -1528,10 +1530,10 @@ export class TestHost {
|
|
|
1528
1530
|
* ```
|
|
1529
1531
|
*/
|
|
1530
1532
|
selectTab(plugin) {
|
|
1531
|
-
const slot = this.layout.sidebar;
|
|
1533
|
+
const slot = [this.layout.sidebar, this.layout.adjust].find((each) => each?.panels?.some((entry) => entry.plugin === plugin));
|
|
1532
1534
|
const panel = slot?.panels?.find((entry) => entry.plugin === plugin);
|
|
1533
1535
|
if (!slot || !panel)
|
|
1534
|
-
throw new Error(`${plugin} has no
|
|
1536
|
+
throw new Error(`${plugin} has no tab`);
|
|
1535
1537
|
slot.owner = panel.owner;
|
|
1536
1538
|
this.emit("window.state", this.#windowFacts());
|
|
1537
1539
|
}
|
|
@@ -1539,15 +1541,18 @@ export class TestHost {
|
|
|
1539
1541
|
const state = this.layout[slot];
|
|
1540
1542
|
if (state)
|
|
1541
1543
|
state.panels = [...(state.panels ?? []), panel];
|
|
1542
|
-
else
|
|
1544
|
+
else {
|
|
1545
|
+
// The adjust column starts hidden, as the player's.
|
|
1546
|
+
const shown = slot !== "adjust";
|
|
1543
1547
|
this.layout[slot] = {
|
|
1544
1548
|
owner: panel.owner,
|
|
1545
|
-
preferred:
|
|
1546
|
-
effective:
|
|
1547
|
-
reason: null,
|
|
1549
|
+
preferred: shown,
|
|
1550
|
+
effective: shown,
|
|
1551
|
+
reason: shown ? null : "userHidden",
|
|
1548
1552
|
detached: false,
|
|
1549
1553
|
panels: [panel],
|
|
1550
1554
|
};
|
|
1555
|
+
}
|
|
1551
1556
|
}
|
|
1552
1557
|
#removePanel(slot, owner) {
|
|
1553
1558
|
const state = this.layout[slot];
|
|
@@ -4247,11 +4252,16 @@ export class TestHost {
|
|
|
4247
4252
|
for (const why of this.listeningWhy.get(event) ?? ["a listener"])
|
|
4248
4253
|
reasons.push(`listens to ${event}: ${why}`);
|
|
4249
4254
|
// A sidebar panel is never live (player 0.7.3): the current tab keeps
|
|
4250
|
-
// the plugin running as busy work, a background tab not at all.
|
|
4255
|
+
// the plugin running as busy work, a background tab not at all. The
|
|
4256
|
+
// adjust column's current tab only while the column is open (0.7.6).
|
|
4251
4257
|
if (this.panel?.slot === "sidebar") {
|
|
4252
4258
|
if (this.layout.sidebar?.owner === 1)
|
|
4253
4259
|
reasons.push("its sidebar tab is the current tab");
|
|
4254
4260
|
}
|
|
4261
|
+
else if (this.panel?.slot === "adjust") {
|
|
4262
|
+
if (this.layout.adjust?.owner === 1 && this.layout.adjust.preferred)
|
|
4263
|
+
reasons.push("its adjust tab is the current tab");
|
|
4264
|
+
}
|
|
4255
4265
|
else if (this.panel)
|
|
4256
4266
|
reasons.push("a mounted panel");
|
|
4257
4267
|
for (const [id, window] of this.windows)
|
|
@@ -4487,8 +4497,11 @@ export class TestHost {
|
|
|
4487
4497
|
"ui.mount": (p) => {
|
|
4488
4498
|
if (this.panel)
|
|
4489
4499
|
throw new HostError("conflict", "owner already mounted");
|
|
4500
|
+
if (!(String(p.slot) in SLOT_LABELS))
|
|
4501
|
+
throw new HostError("invalid", "unknown mount slot");
|
|
4490
4502
|
// Like the player: one panel in the bottom bar and the backdrop, any
|
|
4491
|
-
// number of tabs in the sidebar (player 0.7.3; 0.4.x to 0.7.2 took 8)
|
|
4503
|
+
// number of tabs in the sidebar (player 0.7.3; 0.4.x to 0.7.2 took 8)
|
|
4504
|
+
// and in the adjust column (player 0.7.6).
|
|
4492
4505
|
const holder = p.slot === "backdrop"
|
|
4493
4506
|
? this.backdrop?.owner
|
|
4494
4507
|
: p.slot === "bottom"
|
|
@@ -4542,7 +4555,10 @@ export class TestHost {
|
|
|
4542
4555
|
"ui.menu": (p) => {
|
|
4543
4556
|
if (!this.panel)
|
|
4544
4557
|
throw new HostError("unavailable", "panel not shown");
|
|
4545
|
-
|
|
4558
|
+
const tabs = this.panel.slot === "sidebar" || this.panel.slot === "adjust"
|
|
4559
|
+
? this.layout[this.panel.slot]
|
|
4560
|
+
: null;
|
|
4561
|
+
if (tabs && tabs.owner !== 1)
|
|
4546
4562
|
throw new HostError("unavailable", "the panel's tab is not the current one");
|
|
4547
4563
|
const items = p.items;
|
|
4548
4564
|
for (const id of items)
|
|
@@ -4559,11 +4575,15 @@ export class TestHost {
|
|
|
4559
4575
|
}),
|
|
4560
4576
|
"layout.set": (p) => {
|
|
4561
4577
|
const slot = String(p.slot);
|
|
4578
|
+
if (!["bottom", "sidebar", "adjust"].includes(slot))
|
|
4579
|
+
throw new HostError("invalid", "unknown slot");
|
|
4562
4580
|
const state = this.layout[slot];
|
|
4563
4581
|
if (!state)
|
|
4564
4582
|
throw new HostError("missing", "no panel at this position");
|
|
4565
4583
|
if (p.detached !== undefined && slot === "bottom")
|
|
4566
4584
|
throw new HostError("invalid", "the bottom bar cannot be detached");
|
|
4585
|
+
if (p.detached === true && slot === "adjust")
|
|
4586
|
+
throw new HostError("invalid", "the adjust column cannot be detached");
|
|
4567
4587
|
if (p.active !== undefined && p.active !== null) {
|
|
4568
4588
|
const panel = state.panels?.find((entry) => entry.plugin === p.active);
|
|
4569
4589
|
if (!panel)
|
package/dist/plugin/types.d.ts
CHANGED
|
@@ -2578,12 +2578,13 @@ export interface PanelState {
|
|
|
2578
2578
|
* else the position's reason ({@link SlotState.reason}).
|
|
2579
2579
|
*/
|
|
2580
2580
|
reason: SlotState["reason"] | "otherTab";
|
|
2581
|
-
/** The sidebar floats in its own window (with all its tabs). */
|
|
2581
|
+
/** The sidebar floats in its own window (with all its tabs); always `false` in the adjust column. */
|
|
2582
2582
|
detached: boolean;
|
|
2583
2583
|
}
|
|
2584
2584
|
/**
|
|
2585
|
-
* The bottom bar or the
|
|
2586
|
-
* `plugin.panels.sidebar`),
|
|
2585
|
+
* The bottom bar, the sidebar or the adjust column position
|
|
2586
|
+
* (`plugin.panels.bottom`, `plugin.panels.sidebar`, `plugin.panels.adjust`),
|
|
2587
|
+
* independent of who mounted the panel there.
|
|
2587
2588
|
*
|
|
2588
2589
|
* @remarks
|
|
2589
2590
|
* The position does not belong to the plugin that mounted the panel: any
|
|
@@ -2704,7 +2705,9 @@ export interface PanelSlot {
|
|
|
2704
2705
|
*
|
|
2705
2706
|
* @remarks
|
|
2706
2707
|
* Reads the state from the player first, so it is right even without
|
|
2707
|
-
* `events`.
|
|
2708
|
+
* `events`. A sidebar or adjust column wanted shown but collapsed at the
|
|
2709
|
+
* right edge of fullscreen (`reason: "fullscreen"`) slides out instead of
|
|
2710
|
+
* being turned off (SDK 1.6.12; player 0.7.6 pops it out).
|
|
2708
2711
|
*
|
|
2709
2712
|
* @capability ui.mount
|
|
2710
2713
|
*
|
|
@@ -3009,7 +3012,10 @@ export interface Panel extends View, Disposable {
|
|
|
3009
3012
|
* Reads the state from the player first. When the position is shown
|
|
3010
3013
|
* (`preferred`) and this panel's tab is the current one, it hides the
|
|
3011
3014
|
* position; otherwise it does {@link Panel.show}: a panel in a background
|
|
3012
|
-
* tab comes to the front instead of the sidebar closing
|
|
3015
|
+
* tab comes to the front instead of the sidebar closing, and a column
|
|
3016
|
+
* collapsed at the right edge of fullscreen (`reason: "fullscreen"`) slides
|
|
3017
|
+
* out instead of being turned off (SDK 1.6.12; player 0.7.6 pops it out),
|
|
3018
|
+
* like the player's own Alt+1…4 and Adjust button. A "Show list"
|
|
3013
3019
|
* command checked by `state.preferred && state.active` matches it. Waits
|
|
3014
3020
|
* for {@link Panel.ready} first.
|
|
3015
3021
|
*
|
|
@@ -4140,6 +4146,38 @@ export interface Panels {
|
|
|
4140
4146
|
* takes no pointer or keyboard input. While it is mounted, the player does
|
|
4141
4147
|
* not show the cover art of audio files itself (from the next file
|
|
4142
4148
|
* opened on), so the plugin can draw it.
|
|
4149
|
+
* - `"adjust"` (player 0.7.6, SDK 1.6.11): **controls that change what is
|
|
4150
|
+
* playing**: the picture, the sound, the subtitles, the speed (the
|
|
4151
|
+
* player's own Picture, Audio, Subtitles and Playback panels, a colour
|
|
4152
|
+
* grading tool). Several plugins may mount here, one tab each, labelled
|
|
4153
|
+
* `title` (else the plugin's name). Declare `minPlayerVersion: "0.7.6"`:
|
|
4154
|
+
* older players refuse the slot (`invalid`, "unknown mount slot"), and a
|
|
4155
|
+
* mount refused during `activate` fails the activation.
|
|
4156
|
+
*
|
|
4157
|
+
* **Choosing between `"sidebar"` and `"adjust"`.** The slot says what the
|
|
4158
|
+
* panel is for; the player decides how it looks and may change that at any
|
|
4159
|
+
* time. Ask: after the user closes the panel, do they still look at what it
|
|
4160
|
+
* showed? If yes (a list, chapters, search results, another video), it is
|
|
4161
|
+
* content: `"sidebar"`. If it only changes what is playing, it is
|
|
4162
|
+
* `"adjust"`.
|
|
4163
|
+
*
|
|
4164
|
+
* **What the player shows today (may change; do not depend on it).** The
|
|
4165
|
+
* adjust column is a second column between the video and the sidebar
|
|
4166
|
+
* (`[video][adjust][sidebar]`; both can be open, the sidebar always
|
|
4167
|
+
* rightmost), 317 wide by default, 280 to half the window, with text tabs
|
|
4168
|
+
* and the key that opens each tab under its title (the key of the command
|
|
4169
|
+
* your other commands name as their {@link CommandOptions.home | home}). It
|
|
4170
|
+
* has no close or detach button: the bottom bar's Adjust button, your
|
|
4171
|
+
* panel's own command ({@link Panel.toggle}) and the menus open and close
|
|
4172
|
+
* it. It starts closed. In a window too narrow to dock it beside a video at
|
|
4173
|
+
* least 480 wide, it floats over the right of the video, 300 wide; in
|
|
4174
|
+
* fullscreen it slides out from the right edge, left of the sidebar. Its
|
|
4175
|
+
* width, open state, current tab and tab order are remembered across
|
|
4176
|
+
* launches. Its strip is always drawn, so panel coordinates (drops) start
|
|
4177
|
+
* below it, and the bridge global's `tabbed` property, if declared, is
|
|
4178
|
+
* `true`. As in the sidebar, only the current tab's plugin is kept
|
|
4179
|
+
* running while the column is open, and the player compiles a panel the
|
|
4180
|
+
* first time it is shown, not at start-up.
|
|
4143
4181
|
*
|
|
4144
4182
|
* @capability ui.mount
|
|
4145
4183
|
*
|
|
@@ -4176,12 +4214,12 @@ export interface Panels {
|
|
|
4176
4214
|
*
|
|
4177
4215
|
* @since 1.0.0
|
|
4178
4216
|
*/
|
|
4179
|
-
mount(slot: "bottom" | "sidebar" | "backdrop", file: string, options?: {
|
|
4217
|
+
mount(slot: "bottom" | "sidebar" | "adjust" | "backdrop", file: string, options?: {
|
|
4180
4218
|
/** The exported component to mount, if the file exports several. */
|
|
4181
4219
|
component?: string;
|
|
4182
4220
|
/** The exported global to use as the bridge, if the file exports several. */
|
|
4183
4221
|
global?: string;
|
|
4184
|
-
/** The floating sidebar window's title (at most 128 bytes). */
|
|
4222
|
+
/** The tab label and the floating sidebar window's title (at most 128 bytes). */
|
|
4185
4223
|
title?: string;
|
|
4186
4224
|
}): Panel;
|
|
4187
4225
|
/**
|
|
@@ -4193,6 +4231,51 @@ export interface Panels {
|
|
|
4193
4231
|
readonly bottom: PanelSlot;
|
|
4194
4232
|
/** The sidebar position, including its floating state; any plugin may read and toggle it. */
|
|
4195
4233
|
readonly sidebar: SidebarSlot;
|
|
4234
|
+
/**
|
|
4235
|
+
* The adjust column (player 0.7.6, SDK 1.6.11), whoever mounted there;
|
|
4236
|
+
* any plugin may read and toggle it. The same shape as
|
|
4237
|
+
* {@link Panels.sidebar} without floating: the adjust column never
|
|
4238
|
+
* detaches.
|
|
4239
|
+
*
|
|
4240
|
+
* @remarks
|
|
4241
|
+
* `state` is `null` while no plugin has a panel there. Players before
|
|
4242
|
+
* 0.7.6 build the `plugin` object with their own SDK, which has no
|
|
4243
|
+
* `adjust`: there `plugin.panels.adjust` is `undefined` and reading its
|
|
4244
|
+
* `state` throws a `TypeError`, so declare `"minPlayerVersion": "0.7.6"`
|
|
4245
|
+
* (or test `plugin.panels.adjust` first). The column starts hidden (`preferred: false`, `reason:
|
|
4246
|
+
* "userHidden"`) until the user opens it. To open the column at your own
|
|
4247
|
+
* panel, call {@link Panel.show} (or {@link Panel.toggle}, which closes it
|
|
4248
|
+
* again when your tab is already the current one, the behaviour of the
|
|
4249
|
+
* player's own `Alt+1…4`).
|
|
4250
|
+
*
|
|
4251
|
+
* @example Open the adjust column at this plugin's tab with a key
|
|
4252
|
+
* One command opens the column at the plugin's tab, and pressing it again
|
|
4253
|
+
* while that tab is shown closes the column. The panel's other commands
|
|
4254
|
+
* name it as their `home`, so the player shows its key under the tab and
|
|
4255
|
+
* the command palette knows where they live.
|
|
4256
|
+
* ```ts
|
|
4257
|
+
* const panel = plugin.panels.mount("adjust", "ui/wheels.slint", { title: "Wheels" });
|
|
4258
|
+
* plugin.commands.add({
|
|
4259
|
+
* id: "wheels",
|
|
4260
|
+
* title: "Colour Wheels",
|
|
4261
|
+
* bindings: ["Alt+5"],
|
|
4262
|
+
* // Opens the column at this tab; closes it when this tab is shown.
|
|
4263
|
+
* run: () => panel.toggle(),
|
|
4264
|
+
* });
|
|
4265
|
+
* plugin.commands.add({
|
|
4266
|
+
* id: "wheels-reset",
|
|
4267
|
+
* title: "Reset Colour Wheels",
|
|
4268
|
+
* // Lives in the panel `wheels` opens: its key shows under the tab.
|
|
4269
|
+
* home: "wheels",
|
|
4270
|
+
* run: () => panel.set({ lift: 0 }),
|
|
4271
|
+
* });
|
|
4272
|
+
* ```
|
|
4273
|
+
*
|
|
4274
|
+
* @player 0.7.6 (declare `"minPlayerVersion": "0.7.6"`; older players
|
|
4275
|
+
* have no `adjust`: reading its `state` throws a `TypeError`).
|
|
4276
|
+
* @since 1.6.11
|
|
4277
|
+
*/
|
|
4278
|
+
readonly adjust: PanelSlot;
|
|
4196
4279
|
}
|
|
4197
4280
|
/**
|
|
4198
4281
|
* What a timeline mark stands for; readers choose colours by it.
|
|
@@ -9417,7 +9500,7 @@ export interface PluginWarning {
|
|
|
9417
9500
|
* 0.4.1); a newer player may add codes.
|
|
9418
9501
|
*/
|
|
9419
9502
|
code: string;
|
|
9420
|
-
/** The panel position (`"bottom"`, `"
|
|
9503
|
+
/** The panel position (`"bottom"`, `"backdrop"`; the sidebar and the adjust column hold any number of tabs) for `slot-occupied`. */
|
|
9421
9504
|
slot?: string;
|
|
9422
9505
|
/**
|
|
9423
9506
|
* What was taken, for `resolver-taken`: 「文件类型 .nes」 or the
|
package/dist/plugin/ui.js
CHANGED
|
@@ -917,7 +917,9 @@ class PanelHandle extends ViewBase {
|
|
|
917
917
|
throw failure("invalid", "the player shows the backdrop by itself");
|
|
918
918
|
await layoutOf(this.scope.runtime).read();
|
|
919
919
|
const state = this.state;
|
|
920
|
-
|
|
920
|
+
// Collapsed at the edge of fullscreen (wanted shown, not on screen):
|
|
921
|
+
// slide it out rather than turn it off (player 0.7.6 pops it out).
|
|
922
|
+
if (state?.preferred && state.active && state.reason !== "fullscreen")
|
|
921
923
|
await layoutOf(this.scope.runtime).set(this.#slot, { visible: false });
|
|
922
924
|
else
|
|
923
925
|
await this.show();
|
|
@@ -991,7 +993,8 @@ class LayoutService {
|
|
|
991
993
|
state = {
|
|
992
994
|
bottom: null,
|
|
993
995
|
sidebar: null,
|
|
994
|
-
|
|
996
|
+
adjust: null,
|
|
997
|
+
tabs: { bottom: null, sidebar: null, adjust: null },
|
|
995
998
|
backdrop: null,
|
|
996
999
|
};
|
|
997
1000
|
#listeners = new Set();
|
|
@@ -1051,10 +1054,16 @@ class LayoutService {
|
|
|
1051
1054
|
active: panel.owner === slot.owner,
|
|
1052
1055
|
})))
|
|
1053
1056
|
: null;
|
|
1057
|
+
const adjust = raw.adjust ?? null;
|
|
1054
1058
|
const next = {
|
|
1055
1059
|
bottom: view(raw.bottom),
|
|
1056
1060
|
sidebar: view(raw.sidebar),
|
|
1057
|
-
|
|
1061
|
+
adjust: view(adjust),
|
|
1062
|
+
tabs: {
|
|
1063
|
+
bottom: tabs(raw.bottom),
|
|
1064
|
+
sidebar: tabs(raw.sidebar),
|
|
1065
|
+
adjust: tabs(adjust),
|
|
1066
|
+
},
|
|
1058
1067
|
backdrop: raw.backdrop
|
|
1059
1068
|
? Object.freeze({ effective: raw.backdrop.effective === true })
|
|
1060
1069
|
: null,
|
|
@@ -1110,7 +1119,11 @@ function slot(scope, name) {
|
|
|
1110
1119
|
async toggle() {
|
|
1111
1120
|
await service().read();
|
|
1112
1121
|
const current = service().state[name];
|
|
1113
|
-
|
|
1122
|
+
// Collapsed at the edge of fullscreen: slide it out (see Panel.toggle).
|
|
1123
|
+
const collapsed = current?.reason === "fullscreen";
|
|
1124
|
+
await service().set(name, {
|
|
1125
|
+
visible: collapsed || !(current?.preferred ?? true),
|
|
1126
|
+
});
|
|
1114
1127
|
},
|
|
1115
1128
|
setDetached: (detached) => service().set(name, { detached }),
|
|
1116
1129
|
activate: (plugin) => service().set(name, { active: plugin }),
|
|
@@ -1145,6 +1158,7 @@ export const panels = (scope) => ({
|
|
|
1145
1158
|
mount: (position, file, options = {}) => new PanelHandle(scope, position, file, options),
|
|
1146
1159
|
bottom: slot(scope, "bottom"),
|
|
1147
1160
|
sidebar: slot(scope, "sidebar"),
|
|
1161
|
+
adjust: slot(scope, "adjust"),
|
|
1148
1162
|
backdrop: backdrop(scope),
|
|
1149
1163
|
});
|
|
1150
1164
|
export const dialogs = (scope) => {
|
package/dist/wire.d.ts
CHANGED
|
@@ -288,7 +288,10 @@ export interface LayoutPanel {
|
|
|
288
288
|
/** The tab label: the mount's `title`, else the plugin's name. */
|
|
289
289
|
title: string;
|
|
290
290
|
}
|
|
291
|
-
/**
|
|
291
|
+
/**
|
|
292
|
+
* One position (the bottom bar, the sidebar or, from player 0.7.6, the
|
|
293
|
+
* adjust column) as `layout.state` reports it.
|
|
294
|
+
*/
|
|
292
295
|
export interface LayoutSlot {
|
|
293
296
|
/** The current tab's panel (the only panel of the bottom bar). */
|
|
294
297
|
owner: number;
|
|
@@ -696,10 +699,13 @@ export declare function createWire(transport: Transport): {
|
|
|
696
699
|
state: () => Promise<{
|
|
697
700
|
bottom: LayoutSlot | null;
|
|
698
701
|
sidebar: LayoutSlot | null;
|
|
702
|
+
/** Player 0.7.6 (absent from older players). */
|
|
703
|
+
adjust?: LayoutSlot | null;
|
|
699
704
|
backdrop?: LayoutBackdrop | null;
|
|
700
705
|
}>;
|
|
701
706
|
set: (options: {
|
|
702
|
-
|
|
707
|
+
/** `"adjust"`: player 0.7.6 (older players refuse it with `invalid`). */
|
|
708
|
+
slot: "bottom" | "sidebar" | "adjust";
|
|
703
709
|
visible?: boolean;
|
|
704
710
|
detached?: boolean;
|
|
705
711
|
/** The plugin id of the panel whose tab becomes the current one. */
|
|
@@ -727,7 +733,8 @@ export declare function createWire(transport: Transport): {
|
|
|
727
733
|
images: string[];
|
|
728
734
|
}>;
|
|
729
735
|
mount: (options: {
|
|
730
|
-
|
|
736
|
+
/** `"adjust"`: player 0.7.6 (older players refuse it with `invalid`). */
|
|
737
|
+
slot: "bottom" | "sidebar" | "backdrop" | "adjust";
|
|
731
738
|
file: string;
|
|
732
739
|
component: string;
|
|
733
740
|
global: 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.12: 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
|
|
package/package.json
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// player's own. Since SDK 1.6.8 (menu redesign step 3a) the player draws
|
|
4
4
|
// the strip itself, at the right end of the bottom slot, its 40px cells on
|
|
5
5
|
// the slot's bottom edge (a bar's 40px button row), and also when no plugin
|
|
6
|
-
// mounts a bottom bar: `[plugin buttons…][»][Command Palette][
|
|
7
|
-
// Sidebar]`. A bottom bar places nothing: it lays its controls out left of
|
|
6
|
+
// mounts a bottom bar: `[plugin buttons…][»][Command Palette][Adjust]
|
|
7
|
+
// [Toggle Sidebar]`. A bottom bar places nothing: it lays its controls out left of
|
|
8
8
|
// the strip and says how wide they must stay:
|
|
9
9
|
// import { BarButtonsHost } from "@vestra/bar-buttons.slint";
|
|
10
10
|
// export component MyBar inherits Rectangle {
|
|
@@ -65,12 +65,16 @@ export global BarButtonsHost {
|
|
|
65
65
|
in property <BarButtonItem> toggle;
|
|
66
66
|
// The command palette (`host/palette`; `token` "" while there is none).
|
|
67
67
|
in property <BarButtonItem> palette;
|
|
68
|
+
// The adjust column's toggle (`host/adjust`, player 0.7.6; `token` ""
|
|
69
|
+
// while there is none).
|
|
70
|
+
in property <BarButtonItem> adjust;
|
|
68
71
|
// Set when a (deprecated) `BarButtons` exists in this window.
|
|
69
72
|
in-out property <bool> placed;
|
|
70
73
|
// The width the player gives its strip (the player sets it).
|
|
71
74
|
in-out property <length> room;
|
|
72
|
-
// The player's own cells, always shown
|
|
73
|
-
|
|
75
|
+
// The player's own cells, always shown: Command Palette, Adjust,
|
|
76
|
+
// Toggle Sidebar.
|
|
77
|
+
private property <int> own: (root.palette.token != "" ? 1 : 0) + (root.adjust.token != "" ? 1 : 0) + (root.toggle.token != "" ? 1 : 0);
|
|
74
78
|
// The cells a bar must always leave free, 41px each: the player's own,
|
|
75
79
|
// and the `»` cell whenever any plugin button exists (drawn only on
|
|
76
80
|
// overflow, but always reserved, so `»` never covers the bar). Never
|
package/slint/vestra/icons.slint
CHANGED
|
@@ -52,6 +52,9 @@ export global Icons {
|
|
|
52
52
|
// small cross (AI), the default 14 × 12 box; bar buttons use them.
|
|
53
53
|
if name == "store" { return "M 2.5 4 H 11.5 L 10.9 9.6 Q 10.8 10.5 9.9 10.5 H 4.1 Q 3.2 10.5 3.1 9.6 Z M 5.2 4 V 3.3 A 1.8 1.8 0 0 1 8.8 3.3 V 4"; }
|
|
54
54
|
if name == "ai" { return "M 6 1.5 Q 6.6 5.4 10.5 6 Q 6.6 6.6 6 10.5 Q 5.4 6.6 1.5 6 Q 5.4 5.4 6 1.5 Z M 11.5 1.2 V 3.8 M 10.2 2.5 H 12.8"; }
|
|
55
|
+
// Three sliders with their knobs (the adjust column, `host/adjust`),
|
|
56
|
+
// the default 14 × 12 box.
|
|
57
|
+
if name == "sliders" { return "M 1.5 2.5 H 7.6 M 10.4 2.5 H 12.5 M 10.4 2.5 A 1.4 1.4 0 1 1 7.6 2.5 A 1.4 1.4 0 1 1 10.4 2.5 Z M 1.5 6 H 3.1 M 5.9 6 H 12.5 M 5.9 6 A 1.4 1.4 0 1 1 3.1 6 A 1.4 1.4 0 1 1 5.9 6 Z M 1.5 9.5 H 6.6 M 9.4 9.5 H 12.5 M 9.4 9.5 A 1.4 1.4 0 1 1 6.6 9.5 A 1.4 1.4 0 1 1 9.4 9.5 Z"; }
|
|
55
58
|
return "";
|
|
56
59
|
}
|
|
57
60
|
// Line width.
|