@vestra-player/sdk 1.6.14 → 1.6.16

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 CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.6.16
4
+
5
+ **Who must change: nobody.** A data refresh for the player's four built-in panels (player 0.7.6) and one additive read; no API is removed, `apiVersion` stays `vestra/1`.
6
+
7
+ - **`BUILTIN_COMMANDS`**: the Picture, Audio, Subtitles and Playback panels open from four top-level items, **Picture…** (`vestra.video`, `video.picture`, Alt+1), **Audio…** (`audio.workbench`, Alt+2), **Subtitles…** (`tune`, Alt+3) and **Playback…** (`vestra.playback`, `panel`, Alt+4), next to Playlist. The commands their panels hold left the menus for the command palette, with their keys and categories: Video's Pixel Shaders, Deinterlace, Aspect, Crop/Stretch, Framing, Image Processing and Rotation, the picture steps and GPU Rendering; Audio's track, channel, equalizer, preset, sync and processing items; Subtitles' show, style, secondary subtitle, sync and reload items; the playback plugin's seek steps, keyframes, Jump to Time… and speeds; the Reset items of the video, audio and subtitles plugins (the visualizer's Reset ▸ Idle Logo stays). **Subtitle Track ▸ Off** and **Audio Track ▸ No Audio Tracks** are now top-level submenus. New palette commands: Remove External Subtitle (`vestra.subtitles`, `remove`) and Picture Panel (`video.resetPanel`, a reset of the panel's values). Audio ▸ Output Device ▸ No Output Devices (`audio.device.none`) is gone.
8
+ - **`BUILTIN_SUBMENUS`**: the Video, Audio and Subtitles submenus of these plugins are gone; **Subtitle Track** (`vestra.subtitles`) and **Audio Track** (`vestra.audio`) are new, at the top level. Online Subtitles and Snapshot stay.
9
+ - **`plugin.commands.keys("host/<id>")`** reads the keys of the player's own commands (`host/volume-up`, `host/mute`, the ids of the `host` rows of `BUILTIN_COMMANDS`), read-only and current like a plugin's own, so a panel can show the player's key caps beside its controls; `onKeysChange` fires for their changes too. The `commands.keys` answer and event carry them as `host/<id>` entries (a local id has no `/`); player 0.7.6, like `keys` itself. A side effect: rebinding one of the player's own commands now sends `commands.keys` to every plugin listening for it, and a listener in standby is started for it (as for a change to its own keys); `onKeysChange` still fires only when a label the plugin can read changed.
10
+ - **`TestHost`**: `commands.keys` answers the core commands as `host/<id>` too, and `rebind` takes a core command's `host/<id>`.
11
+
12
+ ## 1.6.15
13
+
14
+ **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`.
15
+
16
+ - **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.
17
+ - **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`.
18
+ - **`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.
19
+ - **`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
20
  ## 1.6.14
4
21
 
5
22
  **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`.
@@ -348,8 +348,9 @@ export type CommandDeclaration = {
348
348
  };
349
349
  /**
350
350
  * `commands.keys` (player 0.7.6): each of the plugin's own commands, by
351
- * local id, with the labels of its current keys; the answer to the request
352
- * and the data of the event of that name.
351
+ * local id, and (SDK 1.6.16) each of the player's own as `host/<id>`, with
352
+ * the labels of its current keys; the answer to the request and the data of
353
+ * the event of that name.
353
354
  */
354
355
  export type CommandKeys = {
355
356
  keys: Record<string, string[]>;
package/dist/commands.js CHANGED
@@ -275,32 +275,7 @@ export const BUILTIN_SUBMENUS = Object.freeze([
275
275
  path: ["Subtitles", "Online Subtitles"],
276
276
  plugins: ["vestra.subtitle-online"],
277
277
  },
278
- { path: ["Subtitles", "Secondary Subtitle"], plugins: ["vestra.subtitles"] },
279
- {
280
- path: ["Subtitles", "Secondary Subtitle", "Position"],
281
- plugins: ["vestra.subtitles"],
282
- },
283
- { path: ["Subtitles", "Subtitle Style"], plugins: ["vestra.subtitles"] },
284
- { path: ["Subtitles", "Subtitle Track"], plugins: ["vestra.subtitles"] },
285
- { path: ["Subtitles", "Sync"], plugins: ["vestra.subtitles"] },
286
- { path: ["Video", "Aspect"], plugins: ["vestra.video"] },
287
- { path: ["Video", "Crop/Stretch"], plugins: ["vestra.video"] },
288
- { path: ["Video", "Deinterlace"], plugins: ["vestra.video"] },
289
- { path: ["Video", "Deinterlace", "Output"], plugins: ["vestra.video"] },
290
- { path: ["Video", "Framing"], plugins: ["vestra.video"] },
291
- { path: ["Video", "Image Processing"], plugins: ["vestra.video"] },
292
- { path: ["Video", "Pixel Shaders"], plugins: ["vestra.video"] },
293
- {
294
- path: ["Video", "Pixel Shaders", "HDR Tone Mapping"],
295
- plugins: ["vestra.video"],
296
- },
297
- { path: ["Video", "Rotation"], plugins: ["vestra.video"] },
298
278
  { path: ["Video", "Snapshot"], plugins: ["vestra.capture"] },
299
- { path: ["Audio", "Audio Sync"], plugins: ["vestra.audio"] },
300
- { path: ["Audio", "Audio Track"], plugins: ["vestra.audio"] },
301
- { path: ["Audio", "Output Device"], plugins: ["vestra.audio"] },
302
- { path: ["Audio", "Preset"], plugins: ["vestra.audio"] },
303
- { path: ["Audio", "Processing"], plugins: ["vestra.audio"] },
304
279
  { path: ["Screen", "Always on Top"], plugins: ["vestra.window"] },
305
280
  { path: ["Screen", "When Playback Starts"], plugins: ["vestra.window"] },
306
281
  {
@@ -309,6 +284,8 @@ export const BUILTIN_SUBMENUS = Object.freeze([
309
284
  },
310
285
  { path: ["Settings", "Language"], plugins: ["vestra.preferences"] },
311
286
  { path: ["Copy to Clipboard"], plugins: ["vestra.playback"] },
287
+ { path: ["Subtitle Track"], plugins: ["vestra.subtitles"] },
288
+ { path: ["Audio Track"], plugins: ["vestra.audio"] },
312
289
  ]);
313
290
  /** owner | id | key path, or (title) outside the menu | bindings | panel */
314
291
  const BUILTIN_COMMAND_TABLE = `
@@ -330,31 +307,6 @@ vestra.playback | copy-path | Copy to Clipboard ▸ Full Path
330
307
  host | play-pause | Playback ▸ Play/Pause | Space, Mouse:LeftClick
331
308
  host | previous | Playback ▸ Previous | PageUp, Mouse:X2
332
309
  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
- vestra.playback | keyframe-back | Playback ▸ Seek ▸ Previous Keyframe | Ctrl+Shift+ArrowLeft
336
- 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
- vestra.playback | backward-2 | Playback ▸ Seek ▸ Back 30 s | Ctrl+ArrowLeft
340
- vestra.playback | forward-2 | Playback ▸ Seek ▸ Forward 30 s | Ctrl+ArrowRight
341
- vestra.playback | backward-3 | Playback ▸ Seek ▸ Back 60 s | Shift+ArrowLeft
342
- vestra.playback | forward-3 | Playback ▸ Seek ▸ Forward 60 s | Shift+ArrowRight
343
- vestra.playback | backward-4 | Playback ▸ Seek ▸ Back 300 s | Ctrl+Alt+ArrowLeft
344
- vestra.playback | forward-4 | Playback ▸ Seek ▸ Forward 300 s | Ctrl+Alt+ArrowRight
345
- vestra.subtitles | dialogue-previous | Playback ▸ Seek ▸ Previous Dialogue | Home
346
- vestra.subtitles | dialogue-next | Playback ▸ Seek ▸ Next Dialogue | End
347
- vestra.subtitles | dialogue-current | Playback ▸ Seek ▸ Current Subtitle Start | Ctrl+Home
348
- vestra.playback | jump-start | Playback ▸ Seek ▸ Jump to Start | Backspace
349
- vestra.playback | time-jump | Playback ▸ Seek ▸ Seek Steps…
350
- 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
- vestra.playback | rate-05 | Playback ▸ Speed ▸ 0.5×
355
- vestra.playback | rate-15 | Playback ▸ Speed ▸ 1.5×
356
- vestra.playback | rate-20 | Playback ▸ Speed ▸ 2×
357
- vestra.playback | rate-custom | Playback ▸ Speed ▸ Custom…
358
310
  vestra.playlist | playlist.add | Playback ▸ Playlist ▸ Add Files…
359
311
  vestra.playlist | playlist.link | Playback ▸ Playlist ▸ Add Link…
360
312
  vestra.playlist | playlist.add-folder | Playback ▸ Playlist ▸ Add Folder…
@@ -404,139 +356,19 @@ vestra.marks | ab.count.2 | Playback ▸ A-B Loop ▸ Loop Count ▸ 2 Times
404
356
  vestra.marks | ab.count.3 | Playback ▸ A-B Loop ▸ Loop Count ▸ 3 Times
405
357
  vestra.marks | ab.count.5 | Playback ▸ A-B Loop ▸ Loop Count ▸ 5 Times
406
358
  vestra.marks | ab.count.10 | Playback ▸ A-B Loop ▸ Loop Count ▸ 10 Times
407
- vestra.subtitles | show | Subtitles ▸ Show Subtitles | Alt+H
408
- vestra.subtitles | track-off | Subtitles ▸ Subtitle Track ▸ Off
409
- vestra.subtitles | track-cycle | Subtitles ▸ Subtitle Track ▸ Cycle | Alt+L
410
- vestra.subtitles | second-off | Subtitles ▸ Secondary Subtitle ▸ Off
411
- vestra.subtitles | second-position-top | Subtitles ▸ Secondary Subtitle ▸ Position ▸ Top
412
- vestra.subtitles | second-position-aboveMain | Subtitles ▸ Secondary Subtitle ▸ Position ▸ Above Main Subtitle
413
- vestra.subtitles | bold | Subtitles ▸ Subtitle Style ▸ Bold | Alt+B
414
- vestra.subtitles | size-up | Subtitles ▸ Subtitle Style ▸ Size + | Alt+PageUp
415
- vestra.subtitles | size-down | Subtitles ▸ Subtitle Style ▸ Size − | Alt+PageDown
416
- vestra.subtitles | move-up | Subtitles ▸ Subtitle Style ▸ Move Up
417
- vestra.subtitles | move-down | Subtitles ▸ Subtitle Style ▸ Move Down
418
- vestra.subtitles | shadow | Subtitles ▸ Subtitle Style ▸ Shadow
419
- vestra.subtitles | box | Subtitles ▸ Subtitle Style ▸ Opaque Box
420
- vestra.subtitles | ass-original | Subtitles ▸ Original ASS/SSA Styles
421
- vestra.subtitles | sync-earlier | Subtitles ▸ Sync ▸ Earlier 0.5 s | .
422
- vestra.subtitles | sync-later | Subtitles ▸ Sync ▸ Later 0.5 s | ,
423
- vestra.subtitles | sync-input | Subtitles ▸ Sync ▸ Adjust Sync…
424
359
  vestra.subtitle-online | search | Subtitles ▸ Online Subtitles ▸ Search and Download Subtitles…
425
360
  vestra.subtitle-online | settings | Subtitles ▸ Online Subtitles ▸ Online Subtitle Settings…
426
- vestra.subtitles | reload | Subtitles ▸ Reload Subtitles | Ctrl+Alt+Y
427
- vestra.subtitles | refresh | Subtitles ▸ Refresh Subtitle Tracks
428
- vestra.subtitles | tune | Subtitles ▸ Subtitle Tuning…
429
- vestra.subtitles | settings | Subtitles ▸ Subtitle Settings…
430
- vestra.video | video.shader.use | Video ▸ Pixel Shaders ▸ Use Shaders | Shift+P
431
- vestra.video | video.shader.list | Video ▸ Pixel Shaders ▸ Shader List…
432
- vestra.video | video.shader.folder | Video ▸ Pixel Shaders ▸ Open Shader Folder
433
- vestra.video | video.shader.reload | Video ▸ Pixel Shaders ▸ Reload Shaders
434
- vestra.video | video.hdr.auto | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Auto
435
- vestra.video | video.hdr.off | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Off
436
- vestra.video | video.hdr.bt.2390 | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ BT.2390
437
- vestra.video | video.hdr.hable | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Hable
438
- vestra.video | video.hdr.mobius | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Mobius
439
- vestra.video | video.hdr.reinhard | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Reinhard
440
- vestra.video | video.hdr.clip | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Clip
441
- vestra.video | video.hdr.peak | Video ▸ Pixel Shaders ▸ HDR Tone Mapping ▸ Dynamic Peak Detection
442
- vestra.video | video.hdr.settings | Video ▸ Pixel Shaders ▸ HDR Settings…
443
- vestra.video | video.deinterlace.cycle | Video ▸ Deinterlace ▸ Cycle | Ctrl+I
444
- vestra.video | video.deinterlace.auto | Video ▸ Deinterlace ▸ Auto
445
- vestra.video | video.deinterlace.on | Video ▸ Deinterlace ▸ Always
446
- vestra.video | video.deinterlace.off | Video ▸ Deinterlace ▸ Never
447
- vestra.video | video.deinterlace.output.frame | Video ▸ Deinterlace ▸ Output ▸ By Frame
448
- vestra.video | video.deinterlace.output.field | Video ▸ Deinterlace ▸ Output ▸ By Field (Double Rate)
449
- vestra.video | video.fit.keep | Video ▸ Aspect ▸ Keep Aspect Ratio
450
- vestra.video | video.fit.stretch | Video ▸ Aspect ▸ Ignore Aspect Ratio
451
- vestra.video | video.fit.fill | Video ▸ Aspect ▸ Fill Window (Crop Excess)
452
- vestra.video | video.aspect.cycle | Video ▸ Aspect ▸ Cycle Aspect Ratio | Shift+F6
453
- vestra.video | video.aspect.default | Video ▸ Aspect ▸ Default (Source)
454
- vestra.video | video.aspect.0 | Video ▸ Aspect ▸ 4:3
455
- vestra.video | video.aspect.1 | Video ▸ Aspect ▸ 16:9
456
- vestra.video | video.aspect.2 | Video ▸ Aspect ▸ 16:10
457
- vestra.video | video.aspect.3 | Video ▸ Aspect ▸ 1.85:1
458
- vestra.video | video.aspect.4 | Video ▸ Aspect ▸ 2.35:1
459
- vestra.video | video.aspect.custom | Video ▸ Aspect ▸ Custom (x:y)
460
- vestra.video | video.aspect.edit | Video ▸ Aspect ▸ Custom Aspect Ratio…
461
- vestra.video | video.reframe.none | Video ▸ Crop/Stretch ▸ None
462
- vestra.video | video.reframe.pad.0 | Video ▸ Crop/Stretch ▸ Stretch to 4:3
463
- vestra.video | video.reframe.pad.1 | Video ▸ Crop/Stretch ▸ Stretch to 16:9
464
- vestra.video | video.reframe.pad.2 | Video ▸ Crop/Stretch ▸ Stretch to 16:10
465
- vestra.video | video.reframe.pad.3 | Video ▸ Crop/Stretch ▸ Stretch to 1.85:1
466
- vestra.video | video.reframe.pad.4 | Video ▸ Crop/Stretch ▸ Stretch to 2.35:1
467
- vestra.video | video.reframe.crop.0 | Video ▸ Crop/Stretch ▸ Crop to 4:3
468
- vestra.video | video.reframe.crop.1 | Video ▸ Crop/Stretch ▸ Crop to 16:9
469
- vestra.video | video.reframe.crop.2 | Video ▸ Crop/Stretch ▸ Crop to 16:10
470
- vestra.video | video.reframe.crop.3 | Video ▸ Crop/Stretch ▸ Crop to 1.85:1
471
- vestra.video | video.reframe.crop.4 | Video ▸ Crop/Stretch ▸ Crop to 2.35:1
472
- vestra.video | video.reframe.left | Video ▸ Crop/Stretch ▸ Move Left
473
- vestra.video | video.reframe.right | Video ▸ Crop/Stretch ▸ Move Right
474
- vestra.video | video.reframe.up | Video ▸ Crop/Stretch ▸ Move Up
475
- vestra.video | video.reframe.down | Video ▸ Crop/Stretch ▸ Move Down
476
- vestra.video | video.frame.zoom-in | Video ▸ Framing ▸ Zoom In | Numpad9, Alt+Plus
477
- vestra.video | video.frame.zoom-out | Video ▸ Framing ▸ Zoom Out | Numpad1, Alt+-
478
- vestra.video | video.frame.right | Video ▸ Framing ▸ Pan → | Ctrl+Numpad6, Alt+ArrowRight
479
- vestra.video | video.frame.left | Video ▸ Framing ▸ Pan ← | Ctrl+Numpad4, Alt+ArrowLeft
480
- vestra.video | video.frame.up | Video ▸ Framing ▸ Pan ↑ | Ctrl+Numpad8, Alt+ArrowUp
481
- vestra.video | video.frame.down | Video ▸ Framing ▸ Pan ↓ | Ctrl+Numpad2, Alt+ArrowDown
482
- vestra.video | video.frame.centre | Video ▸ Framing ▸ Center Frame | Ctrl+Numpad5
483
- vestra.video | video.flip.vertical | Video ▸ Image Processing ▸ Flip Vertically | Ctrl+P
484
- vestra.video | video.flip.horizontal | Video ▸ Image Processing ▸ Flip Horizontally | Ctrl+Z
485
- vestra.video | video.gaussian | Video ▸ Image Processing ▸ Gaussian Blur | Ctrl+B
486
- vestra.video | video.sharpen | Video ▸ Image Processing ▸ Sharpen | Ctrl+R
487
- vestra.video | video.edge | Video ▸ Image Processing ▸ Edge Sharpen
488
- vestra.video | video.deblock | Video ▸ Image Processing ▸ Deblock | Ctrl+H
489
- vestra.video | video.deband | Video ▸ Image Processing ▸ Deband
490
- vestra.video | video.denoise.d3 | Video ▸ Image Processing ▸ 3D Denoise | Ctrl+N
491
- vestra.video | video.denoise.gradual | Video ▸ Image Processing ▸ Gradual Denoise
492
- vestra.video | video.denoise.temporal | Video ▸ Image Processing ▸ Temporal Denoise | Ctrl+M
493
- vestra.video | video.frame-blend | Video ▸ Image Processing ▸ Frame Doubling
494
- vestra.video | video.interpolation | Video ▸ Image Processing ▸ Motion Interpolation
495
- vestra.video | video.rotate.cycle | Video ▸ Rotation ▸ Cycle | Alt+K
496
- vestra.video | video.rotate.0 | Video ▸ Rotation ▸ No Rotation
497
- vestra.video | video.rotate.90 | Video ▸ Rotation ▸ 90°
498
- vestra.video | video.rotate.180 | Video ▸ Rotation ▸ 180°
499
- vestra.video | video.rotate.270 | Video ▸ Rotation ▸ 270°
361
+ vestra.subtitles | track-off | Subtitle Track ▸ Off
500
362
  vestra.capture | capture.save | Video ▸ Snapshot ▸ Save Current Frame | Ctrl+E
501
363
  vestra.capture | capture.panel | Video ▸ Snapshot ▸ Capture… | Alt+S
502
364
  vestra.capture | capture.copy | Video ▸ Snapshot ▸ Copy Current Frame | Ctrl+C
503
365
  vestra.capture | capture.burst | Video ▸ Snapshot ▸ Burst… | Ctrl+G
504
366
  vestra.capture | capture.clip | Video ▸ Snapshot ▸ Export Clip…
505
- vestra.video | video.picture | Video ▸ Picture Adjustments…
506
- vestra.video | video.brightness.down | Video ▸ Brightness -1% | W
507
- vestra.video | video.brightness.up | Video ▸ Brightness +1% | E
508
- vestra.video | video.contrast.down | Video ▸ Contrast -1% | R
509
- vestra.video | video.contrast.up | Video ▸ Contrast +1% | T
510
- vestra.video | video.saturation.down | Video ▸ Saturation -1% | Y
511
- vestra.video | video.saturation.up | Video ▸ Saturation +1% | U
512
- vestra.video | video.hue.down | Video ▸ Hue -1% | I
513
- vestra.video | video.hue.up | Video ▸ Hue +1% | O
514
- vestra.video | video.gpu-rendering | Video ▸ GPU Rendering (Experimental)
515
367
  host | volume-up | Audio ▸ Volume + | ArrowUp, Mouse:WheelUp
516
368
  host | volume-down | Audio ▸ Volume − | ArrowDown, Mouse:WheelDown
517
369
  host | mute | Audio ▸ Mute | M
518
- vestra.audio | audio.workbench | Audio ▸ Audio Workbench…
519
- vestra.audio | audio.track.none | Audio ▸ Audio Track ▸ No Audio Tracks
520
- vestra.audio | audio.track.cycle | Audio ▸ Audio Track ▸ Cycle Tracks | Alt+A
521
- vestra.audio | audio.channels.stereo | Audio ▸ Audio Track ▸ Stereo
522
- vestra.audio | audio.channels.left | Audio ▸ Audio Track ▸ Left Channel
523
- vestra.audio | audio.channels.right | Audio ▸ Audio Track ▸ Right Channel
524
- vestra.audio | audio.channels.mono | Audio ▸ Audio Track ▸ Mono Mix
525
- vestra.audio | audio.channels.swap | Audio ▸ Audio Track ▸ Swap Left and Right
526
- vestra.audio | audio.device.none | Audio ▸ Output Device ▸ No Output Devices
527
- vestra.audio | audio.equalizer | Audio ▸ Equalizer | Shift+E
528
- vestra.audio | audio.preset.0 | Audio ▸ Preset ▸ Flat
529
- vestra.audio | audio.preset.1 | Audio ▸ Preset ▸ Voice
530
- vestra.audio | audio.preset.2 | Audio ▸ Preset ▸ Music
531
- vestra.audio | audio.preset.3 | Audio ▸ Preset ▸ Cinema
532
- vestra.audio | audio.sync.earlier | Audio ▸ Audio Sync ▸ 50 ms Earlier | >
533
- vestra.audio | audio.sync.later | Audio ▸ Audio Sync ▸ 50 ms Later | <
534
- vestra.audio | audio.sync.keep.none | Audio ▸ Audio Sync ▸ Don't Keep
535
- vestra.audio | audio.sync.keep.session | Audio ▸ Audio Sync ▸ Keep for This Run
536
- vestra.audio | audio.sync.keep.always | Audio ▸ Audio Sync ▸ Always Keep
537
- vestra.audio | audio.normalize | Audio ▸ Processing ▸ Normalize | Shift+N
538
- vestra.audio | audio.clarity | Audio ▸ Processing ▸ Crystalizer | Shift+C
539
370
  vestra.visualizer | audio-cover | Audio ▸ Show Album Cover for Audio Only
371
+ vestra.audio | audio.track.none | Audio Track ▸ No Audio Tracks
540
372
  host | fullscreen | Toggle Fullscreen | Escape
541
373
  vestra.window | pip | Screen ▸ Picture-in-Picture
542
374
  vestra.window | mini | Screen ▸ Mini Size | \`
@@ -557,6 +389,10 @@ vestra.window | top.never | Screen ▸ Always on Top ▸ Never
557
389
  vestra.window | top.always | Screen ▸ Always on Top ▸ Always
558
390
  vestra.window | top.playing | Screen ▸ Always on Top ▸ While Playing
559
391
  vestra.window | top.fullscreen | Screen ▸ Always on Top ▸ While Playing in Fullscreen
392
+ vestra.video | video.picture | Picture… | Alt+1
393
+ vestra.audio | audio.workbench | Audio… | Alt+2
394
+ vestra.subtitles | tune | Subtitles… | Alt+3
395
+ vestra.playback | panel | Playback… | Alt+4
560
396
  vestra.playlist | playlist.visibility | Playlist | F6
561
397
  vestra.info | info.toggle | Playback Info | Tab
562
398
  vestra.info | info.properties | Playback Properties… | Ctrl+F1
@@ -578,13 +414,6 @@ vestra.preferences | language.ko | Settings ▸ Language ▸ 한국어
578
414
  vestra.preferences | language.de | Settings ▸ Language ▸ Deutsch
579
415
  vestra.preferences | language.fr | Settings ▸ Language ▸ Français
580
416
  vestra.preferences | language.es | Settings ▸ Language ▸ Español
581
- vestra.subtitles | move-reset | Reset ▸ Subtitle Position | Alt+Home
582
- vestra.subtitles | sync-reset | Reset ▸ Subtitle Sync | /
583
- vestra.video | video.reset | Reset ▸ Picture | Q
584
- vestra.video | video.frame.reset | Reset ▸ Framing | Numpad5, Alt+Backspace
585
- vestra.video | video.reframe.reset | Reset ▸ Crop Position
586
- vestra.audio | audio.sync.reset | Reset ▸ Audio Sync | ?
587
- vestra.audio | audio.reset | Reset ▸ Audio Processing
588
417
  vestra.visualizer | reset-logo | Reset ▸ Idle Logo
589
418
  vestra.info | info.about | About Vestra Player… | F1
590
419
  vestra.info | info.update | Check for Updates…
@@ -594,6 +423,33 @@ host | exit-fullscreen | (Leave Fullscreen)
594
423
  host | sidebar | (Toggle Sidebar)
595
424
  host | palette | (Command Palette) | Ctrl+Shift+P
596
425
  host | adjust | (Adjust)
426
+ host | frame-back | (Previous Frame) | D
427
+ host | frame-forward | (Next Frame) | F
428
+ host | seek-backward | (Back 5 s) | ArrowLeft
429
+ host | seek-forward | (Forward 5 s) | ArrowRight
430
+ host | rate-normal | (Normal) | Z
431
+ host | rate-down | (Slower 0.1) | X
432
+ host | rate-up | (Faster 0.1) | C
433
+ vestra.audio | audio.track.cycle | (Cycle Tracks) | Alt+A
434
+ vestra.audio | audio.channels.stereo | (Stereo)
435
+ vestra.audio | audio.channels.left | (Left Channel)
436
+ vestra.audio | audio.channels.right | (Right Channel)
437
+ vestra.audio | audio.channels.mono | (Mono Mix)
438
+ vestra.audio | audio.channels.swap | (Swap Left and Right)
439
+ vestra.audio | audio.equalizer | (Equalizer) | Shift+E
440
+ vestra.audio | audio.preset.0 | (Flat)
441
+ vestra.audio | audio.preset.1 | (Voice)
442
+ vestra.audio | audio.preset.2 | (Music)
443
+ vestra.audio | audio.preset.3 | (Cinema)
444
+ vestra.audio | audio.sync.earlier | (50 ms Earlier) | >
445
+ vestra.audio | audio.sync.later | (50 ms Later) | <
446
+ vestra.audio | audio.sync.reset | (Reset) | ?
447
+ vestra.audio | audio.sync.keep.none | (Don't Keep)
448
+ vestra.audio | audio.sync.keep.session | (Keep for This Run)
449
+ vestra.audio | audio.sync.keep.always | (Always Keep)
450
+ vestra.audio | audio.normalize | (Normalize) | Shift+N
451
+ vestra.audio | audio.clarity | (Crystalizer) | Shift+C
452
+ vestra.audio | audio.reset | (Reset)
597
453
  vestra.capture | capture.clipExport | (Export Clip Range) | Ctrl+Shift+E
598
454
  vestra.capture | capture.saveAs | (Save Current Frame As…)
599
455
  vestra.capture | capture.saveScreen | (Save Rendered Frame) | Ctrl+Alt+E
@@ -615,6 +471,21 @@ vestra.capture | capture.clipStart | (Set Clip Start)
615
471
  vestra.capture | capture.clipEnd | (Set Clip End)
616
472
  vestra.capture | capture.last30 | (Export Last 30 Seconds) | Ctrl+Shift+R
617
473
  vestra.info | info.brief | (Brief Info) | Shift+Tab
474
+ vestra.playback | keyframe-back | (Previous Keyframe) | Ctrl+Shift+ArrowLeft
475
+ vestra.playback | keyframe-forward | (Next Keyframe) | Ctrl+Shift+ArrowRight
476
+ vestra.playback | jump | (Jump to Time…) | G
477
+ vestra.playback | jump-start | (Jump to Start) | Backspace
478
+ vestra.playback | time-jump | (Seek Steps…)
479
+ vestra.playback | rate-05 | (0.5×)
480
+ vestra.playback | rate-15 | (1.5×)
481
+ vestra.playback | rate-20 | (2×)
482
+ vestra.playback | rate-custom | (Custom…)
483
+ vestra.playback | backward-2 | (Back 30 s) | Ctrl+ArrowLeft
484
+ vestra.playback | forward-2 | (Forward 30 s) | Ctrl+ArrowRight
485
+ vestra.playback | backward-3 | (Back 60 s) | Shift+ArrowLeft
486
+ vestra.playback | forward-3 | (Forward 60 s) | Shift+ArrowRight
487
+ vestra.playback | backward-4 | (Back 300 s) | Ctrl+Alt+ArrowLeft
488
+ vestra.playback | forward-4 | (Forward 300 s) | Ctrl+Alt+ArrowRight
618
489
  vestra.playlist | playlist.play | (Play) | Enter, P | panel
619
490
  vestra.playlist | playlist.list-add | (Add Files…) | Ctrl+I | panel
620
491
  vestra.playlist | playlist.list-add-folder | (Add Folder…) | Ctrl+O | panel
@@ -646,6 +517,114 @@ vestra.playlist | playlist.list-open-list | (Open List File…) | F3 | panel
646
517
  vestra.playlist | playlist.list-save-list | (Save List…) | F2 | panel
647
518
  vestra.playlist | playlist.close | (Close List) | F6 | panel
648
519
  vestra.playlist | playlist.settings | (Playlist Settings…) | F5 | panel
520
+ vestra.subtitles | show | (Show Subtitles) | Alt+H
521
+ vestra.subtitles | second-off | (Off)
522
+ vestra.subtitles | second-position-top | (Top)
523
+ vestra.subtitles | second-position-aboveMain | (Above Main Subtitle)
524
+ vestra.subtitles | track-cycle | (Cycle) | Alt+L
525
+ vestra.subtitles | size-up | (Size +) | Alt+PageUp
526
+ vestra.subtitles | size-down | (Size −) | Alt+PageDown
527
+ vestra.subtitles | move-up | (Move Up)
528
+ vestra.subtitles | move-down | (Move Down)
529
+ vestra.subtitles | move-reset | (Subtitle Position) | Alt+Home
530
+ vestra.subtitles | bold | (Bold) | Alt+B
531
+ vestra.subtitles | shadow | (Shadow)
532
+ vestra.subtitles | box | (Opaque Box)
533
+ vestra.subtitles | ass-original | (Original ASS/SSA Styles)
534
+ vestra.subtitles | dialogue-previous | (Previous Dialogue) | Home
535
+ vestra.subtitles | dialogue-next | (Next Dialogue) | End
536
+ vestra.subtitles | dialogue-current | (Current Subtitle Start) | Ctrl+Home
537
+ vestra.subtitles | sync-reset | (Subtitle Sync) | /
538
+ vestra.subtitles | sync-input | (Adjust Sync…)
539
+ vestra.subtitles | reload | (Reload Subtitles) | Ctrl+Alt+Y
540
+ vestra.subtitles | refresh | (Refresh Subtitle Tracks)
541
+ vestra.subtitles | remove | (Remove External Subtitle)
542
+ vestra.subtitles | settings | (Subtitle Settings…)
543
+ vestra.subtitles | sync-earlier | (Earlier 0.5 s) | .
544
+ vestra.subtitles | sync-later | (Later 0.5 s) | ,
545
+ vestra.video | video.shader.use | (Use Shaders) | Shift+P
546
+ vestra.video | video.shader.list | (Shader List…)
547
+ vestra.video | video.shader.folder | (Open Shader Folder)
548
+ vestra.video | video.shader.reload | (Reload Shaders)
549
+ vestra.video | video.hdr.auto | (Auto)
550
+ vestra.video | video.hdr.off | (Off)
551
+ vestra.video | video.hdr.bt.2390 | (BT.2390)
552
+ vestra.video | video.hdr.hable | (Hable)
553
+ vestra.video | video.hdr.mobius | (Mobius)
554
+ vestra.video | video.hdr.reinhard | (Reinhard)
555
+ vestra.video | video.hdr.clip | (Clip)
556
+ vestra.video | video.hdr.peak | (Dynamic Peak Detection)
557
+ vestra.video | video.hdr.settings | (HDR Settings…)
558
+ vestra.video | video.deinterlace.cycle | (Cycle) | Ctrl+I
559
+ vestra.video | video.deinterlace.auto | (Auto)
560
+ vestra.video | video.deinterlace.on | (Always)
561
+ vestra.video | video.deinterlace.off | (Never)
562
+ vestra.video | video.deinterlace.output.frame | (By Frame)
563
+ vestra.video | video.deinterlace.output.field | (By Field (Double Rate))
564
+ vestra.video | video.fit.keep | (Keep Aspect Ratio)
565
+ vestra.video | video.fit.stretch | (Ignore Aspect Ratio)
566
+ vestra.video | video.fit.fill | (Fill Window (Crop Excess))
567
+ vestra.video | video.aspect.cycle | (Cycle Aspect Ratio) | Shift+F6
568
+ vestra.video | video.aspect.default | (Default (Source))
569
+ vestra.video | video.aspect.0 | (4:3)
570
+ vestra.video | video.aspect.1 | (16:9)
571
+ vestra.video | video.aspect.2 | (16:10)
572
+ vestra.video | video.aspect.3 | (1.85:1)
573
+ vestra.video | video.aspect.4 | (2.35:1)
574
+ vestra.video | video.aspect.custom | (Custom (x:y))
575
+ vestra.video | video.aspect.edit | (Custom Aspect Ratio…)
576
+ vestra.video | video.reframe.none | (None)
577
+ vestra.video | video.reframe.pad.0 | (Stretch to 4:3)
578
+ vestra.video | video.reframe.pad.1 | (Stretch to 16:9)
579
+ vestra.video | video.reframe.pad.2 | (Stretch to 16:10)
580
+ vestra.video | video.reframe.pad.3 | (Stretch to 1.85:1)
581
+ vestra.video | video.reframe.pad.4 | (Stretch to 2.35:1)
582
+ vestra.video | video.reframe.crop.0 | (Crop to 4:3)
583
+ vestra.video | video.reframe.crop.1 | (Crop to 16:9)
584
+ vestra.video | video.reframe.crop.2 | (Crop to 16:10)
585
+ vestra.video | video.reframe.crop.3 | (Crop to 1.85:1)
586
+ vestra.video | video.reframe.crop.4 | (Crop to 2.35:1)
587
+ vestra.video | video.reframe.left | (Move Left)
588
+ vestra.video | video.reframe.right | (Move Right)
589
+ vestra.video | video.reframe.up | (Move Up)
590
+ vestra.video | video.reframe.down | (Move Down)
591
+ vestra.video | video.reframe.reset | (Crop Position)
592
+ vestra.video | video.frame.zoom-in | (Zoom In) | Alt+Plus, Numpad9
593
+ vestra.video | video.frame.zoom-out | (Zoom Out) | Alt+-, Numpad1
594
+ vestra.video | video.frame.right | (Pan →) | Alt+ArrowRight, Ctrl+Numpad6
595
+ vestra.video | video.frame.left | (Pan ←) | Alt+ArrowLeft, Ctrl+Numpad4
596
+ vestra.video | video.frame.up | (Pan ↑) | Alt+ArrowUp, Ctrl+Numpad8
597
+ vestra.video | video.frame.down | (Pan ↓) | Alt+ArrowDown, Ctrl+Numpad2
598
+ vestra.video | video.frame.centre | (Center Frame) | Ctrl+Numpad5
599
+ vestra.video | video.frame.reset | (Framing) | Alt+Backspace, Numpad5
600
+ vestra.video | video.flip.vertical | (Flip Vertically) | Ctrl+P
601
+ vestra.video | video.flip.horizontal | (Flip Horizontally) | Ctrl+Z
602
+ vestra.video | video.gaussian | (Gaussian Blur) | Ctrl+B
603
+ vestra.video | video.sharpen | (Sharpen) | Ctrl+R
604
+ vestra.video | video.edge | (Edge Sharpen)
605
+ vestra.video | video.deblock | (Deblock) | Ctrl+H
606
+ vestra.video | video.deband | (Deband)
607
+ vestra.video | video.denoise.d3 | (3D Denoise) | Ctrl+N
608
+ vestra.video | video.denoise.gradual | (Gradual Denoise)
609
+ vestra.video | video.denoise.temporal | (Temporal Denoise) | Ctrl+M
610
+ vestra.video | video.frame-blend | (Frame Doubling)
611
+ vestra.video | video.interpolation | (Motion Interpolation)
612
+ vestra.video | video.rotate.cycle | (Cycle) | Alt+K
613
+ vestra.video | video.rotate.0 | (No Rotation)
614
+ vestra.video | video.rotate.90 | (90°)
615
+ vestra.video | video.rotate.180 | (180°)
616
+ vestra.video | video.rotate.270 | (270°)
617
+ vestra.video | video.reset | (Picture) | Q
618
+ vestra.video | video.resetPanel | (Picture Panel)
619
+ vestra.video | video.brightness.down | (Brightness -1%) | W
620
+ vestra.video | video.brightness.up | (Brightness +1%) | E
621
+ vestra.video | video.contrast.down | (Contrast -1%) | R
622
+ vestra.video | video.contrast.up | (Contrast +1%) | T
623
+ vestra.video | video.saturation.down | (Saturation -1%) | Y
624
+ vestra.video | video.saturation.up | (Saturation +1%) | U
625
+ vestra.video | video.hue.down | (Hue -1%) | I
626
+ vestra.video | video.hue.up | (Hue +1%) | O
627
+ vestra.video | video.gpu-rendering | (GPU Rendering (Experimental))
649
628
  `;
650
629
  /**
651
630
  * The commands the player and its built-in plugins register when the
Binary file
@@ -13,7 +13,9 @@ const SDK_DIRECTORY = fileURLToPath(new URL("..", import.meta.url));
13
13
  export const WAKE_EVENTS = [
14
14
  "packages.changed",
15
15
  "bindings.changed",
16
- // Player 0.7.6: sent only to the plugin whose own keys changed.
16
+ // Player 0.7.6: sent to a plugin whose own keys changed and, from SDK
17
+ // 1.6.16, to every listener when a player command (`host/<id>`) is
18
+ // rebound, which wakes each parked listener.
17
19
  "commands.keys",
18
20
  "appearance.changed",
19
21
  "resolvers.fileTypes",
@@ -248,7 +250,15 @@ export class Runtime {
248
250
  this.directory = directory;
249
251
  transport.on("ui.callback", (data) => {
250
252
  const event = data;
251
- this.views.get(event.view)?.(event.callback, event.args);
253
+ // A panel's callback names its slot (player 0.7.6: one panel per
254
+ // slot); older players name only `"$mount"`, and the plugin has one.
255
+ const view = event.view === "$mount"
256
+ ? event.slot
257
+ ? `panel:${event.slot}`
258
+ : [...this.views.keys()].find((key) => key.startsWith("panel:"))
259
+ : event.view;
260
+ if (view)
261
+ this.views.get(view)?.(event.callback, event.args);
252
262
  });
253
263
  transport.on("commands.execute", (data) => {
254
264
  this.commands.get(data.id)?.();
@@ -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 panel (bottom bar, sidebar or backdrop), or `null`.
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
- panel: (Omit<FakeWindow, "callbacks" | "userClose"> & {
604
- /** `"bottom"`, `"sidebar"`, `"adjust"` or `"backdrop"`. */
605
- slot: string;
606
- /** The exported component the SDK found in the file. */
607
- component: string;
608
- /** The last `panel.setInteraction(...)` locks, `null` before any. */
609
- locks: Json;
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`. */
@@ -2527,8 +2557,9 @@ export declare class TestHost implements Transport {
2527
2557
  */
2528
2558
  get conflicts(): string[][];
2529
2559
  /**
2530
- * Change the keys of one of the plugin's commands as the user does in
2531
- * Keyboard Shortcuts (SDK 1.6.6): `inputs` replaces its keys (`[]`
2560
+ * Change the keys of one of the plugin's commands, or of one of the
2561
+ * player's core commands by its `host/<id>` (SDK 1.6.16), as the user
2562
+ * does in Keyboard Shortcuts (SDK 1.6.6): `inputs` replaces its keys (`[]`
2532
2563
  * clears them, as Ignore Default Shortcuts does for every default),
2533
2564
  * `null` gives it its own defaults back. It writes
2534
2565
  * {@link TestHost.bindings | bindings.overlay}, and the plugin hears it
@@ -2550,7 +2581,8 @@ export declare class TestHost implements Transport {
2550
2581
  * ```
2551
2582
  *
2552
2583
  * @capability None.
2553
- * @throws `Error` `command` is not a command of the plugin.
2584
+ * @throws `Error` `command` is neither a command of the plugin nor a
2585
+ * core command.
2554
2586
  * @since 1.6.6
2555
2587
  */
2556
2588
  rebind(command: string, inputs: readonly string[] | null): void;
@@ -2814,9 +2846,11 @@ export declare class TestHost implements Transport {
2814
2846
  */
2815
2847
  window(id: string): FakeWindow;
2816
2848
  /**
2817
- * The plugin's mounted panel (bottom bar, sidebar or backdrop). Throws when
2818
- * none is mounted. Its `props` use Slint (kebab-case) names; `emit` fires a
2819
- * callback on the panel's global.
2849
+ * The plugin's mounted panel (bottom bar, sidebar, adjust column or
2850
+ * backdrop): the one in `slot`, or without it the only one (SDK 1.6.15: a
2851
+ * plugin mounts one panel per slot; with several, name the slot). Throws
2852
+ * when there is none. Its `props` use Slint (kebab-case) names; `emit`
2853
+ * fires a callback on the panel's global.
2820
2854
  *
2821
2855
  * @example Select a row in the plugin's sidebar panel
2822
2856
  * The test checks the panel sits in the sidebar and selects its third
@@ -2835,14 +2869,7 @@ export declare class TestHost implements Transport {
2835
2869
  * @capability None.
2836
2870
  * @since 1.0.0
2837
2871
  */
2838
- mounted(): Omit<FakeWindow, "callbacks" | "userClose"> & {
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
- };
2872
+ mounted(slot?: "bottom" | "sidebar" | "adjust" | "backdrop"): FakePanel;
2846
2873
  /**
2847
2874
  * The parameters of every request of one wire method, oldest first.
2848
2875
  *
@@ -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 panel (bottom bar, sidebar or backdrop), or `null`.
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
- panel = null;
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`. */
@@ -3234,8 +3244,9 @@ export class TestHost {
3234
3244
  return this.coreCommands.has(id) ? id : `${this.manifestId}/${id}`;
3235
3245
  }
3236
3246
  /**
3237
- * Change the keys of one of the plugin's commands as the user does in
3238
- * Keyboard Shortcuts (SDK 1.6.6): `inputs` replaces its keys (`[]`
3247
+ * Change the keys of one of the plugin's commands, or of one of the
3248
+ * player's core commands by its `host/<id>` (SDK 1.6.16), as the user
3249
+ * does in Keyboard Shortcuts (SDK 1.6.6): `inputs` replaces its keys (`[]`
3239
3250
  * clears them, as Ignore Default Shortcuts does for every default),
3240
3251
  * `null` gives it its own defaults back. It writes
3241
3252
  * {@link TestHost.bindings | bindings.overlay}, and the plugin hears it
@@ -3257,11 +3268,12 @@ export class TestHost {
3257
3268
  * ```
3258
3269
  *
3259
3270
  * @capability None.
3260
- * @throws `Error` `command` is not a command of the plugin.
3271
+ * @throws `Error` `command` is neither a command of the plugin nor a
3272
+ * core command.
3261
3273
  * @since 1.6.6
3262
3274
  */
3263
3275
  rebind(command, inputs) {
3264
- if (!this.commands.has(command))
3276
+ if (!this.commands.has(command) && !this.coreCommands.has(command))
3265
3277
  throw new Error(`test host: no command ${command}`);
3266
3278
  const key = this.#bindingKey(command);
3267
3279
  if (inputs === null)
@@ -3277,15 +3289,19 @@ export class TestHost {
3277
3289
  #keysRevision = 0;
3278
3290
  /**
3279
3291
  * `commands.keys` as the player answers it: each of the plugin's own
3280
- * commands with the labels of the keys it holds now (the overlay, else
3281
- * its defaults; not a key an earlier command holds), as menus write
3282
- * them on `systemInfo.os`.
3292
+ * commands, and the player's core commands as `host/<id>` (SDK 1.6.16),
3293
+ * with the labels of the keys it holds now (the overlay, else its
3294
+ * defaults; not a key an earlier command holds), as menus write them on
3295
+ * `systemInfo.os`.
3283
3296
  */
3284
3297
  #keys() {
3285
3298
  const claims = this.#claims();
3286
3299
  const mac = this.systemInfo.os === "macos";
3287
3300
  const keys = {};
3288
- for (const [id, { declaration }] of this.commands) {
3301
+ for (const [id, { declaration }] of [
3302
+ ...this.commands,
3303
+ ...this.coreCommands,
3304
+ ]) {
3289
3305
  const scope = declaration.focus ? "panel" : "global";
3290
3306
  const current = this.bindings.overlay.get(this.#bindingKey(id)) ??
3291
3307
  declaration.bindings ??
@@ -3627,9 +3643,11 @@ export class TestHost {
3627
3643
  return window;
3628
3644
  }
3629
3645
  /**
3630
- * The plugin's mounted panel (bottom bar, sidebar or backdrop). Throws when
3631
- * none is mounted. Its `props` use Slint (kebab-case) names; `emit` fires a
3632
- * callback on the panel's global.
3646
+ * The plugin's mounted panel (bottom bar, sidebar, adjust column or
3647
+ * backdrop): the one in `slot`, or without it the only one (SDK 1.6.15: a
3648
+ * plugin mounts one panel per slot; with several, name the slot). Throws
3649
+ * when there is none. Its `props` use Slint (kebab-case) names; `emit`
3650
+ * fires a callback on the panel's global.
3633
3651
  *
3634
3652
  * @example Select a row in the plugin's sidebar panel
3635
3653
  * The test checks the panel sits in the sidebar and selects its third
@@ -3648,11 +3666,27 @@ export class TestHost {
3648
3666
  * @capability None.
3649
3667
  * @since 1.0.0
3650
3668
  */
3651
- mounted() {
3669
+ mounted(slot) {
3670
+ if (slot !== undefined) {
3671
+ const panel = this.panels.get(slot);
3672
+ if (!panel)
3673
+ throw new Error(`no panel mounted in the ${slot}`);
3674
+ return panel;
3675
+ }
3676
+ if (this.panels.size > 1)
3677
+ throw new Error(`panels are mounted in ${[...this.panels.keys()].join(", ")}: name the slot`);
3652
3678
  if (!this.panel)
3653
3679
  throw new Error("no panel mounted");
3654
3680
  return this.panel;
3655
3681
  }
3682
+ /** The panel a request is about: its `slot`, else the only one. */
3683
+ #panelFor(p) {
3684
+ const slot = typeof p.slot === "string" ? p.slot : undefined;
3685
+ const panel = slot ? this.panels.get(slot) : this.panel;
3686
+ if (!panel)
3687
+ throw new HostError("missing", "owner has no ready mount");
3688
+ return panel;
3689
+ }
3656
3690
  /**
3657
3691
  * The parameters of every request of one wire method, oldest first.
3658
3692
  *
@@ -4260,16 +4294,18 @@ export class TestHost {
4260
4294
  // A sidebar panel is never live (player 0.7.3): the current tab keeps
4261
4295
  // the plugin running as busy work, a background tab not at all. The
4262
4296
  // adjust column's current tab only while the column is open (0.7.6).
4263
- if (this.panel?.slot === "sidebar") {
4264
- if (this.layout.sidebar?.owner === 1)
4265
- reasons.push("its sidebar tab is the current tab");
4266
- }
4267
- else if (this.panel?.slot === "adjust") {
4268
- if (this.layout.adjust?.owner === 1 && this.layout.adjust.preferred)
4269
- reasons.push("its adjust tab is the current tab");
4297
+ for (const panel of this.panels.values()) {
4298
+ if (panel.slot === "sidebar") {
4299
+ if (this.layout.sidebar?.owner === 1)
4300
+ reasons.push("its sidebar tab is the current tab");
4301
+ }
4302
+ else if (panel.slot === "adjust") {
4303
+ if (this.layout.adjust?.owner === 1 && this.layout.adjust.preferred)
4304
+ reasons.push("its adjust tab is the current tab");
4305
+ }
4306
+ else
4307
+ reasons.push("a mounted panel");
4270
4308
  }
4271
- else if (this.panel)
4272
- reasons.push("a mounted panel");
4273
4309
  for (const [id, window] of this.windows)
4274
4310
  if (window.visible)
4275
4311
  reasons.push(`an open window (${id})`);
@@ -4501,10 +4537,11 @@ export class TestHost {
4501
4537
  throw new HostError("missing", "view not found");
4502
4538
  },
4503
4539
  "ui.mount": (p) => {
4504
- if (this.panel)
4505
- throw new HostError("conflict", "owner already mounted");
4506
4540
  if (!(String(p.slot) in SLOT_LABELS))
4507
4541
  throw new HostError("invalid", "unknown mount slot");
4542
+ // One panel per slot and plugin (player 0.7.6).
4543
+ if (this.panels.has(String(p.slot)))
4544
+ throw new HostError("conflict", "this plugin already has a panel in this slot");
4508
4545
  // Like the player: one panel in the bottom bar and the backdrop, any
4509
4546
  // number of tabs in the sidebar (player 0.7.3; 0.4.x to 0.7.2 took 8)
4510
4547
  // and in the adjust column (player 0.7.6).
@@ -4520,10 +4557,11 @@ export class TestHost {
4520
4557
  ]));
4521
4558
  const properties = this.#initialValues(this.#shownValues(p.properties));
4522
4559
  this.#seeImages(properties);
4523
- this.#resetWrites("$mount");
4560
+ const panelSlot = String(p.slot);
4561
+ this.#resetWrites(mountView(panelSlot));
4524
4562
  const host = this;
4525
- this.panel = {
4526
- slot: String(p.slot),
4563
+ this.panels.set(panelSlot, {
4564
+ slot: panelSlot,
4527
4565
  file: String(p.file),
4528
4566
  component: String(p.component),
4529
4567
  visible: true,
@@ -4532,11 +4570,12 @@ export class TestHost {
4532
4570
  emit(callback, ...args) {
4533
4571
  host.emit("ui.callback", {
4534
4572
  view: "$mount",
4573
+ slot: panelSlot,
4535
4574
  callback: kebab(callback),
4536
4575
  args,
4537
4576
  });
4538
4577
  },
4539
- };
4578
+ });
4540
4579
  if (p.slot === "backdrop") {
4541
4580
  this.backdrop = { owner: this.manifestId, effective: true };
4542
4581
  return { status: "ready", prepareMs: 0, shellGeneration: 1 };
@@ -4551,18 +4590,24 @@ export class TestHost {
4551
4590
  });
4552
4591
  return { status: "ready", prepareMs: 0, shellGeneration: 1 };
4553
4592
  },
4554
- "ui.unmount": () => {
4555
- if (this.panel?.slot === "backdrop")
4556
- this.backdrop = null;
4557
- else if (this.panel)
4558
- this.#removePanel(this.panel.slot, 1);
4559
- this.panel = null;
4593
+ "ui.unmount": (p) => {
4594
+ // The slot's panel, or without one every panel (as the player).
4595
+ const slots = typeof p.slot === "string" ? [p.slot] : [...this.panels.keys()];
4596
+ for (const slot of slots) {
4597
+ if (!this.panels.delete(slot))
4598
+ continue;
4599
+ if (slot === "backdrop")
4600
+ this.backdrop = null;
4601
+ else
4602
+ this.#removePanel(slot, 1);
4603
+ }
4560
4604
  },
4561
4605
  "ui.menu": (p) => {
4562
- if (!this.panel)
4606
+ if (!this.panels.size)
4563
4607
  throw new HostError("unavailable", "panel not shown");
4564
- const tabs = this.panel.slot === "sidebar" || this.panel.slot === "adjust"
4565
- ? this.layout[this.panel.slot]
4608
+ const panel = this.#panelFor(p);
4609
+ const tabs = panel.slot === "sidebar" || panel.slot === "adjust"
4610
+ ? this.layout[panel.slot]
4566
4611
  : null;
4567
4612
  if (tabs && tabs.owner !== 1)
4568
4613
  throw new HostError("unavailable", "the panel's tab is not the current one");
@@ -4613,8 +4658,9 @@ export class TestHost {
4613
4658
  : collapsed
4614
4659
  ? "fullscreen"
4615
4660
  : null;
4616
- if (this.panel?.slot === slot)
4617
- this.panel.visible = p.visible;
4661
+ const panel = this.panels.get(slot);
4662
+ if (panel)
4663
+ panel.visible = p.visible;
4618
4664
  }
4619
4665
  if (typeof p.detached === "boolean")
4620
4666
  state.detached = p.detached;
@@ -4880,9 +4926,9 @@ export class TestHost {
4880
4926
  this.emit("sequence.step", { direction });
4881
4927
  },
4882
4928
  "ui.setMount": (p) => {
4883
- const panel = this.mounted();
4929
+ const panel = this.#panelFor(p);
4884
4930
  const property = String(p.property);
4885
- const next = this.#write("$mount", property, p, panel.props[property]);
4931
+ const next = this.#write(mountView(panel.slot), property, p, panel.props[property]);
4886
4932
  // A part of a value sent in parts is held until the last (player 0.6.0).
4887
4933
  if (next !== undefined)
4888
4934
  panel.props[property] = next;
@@ -5122,7 +5168,10 @@ export class TestHost {
5122
5168
  })));
5123
5169
  return { applied: true };
5124
5170
  },
5125
- "ui.interaction": (p) => void (this.mounted().locks = p),
5171
+ "ui.interaction": (p) => {
5172
+ const { slot: _, ...locks } = p;
5173
+ this.#panelFor(p).locks = locks;
5174
+ },
5126
5175
  "dialogs.confirm": (p) => {
5127
5176
  this.confirms.push(p);
5128
5177
  const shown = (field, formatted = false) => p[field] === undefined
@@ -1470,8 +1470,11 @@ export interface Commands {
1470
1470
  * player, and until its answer arrives (a few milliseconds) every
1471
1471
  * command reads `[]`; a binding that read it runs again then.
1472
1472
  *
1473
- * Only the plugin's own commands: an id the plugin has not added (or
1474
- * another plugin's) reads `[]`. Needs nothing beyond `commands`, not
1473
+ * The plugin's own commands, and the player's own as `host/<id>` (SDK
1474
+ * 1.6.16; `host/volume-up`, `host/mute`: the ids of
1475
+ * {@link "@vestra-player/sdk/testing"!BUILTIN_COMMANDS | BUILTIN_COMMANDS} rows owned by `host`), for a panel that
1476
+ * shows the player's keys beside its own controls; an id the plugin has
1477
+ * not added (or another plugin's) reads `[]`. Needs nothing beyond `commands`, not
1475
1478
  * `bindings.manage` (the whole binding table, every plugin's, is
1476
1479
  * {@link Bindings.list}). Without the `commands` capability every
1477
1480
  * command reads `[]`. Players before 0.7.6 have no `keys` (the player
@@ -4095,9 +4098,14 @@ export interface BackdropSlot {
4095
4098
  * or the sidebar moves to or from its floating window, while the global is
4096
4099
  * one stable address.
4097
4100
  *
4098
- * Each plugin mounts at most one panel, and each position holds one panel
4099
- * for all plugins. {@link Panels.bottom} and {@link Panels.sidebar} describe
4100
- * the positions, whoever mounted there.
4101
+ * A plugin mounts at most one panel per slot (SDK 1.6.15, player 0.7.6;
4102
+ * before, one panel in all): a playback plugin can have its control bar in
4103
+ * `"bottom"` and its controls in `"adjust"`. Each {@link Panel} has its own
4104
+ * bridge, state, `toggle()` and `setInteraction()`; disposing one leaves
4105
+ * the others, and stopping the plugin unmounts them all. The bottom bar and
4106
+ * the backdrop hold one panel for all plugins; the sidebar and the adjust
4107
+ * column one tab per plugin. {@link Panels.bottom}, {@link Panels.sidebar}
4108
+ * and {@link Panels.adjust} describe the positions, whoever mounted there.
4101
4109
  *
4102
4110
  * @capability ui.mount
4103
4111
  *
@@ -4194,8 +4202,11 @@ export interface Panels {
4194
4202
  * @returns A {@link Panel} owned by this scope; await {@link Panel.ready}
4195
4203
  * for the result.
4196
4204
  *
4197
- * @throws `Error` without a code (throws) This plugin already has a panel,
4198
- * or the file exports no component or global, or several and `options`
4205
+ * @throws `Error` without a code (throws) This plugin already has a panel
4206
+ * in this slot (a panel in another slot is fine from player 0.7.6;
4207
+ * players before 0.7.6 refuse a second panel with `conflict`, so a
4208
+ * plugin that mounts two declares `minPlayerVersion` 0.7.6), or the file
4209
+ * exports no component or global, or several and `options`
4199
4210
  * does not name one. A file that cannot be read throws Node's file system
4200
4211
  * error (`code` `ENOENT` and so on).
4201
4212
  * @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
- if (scope.runtime.views.has("$mount"))
843
- throw new Error("a plugin can mount only one panel");
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("$mount", (callback, args) => this.dispatch(callback, args));
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("$mount");
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
- setMount: (property, write) => transport.request("ui.setMount", { property, ...write }),
332
- interaction: (locks) => transport.request("ui.interaction", locks),
333
- unmount: () => transport.request("ui.unmount"),
334
- menu: (items, x, y) => transport.request("ui.menu", { items, x, y }),
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.14: 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/.
3
+ > @vestra-player/sdk 1.6.16: 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`: 33 entries: CapabilityInfo, CAPABILITY_INFO, MESSAGE_LIMIT, COMMAND_LIMITS, MENU_IDS, BAR_BUTTON_ICONS, BAR_BUTTON_LIMITS, BuiltinSubmenu, BUILTIN_SUBMENUS, BuiltinCommand, BUILTIN_COMMANDS, LOCALES,…
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.6.14",
3
+ "version": "1.6.16",
4
4
  "description": "Plugin API types, test host and plugin CLI for Vestra Player (apiVersion vestra/1)",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {