@justai/cuts 0.129.0 → 0.129.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justai/cuts",
3
- "version": "0.129.0",
3
+ "version": "0.129.2",
4
4
  "description": "A persona's named parts \u2014 the page shell that holds them, and the cuts themselves.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
package/src/Preroll.astro CHANGED
@@ -473,7 +473,13 @@ const safeId = "game-" + (id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/
473
473
  // and on a phone the only escape from that is closing the tab. So: a game that has not
474
474
  // declared protocol 2 in its boot answer is never given the stage. The declaration IS the
475
475
  // promise that `pause` is registered, and the shell holds it to that.
476
- let protocol = 1;
476
+ // The declaration is the boot ANSWER, and a game may announce `playing` in the same breath
477
+ // as resolving boot — airdrop does, and its announcement beats its own answer onto the wire.
478
+ // So the stage handler never reads a variable; it AWAITS the declaration, and a message that
479
+ // arrives early is simply held until the shell knows who it is talking to. The watchdog path
480
+ // declares protocol 1, so a silent game's early announcement resolves to a refusal, not a hang.
481
+ let declareProtocol: (v: number) => void = () => {};
482
+ const declared: Promise<number> = new Promise((r) => { declareProtocol = r; });
477
483
  if (bus) {
478
484
  // The room's lights, hung on <body> — see the global style block for why not in here.
479
485
  const room = document.createElement("div");
@@ -530,9 +536,9 @@ const safeId = "game-" + (id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/
530
536
 
531
537
  bus.register({
532
538
  method: "stage",
533
- handler: (a: any) => {
534
- // No protocol 2, no stage — the rule, not a courtesy. See the boot answer below.
535
- if (protocol < 2) return { granted: false };
539
+ handler: async (a: any) => {
540
+ // No protocol 2, no stage — the rule, not a courtesy. Awaited, not read: see above.
541
+ if ((await declared) < 2) return { granted: false };
536
542
  setStage(a?.mode === "playing" ? "playing" : "paused");
537
543
  return { granted: true };
538
544
  },
@@ -625,9 +631,10 @@ const safeId = "game-" + (id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/
625
631
  if (watchdog) clearTimeout(watchdog);
626
632
 
627
633
  // The boot answer carries the game's protocol version, and version 2 is the declaration
628
- // that `pause` is registered and will be honoured. Read before open(), so a game announcing
629
- // `playing` in its very first frame is already trusted.
630
- protocol = typeof answer?.protocol === "number" ? answer.protocol : 1;
634
+ // that `pause` is registered and will be honoured. Resolved before open(), so a game
635
+ // announcing `playing` in its very first frame — or a breath before its own answer — is
636
+ // already trusted by the time the door goes up.
637
+ declareProtocol(typeof answer?.protocol === "number" ? answer.protocol : 1);
631
638
 
632
639
  open();
633
640
  // `curtain: true` is the game saying "hand over early, I will cover the rest myself" — which
package/src/Toolbar.astro CHANGED
@@ -280,7 +280,7 @@ import AuthMount from "./AuthMount.astro";
280
280
  iPhone. -->
281
281
  <span class="tb-stage" id="tbStage">
282
282
  <button class="tb-nav tb-pausebtn" id="tbPause" type="button" aria-label={pauseLabel} title={pauseLabel}>
283
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M9 5v14M15 5v14"/></svg>
283
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M9 3v18M15 3v18"/></svg>
284
284
  </button>
285
285
  <button class="tb-nav tb-fsbtn" id="tbFullscreen" type="button" aria-label={fullscreenLabel} title={fullscreenLabel} hidden>
286
286
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg>