@justai/cuts 0.3.1 → 0.4.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 +2 -2
- package/src/Base.astro +37 -13
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justai/cuts",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A persona's named parts
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "A persona's named parts — the page shell that holds them, and the cuts themselves.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
package/src/Base.astro
CHANGED
|
@@ -40,8 +40,11 @@ interface Props {
|
|
|
40
40
|
/** Defaults are the fleet majority, measured. Override when the persona genuinely differs. */
|
|
41
41
|
themeDark?: string;
|
|
42
42
|
themeLight?: string;
|
|
43
|
-
/** app.zone's property. justai.pro
|
|
44
|
-
|
|
43
|
+
/** app.zone's property by default. justai.pro passes its own. Pass `null` for a persona that runs
|
|
44
|
+
* NO analytics at all — the whole GA block and the consent banner are then not rendered. The
|
|
45
|
+
* privacy/terms sub-personas use this: the surface that proves it collects nothing by running
|
|
46
|
+
* nothing. */
|
|
47
|
+
gaId?: string | null;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
const {
|
|
@@ -61,19 +64,28 @@ const localePath = (l: string) => (l === defaultLocale ? "/" : `/${l}/`);
|
|
|
61
64
|
<meta charset="utf-8" />
|
|
62
65
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
63
66
|
<link rel="preconnect" href="https://api.justai.pro" crossorigin />
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
{/* justai auth: capture the SSO token here, synchronously, BEFORE anything else can touch the
|
|
68
|
+
hash. Its position in the head is LOAD-BEARING, not stylistic, because two later scripts
|
|
69
|
+
would otherwise take the token: the scroll-spy rewrites the hash to #kernel and CLOBBERS
|
|
70
|
+
#auth_token before the deferred sdk.js can read it; and the language picker copies the
|
|
71
|
+
current hash onto the next locale's URL (LangPicker), which would PROPAGATE the raw token
|
|
72
|
+
into a new page and into history. Capturing first defeats both. Move this and sign-in breaks
|
|
73
|
+
silently — no error, the visitor simply lands back signed out. */}
|
|
68
74
|
<script is:inline>(function(){try{var h=location.hash||"";var m=h.match(/(?:^#|&)auth_token=([^&]+)/);if(m){localStorage.setItem("justai_token",decodeURIComponent(m[1]))}else if(/(?:^#|&)auth_signout=1\b/.test(h)){localStorage.removeItem("justai_token")}else{return}history.replaceState(null,"",location.pathname+location.search)}catch(e){}})();</script>
|
|
69
75
|
<script is:inline>document.documentElement.classList.add("js")</script>
|
|
76
|
+
{/* Reveal only after a page marks itself ready — gated behind .js so the page is always visible
|
|
77
|
+
if JS is off, and no scroll flash before we position at the Kernel. This style is a TRAP as
|
|
78
|
+
much as a feature, and the warning must stay beside it: ANY page using this layout without a
|
|
79
|
+
kernel script that sets body.ready stays invisible FOREVER. The 404 is the page that keeps
|
|
80
|
+
falling into this — it carries its own inline body.classList.add("ready") for exactly this
|
|
81
|
+
reason. Nothing errors; the page is simply never shown. */}
|
|
70
82
|
<style is:inline>html.js body{opacity:0}html.js body.ready{opacity:1}</style>
|
|
71
|
-
|
|
83
|
+
{/* The locale list is INJECTED, never typed. It used to be a literal array here — a second
|
|
72
84
|
declaration beside src/i18n/index.ts, kept in agreement by hand, in the one place where
|
|
73
85
|
being wrong is invisible: a locale missing from this list simply never auto-routes, and
|
|
74
86
|
nothing anywhere goes red. The same shape in astro.config had already drifted (twelve
|
|
75
87
|
listed while /it/ was live), which is why this one is wired to the real list instead of
|
|
76
|
-
being checked against it.
|
|
88
|
+
being checked against it. */}
|
|
77
89
|
<script is:inline define:vars={{ supported: locales, def: defaultLocale }}>
|
|
78
90
|
// Auto-route to the visitor's language: a saved choice wins; otherwise the browser
|
|
79
91
|
// language on first visit. Only on the default (en) root — explicit /xx/ links are
|
|
@@ -130,7 +142,7 @@ const localePath = (l: string) => (l === defaultLocale ? "/" : `/${l}/`);
|
|
|
130
142
|
rel="icon"
|
|
131
143
|
href={favicon}
|
|
132
144
|
/>
|
|
133
|
-
|
|
145
|
+
{/* PWA: Workbox service worker (precache + offline) + installable manifest */}
|
|
134
146
|
<link rel="manifest" href="/manifest.webmanifest" />
|
|
135
147
|
<link rel="apple-touch-icon" href="/icon-192.png" />
|
|
136
148
|
{import.meta.env.DEV ? (
|
|
@@ -153,14 +165,21 @@ const localePath = (l: string) => (l === defaultLocale ? "/" : `/${l}/`);
|
|
|
153
165
|
if ("serviceWorker" in navigator) addEventListener("load", () => navigator.serviceWorker.register("/sw.js"));
|
|
154
166
|
</script>
|
|
155
167
|
)}
|
|
156
|
-
|
|
168
|
+
{/* Google Analytics 4 — anonymous (ad signals + personalization OFF), lazy-loaded after idle.
|
|
157
169
|
analytics_storage granted: direct user counting (cookieless modeling needs high traffic to
|
|
158
170
|
surface, so it shows nothing at low/launch volume).
|
|
159
171
|
|
|
160
172
|
gaId comes through define:vars because this script runs in the BROWSER. An is:inline block
|
|
161
173
|
has no access to component scope, so a bare `gaId` here would be undefined at runtime and
|
|
162
174
|
every persona would report to a property that does not exist — silently, with no failed
|
|
163
|
-
request and no console error, which is the only kind of analytics bug nobody finds.
|
|
175
|
+
request and no console error, which is the only kind of analytics bug nobody finds. */}
|
|
176
|
+
{/* gaId can be null: a persona that measures NOTHING. Pass gaId={null} and the entire analytics
|
|
177
|
+
block below and the consent banner in the body are simply not rendered — no gtag, no cookie,
|
|
178
|
+
no request. The privacy/terms sub-personas run this way, and it is not an exception but their
|
|
179
|
+
character: the one surface that can prove it collects nothing by running nothing (doctrine 15).
|
|
180
|
+
The default stays app.zone's property, so every existing persona is unchanged — this costs the
|
|
181
|
+
ten that measure themselves exactly nothing. */}
|
|
182
|
+
{gaId && (
|
|
164
183
|
<script is:inline define:vars={{ gaId }}>
|
|
165
184
|
window.dataLayer = window.dataLayer || [];
|
|
166
185
|
function gtag() { dataLayer.push(arguments); }
|
|
@@ -187,11 +206,15 @@ const localePath = (l: string) => (l === defaultLocale ? "/" : `/${l}/`);
|
|
|
187
206
|
});
|
|
188
207
|
});
|
|
189
208
|
</script>
|
|
209
|
+
)}
|
|
190
210
|
</head>
|
|
191
211
|
<body>
|
|
192
212
|
<slot />
|
|
193
|
-
|
|
194
|
-
Consent Mode region default already denies analytics for the EEA/UK until "Allow".
|
|
213
|
+
{/* Consent banner — shown only to European-timezone visitors with no stored choice. The
|
|
214
|
+
Consent Mode region default already denies analytics for the EEA/UK until "Allow". Gated on
|
|
215
|
+
gaId with the analytics block above: a persona that runs no analytics has nothing to consent
|
|
216
|
+
to, so it renders no banner. */}
|
|
217
|
+
{gaId && (
|
|
195
218
|
<div id="cc" class="cc" role="dialog" aria-label={t.chromePrivacy}>
|
|
196
219
|
<div class="cc-card">
|
|
197
220
|
<p class="cc-text">{t.consentText}</p>
|
|
@@ -237,5 +260,6 @@ const localePath = (l: string) => (l === defaultLocale ? "/" : `/${l}/`);
|
|
|
237
260
|
} catch (_) {}
|
|
238
261
|
})();
|
|
239
262
|
</script>
|
|
263
|
+
)}
|
|
240
264
|
</body>
|
|
241
265
|
</html>
|