@vestra-player/sdk 1.6.11 → 1.6.13

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,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.6.13
4
+
5
+ **Who must change: plugin tests that toggle a column in fullscreen.** In fullscreen `toggle()` now only switches the column on or off (user ruling 2026-11-08); a test that asserted `effective: true` after toggling a column on in fullscreen now sees `effective: false`, `reason: "fullscreen"` (on, revealed by the right edge). Plugin code needs no change; `apiVersion` stays `vestra/1`, and the wire gains one optional field.
6
+
7
+ - **`Panel.toggle` and `PanelSlot.toggle` are a switch, the same in every mode.** Compared with 1.6.11 and older, where turning a column on in fullscreen popped it out, it now only switches it on; 1.6.12's rule (a toggle on a collapsed column popped it out instead of turning it off) is withdrawn. No released player shipped 1.6.12: player 0.7.6, the first with the adjust column, is not released yet. On a column that is on, the toggle turns it off; on one that is off, it turns it on. In fullscreen "on" means the pointer at the right edge reveals the column; the toggle never pops it out (player 0.7.6), and a column switched off while it is out slides in at once. The player's own Adjust and Sidebar buttons, Toggle Sidebar and Alt+1…4 behave the same. `Panel.show` and `PanelSlot.setVisible(true)` still pop a column out in fullscreen, as documented.
8
+ - **Wire**: `layout.set` takes an optional `reveal` (with `visible: true`, `false` switches on without popping out; absent, it reveals as before). `toggle` sends `reveal: false`; older players ignore it and pop the column out as before.
9
+ - **`TestHost`**: `setMainWindow({ fullscreen: true })` collapses the adjust column too, `popSidebar` pops out or collapses both columns (those switched on), and `layout.set` with `reveal: false` in fullscreen leaves a column switched on but collapsed.
10
+
11
+ ## 1.6.12
12
+
13
+ **Who must change: nobody.** Superseded by 1.6.13 for `toggle` in fullscreen: this release's rule is withdrawn, and no released player shipped it. A behaviour fix of `toggle` in fullscreen, an example fix and a development warning; no new API, `apiVersion` stays `vestra/1`.
14
+
15
+ - **`Panel.toggle` and `PanelSlot.toggle` slide a collapsed column out.** In fullscreen a sidebar or adjust column the user wants shown is collapsed at the right edge (`reason: "fullscreen"`). Toggling it then turned it off; it now shows it, which player 0.7.6 answers by sliding the column out, as its own Adjust button and Alt+1…4 do. Toggling a column that is on screen still turns it off. With players before 0.7.6 a show of a collapsed column changes nothing on screen, as before.
16
+ - **[adjust-panel](docs/examples/adjust-panel.md)** declares `events`: without it `plugin.player.state` changes only after the plugin's own calls, so a panel activated before a file was opened stayed disabled at 1×. A new test opens the file after activation. Its presets are four (0.5×, 1×, 1.5×, 2×), which fit the adjust column at its least width. No other example reads `player.state` without `events`.
17
+ - **A development warning for state followed without `events`.** A plugin that registers an `onChange` or a `bind` whose function reads changing state (`player.state`, a panel position, and the like) without the `events` capability gets one line in its log, once per plugin: changes made elsewhere never reach it. Only a warning; behaviour is unchanged. `TestHost` already behaved like the player here (it delivers `source.state` and the other state events only to a plugin that subscribed, which needs `events`): a new test pins that down. The example's old tests passed because they opened the file before starting the plugin or did not check `enabled`. No SDK example or test relied on the opposite, so nobody must change.
3
18
  ## 1.6.11
4
19
 
5
20
  **Who must change: nobody.** Additions only: a mount slot, a panel position, a button icon and `TestHost` support; `apiVersion` stays `vestra/1`, no new capability. A plugin that mounts `"adjust"` sets `minPlayerVersion` to `0.7.6`: older players refuse the slot (`invalid`), which fails the activation like any refused mount.
Binary file
@@ -208,6 +208,8 @@ export class Runtime {
208
208
  /** The plugin was disposed: no safety pass is armed any more. */
209
209
  #stopped = false;
210
210
  #events = new Map();
211
+ /** The missing `events` warning was given (once per plugin). */
212
+ #warnedEvents = false;
211
213
  views = new Map();
212
214
  commands = new Map();
213
215
  services = new Map();
@@ -617,6 +619,10 @@ export class Runtime {
617
619
  group.active++;
618
620
  group.whys.set(token, listenerWhy(this.callSite()));
619
621
  }
622
+ if (created && needs === "events" && !this.can(needs))
623
+ group.unheard = true;
624
+ if (!mirror && group.unheard)
625
+ this.#warnUnheard(event);
620
626
  if (created && (needs === null || this.can(needs))) {
621
627
  group.told = group.active > 0;
622
628
  const whys = group.told ? allWhys(group) : undefined;
@@ -658,6 +664,8 @@ export class Runtime {
658
664
  const group = this.#events.get(event);
659
665
  if (!group)
660
666
  return { dispose: async () => { } };
667
+ if (group.unheard)
668
+ this.#warnUnheard(event);
661
669
  group.active++;
662
670
  const token = {};
663
671
  group.whys.set(token, why ?? listenerWhy(this.callSite()));
@@ -675,6 +683,19 @@ export class Runtime {
675
683
  },
676
684
  };
677
685
  }
686
+ /**
687
+ * A development warning, once per plugin, no behaviour change (SDK
688
+ * 1.6.12): the plugin follows `event` (an `onChange`, a `bind` that reads
689
+ * the state) without the `events` capability, so changes made elsewhere
690
+ * (the user, the player, another plugin) never reach it; the state moves
691
+ * only after its own calls.
692
+ */
693
+ #warnUnheard(event) {
694
+ if (this.#warnedEvents)
695
+ return;
696
+ this.#warnedEvents = true;
697
+ console.warn(`[${this.manifest.id}] follows ${event} (onChange, or a bind() that reads the state) without the "events" capability: changes made elsewhere will not reach it, only its own calls refresh the state. Declare "events" in manifest.json.`);
698
+ }
678
699
  /**
679
700
  * Tell the player when `group` changes between mirror and listener, and
680
701
  * why it listens when that changed.
@@ -2396,8 +2396,9 @@ export declare class TestHost implements Transport {
2396
2396
  }>): void;
2397
2397
  /**
2398
2398
  * The pointer reached the right edge in fullscreen (`true`) or left the
2399
- * popped-out sidebar (`false`): the attached sidebar pops out or collapses
2400
- * (reference §6.3). No effect outside fullscreen, when hidden or floating.
2399
+ * popped-out columns (`false`): the attached sidebar and the adjust column
2400
+ * (SDK 1.6.13), those switched on, pop out or collapse (reference §6.3). No
2401
+ * effect outside fullscreen, or on a column switched off or floating.
2401
2402
  *
2402
2403
  * @capability None.
2403
2404
  *
@@ -3005,19 +3005,22 @@ export class TestHost {
3005
3005
  const entering = patch.fullscreen !== undefined &&
3006
3006
  patch.fullscreen !== this.mainWindow.fullscreen;
3007
3007
  Object.assign(this.mainWindow, patch);
3008
- // Like the player (reference §6.3): in fullscreen the attached sidebar
3009
- // starts collapsed at the right edge; leaving fullscreen docks it again.
3010
- const sidebar = this.layout.sidebar;
3011
- if (entering && sidebar && sidebar.preferred && !sidebar.detached) {
3012
- sidebar.effective = !this.mainWindow.fullscreen;
3013
- sidebar.reason = this.mainWindow.fullscreen ? "fullscreen" : null;
3008
+ // Like the player (reference §6.3): in fullscreen the attached columns
3009
+ // (the sidebar, the adjust column) start collapsed at the right edge;
3010
+ // leaving fullscreen docks them again.
3011
+ for (const column of [this.layout.sidebar, this.layout.adjust]) {
3012
+ if (entering && column && column.preferred && !column.detached) {
3013
+ column.effective = !this.mainWindow.fullscreen;
3014
+ column.reason = this.mainWindow.fullscreen ? "fullscreen" : null;
3015
+ }
3014
3016
  }
3015
3017
  this.emit("window.state", this.#windowFacts());
3016
3018
  }
3017
3019
  /**
3018
3020
  * The pointer reached the right edge in fullscreen (`true`) or left the
3019
- * popped-out sidebar (`false`): the attached sidebar pops out or collapses
3020
- * (reference §6.3). No effect outside fullscreen, when hidden or floating.
3021
+ * popped-out columns (`false`): the attached sidebar and the adjust column
3022
+ * (SDK 1.6.13), those switched on, pop out or collapse (reference §6.3). No
3023
+ * effect outside fullscreen, or on a column switched off or floating.
3021
3024
  *
3022
3025
  * @capability None.
3023
3026
  *
@@ -3036,11 +3039,14 @@ export class TestHost {
3036
3039
  * @since 1.0.0
3037
3040
  */
3038
3041
  popSidebar(popped) {
3039
- const sidebar = this.layout.sidebar;
3040
- if (!this.mainWindow.fullscreen || !sidebar?.preferred || sidebar.detached)
3042
+ if (!this.mainWindow.fullscreen)
3041
3043
  return;
3042
- sidebar.effective = popped;
3043
- sidebar.reason = popped ? null : "fullscreen";
3044
+ for (const column of [this.layout.sidebar, this.layout.adjust]) {
3045
+ if (!column?.preferred || column.detached)
3046
+ continue;
3047
+ column.effective = popped;
3048
+ column.reason = popped ? null : "fullscreen";
3049
+ }
3044
3050
  this.emit("window.state", this.#windowFacts());
3045
3051
  }
3046
3052
  #windowFacts() {
@@ -4591,9 +4597,22 @@ export class TestHost {
4591
4597
  state.owner = panel.owner;
4592
4598
  }
4593
4599
  if (typeof p.visible === "boolean") {
4600
+ const out = state.preferred && state.effective;
4594
4601
  state.preferred = p.visible;
4595
- state.effective = p.visible;
4596
- state.reason = p.visible ? null : "userHidden";
4602
+ // In fullscreen a column switched on by a toggle (`reveal: false`)
4603
+ // stays collapsed at the edge; `show()` and `setVisible` pop it out.
4604
+ const collapsed = p.visible &&
4605
+ p.reveal === false &&
4606
+ slot !== "bottom" &&
4607
+ this.mainWindow.fullscreen &&
4608
+ !state.detached &&
4609
+ !out;
4610
+ state.effective = p.visible && !collapsed;
4611
+ state.reason = !p.visible
4612
+ ? "userHidden"
4613
+ : collapsed
4614
+ ? "fullscreen"
4615
+ : null;
4597
4616
  if (this.panel?.slot === slot)
4598
4617
  this.panel.visible = p.visible;
4599
4618
  }
@@ -2705,7 +2705,10 @@ export interface PanelSlot {
2705
2705
  *
2706
2706
  * @remarks
2707
2707
  * Reads the state from the player first, so it is right even without
2708
- * `events`.
2708
+ * `events`. It is a switch, the same in every mode: in fullscreen a column
2709
+ * switched on is revealed by the pointer at the right edge, and is not
2710
+ * popped out by the toggle (SDK 1.6.13, player 0.7.6; `setVisible(true)`
2711
+ * pops it out). Switching it off while it is out slides it in at once.
2709
2712
  *
2710
2713
  * @capability ui.mount
2711
2714
  *
@@ -3009,8 +3012,13 @@ export interface Panel extends View, Disposable {
3009
3012
  * @remarks
3010
3013
  * Reads the state from the player first. When the position is shown
3011
3014
  * (`preferred`) and this panel's tab is the current one, it hides the
3012
- * position; otherwise it does {@link Panel.show}: a panel in a background
3013
- * tab comes to the front instead of the sidebar closing. A "Show list"
3015
+ * position; otherwise it switches the position on at this panel's tab: a
3016
+ * panel in a background tab comes to the front instead of the sidebar
3017
+ * closing. It is a switch, the same in every mode, like the player's own
3018
+ * Alt+1…4 and Adjust button: in fullscreen a column switched on is revealed
3019
+ * by the pointer at the right edge, not popped out (SDK 1.6.13, player
3020
+ * 0.7.6; {@link Panel.show} pops it out), and one switched off while out
3021
+ * slides in at once. A "Show list"
3014
3022
  * command checked by `state.preferred && state.active` matches it. Waits
3015
3023
  * for {@link Panel.ready} first.
3016
3024
  *
package/dist/plugin/ui.js CHANGED
@@ -917,10 +917,18 @@ class PanelHandle extends ViewBase {
917
917
  throw failure("invalid", "the player shows the backdrop by itself");
918
918
  await layoutOf(this.scope.runtime).read();
919
919
  const state = this.state;
920
+ // A switch, the same in every mode (user ruling 2026-11-08): off when
921
+ // on and current, else on at this tab. `reveal: false`: in fullscreen
922
+ // nothing pops out, the right edge reveals it (player 0.7.6; older
923
+ // players ignore it and pop it out).
920
924
  if (state?.preferred && state.active)
921
925
  await layoutOf(this.scope.runtime).set(this.#slot, { visible: false });
922
926
  else
923
- await this.show();
927
+ await layoutOf(this.scope.runtime).set(this.#slot, {
928
+ active: this.scope.runtime.manifest.id,
929
+ visible: true,
930
+ reveal: false,
931
+ });
924
932
  }
925
933
  async showMenu(items, at) {
926
934
  await this.ready;
@@ -1117,7 +1125,11 @@ function slot(scope, name) {
1117
1125
  async toggle() {
1118
1126
  await service().read();
1119
1127
  const current = service().state[name];
1120
- await service().set(name, { visible: !(current?.preferred ?? true) });
1128
+ // A switch (see Panel.toggle): nothing pops out in fullscreen.
1129
+ await service().set(name, {
1130
+ visible: !(current?.preferred ?? true),
1131
+ reveal: false,
1132
+ });
1121
1133
  },
1122
1134
  setDetached: (detached) => service().set(name, { detached }),
1123
1135
  activate: (plugin) => service().set(name, { active: plugin }),
package/dist/wire.d.ts CHANGED
@@ -710,6 +710,13 @@ export declare function createWire(transport: Transport): {
710
710
  detached?: boolean;
711
711
  /** The plugin id of the panel whose tab becomes the current one. */
712
712
  active?: string;
713
+ /**
714
+ * With `visible: true`: `false` switches the position on without
715
+ * popping a column out in fullscreen (`toggle()`, SDK 1.6.13; player
716
+ * 0.7.6). Absent: reveal, as `show()` and `setVisible` do. Older
717
+ * players ignore it.
718
+ */
719
+ reveal?: boolean;
713
720
  }) => Promise<void>;
714
721
  };
715
722
  events: {
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vestra Player SDK
2
2
 
3
- > @vestra-player/sdk 1.6.11: 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.13: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
4
4
 
5
5
  ## Read the documentation
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.6.11",
3
+ "version": "1.6.13",
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": {