@justai/cuts 0.21.0 → 0.23.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.21.0",
3
+ "version": "0.23.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
@@ -296,7 +296,23 @@ const localePath = (l: string) => (l === defaultLocale ? "/" : `/${l}/`);
296
296
  if (localStorage.getItem("consent")) return;
297
297
  try { if (sessionStorage.getItem("consent-later")) return; } catch (_) {}
298
298
  if (!document.getElementById("kernel")) return; // no kernel = not a content page (e.g. 404) — don't ask for consent on a dead end
299
- if ((Intl.DateTimeFormat().resolvedOptions().timeZone || "").indexOf("Europe/") !== 0) return;
299
+ // WHERE CONSENT IS LEGALLY ASKED — and a timezone is a PROXY for that, never the thing
300
+ // itself. `Europe/` carries almost all of the region list in the head, but not all of it:
301
+ // an EEA country can sit in another continent's zone entirely, and those visitors were
302
+ // DENIED by the regional default while never being offered the dialog — no path to
303
+ // consent at all. Twelve zones, measured, listed here with the country that owns them.
304
+ // The two lists are siblings: if the region array up there ever changes, this changes too.
305
+ var tz = Intl.DateTimeFormat().resolvedOptions().timeZone || "";
306
+ var asked = tz.indexOf("Europe/") === 0 || [
307
+ "Asia/Nicosia", "Asia/Famagusta", // Cyprus
308
+ "Atlantic/Canary", // Spain — Canary Islands
309
+ "Atlantic/Azores", "Atlantic/Madeira", // Portugal
310
+ "Atlantic/Reykjavik", // Iceland
311
+ "Arctic/Longyearbyen", // Norway — Svalbard
312
+ "America/Guadeloupe", "America/Martinique", "America/Cayenne", // France — outermost
313
+ "Indian/Reunion", "Indian/Mayotte" // regions; EU law applies
314
+ ].indexOf(tz) >= 0;
315
+ if (!asked) return;
300
316
  // the first second belongs to the kernel; the question arrives on the second beat
301
317
  setTimeout(open, 700);
302
318
  } catch (_) {}
@@ -24,7 +24,14 @@ const { current, locales, localeNames, localePath, label, searchLabel } = Astro.
24
24
  <button class="trigger" type="button" aria-haspopup="listbox" aria-expanded="false" data-trigger aria-label={`${label}: ${current.toUpperCase()}`} title={label}>{current.toUpperCase()}</button>
25
25
 
26
26
  <div class="menu" role="listbox" data-menu hidden>
27
- <input type="search" class="search" placeholder={searchLabel} autocomplete="off" spellcheck="false" data-search aria-label={searchLabel} />
27
+ {/* `name` and not `id`: a form field with neither makes the browser complain that it cannot
28
+ autofill (Chrome's Issues panel flags it on every page of the fleet, since this cut is on
29
+ every page). `name` answers it without the one risk an `id` would carry — an id must be
30
+ unique in a document, so a persona that ever renders two pickers would ship invalid HTML,
31
+ and a shared cut must be safe to place twice. Prefixed `lang-` so it cannot collide with a
32
+ persona's own field of the same idea. The value is never submitted: this input lives in no
33
+ form and filters the list client-side. */}
34
+ <input type="search" class="search" name="lang-search" placeholder={searchLabel} autocomplete="off" spellcheck="false" data-search aria-label={searchLabel} />
28
35
  <div class="list" data-list>
29
36
  {locales.map((l) => (
30
37
  <a