@justai/cuts 0.149.0 → 0.150.0
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 +3 -3
- package/src/Base.astro +7 -3
- package/src/Preroll.astro +18 -2
- package/src/behaviour/preroll.ts +10 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justai/cuts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.150.0",
|
|
4
4
|
"description": "A persona's named parts — the page shell that holds them, and the cuts themselves.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@justai/core": "^0.
|
|
45
|
-
"@justai/ui": "^0.
|
|
44
|
+
"@justai/core": "^0.40.0",
|
|
45
|
+
"@justai/ui": "^0.27.0",
|
|
46
46
|
"@justai/atrium": "^0.1.9"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
package/src/Base.astro
CHANGED
|
@@ -92,11 +92,14 @@ interface Props {
|
|
|
92
92
|
* sides and is *at least* the active view's height; a GAME kernel keeps no inset at all and is
|
|
93
93
|
* *exactly* that height.
|
|
94
94
|
*
|
|
95
|
+
* `art` (2026-10-09) — a musical or visual work that embeds its piece — takes the game's geometry and
|
|
96
|
+
* its dark ground: it is entered, not scrolled.
|
|
97
|
+
*
|
|
95
98
|
* IT DEFAULTS TO `app` BECAUSE FIFTEEN OF THE SIXTEEN PERSONAS ARE ONE. The failure of getting it
|
|
96
99
|
* wrong is quiet in the right direction: a game persona that forgets it renders as an app — margins
|
|
97
100
|
* where it wanted none — rather than an app persona bleeding to the edges.
|
|
98
101
|
*/
|
|
99
|
-
kind?: "app" | "game" | "now" | "expert" | "knower" | "platform";
|
|
102
|
+
kind?: "app" | "game" | "art" | "now" | "expert" | "knower" | "platform";
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
const {
|
|
@@ -147,8 +150,9 @@ if (siteProp && Astro.site && new URL(siteProp).origin !== Astro.site.origin) {
|
|
|
147
150
|
// bottom of the screen because there is nothing there that is not the room. A persona that wants
|
|
148
151
|
// otherwise passes its own — this is a default, not a law.
|
|
149
152
|
const GAME_GROUND = "#080b12";
|
|
150
|
-
const
|
|
151
|
-
const
|
|
153
|
+
const placeKind = kind === "game" || kind === "art"; // a place you go into: the dark ground in both schemes
|
|
154
|
+
const dark = themeDark ?? (placeKind ? GAME_GROUND : "#000000");
|
|
155
|
+
const light = themeLight ?? (placeKind ? GAME_GROUND : "#f2f2f7");
|
|
152
156
|
|
|
153
157
|
const d = dirOf(lang);
|
|
154
158
|
const consent = consentCopy(lang);
|
package/src/Preroll.astro
CHANGED
|
@@ -46,9 +46,24 @@ import "./ground.css";
|
|
|
46
46
|
type StoreKey = "ios" | "mac" | "android" | "steam" | "windows" | "switch" | "playstation" | "xbox";
|
|
47
47
|
|
|
48
48
|
interface Props {
|
|
49
|
-
/** The game's own document, served from this persona's origin
|
|
49
|
+
/** The game's own document, served from this persona's origin — or a WORK at its own address
|
|
50
|
+
* (`https://…`), embedded as it is. Give this OR `stores`, never both. */
|
|
50
51
|
src?: string;
|
|
51
52
|
|
|
53
|
+
// ── A WORK THAT LIVES AT ITS OWN ADDRESS ──────────────────────────────────────────────────
|
|
54
|
+
//
|
|
55
|
+
// queries: art persona · embed a work by iframe · the kernel embeds another site · city rises ·
|
|
56
|
+
// the work updates without the shell · handover on load · a frame that does not speak atrium
|
|
57
|
+
//
|
|
58
|
+
// An `art` persona's kernel does not carry the work, it EMBEDS it: City Rises lives at
|
|
59
|
+
// city-rises.justrd.pro and keeps changing there, and city-rises.cityinmotion.nl shows it without a
|
|
60
|
+
// release of its own. Such a work does not speak the game–shell protocol — it has its own gate and
|
|
61
|
+
// asks for its own gesture — so waiting for a `boot` answer would hold the door for the whole
|
|
62
|
+
// patience window. `handover` says what ends the wait: `boot` (the protocol's answer, the default
|
|
63
|
+
// for a document on this origin) or `load` (the frame's own load event, the default for a `src`
|
|
64
|
+
// that names another origin). Same door, same gesture, same bar; only the signal differs.
|
|
65
|
+
handover?: "boot" | "load";
|
|
66
|
+
|
|
52
67
|
// ── THE DOOR THAT OPENS OUTWARD ───────────────────────────────────────────────────────────
|
|
53
68
|
//
|
|
54
69
|
// queries: native game persona · a game persona for an app store title · the play button goes
|
|
@@ -113,6 +128,7 @@ interface Props {
|
|
|
113
128
|
id?: string;
|
|
114
129
|
}
|
|
115
130
|
const { src, stores, also = "Also on", kicker, name, tag, sub, play, title, loading = "Loading", id = "game" } = Astro.props;
|
|
131
|
+
const handover = Astro.props.handover ?? (/^https?:\/\//i.test(src ?? "") ? "load" : "boot");
|
|
116
132
|
const safeId = "game-" + (id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "x");
|
|
117
133
|
|
|
118
134
|
// ONE DOOR, ONE DIRECTION. A preroll with both a frame and a store list would render two answers to
|
|
@@ -133,7 +149,7 @@ const storeRows = outward.map(([key, url]) => ({ key, url, label: STORE_NAMES[ke
|
|
|
133
149
|
const primary = storeRows[0]?.url;
|
|
134
150
|
---
|
|
135
151
|
|
|
136
|
-
<div class="pr" id="pr" data-src={src} data-atrium={src ? safeId : undefined} data-door={outward.length ? "store" : "frame"}>
|
|
152
|
+
<div class="pr" id="pr" data-src={src} data-handover={src ? handover : undefined} data-atrium={src ? safeId : undefined} data-door={outward.length ? "store" : "frame"}>
|
|
137
153
|
{/* ── THE STAGE IS THE FLOW; THE DOOR IS A COVER OVER IT ──────────────────────────────────────
|
|
138
154
|
queries: the game boots at zero size · getBoundingClientRect returns 0 in the frame ·
|
|
139
155
|
the game jumps when the preroll hands over · display none iframe · hidden iframe
|
package/src/behaviour/preroll.ts
CHANGED
|
@@ -62,6 +62,8 @@ if (root && root.dataset.door === "store") {
|
|
|
62
62
|
|
|
63
63
|
const src = root.dataset.src as string;
|
|
64
64
|
const atriumId = root.dataset.atrium as string;
|
|
65
|
+
// `load` for a work at its own address (see Preroll.astro, "A WORK THAT LIVES AT ITS OWN ADDRESS").
|
|
66
|
+
const onLoad = root.dataset.handover === "load";
|
|
65
67
|
|
|
66
68
|
let started = false;
|
|
67
69
|
|
|
@@ -268,7 +270,10 @@ if (root && root.dataset.door === "store") {
|
|
|
268
270
|
const here = new URLSearchParams(location.search);
|
|
269
271
|
const url = new URL(src, location.href);
|
|
270
272
|
for (const flag of ["xray", "slow"]) if (here.has(flag)) url.searchParams.set(flag, here.get(flag) ?? "");
|
|
271
|
-
|
|
273
|
+
// A document on this origin is named by its path, as it always was; a work at another address
|
|
274
|
+
// keeps its whole address — the path alone would load a page of this persona into the frame.
|
|
275
|
+
frame.src = url.origin === location.origin ? url.pathname + url.search : url.href;
|
|
276
|
+
const loaded = new Promise<any>((resolve) => frame.addEventListener("load", () => resolve({}), { once: true }));
|
|
272
277
|
|
|
273
278
|
// ── THE HANDOVER ─────────────────────────────────────────────────────────────────────────
|
|
274
279
|
//
|
|
@@ -309,12 +314,16 @@ if (root && root.dataset.door === "store") {
|
|
|
309
314
|
// not lose the game to a broken handshake — but the catch SAYS what went wrong, because the
|
|
310
315
|
// symptom on the other side of it is "the wait ended early" and nothing else. This cut's own
|
|
311
316
|
// one-second bug lived inside an empty catch and was invisible until a spec read the bar.
|
|
317
|
+
// A work that does not speak the protocol is ready when its document has loaded — it draws its
|
|
318
|
+
// own gate from there. Its frame's load event joins the race only for `handover: load`: a game on
|
|
319
|
+
// this origin loads its document long before it can draw, and that is what the boot answer is for.
|
|
312
320
|
const answer: any = await Promise.race([
|
|
313
321
|
boot.catch((e: any) => {
|
|
314
322
|
console.warn("[preroll] the game's boot did not complete:", e?.message || e);
|
|
315
323
|
return {};
|
|
316
324
|
}),
|
|
317
325
|
giveUp,
|
|
326
|
+
...(onLoad ? [loaded] : []),
|
|
318
327
|
]);
|
|
319
328
|
if (watchdog) clearTimeout(watchdog);
|
|
320
329
|
|