@justai/cuts 0.129.7 → 0.130.1
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 +1 -1
- package/src/BackToZone.astro +1 -1
- package/src/Base.astro +40 -3
- package/src/Posts.astro +5 -2
- package/src/Preroll.astro +30 -33
- package/src/Profile.astro +1 -1
- package/src/Toolbar.astro +11 -16
- package/src/ground.css +7 -1
package/package.json
CHANGED
package/src/BackToZone.astro
CHANGED
|
@@ -129,7 +129,7 @@ const href = `https://${apex}`;
|
|
|
129
129
|
Dark is the un-queried default because the fleet's tokens are dark-first. */
|
|
130
130
|
.home:hover .azmark .ctr, .home:focus-visible .azmark .ctr { fill: #F5F1E8; }
|
|
131
131
|
@media (prefers-color-scheme: light) {
|
|
132
|
-
.home:hover .azmark .ctr, .home:focus-visible .azmark .ctr { fill: #2B2724; }
|
|
132
|
+
:global(html:not([data-theme="dark"])) .home:hover .azmark .ctr, :global(html:not([data-theme="dark"])) .home:focus-visible .azmark .ctr { fill: #2B2724; }
|
|
133
133
|
}
|
|
134
134
|
</style>
|
|
135
135
|
|
package/src/Base.astro
CHANGED
|
@@ -54,8 +54,21 @@ interface Props {
|
|
|
54
54
|
* rather than narrowed to a shape only this package would recognise.
|
|
55
55
|
*/
|
|
56
56
|
t: Record<string, unknown>;
|
|
57
|
-
/**
|
|
58
|
-
|
|
57
|
+
/**
|
|
58
|
+
* DEPRECATED — declare the origin in astro.config.mjs (`site:`) and stop passing this.
|
|
59
|
+
*
|
|
60
|
+
* The origin now comes from `Astro.site`, which every persona already has to declare for the
|
|
61
|
+
* build. Passing it here as well is the second declaration site this docstring warns about
|
|
62
|
+
* twenty lines above, and it went wrong exactly the way the locale list did: compress-image was
|
|
63
|
+
* copied from cleanmylink and shipped with cleanmylink's origin in this prop while its
|
|
64
|
+
* astro.config was correct. For a day, canonical, og:site_name and all fourteen hreflang hrefs
|
|
65
|
+
* named another product — while og:url and og:image, three lines apart in the same head, read
|
|
66
|
+
* `Astro.site` and were right. One head, two truths, and nothing said so.
|
|
67
|
+
*
|
|
68
|
+
* Still accepted so no persona has to change on the same day this ships; it is now checked
|
|
69
|
+
* rather than trusted, and a build where the two disagree fails.
|
|
70
|
+
*/
|
|
71
|
+
site?: string;
|
|
59
72
|
/** From the consuming repo's src/i18n — never from this package. */
|
|
60
73
|
locales: readonly string[];
|
|
61
74
|
defaultLocale: string;
|
|
@@ -87,13 +100,37 @@ interface Props {
|
|
|
87
100
|
}
|
|
88
101
|
|
|
89
102
|
const {
|
|
90
|
-
lang, t, site, locales, defaultLocale, favicon,
|
|
103
|
+
lang, t, site: siteProp, locales, defaultLocale, favicon,
|
|
91
104
|
themeDark,
|
|
92
105
|
themeLight,
|
|
93
106
|
gaId = "G-13L3BP6XCW",
|
|
94
107
|
kind = "app",
|
|
95
108
|
} = Astro.props;
|
|
96
109
|
|
|
110
|
+
// ── ONE HOME FOR THE ORIGIN ───────────────────────────────────────────────────────────────────
|
|
111
|
+
// queries: canonical yanlış · başka personanın alan adı · site prop · hreflang yanlış domain
|
|
112
|
+
//
|
|
113
|
+
// A wrong origin renders perfectly. That is the whole difficulty: nothing is red, the page is
|
|
114
|
+
// valid, and the only symptom is search engines being told this page belongs to someone else.
|
|
115
|
+
// The failure has to be moved to a place where it CANNOT be quiet, and a static build is the one
|
|
116
|
+
// place available — so a disagreement is an error, not a warning. A warning in a build log is the
|
|
117
|
+
// same silence with more steps.
|
|
118
|
+
const site = Astro.site?.origin ?? siteProp;
|
|
119
|
+
if (!site) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
"[@justai/cuts] No origin. Declare it once, in astro.config.mjs:\n" +
|
|
122
|
+
' export default personaConfig({ site: "https://<persona>.app.zone", ... })',
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
if (siteProp && Astro.site && new URL(siteProp).origin !== Astro.site.origin) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
"[@justai/cuts] Two origins, and they disagree:\n" +
|
|
128
|
+
` astro.config.mjs ${Astro.site.origin}\n` +
|
|
129
|
+
` <Base site=…> ${new URL(siteProp).origin}\n` +
|
|
130
|
+
"astro.config wins — it is what the sitemap, the build and og:url already use. Drop the prop.",
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
97
134
|
// queries: theme-color · a grey band under the game on iPhone · Safari's bottom bar colour ·
|
|
98
135
|
// the browser chrome does not match the game · game persona theme colour
|
|
99
136
|
//
|
package/src/Posts.astro
CHANGED
|
@@ -172,7 +172,7 @@ const vars = [
|
|
|
172
172
|
/* The light default is white because that is what --card-bg is there; the dark default is a faint white
|
|
173
173
|
overlay because on dark --card-bg equals --surface, which is the column, so copying it would vanish. */
|
|
174
174
|
@media (prefers-color-scheme: light) {
|
|
175
|
-
.post:hover { background: color-mix(in srgb, var(--card-bg, #fff) 55%, transparent); }
|
|
175
|
+
:global(html:not([data-theme="dark"])) .post:hover { background: color-mix(in srgb, var(--card-bg, #fff) 55%, transparent); }
|
|
176
176
|
}
|
|
177
177
|
.post + .post { border-block-start: 1px solid var(--border); }
|
|
178
178
|
|
|
@@ -225,7 +225,7 @@ const vars = [
|
|
|
225
225
|
.pa .mk, .rav .mk { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
226
226
|
/* a dark tile must invert in light mode, not stay dark — personas whose avatar ground is a gradient
|
|
227
227
|
pass avatarBgLight; the rest fall back to their single background. */
|
|
228
|
-
@media (prefers-color-scheme: light) { .pa, .rav { background: var(--feed-av-bg-light, var(--feed-av-bg, #fff)); } }
|
|
228
|
+
@media (prefers-color-scheme: light) { :global(html:not([data-theme="dark"])) .pa, :global(html:not([data-theme="dark"])) .rav { background: var(--feed-av-bg-light, var(--feed-av-bg, #fff)); } }
|
|
229
229
|
|
|
230
230
|
/* A bare mark stands on the feed's own ground, so the tile and its hairline have to go — and they have to
|
|
231
231
|
go from the CONTAINER, which is why this reads upward from the image with :has(). Scoping it to the image
|
|
@@ -256,6 +256,9 @@ const vars = [
|
|
|
256
256
|
.pa .mk.av-light, .rav .mk.av-light { display: none; }
|
|
257
257
|
.pa .mk.av-dark, .rav .mk.av-dark { display: block; }
|
|
258
258
|
}
|
|
259
|
+
/* The dark fork's data-theme twin: a light-system page forced dark must swap the marks too. */
|
|
260
|
+
:global(html[data-theme="dark"]) .pa .mk.av-light, :global(html[data-theme="dark"]) .rav .mk.av-light { display: none; }
|
|
261
|
+
:global(html[data-theme="dark"]) .pa .mk.av-dark, :global(html[data-theme="dark"]) .rav .mk.av-dark { display: block; }
|
|
259
262
|
|
|
260
263
|
.ph { display: flex; align-items: center; gap: 5px; font-size: var(--fs-xs); flex-wrap: wrap; }
|
|
261
264
|
.ph b { font-weight: 600; }
|
package/src/Preroll.astro
CHANGED
|
@@ -364,34 +364,22 @@ 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
|
-
/*
|
|
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; }
|
|
372
|
-
/* ── THE ROOM IS DARK DOT PAPER, NOT A BLACK FILM ───────────────────────────────────────────
|
|
373
|
-
* queries: theater dim flat colour · the dots disappear in the dark · blender dark theme ·
|
|
374
|
-
* dot paper grid in theater mode
|
|
367
|
+
/* queries: theater mode · where did the dim layer go · dark theme while playing
|
|
375
368
|
*
|
|
376
|
-
*
|
|
377
|
-
* sefer duz bir renk gibi algilaniyor"* — and pointing at the fix: 3dviewer's player, whose
|
|
378
|
-
* stage is Blender's own Theme Space grey, read out of a running Blender. A flat translucent
|
|
379
|
-
* black erased the ground's identity; a dark room should still be THE SAME PAPER with the
|
|
380
|
-
* lights down, the way Blender's viewport keeps its grid in the dark.
|
|
369
|
+
* ── THE THEATER IS THE PERSONA'S OWN DARK THEME, NOT A LAYER ────────────────────────────────
|
|
381
370
|
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
html[data-stage="playing"] .pr-room { opacity: 0.86; }
|
|
371
|
+
* Three custom dims were built and struck here in one day — a black film, a z-order dance, dark
|
|
372
|
+
* dot paper — before the founder pointed at what was already true: *"justai ecosisteminde zaten
|
|
373
|
+
* tum personalarin light/dark temalari vardi ve kusursuz calisiyorlardi. senin yapman gereken tek
|
|
374
|
+
* sey vardi: theme switch, simple."* Every persona already ships a complete dark theme - the
|
|
375
|
+
* ground, the dots, the toolbar, the cards, all of it designed and measured - and a second
|
|
376
|
+
* darkness laid over the first is two systems doing one job.
|
|
377
|
+
*
|
|
378
|
+
* So the stage sets data-theme="dark" on <html> while playing and removes it on pause, and the
|
|
379
|
+
* guard the fleet's light forks now carry (time's wall wrote the idiom first: data-theme wins
|
|
380
|
+
* over the media query) does everything the dim layer ever tried to. A dark-system visitor sees
|
|
381
|
+
* no change at all; a light-system visitor gets the persona's own night, and their auto theme
|
|
382
|
+
* back the moment they pause. */
|
|
395
383
|
</style>
|
|
396
384
|
|
|
397
385
|
<script>
|
|
@@ -502,16 +490,25 @@ const safeId = "game-" + (id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/
|
|
|
502
490
|
let declareProtocol: (v: number) => void = () => {};
|
|
503
491
|
const declared: Promise<number> = new Promise((r) => { declareProtocol = r; });
|
|
504
492
|
if (bus) {
|
|
505
|
-
//
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
room.setAttribute("aria-hidden", "true");
|
|
509
|
-
document.body.appendChild(room);
|
|
493
|
+
// Whatever theme the page carried before the stage took it — restored on pause, so a manual
|
|
494
|
+
// data-theme set by anything else survives a round trip through a game.
|
|
495
|
+
let priorTheme: string | undefined;
|
|
510
496
|
|
|
511
497
|
const setStage = (mode: string) => {
|
|
512
498
|
const playing = mode === "playing";
|
|
513
|
-
if (playing)
|
|
514
|
-
|
|
499
|
+
if (playing) {
|
|
500
|
+
document.documentElement.dataset.stage = "playing";
|
|
501
|
+
// The theater: the persona's own dark theme, switched on. See the style block above.
|
|
502
|
+
if (document.documentElement.dataset.theme !== "dark") {
|
|
503
|
+
priorTheme = document.documentElement.dataset.theme;
|
|
504
|
+
document.documentElement.dataset.theme = "dark";
|
|
505
|
+
}
|
|
506
|
+
} else {
|
|
507
|
+
delete document.documentElement.dataset.stage;
|
|
508
|
+
if (priorTheme === undefined) delete document.documentElement.dataset.theme;
|
|
509
|
+
else document.documentElement.dataset.theme = priorTheme;
|
|
510
|
+
priorTheme = undefined;
|
|
511
|
+
}
|
|
515
512
|
// The other cuts go INERT rather than display:none — the page must not reflow, and the
|
|
516
513
|
// player's scroll position has to survive a pause. inert takes them out of tab order and
|
|
517
514
|
// the accessibility tree both; a screen reader must not keep walking a page the player
|
package/src/Profile.astro
CHANGED
|
@@ -129,7 +129,7 @@ const vars = [
|
|
|
129
129
|
.avatar :global(svg) { width: 100%; height: 100%; display: block; }
|
|
130
130
|
.avatar :global(img) { width: 100%; height: 100%; display: block; object-fit: cover; }
|
|
131
131
|
@media (prefers-color-scheme: light) {
|
|
132
|
-
.avatar { border-color: #fff; background: var(--avatar-bg-light, var(--avatar-bg, #fff)); }
|
|
132
|
+
:global(html:not([data-theme="dark"])) .avatar { border-color: #fff; background: var(--avatar-bg-light, var(--avatar-bg, #fff)); }
|
|
133
133
|
}
|
|
134
134
|
.pname { font-size: var(--fs-lg); font-weight: 700; letter-spacing: -0.02em; margin-block-start: 12px; }
|
|
135
135
|
.handle { color: var(--text-dim); font-size: var(--fs-xs); margin-block-start: 2px; }
|
package/src/Toolbar.astro
CHANGED
|
@@ -344,7 +344,8 @@ import AuthMount from "./AuthMount.astro";
|
|
|
344
344
|
FALLBACK differs by scheme, and a band carrying only the dark default is a black strip on a light
|
|
345
345
|
page — which is exactly what the founder photographed a minute after the first attempt. */
|
|
346
346
|
@media (prefers-color-scheme: light) {
|
|
347
|
-
|
|
347
|
+
/* Guarded like every light fork in the fleet: data-theme="dark" wins over the media query. */
|
|
348
|
+
:global(html:not([data-theme="dark"])) .tb-slot::before { background: var(--toolbar-bg, #fbfcfe); }
|
|
348
349
|
}
|
|
349
350
|
.toolbar {
|
|
350
351
|
/* position, top and z-index live on .tb-slot above — this element is static inside it now. */
|
|
@@ -389,7 +390,7 @@ import AuthMount from "./AuthMount.astro";
|
|
|
389
390
|
border-block-end: none;
|
|
390
391
|
}
|
|
391
392
|
@media (prefers-color-scheme: light) {
|
|
392
|
-
.toolbar {
|
|
393
|
+
:global(html:not([data-theme="dark"])) .toolbar {
|
|
393
394
|
background: var(--toolbar-bg, #fbfcfe);
|
|
394
395
|
box-shadow: var(--toolbar-shadow, 0 1px 2px rgba(16, 24, 40, 0.06), 0 8px 20px -8px rgba(16, 24, 40, 0.16));
|
|
395
396
|
}
|
|
@@ -457,21 +458,15 @@ import AuthMount from "./AuthMount.astro";
|
|
|
457
458
|
* Opacity and pointer-events here; the INERT that takes them out of the tab order is set in the
|
|
458
459
|
* script, because CSS cannot. The transition matters more than it looks: chrome that snaps away
|
|
459
460
|
* reads as breaking, chrome that withdraws reads as dimming the house lights. */
|
|
460
|
-
/* ──
|
|
461
|
-
* queries: toolbar
|
|
461
|
+
/* ── HOW THIS ROW GOES QUIET WHILE A GAME PLAYS: IT DOES NOT ─────────────────────────────────
|
|
462
|
+
* queries: toolbar during a game · theater · toolbar looks disabled
|
|
462
463
|
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
* disabled, they are in the dark. So the elements keep their full strength and the ROW STEPS
|
|
470
|
-
* UNDER THE ROOM'S DIM - below the .pr-room layer the stage protocol hangs over the page - and
|
|
471
|
-
* darkens with everything else in one coherent light change. The dim takes no pointer events,
|
|
472
|
-
* so every control underneath works exactly as it looks like it should: like chrome in a dark
|
|
473
|
-
* room, waiting. */
|
|
474
|
-
:global(html[data-stage="playing"]) .tb-slot { z-index: 20; }
|
|
464
|
+
* Four versions in one day, each struck by the founder, and the last one is no CSS at all. A
|
|
465
|
+
* pause handle in the row; then inert; then faded ("disable gibi gozukmemeli - tiklanabiliyorsa");
|
|
466
|
+
* then stepped under a custom dim layer. The standing answer: the stage switches the page to the
|
|
467
|
+
* persona's own DARK THEME (data-theme on html, see Preroll), and this row simply wears it - the
|
|
468
|
+
* same bar every dark-system visitor already sees, fully alive, auto-pausing the game when
|
|
469
|
+
* touched. Nothing here needs to know a game exists. */
|
|
475
470
|
/* icon-only section nav — current cut highlighted */
|
|
476
471
|
.tb-nav {
|
|
477
472
|
display: grid; place-items: center; width: 38px; height: 36px;
|
package/src/ground.css
CHANGED
|
@@ -83,8 +83,14 @@
|
|
|
83
83
|
--card-shadow-hover: 0 2px 6px rgba(0, 0, 0, 0.4), 0 12px 28px -14px rgba(0, 0, 0, 0.55);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
/* queries: force dark while playing · theme switch · data-theme dark · stage theme
|
|
87
|
+
* THE GUARD IS THE FLEET'S OWN IDIOM — time's wall wrote it first: *"data-theme wins over the media
|
|
88
|
+
* query in both directions."* A light-system visitor whose page carries data-theme="dark" gets the
|
|
89
|
+
* dark theme this file already ships; removing the attribute returns them to auto. The stage
|
|
90
|
+
* protocol is the first caster (a game playing = dark), and any future manual theme switch gets
|
|
91
|
+
* this seam for free. */
|
|
86
92
|
@media (prefers-color-scheme: light) {
|
|
87
|
-
:where(:root) {
|
|
93
|
+
:where(:root:not([data-theme="dark"])) {
|
|
88
94
|
--bg: #f6f7f9;
|
|
89
95
|
--text: #0a0c10;
|
|
90
96
|
--text-dim: #5b6472;
|