@justai/cuts 0.129.1 → 0.129.3

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/Preroll.astro +19 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justai/cuts",
3
- "version": "0.129.1",
3
+ "version": "0.129.3",
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
@@ -364,7 +364,11 @@ const safeId = "game-" + (id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/
364
364
  * container, and layout containment makes it the containing block for FIXED descendants too — a
365
365
  * fixed overlay written inside it would be clipped to the frame it is meant to surround. */
366
366
  html[data-stage="playing"] { overflow: hidden; }
367
- html[data-stage="playing"] .cut.kernel { position: relative; z-index: 35; }
367
+ /* The kernel that holds a preroll rides above the room PERMANENTLY, not only while playing: the
368
+ * raise used to follow data-stage, so pausing dropped the kernel in one frame while the dim took
369
+ * half a second to fade - and every pause put a brief wave of darkness across the game itself.
370
+ * Static costs nothing (the room is transparent outside playing) and cannot mistime. */
371
+ .cut.kernel:has(#pr) { position: relative; z-index: 35; }
368
372
  .pr-room {
369
373
  position: fixed; inset: 0; z-index: 30;
370
374
  background: #05070d; opacity: 0; pointer-events: none;
@@ -473,7 +477,13 @@ const safeId = "game-" + (id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/
473
477
  // and on a phone the only escape from that is closing the tab. So: a game that has not
474
478
  // declared protocol 2 in its boot answer is never given the stage. The declaration IS the
475
479
  // promise that `pause` is registered, and the shell holds it to that.
476
- let protocol = 1;
480
+ // The declaration is the boot ANSWER, and a game may announce `playing` in the same breath
481
+ // as resolving boot — airdrop does, and its announcement beats its own answer onto the wire.
482
+ // So the stage handler never reads a variable; it AWAITS the declaration, and a message that
483
+ // arrives early is simply held until the shell knows who it is talking to. The watchdog path
484
+ // declares protocol 1, so a silent game's early announcement resolves to a refusal, not a hang.
485
+ let declareProtocol: (v: number) => void = () => {};
486
+ const declared: Promise<number> = new Promise((r) => { declareProtocol = r; });
477
487
  if (bus) {
478
488
  // The room's lights, hung on <body> — see the global style block for why not in here.
479
489
  const room = document.createElement("div");
@@ -530,9 +540,9 @@ const safeId = "game-" + (id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/
530
540
 
531
541
  bus.register({
532
542
  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 };
543
+ handler: async (a: any) => {
544
+ // No protocol 2, no stage — the rule, not a courtesy. Awaited, not read: see above.
545
+ if ((await declared) < 2) return { granted: false };
536
546
  setStage(a?.mode === "playing" ? "playing" : "paused");
537
547
  return { granted: true };
538
548
  },
@@ -625,9 +635,10 @@ const safeId = "game-" + (id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/
625
635
  if (watchdog) clearTimeout(watchdog);
626
636
 
627
637
  // 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;
638
+ // that `pause` is registered and will be honoured. Resolved before open(), so a game
639
+ // announcing `playing` in its very first frame — or a breath before its own answer — is
640
+ // already trusted by the time the door goes up.
641
+ declareProtocol(typeof answer?.protocol === "number" ? answer.protocol : 1);
631
642
 
632
643
  open();
633
644
  // `curtain: true` is the game saying "hand over early, I will cover the rest myself" — which