@vestra-player/sdk 1.6.14 → 1.6.15
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 +8 -0
- package/dist/commands.js +7 -7
- package/dist/docs-tree.json.gz +0 -0
- package/dist/plugin/runtime.js +9 -1
- package/dist/plugin/testing.d.ts +46 -21
- package/dist/plugin/testing.js +79 -36
- package/dist/plugin/types.d.ts +13 -5
- package/dist/plugin/ui.js +19 -8
- package/dist/wire.d.ts +6 -4
- package/dist/wire.js +18 -4
- package/llms.txt +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.15
|
|
4
|
+
|
|
5
|
+
**Who must change: nobody.** A plugin may now mount one panel per slot (player 0.7.6) instead of one panel in all; plugins with one panel work as before, and `apiVersion` stays `vestra/1`.
|
|
6
|
+
|
|
7
|
+
- **One panel per slot.** `plugin.panels.mount` takes a second call for another slot: a playback plugin can have its control bar in `"bottom"` and its controls in `"adjust"`. Each `Panel` has its own bridge global, properties, callbacks, `state`, `show()`, `toggle()`, `setInteraction()` and menu; disposing one leaves the others, and stopping the plugin unmounts them all. A failed mount affects only that panel. A second panel in the same slot still throws (an `Error`, before anything is sent); the bottom bar and the backdrop still hold one panel for all plugins. Players before 0.7.6 refuse a second panel with `conflict` (they keep one panel per plugin); a plugin that mounts two declares `minPlayerVersion` 0.7.6.
|
|
8
|
+
- **Wire** (additive): `ui.setMount`, `ui.interaction`, `ui.menu` and `ui.unmount` take an optional `slot` naming the panel (without it: the plugin's only panel; `ui.unmount` without it removes every panel); `ui.callback` and `ui.preparing` carry the panel's `slot`, and `ui.callback` keeps `view: "$mount"`. Older players ignore `slot`.
|
|
9
|
+
- **`BUILTIN_COMMANDS`**: the player's seek, frame and speed actions (`host/seek-backward`, `seek-forward`, `frame-back`, `frame-forward`, `rate-down`, `rate-up`, `rate-normal`) leave Playback ▸ Seek and Speed for the command palette (player 0.7.6, with the playback plugin's panel); their keys and categories stay.
|
|
10
|
+
- **`TestHost`**: `panels` (by slot, in mount order) and `mounted(slot?)`; without a slot `mounted()` returns the only panel and throws when there are several. `panel` stays, as the last panel mounted. The handlers take `slot` as the player does; `ui.interaction` stores the locks without it.
|
|
3
11
|
## 1.6.14
|
|
4
12
|
|
|
5
13
|
**Who must change: nobody.** A data refresh: `BUILTIN_COMMANDS` lists the built-in capture plugin's commands as the player registers them since its capture tab; no API change, `apiVersion` stays `vestra/1`.
|
package/dist/commands.js
CHANGED
|
@@ -330,12 +330,8 @@ vestra.playback | copy-path | Copy to Clipboard ▸ Full Path
|
|
|
330
330
|
host | play-pause | Playback ▸ Play/Pause | Space, Mouse:LeftClick
|
|
331
331
|
host | previous | Playback ▸ Previous | PageUp, Mouse:X2
|
|
332
332
|
host | next | Playback ▸ Next | PageDown, Mouse:X1
|
|
333
|
-
host | frame-back | Playback ▸ Seek ▸ Previous Frame | D
|
|
334
|
-
host | frame-forward | Playback ▸ Seek ▸ Next Frame | F
|
|
335
333
|
vestra.playback | keyframe-back | Playback ▸ Seek ▸ Previous Keyframe | Ctrl+Shift+ArrowLeft
|
|
336
334
|
vestra.playback | keyframe-forward | Playback ▸ Seek ▸ Next Keyframe | Ctrl+Shift+ArrowRight
|
|
337
|
-
host | seek-backward | Playback ▸ Seek ▸ Back 5 s | ArrowLeft
|
|
338
|
-
host | seek-forward | Playback ▸ Seek ▸ Forward 5 s | ArrowRight
|
|
339
335
|
vestra.playback | backward-2 | Playback ▸ Seek ▸ Back 30 s | Ctrl+ArrowLeft
|
|
340
336
|
vestra.playback | forward-2 | Playback ▸ Seek ▸ Forward 30 s | Ctrl+ArrowRight
|
|
341
337
|
vestra.playback | backward-3 | Playback ▸ Seek ▸ Back 60 s | Shift+ArrowLeft
|
|
@@ -348,9 +344,6 @@ vestra.subtitles | dialogue-current | Playback ▸ Seek ▸ Current Subtitle Sta
|
|
|
348
344
|
vestra.playback | jump-start | Playback ▸ Seek ▸ Jump to Start | Backspace
|
|
349
345
|
vestra.playback | time-jump | Playback ▸ Seek ▸ Seek Steps…
|
|
350
346
|
vestra.playback | jump | Playback ▸ Jump to Time… | G
|
|
351
|
-
host | rate-normal | Playback ▸ Speed ▸ Normal | Z
|
|
352
|
-
host | rate-down | Playback ▸ Speed ▸ Slower 0.1 | X
|
|
353
|
-
host | rate-up | Playback ▸ Speed ▸ Faster 0.1 | C
|
|
354
347
|
vestra.playback | rate-05 | Playback ▸ Speed ▸ 0.5×
|
|
355
348
|
vestra.playback | rate-15 | Playback ▸ Speed ▸ 1.5×
|
|
356
349
|
vestra.playback | rate-20 | Playback ▸ Speed ▸ 2×
|
|
@@ -594,6 +587,13 @@ host | exit-fullscreen | (Leave Fullscreen)
|
|
|
594
587
|
host | sidebar | (Toggle Sidebar)
|
|
595
588
|
host | palette | (Command Palette) | Ctrl+Shift+P
|
|
596
589
|
host | adjust | (Adjust)
|
|
590
|
+
host | frame-back | (Previous Frame) | D
|
|
591
|
+
host | frame-forward | (Next Frame) | F
|
|
592
|
+
host | seek-backward | (Back 5 s) | ArrowLeft
|
|
593
|
+
host | seek-forward | (Forward 5 s) | ArrowRight
|
|
594
|
+
host | rate-normal | (Normal) | Z
|
|
595
|
+
host | rate-down | (Slower 0.1) | X
|
|
596
|
+
host | rate-up | (Faster 0.1) | C
|
|
597
597
|
vestra.capture | capture.clipExport | (Export Clip Range) | Ctrl+Shift+E
|
|
598
598
|
vestra.capture | capture.saveAs | (Save Current Frame As…)
|
|
599
599
|
vestra.capture | capture.saveScreen | (Save Rendered Frame) | Ctrl+Alt+E
|
package/dist/docs-tree.json.gz
CHANGED
|
Binary file
|
package/dist/plugin/runtime.js
CHANGED
|
@@ -248,7 +248,15 @@ export class Runtime {
|
|
|
248
248
|
this.directory = directory;
|
|
249
249
|
transport.on("ui.callback", (data) => {
|
|
250
250
|
const event = data;
|
|
251
|
-
|
|
251
|
+
// A panel's callback names its slot (player 0.7.6: one panel per
|
|
252
|
+
// slot); older players name only `"$mount"`, and the plugin has one.
|
|
253
|
+
const view = event.view === "$mount"
|
|
254
|
+
? event.slot
|
|
255
|
+
? `panel:${event.slot}`
|
|
256
|
+
: [...this.views.keys()].find((key) => key.startsWith("panel:"))
|
|
257
|
+
: event.view;
|
|
258
|
+
if (view)
|
|
259
|
+
this.views.get(view)?.(event.callback, event.args);
|
|
252
260
|
});
|
|
253
261
|
transport.on("commands.execute", (data) => {
|
|
254
262
|
this.commands.get(data.id)?.();
|
package/dist/plugin/testing.d.ts
CHANGED
|
@@ -480,6 +480,37 @@ export interface MarkdownWire {
|
|
|
480
480
|
/** Remote image address → the file the SDK downloaded for it. */
|
|
481
481
|
images: Record<string, string>;
|
|
482
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* One mounted panel of the plugin under test ({@link TestHost.mounted},
|
|
485
|
+
* {@link TestHost.panels}): a {@link FakeWindow} record with its slot,
|
|
486
|
+
* component and interaction locks.
|
|
487
|
+
*
|
|
488
|
+
* @remarks
|
|
489
|
+
* One per slot the plugin mounted (SDK 1.6.15), kept in
|
|
490
|
+
* {@link TestHost.panels}; disposing a panel removes only its own.
|
|
491
|
+
*
|
|
492
|
+
* @example Check the adjust panel's locks
|
|
493
|
+
* The plugin's adjust panel reports a drag; the test reads the locks it
|
|
494
|
+
* sent. `mounted(slot)` picks one of the plugin's panels by slot.
|
|
495
|
+
* ```ts
|
|
496
|
+
* import assert from "node:assert/strict";
|
|
497
|
+
* declare const host: TestHost;
|
|
498
|
+
* // ---cut---
|
|
499
|
+
* const panel: FakePanel = host.mounted("adjust");
|
|
500
|
+
* assert.deepEqual(panel.locks, { focused: false, dragging: true, popupOpen: false });
|
|
501
|
+
* ```n *
|
|
502
|
+
* @capability None.
|
|
503
|
+
* @since 1.6.15
|
|
504
|
+
* @category Testing
|
|
505
|
+
*/
|
|
506
|
+
export type FakePanel = Omit<FakeWindow, "callbacks" | "userClose"> & {
|
|
507
|
+
/** `"bottom"`, `"sidebar"`, `"adjust"` or `"backdrop"`. */
|
|
508
|
+
slot: string;
|
|
509
|
+
/** The exported component the SDK found in the file. */
|
|
510
|
+
component: string;
|
|
511
|
+
/** The last `panel.setInteraction(...)` locks, `null` before any. */
|
|
512
|
+
locks: Json;
|
|
513
|
+
};
|
|
483
514
|
/**
|
|
484
515
|
* An in-memory fake Vestra Player for plugin tests.
|
|
485
516
|
*
|
|
@@ -597,17 +628,16 @@ export declare class TestHost implements Transport {
|
|
|
597
628
|
/** Tool windows by id (see {@link TestHost.window}). */
|
|
598
629
|
readonly windows: Map<string, FakeWindow>;
|
|
599
630
|
/**
|
|
600
|
-
* The plugin's mounted
|
|
601
|
-
* See {@link TestHost.mounted}.
|
|
631
|
+
* The plugin's mounted panels by slot, in mount order (SDK 1.6.15: one
|
|
632
|
+
* panel per slot). See {@link TestHost.mounted}.
|
|
602
633
|
*/
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
}) | null;
|
|
634
|
+
readonly panels: Map<string, FakePanel>;
|
|
635
|
+
/**
|
|
636
|
+
* The plugin's mounted panel (bottom bar, sidebar, adjust column or
|
|
637
|
+
* backdrop), the last mounted when it has several, or `null`. See
|
|
638
|
+
* {@link TestHost.mounted}.
|
|
639
|
+
*/
|
|
640
|
+
get panel(): FakePanel | null;
|
|
611
641
|
/** The plugin's commands by local id (see {@link TestHost.command}). */
|
|
612
642
|
readonly commands: Map<string, FakeCommand>;
|
|
613
643
|
/** The player's 14 core actions (`host/<id>`), kept apart from the plugin's `commands`. */
|
|
@@ -2814,9 +2844,11 @@ export declare class TestHost implements Transport {
|
|
|
2814
2844
|
*/
|
|
2815
2845
|
window(id: string): FakeWindow;
|
|
2816
2846
|
/**
|
|
2817
|
-
* The plugin's mounted panel (bottom bar, sidebar
|
|
2818
|
-
*
|
|
2819
|
-
*
|
|
2847
|
+
* The plugin's mounted panel (bottom bar, sidebar, adjust column or
|
|
2848
|
+
* backdrop): the one in `slot`, or without it the only one (SDK 1.6.15: a
|
|
2849
|
+
* plugin mounts one panel per slot; with several, name the slot). Throws
|
|
2850
|
+
* when there is none. Its `props` use Slint (kebab-case) names; `emit`
|
|
2851
|
+
* fires a callback on the panel's global.
|
|
2820
2852
|
*
|
|
2821
2853
|
* @example Select a row in the plugin's sidebar panel
|
|
2822
2854
|
* The test checks the panel sits in the sidebar and selects its third
|
|
@@ -2835,14 +2867,7 @@ export declare class TestHost implements Transport {
|
|
|
2835
2867
|
* @capability None.
|
|
2836
2868
|
* @since 1.0.0
|
|
2837
2869
|
*/
|
|
2838
|
-
mounted(
|
|
2839
|
-
/** `"bottom"`, `"sidebar"`, `"adjust"` or `"backdrop"`. */
|
|
2840
|
-
slot: string;
|
|
2841
|
-
/** The exported component the SDK found in the file. */
|
|
2842
|
-
component: string;
|
|
2843
|
-
/** The last `panel.setInteraction(...)` locks, `null` before any. */
|
|
2844
|
-
locks: Json;
|
|
2845
|
-
};
|
|
2870
|
+
mounted(slot?: "bottom" | "sidebar" | "adjust" | "backdrop"): FakePanel;
|
|
2846
2871
|
/**
|
|
2847
2872
|
* The parameters of every request of one wire method, oldest first.
|
|
2848
2873
|
*
|
package/dist/plugin/testing.js
CHANGED
|
@@ -330,6 +330,8 @@ const SLOT_LABELS = {
|
|
|
330
330
|
backdrop: "video backdrop",
|
|
331
331
|
adjust: "adjust column",
|
|
332
332
|
};
|
|
333
|
+
/** The view a panel's writes are kept under (the player's, per slot). */
|
|
334
|
+
const mountView = (slot) => slot === "bottom" ? "$mount" : `$mount:${slot}`;
|
|
333
335
|
/** Texts only the test host uses, with their Chinese of SDK 1.5.8. */
|
|
334
336
|
const TEST_ZH_HANS = {
|
|
335
337
|
"this site": "这个网站",
|
|
@@ -521,10 +523,18 @@ export class TestHost {
|
|
|
521
523
|
/** Tool windows by id (see {@link TestHost.window}). */
|
|
522
524
|
windows = new Map();
|
|
523
525
|
/**
|
|
524
|
-
* The plugin's mounted
|
|
525
|
-
* See {@link TestHost.mounted}.
|
|
526
|
+
* The plugin's mounted panels by slot, in mount order (SDK 1.6.15: one
|
|
527
|
+
* panel per slot). See {@link TestHost.mounted}.
|
|
526
528
|
*/
|
|
527
|
-
|
|
529
|
+
panels = new Map();
|
|
530
|
+
/**
|
|
531
|
+
* The plugin's mounted panel (bottom bar, sidebar, adjust column or
|
|
532
|
+
* backdrop), the last mounted when it has several, or `null`. See
|
|
533
|
+
* {@link TestHost.mounted}.
|
|
534
|
+
*/
|
|
535
|
+
get panel() {
|
|
536
|
+
return [...this.panels.values()].at(-1) ?? null;
|
|
537
|
+
}
|
|
528
538
|
/** The plugin's commands by local id (see {@link TestHost.command}). */
|
|
529
539
|
commands = new Map();
|
|
530
540
|
/** The player's 14 core actions (`host/<id>`), kept apart from the plugin's `commands`. */
|
|
@@ -3627,9 +3637,11 @@ export class TestHost {
|
|
|
3627
3637
|
return window;
|
|
3628
3638
|
}
|
|
3629
3639
|
/**
|
|
3630
|
-
* The plugin's mounted panel (bottom bar, sidebar
|
|
3631
|
-
*
|
|
3632
|
-
*
|
|
3640
|
+
* The plugin's mounted panel (bottom bar, sidebar, adjust column or
|
|
3641
|
+
* backdrop): the one in `slot`, or without it the only one (SDK 1.6.15: a
|
|
3642
|
+
* plugin mounts one panel per slot; with several, name the slot). Throws
|
|
3643
|
+
* when there is none. Its `props` use Slint (kebab-case) names; `emit`
|
|
3644
|
+
* fires a callback on the panel's global.
|
|
3633
3645
|
*
|
|
3634
3646
|
* @example Select a row in the plugin's sidebar panel
|
|
3635
3647
|
* The test checks the panel sits in the sidebar and selects its third
|
|
@@ -3648,11 +3660,27 @@ export class TestHost {
|
|
|
3648
3660
|
* @capability None.
|
|
3649
3661
|
* @since 1.0.0
|
|
3650
3662
|
*/
|
|
3651
|
-
mounted() {
|
|
3663
|
+
mounted(slot) {
|
|
3664
|
+
if (slot !== undefined) {
|
|
3665
|
+
const panel = this.panels.get(slot);
|
|
3666
|
+
if (!panel)
|
|
3667
|
+
throw new Error(`no panel mounted in the ${slot}`);
|
|
3668
|
+
return panel;
|
|
3669
|
+
}
|
|
3670
|
+
if (this.panels.size > 1)
|
|
3671
|
+
throw new Error(`panels are mounted in ${[...this.panels.keys()].join(", ")}: name the slot`);
|
|
3652
3672
|
if (!this.panel)
|
|
3653
3673
|
throw new Error("no panel mounted");
|
|
3654
3674
|
return this.panel;
|
|
3655
3675
|
}
|
|
3676
|
+
/** The panel a request is about: its `slot`, else the only one. */
|
|
3677
|
+
#panelFor(p) {
|
|
3678
|
+
const slot = typeof p.slot === "string" ? p.slot : undefined;
|
|
3679
|
+
const panel = slot ? this.panels.get(slot) : this.panel;
|
|
3680
|
+
if (!panel)
|
|
3681
|
+
throw new HostError("missing", "owner has no ready mount");
|
|
3682
|
+
return panel;
|
|
3683
|
+
}
|
|
3656
3684
|
/**
|
|
3657
3685
|
* The parameters of every request of one wire method, oldest first.
|
|
3658
3686
|
*
|
|
@@ -4260,16 +4288,18 @@ export class TestHost {
|
|
|
4260
4288
|
// A sidebar panel is never live (player 0.7.3): the current tab keeps
|
|
4261
4289
|
// the plugin running as busy work, a background tab not at all. The
|
|
4262
4290
|
// adjust column's current tab only while the column is open (0.7.6).
|
|
4263
|
-
|
|
4264
|
-
if (
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
if (
|
|
4269
|
-
|
|
4291
|
+
for (const panel of this.panels.values()) {
|
|
4292
|
+
if (panel.slot === "sidebar") {
|
|
4293
|
+
if (this.layout.sidebar?.owner === 1)
|
|
4294
|
+
reasons.push("its sidebar tab is the current tab");
|
|
4295
|
+
}
|
|
4296
|
+
else if (panel.slot === "adjust") {
|
|
4297
|
+
if (this.layout.adjust?.owner === 1 && this.layout.adjust.preferred)
|
|
4298
|
+
reasons.push("its adjust tab is the current tab");
|
|
4299
|
+
}
|
|
4300
|
+
else
|
|
4301
|
+
reasons.push("a mounted panel");
|
|
4270
4302
|
}
|
|
4271
|
-
else if (this.panel)
|
|
4272
|
-
reasons.push("a mounted panel");
|
|
4273
4303
|
for (const [id, window] of this.windows)
|
|
4274
4304
|
if (window.visible)
|
|
4275
4305
|
reasons.push(`an open window (${id})`);
|
|
@@ -4501,10 +4531,11 @@ export class TestHost {
|
|
|
4501
4531
|
throw new HostError("missing", "view not found");
|
|
4502
4532
|
},
|
|
4503
4533
|
"ui.mount": (p) => {
|
|
4504
|
-
if (this.panel)
|
|
4505
|
-
throw new HostError("conflict", "owner already mounted");
|
|
4506
4534
|
if (!(String(p.slot) in SLOT_LABELS))
|
|
4507
4535
|
throw new HostError("invalid", "unknown mount slot");
|
|
4536
|
+
// One panel per slot and plugin (player 0.7.6).
|
|
4537
|
+
if (this.panels.has(String(p.slot)))
|
|
4538
|
+
throw new HostError("conflict", "this plugin already has a panel in this slot");
|
|
4508
4539
|
// Like the player: one panel in the bottom bar and the backdrop, any
|
|
4509
4540
|
// number of tabs in the sidebar (player 0.7.3; 0.4.x to 0.7.2 took 8)
|
|
4510
4541
|
// and in the adjust column (player 0.7.6).
|
|
@@ -4520,10 +4551,11 @@ export class TestHost {
|
|
|
4520
4551
|
]));
|
|
4521
4552
|
const properties = this.#initialValues(this.#shownValues(p.properties));
|
|
4522
4553
|
this.#seeImages(properties);
|
|
4523
|
-
|
|
4554
|
+
const panelSlot = String(p.slot);
|
|
4555
|
+
this.#resetWrites(mountView(panelSlot));
|
|
4524
4556
|
const host = this;
|
|
4525
|
-
this.
|
|
4526
|
-
slot:
|
|
4557
|
+
this.panels.set(panelSlot, {
|
|
4558
|
+
slot: panelSlot,
|
|
4527
4559
|
file: String(p.file),
|
|
4528
4560
|
component: String(p.component),
|
|
4529
4561
|
visible: true,
|
|
@@ -4532,11 +4564,12 @@ export class TestHost {
|
|
|
4532
4564
|
emit(callback, ...args) {
|
|
4533
4565
|
host.emit("ui.callback", {
|
|
4534
4566
|
view: "$mount",
|
|
4567
|
+
slot: panelSlot,
|
|
4535
4568
|
callback: kebab(callback),
|
|
4536
4569
|
args,
|
|
4537
4570
|
});
|
|
4538
4571
|
},
|
|
4539
|
-
};
|
|
4572
|
+
});
|
|
4540
4573
|
if (p.slot === "backdrop") {
|
|
4541
4574
|
this.backdrop = { owner: this.manifestId, effective: true };
|
|
4542
4575
|
return { status: "ready", prepareMs: 0, shellGeneration: 1 };
|
|
@@ -4551,18 +4584,24 @@ export class TestHost {
|
|
|
4551
4584
|
});
|
|
4552
4585
|
return { status: "ready", prepareMs: 0, shellGeneration: 1 };
|
|
4553
4586
|
},
|
|
4554
|
-
"ui.unmount": () => {
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4587
|
+
"ui.unmount": (p) => {
|
|
4588
|
+
// The slot's panel, or without one every panel (as the player).
|
|
4589
|
+
const slots = typeof p.slot === "string" ? [p.slot] : [...this.panels.keys()];
|
|
4590
|
+
for (const slot of slots) {
|
|
4591
|
+
if (!this.panels.delete(slot))
|
|
4592
|
+
continue;
|
|
4593
|
+
if (slot === "backdrop")
|
|
4594
|
+
this.backdrop = null;
|
|
4595
|
+
else
|
|
4596
|
+
this.#removePanel(slot, 1);
|
|
4597
|
+
}
|
|
4560
4598
|
},
|
|
4561
4599
|
"ui.menu": (p) => {
|
|
4562
|
-
if (!this.
|
|
4600
|
+
if (!this.panels.size)
|
|
4563
4601
|
throw new HostError("unavailable", "panel not shown");
|
|
4564
|
-
const
|
|
4565
|
-
|
|
4602
|
+
const panel = this.#panelFor(p);
|
|
4603
|
+
const tabs = panel.slot === "sidebar" || panel.slot === "adjust"
|
|
4604
|
+
? this.layout[panel.slot]
|
|
4566
4605
|
: null;
|
|
4567
4606
|
if (tabs && tabs.owner !== 1)
|
|
4568
4607
|
throw new HostError("unavailable", "the panel's tab is not the current one");
|
|
@@ -4613,8 +4652,9 @@ export class TestHost {
|
|
|
4613
4652
|
: collapsed
|
|
4614
4653
|
? "fullscreen"
|
|
4615
4654
|
: null;
|
|
4616
|
-
|
|
4617
|
-
|
|
4655
|
+
const panel = this.panels.get(slot);
|
|
4656
|
+
if (panel)
|
|
4657
|
+
panel.visible = p.visible;
|
|
4618
4658
|
}
|
|
4619
4659
|
if (typeof p.detached === "boolean")
|
|
4620
4660
|
state.detached = p.detached;
|
|
@@ -4880,9 +4920,9 @@ export class TestHost {
|
|
|
4880
4920
|
this.emit("sequence.step", { direction });
|
|
4881
4921
|
},
|
|
4882
4922
|
"ui.setMount": (p) => {
|
|
4883
|
-
const panel = this
|
|
4923
|
+
const panel = this.#panelFor(p);
|
|
4884
4924
|
const property = String(p.property);
|
|
4885
|
-
const next = this.#write(
|
|
4925
|
+
const next = this.#write(mountView(panel.slot), property, p, panel.props[property]);
|
|
4886
4926
|
// A part of a value sent in parts is held until the last (player 0.6.0).
|
|
4887
4927
|
if (next !== undefined)
|
|
4888
4928
|
panel.props[property] = next;
|
|
@@ -5122,7 +5162,10 @@ export class TestHost {
|
|
|
5122
5162
|
})));
|
|
5123
5163
|
return { applied: true };
|
|
5124
5164
|
},
|
|
5125
|
-
"ui.interaction": (p) =>
|
|
5165
|
+
"ui.interaction": (p) => {
|
|
5166
|
+
const { slot: _, ...locks } = p;
|
|
5167
|
+
this.#panelFor(p).locks = locks;
|
|
5168
|
+
},
|
|
5126
5169
|
"dialogs.confirm": (p) => {
|
|
5127
5170
|
this.confirms.push(p);
|
|
5128
5171
|
const shown = (field, formatted = false) => p[field] === undefined
|
package/dist/plugin/types.d.ts
CHANGED
|
@@ -4095,9 +4095,14 @@ export interface BackdropSlot {
|
|
|
4095
4095
|
* or the sidebar moves to or from its floating window, while the global is
|
|
4096
4096
|
* one stable address.
|
|
4097
4097
|
*
|
|
4098
|
-
*
|
|
4099
|
-
*
|
|
4100
|
-
*
|
|
4098
|
+
* A plugin mounts at most one panel per slot (SDK 1.6.15, player 0.7.6;
|
|
4099
|
+
* before, one panel in all): a playback plugin can have its control bar in
|
|
4100
|
+
* `"bottom"` and its controls in `"adjust"`. Each {@link Panel} has its own
|
|
4101
|
+
* bridge, state, `toggle()` and `setInteraction()`; disposing one leaves
|
|
4102
|
+
* the others, and stopping the plugin unmounts them all. The bottom bar and
|
|
4103
|
+
* the backdrop hold one panel for all plugins; the sidebar and the adjust
|
|
4104
|
+
* column one tab per plugin. {@link Panels.bottom}, {@link Panels.sidebar}
|
|
4105
|
+
* and {@link Panels.adjust} describe the positions, whoever mounted there.
|
|
4101
4106
|
*
|
|
4102
4107
|
* @capability ui.mount
|
|
4103
4108
|
*
|
|
@@ -4194,8 +4199,11 @@ export interface Panels {
|
|
|
4194
4199
|
* @returns A {@link Panel} owned by this scope; await {@link Panel.ready}
|
|
4195
4200
|
* for the result.
|
|
4196
4201
|
*
|
|
4197
|
-
* @throws `Error` without a code (throws) This plugin already has a panel
|
|
4198
|
-
*
|
|
4202
|
+
* @throws `Error` without a code (throws) This plugin already has a panel
|
|
4203
|
+
* in this slot (a panel in another slot is fine from player 0.7.6;
|
|
4204
|
+
* players before 0.7.6 refuse a second panel with `conflict`, so a
|
|
4205
|
+
* plugin that mounts two declares `minPlayerVersion` 0.7.6), or the file
|
|
4206
|
+
* exports no component or global, or several and `options`
|
|
4199
4207
|
* does not name one. A file that cannot be read throws Node's file system
|
|
4200
4208
|
* error (`code` `ENOENT` and so on).
|
|
4201
4209
|
* @throws The errors of {@link Panel.ready} (rejects `ready`) The player
|
package/dist/plugin/ui.js
CHANGED
|
@@ -839,13 +839,15 @@ class PanelHandle extends ViewBase {
|
|
|
839
839
|
#locks = { focused: false, dragging: false, popupOpen: false };
|
|
840
840
|
#interaction;
|
|
841
841
|
constructor(scope, slot, file, options) {
|
|
842
|
-
|
|
843
|
-
|
|
842
|
+
// One panel per slot (SDK 1.6.15, player 0.7.6; a player before 0.7.6
|
|
843
|
+
// refuses a second panel with `conflict`).
|
|
844
|
+
if (scope.runtime.views.has(`panel:${slot}`))
|
|
845
|
+
throw new Error(`this plugin already has a panel in the ${slot}`);
|
|
844
846
|
super(scope);
|
|
845
847
|
this.#slot = slot;
|
|
846
848
|
const runtime = scope.runtime;
|
|
847
|
-
runtime.views.set(
|
|
848
|
-
this.#interaction = new Latest((locks) => runtime.host.ui.interaction(locks));
|
|
849
|
+
runtime.views.set(`panel:${slot}`, (callback, args) => this.dispatch(callback, args));
|
|
850
|
+
this.#interaction = new Latest((locks) => runtime.host.ui.interaction(locks, this.#named()));
|
|
849
851
|
const component = options.component ?? exported(runtime.directory, file, "component");
|
|
850
852
|
const global = options.global ?? exported(runtime.directory, file, "global");
|
|
851
853
|
scope.add(this);
|
|
@@ -874,8 +876,17 @@ class PanelHandle extends ViewBase {
|
|
|
874
876
|
live() {
|
|
875
877
|
return this.#mounted && !this.closed;
|
|
876
878
|
}
|
|
879
|
+
/**
|
|
880
|
+
* The slot to name in a request about this panel: only while the plugin
|
|
881
|
+
* has panels in more than one slot (the player then needs it); a plugin
|
|
882
|
+
* with one panel sends what it always sent.
|
|
883
|
+
*/
|
|
884
|
+
#named() {
|
|
885
|
+
const panels = [...this.scope.runtime.views.keys()].filter((key) => key.startsWith("panel:"));
|
|
886
|
+
return panels.length > 1 ? this.#slot : undefined;
|
|
887
|
+
}
|
|
877
888
|
write(property, write) {
|
|
878
|
-
return this.scope.runtime.host.ui.setMount(property, write);
|
|
889
|
+
return this.scope.runtime.host.ui.setMount(property, write, this.#named());
|
|
879
890
|
}
|
|
880
891
|
get state() {
|
|
881
892
|
const service = layoutOf(this.scope.runtime);
|
|
@@ -932,7 +943,7 @@ class PanelHandle extends ViewBase {
|
|
|
932
943
|
}
|
|
933
944
|
async showMenu(items, at) {
|
|
934
945
|
await this.ready;
|
|
935
|
-
await this.scope.runtime.host.ui.menu(items.map((item) => item?.id ?? null), at.x, at.y);
|
|
946
|
+
await this.scope.runtime.host.ui.menu(items.map((item) => item?.id ?? null), at.x, at.y, this.#named());
|
|
936
947
|
}
|
|
937
948
|
onDrop(listener) {
|
|
938
949
|
const watch = dropsOf(this.scope.runtime).listen(this.scope, (event) => this.scope.run(listener, event));
|
|
@@ -950,10 +961,10 @@ class PanelHandle extends ViewBase {
|
|
|
950
961
|
this.scope.forget(this);
|
|
951
962
|
this.#interaction.stop();
|
|
952
963
|
if (this.#mounted)
|
|
953
|
-
await this.scope.runtime.host.ui.unmount().catch(() => { });
|
|
964
|
+
await this.scope.runtime.host.ui.unmount(this.#named()).catch(() => { });
|
|
954
965
|
this.#mounted = false;
|
|
955
966
|
await this.release();
|
|
956
|
-
this.scope.runtime.views.delete(
|
|
967
|
+
this.scope.runtime.views.delete(`panel:${this.#slot}`);
|
|
957
968
|
// Listeners of `onChange` hear `null`.
|
|
958
969
|
this.scope.runtime.invalidate();
|
|
959
970
|
await layoutOf(this.scope.runtime).refresh();
|
package/dist/wire.d.ts
CHANGED
|
@@ -275,6 +275,8 @@ export interface WireTimeline {
|
|
|
275
275
|
marks: WireMark[];
|
|
276
276
|
ranges?: WireRange[];
|
|
277
277
|
}
|
|
278
|
+
/** A panel position (`ui.mount`'s `slot`). */
|
|
279
|
+
export type MountSlot = "bottom" | "sidebar" | "backdrop" | "adjust";
|
|
278
280
|
/** The backdrop as `layout.state` reports it; `null` without one. */
|
|
279
281
|
export interface LayoutBackdrop {
|
|
280
282
|
owner: number;
|
|
@@ -752,14 +754,14 @@ export declare function createWire(transport: Transport): {
|
|
|
752
754
|
prepareMs: number;
|
|
753
755
|
shellGeneration: number;
|
|
754
756
|
}>;
|
|
755
|
-
setMount: (property: string, write: PropertyWrite) => Promise<void>;
|
|
757
|
+
setMount: (property: string, write: PropertyWrite, slot?: MountSlot) => Promise<void>;
|
|
756
758
|
interaction: (locks: {
|
|
757
759
|
focused: boolean;
|
|
758
760
|
dragging: boolean;
|
|
759
761
|
popupOpen: boolean;
|
|
760
|
-
}) => Promise<void>;
|
|
761
|
-
unmount: () => Promise<void>;
|
|
762
|
-
menu: (items: (string | null)[], x: number, y: number) => Promise<void>;
|
|
762
|
+
}, slot?: MountSlot) => Promise<void>;
|
|
763
|
+
unmount: (slot?: MountSlot) => Promise<void>;
|
|
764
|
+
menu: (items: (string | null)[], x: number, y: number, slot?: MountSlot) => Promise<void>;
|
|
763
765
|
open: (id: string, file: string, properties?: Record<string, Json>, callbacks?: string[]) => Promise<void>;
|
|
764
766
|
set: (id: string, property: string, write: PropertyWrite) => Promise<void>;
|
|
765
767
|
show: (id: string, visible: boolean) => Promise<void>;
|
package/dist/wire.js
CHANGED
|
@@ -328,10 +328,24 @@ export function createWire(transport) {
|
|
|
328
328
|
base,
|
|
329
329
|
}),
|
|
330
330
|
mount: (options) => transport.request("ui.mount", options),
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
331
|
+
// `slot`: which of the plugin's panels (player 0.7.6: one per slot;
|
|
332
|
+
// older players have one and ignore it).
|
|
333
|
+
setMount: (property, write, slot) => transport.request("ui.setMount", {
|
|
334
|
+
property,
|
|
335
|
+
...write,
|
|
336
|
+
...(slot ? { slot } : {}),
|
|
337
|
+
}),
|
|
338
|
+
interaction: (locks, slot) => transport.request("ui.interaction", {
|
|
339
|
+
...locks,
|
|
340
|
+
...(slot ? { slot } : {}),
|
|
341
|
+
}),
|
|
342
|
+
unmount: (slot) => transport.request("ui.unmount", slot ? { slot } : undefined),
|
|
343
|
+
menu: (items, x, y, slot) => transport.request("ui.menu", {
|
|
344
|
+
items,
|
|
345
|
+
x,
|
|
346
|
+
y,
|
|
347
|
+
...(slot ? { slot } : {}),
|
|
348
|
+
}),
|
|
335
349
|
open: (id, file, properties = {}, callbacks = []) => transport.request("ui.open", { id, file, properties, callbacks }),
|
|
336
350
|
set: (id, property, write) => transport.request("ui.set", { id, property, ...write }),
|
|
337
351
|
show: (id, visible) => transport.request("ui.show", { id, visible }),
|
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.15: 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
|
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
- `/API/Sources`: 7 entries: ResolverOptions, WebResolverOptions, FileResolverOptions, ResolvedStream, Resolver, Resolvers, ResolvedMedia
|
|
39
39
|
- `/API/Subtitles`: 5 entries: SubtitleTrack, SubtitlesState, SubtitleConditional, Subtitles, SubtitleStyle
|
|
40
40
|
- `/API/UI`: 44 entries: ImageRef, ImageOptions, MarkdownRef, MarkdownOptions, PropValue, PropData, Props, Ui, View, Window, Windows, SlotState, SlotPanel, PanelState, PanelSlot, SidebarSlot, Panel,…
|
|
41
|
-
- `/API/Testing`:
|
|
41
|
+
- `/API/Testing`: 34 entries: CapabilityInfo, CAPABILITY_INFO, MESSAGE_LIMIT, COMMAND_LIMITS, MENU_IDS, BAR_BUTTON_ICONS, BAR_BUTTON_LIMITS, BuiltinSubmenu, BUILTIN_SUBMENUS, BuiltinCommand, BUILTIN_COMMANDS, LOCALES,…
|
|
42
42
|
- `/API/Docs`: 4 entries: DOCS_PAGE_BYTES, DocsOptions, docs, searchDocs
|