@justai/cuts 0.155.0 → 0.157.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justai/cuts",
3
- "version": "0.155.0",
3
+ "version": "0.157.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": {
package/src/Base.astro CHANGED
@@ -82,6 +82,12 @@ interface Props {
82
82
  * privacy/terms child personas use this: the surface that proves it collects nothing by running
83
83
  * nothing. */
84
84
  gaId?: string | null;
85
+ /** Sign-in, ON by default. Pass `false` for a persona with no accounts: the SSO token capture is
86
+ * then not rendered in the head, so nothing reads `#auth_token` or writes `justai_token`. It is
87
+ * the head half of the opt-out — pass the same `auth={false}` to Toolbar, which then renders no
88
+ * AuthMount and loads no sdk.js. Everything else in the head (language, theme, the service
89
+ * worker, analytics as `gaId` decides) is untouched. */
90
+ auth?: boolean;
85
91
  /**
86
92
  * queries: kind game · data-kind · my game persona has margins · the game does not fill the screen ·
87
93
  * which storefront lists this persona · persona kind in the layout
@@ -135,6 +141,7 @@ const {
135
141
  themeDark,
136
142
  themeLight,
137
143
  gaId = "G-13L3BP6XCW",
144
+ auth = true,
138
145
  kind = "app",
139
146
  page,
140
147
  } = Astro.props;
@@ -210,8 +217,10 @@ const ogImage = page?.image
210
217
  #auth_token before the deferred sdk.js can read it; and the language picker copies the
211
218
  current hash onto the next locale's URL (LangPicker), which would PROPAGATE the raw token
212
219
  into a new page and into history. Capturing first defeats both. Move this and sign-in breaks
213
- silently — no error, the visitor simply lands back signed out. */}
214
- <script is:inline set:html={AUTH_CAPTURE_JS} />
220
+ silently — no error, the visitor simply lands back signed out.
221
+ A persona with no accounts (`auth={false}`) has no token to capture, so the script is not
222
+ rendered at all. */}
223
+ {auth && <script is:inline set:html={AUTH_CAPTURE_JS} />}
215
224
  <script is:inline set:html={JS_CLASS_JS} />
216
225
  {/* queries: design grid · x-ray · ?xray · safe area · debug overlay · see the layout ·
217
226
  is my content inside the safe area · layout debugging
package/src/Preroll.astro CHANGED
@@ -457,6 +457,22 @@ const primary = storeRows[0]?.url;
457
457
  * container, and layout containment makes it the containing block for FIXED descendants too — a
458
458
  * fixed overlay written inside it would be clipped to the frame it is meant to surround. */
459
459
  html[data-stage="playing"] { overflow: hidden; }
460
+ /* queries: oyun başlayınca toolbar gitsin · the toolbar hides while playing · full screen game ·
461
+ * tam ekran oyun · ipad oyun ekranı · the hud is under the toolbar
462
+ *
463
+ * ── WHILE A GAME PLAYS, THE TOOLBAR STEPS OUT AND THE GAME TAKES THE WHOLE SCREEN ──────────────
464
+ *
465
+ * The founder, 2026-10-22, on an iPad and a phone: the game's own money, day and full-screen keys
466
+ * were crowded by the bar above them. So while the stage is `playing` the bar's sticky slot leaves
467
+ * the flow and the game kernel grows by exactly what it gave up — the whole painted screen. The
468
+ * moment the game announces `paused` (its own pause key, the tab hidden, leaving full screen) the
469
+ * attribute goes and the bar comes back with the page as it was. The protocol is unchanged: the
470
+ * game says what the player is doing, and this is only what the shell does with it. */
471
+ html[data-stage="playing"] .tb-slot { display: none; }
472
+ /* What the shell takes above the kernel is nothing, while it plays: the one number the game
473
+ * kernel's height law and its landing offset both read (@justai/ui base.css). `!important` because
474
+ * the shell measures the bar and writes the number inline on the root — and the bar is gone. */
475
+ html[data-stage="playing"] { --shell-top: 0px !important; }
460
476
  /* queries: theater mode · where did the dim layer go · dark theme while playing
461
477
  *
462
478
  * ── THE THEATER IS THE PERSONA'S OWN DARK THEME, NOT A LAYER ────────────────────────────────
package/src/Toolbar.astro CHANGED
@@ -54,9 +54,12 @@ interface Props {
54
54
  // translated it yet gets a working, named control rather than an unlabelled icon.
55
55
  files?: boolean;
56
56
  filesLabel?: string;
57
- // The home affordance points at app.zone, so the two apexes suppress it: app.zone IS that
58
- // destination, and justai.pro is above it (sending the parent "home" to a child platform is
59
- // the wrong direction). Every other persona keeps it.
57
+ // The home affordance points at the persona's apex (`apex` below), so EVERY apex suppresses it:
58
+ // app.zone, pin.games and cityinmotion.nl ARE that destination — told their own host, the row
59
+ // draws their mark as a link to the page it sits on, beside the profile icon that is the same
60
+ // face (cityinmotion.nl shipped exactly that until 2026-10-15) — and justai.pro is above them
61
+ // all (sending the parent "home" to a child platform is the wrong direction). Every other
62
+ // persona keeps it. The template passes `home={false}` for every apex it generates.
60
63
  home?: boolean;
61
64
  /**
62
65
  * Which storefront the home affordance points at. Defaults to app.zone — see BackToZone, which owns
@@ -147,11 +150,25 @@ interface Props {
147
150
  // `feed` already reads the host persona's avatar from the registry, and an absolute URL it was
148
151
  // given beats a relative one it inferred.
149
152
  parent?: { href?: string; label?: string; icon?: string };
153
+
154
+ // queries: accountless persona · no sign-in on this persona · turn auth off · auth={false} ·
155
+ // the toolbar loads sdk.js on a persona with no accounts · privacy-sensitive persona
156
+ //
157
+ // ── SIGN-IN, ON UNLESS A PERSONA SAYS IT HAS NO ACCOUNTS ─────────────────────────────────────
158
+ //
159
+ // ON by default, because every persona today is one place in one signed-in ecosystem. Pass
160
+ // `auth={false}` and the AuthMount is not RENDERED — no mount point, no sdk.js tag,
161
+ // so no request to auth.justai.pro is ever made. Hiding it would not be the same thing: a hidden
162
+ // mount still loads a third-party script into the persona's origin, which is the surface an
163
+ // accountless, privacy-sensitive persona is opting out of.
164
+ //
165
+ // Pass the same `auth={false}` to Base, which owns the other half (the token capture in the head).
166
+ auth?: boolean;
150
167
  }
151
168
  import BackToZone from "./BackToZone.astro";
152
169
  const {
153
170
  sectionsLabel, profileLabel, kernelLabel, postsLabel,
154
- files = true, filesLabel = "Files", landOn, avatar = "/icon.svg", parent,
171
+ files = true, filesLabel = "Files", landOn, avatar = "/icon.svg", parent, auth = true,
155
172
  } = Astro.props;
156
173
  // A child's row answers UP to its parent, not out to the zone, and has no stream of its own — so
157
174
  // both default off the moment `parent` is given, rather than each consumer remembering to say so.
@@ -274,7 +291,7 @@ import AuthMount from "./AuthMount.astro";
274
291
  <div class="tb-right">
275
292
  <slot name="toolbarRight" />
276
293
  <slot name="langPicker" />
277
- <AuthMount />
294
+ {auth && <AuthMount />}
278
295
  </div>
279
296
  </div>
280
297
  </header>
@@ -180,8 +180,11 @@ if (root && root.dataset.door === "store") {
180
180
  const el = document.getElementById(id);
181
181
  if (el) el.toggleAttribute("inert", playing);
182
182
  }
183
- // The page is locked, so make sure what it is locked ON is the stage.
184
- if (playing) stage.scrollIntoView({ block: "nearest" });
183
+ // The page is locked, so make sure what it is locked ON is the stage — the whole of it, now
184
+ // that the toolbar has stepped out (see Preroll.astro). On pause the bar is back, and the
185
+ // kernel lands under it again by its own scroll-margin.
186
+ if (playing) (document.getElementById("kernel") ?? stage).scrollIntoView({ block: "start" });
187
+ else document.getElementById("kernel")?.scrollIntoView({ block: "start" });
185
188
  // queries: space does nothing · keyboard not reaching the game · input bind · focus
186
189
  //
187
190
  // AND THE KEYBOARD GOES WITH THE STAGE. The play press happened in THIS document, so this
@@ -245,6 +248,11 @@ if (root && root.dataset.door === "store") {
245
248
  document.addEventListener("justai:pause-request", () => askPause("chrome"));
246
249
  document.addEventListener("justai:fullscreen-request", () =>
247
250
  (stage as any).requestFullscreen?.().catch(() => {}));
251
+ // Leaving full screen is leaving the game for a moment: it pauses, and with the pause the
252
+ // toolbar comes back — the same way back as the game's own pause key.
253
+ document.addEventListener("fullscreenchange", () => {
254
+ if (!document.fullscreenElement && document.documentElement.dataset.stage === "playing") askPause("fullscreen");
255
+ });
248
256
  document.addEventListener("visibilitychange", () => {
249
257
  if (document.hidden && document.documentElement.dataset.stage === "playing") askPause("hidden");
250
258
  });