@sensorario/sg-components 0.0.102 → 0.0.104

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.
@@ -3,11 +3,13 @@ export declare const DEFAULT_APPS_URL = "https://api.simonegentili.com/heimdall/
3
3
  export type LauncherApp = {
4
4
  name: string;
5
5
  url: string;
6
+ /** Not granted to the logged-in user: opening it shows AppAccessGate's "Richiedi accesso". */
7
+ locked?: boolean;
6
8
  };
7
9
  interface AppLauncherProps {
8
10
  /** Endpoint returning `{ apps: LauncherApp[] }` - the enabled apps, managed from Heimdall. */
9
11
  appsUrl?: string;
10
- /** Accessible name of the grid button - this library has no i18n of its own. */
12
+ /** Accessible name of the grid button. Default: the translated "App". */
11
13
  label?: string;
12
14
  /** Logged-in user's JWT: the API then lists only the apps this user may use. */
13
15
  token?: string | null;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import './Icon.css';
3
- export type IconName = 'menu' | 'layout' | 'edit' | 'help-circle' | 'check' | 'x' | 'file-plus' | 'link' | 'share-2' | 'download' | 'book' | 'printer' | 'eye' | 'columns' | 'sidebar-left' | 'sidebar-right' | 'play' | 'pause' | 'volume-2' | 'tech-node' | 'tech-go' | 'tech-php' | 'grid';
3
+ export type IconName = 'menu' | 'layout' | 'edit' | 'help-circle' | 'check' | 'x' | 'file-plus' | 'link' | 'share-2' | 'download' | 'book' | 'printer' | 'eye' | 'columns' | 'sidebar-left' | 'sidebar-right' | 'play' | 'pause' | 'volume-2' | 'tech-node' | 'tech-go' | 'tech-php' | 'grid' | 'lock';
4
4
  export interface IconProps extends React.SVGAttributes<SVGSVGElement> {
5
5
  name: IconName;
6
6
  size?: number;
@@ -0,0 +1,6 @@
1
+ import './LanguageSwitcher.css';
2
+ interface LanguageSwitcherProps {
3
+ className?: string;
4
+ }
5
+ export declare const LanguageSwitcher: ({ className }: LanguageSwitcherProps) => import("react/jsx-runtime").JSX.Element;
6
+ export default LanguageSwitcher;
@@ -0,0 +1,5 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { LanguageSwitcher } from './LanguageSwitcher';
3
+ declare const meta: Meta<typeof LanguageSwitcher>;
4
+ export default meta;
5
+ export declare const Default: StoryObj<typeof LanguageSwitcher>;
@@ -12,6 +12,7 @@ interface ModalProps {
12
12
  children: React.ReactNode;
13
13
  buttons?: ModalButton[];
14
14
  footer?: React.ReactNode;
15
+ /** Accessible name of the dismiss button. Default: the translated "Close". */
15
16
  closeLabel?: string;
16
17
  className?: string;
17
18
  }
@@ -1,4 +1,6 @@
1
1
  import './QuadratoHeader.css';
2
+ /** Dispatch on `window` with the new avatar (data URL or null) as `detail` after changing it, so the header updates without a reload. */
3
+ export declare const AVATAR_CHANGE_EVENT = "sg-avatar-change";
2
4
  interface QuadratoHeaderProps {
3
5
  title?: string;
4
6
  homePageKey?: string;
@@ -0,0 +1,13 @@
1
+ import { type i18n } from 'i18next';
2
+ export declare const SUPPORTED_LANGUAGES: readonly ["it", "en", "fr", "de", "es", "ja"];
3
+ export type SupportedLanguage = typeof SUPPORTED_LANGUAGES[number];
4
+ export declare const LANGUAGE_NAMES: Record<SupportedLanguage, string>;
5
+ export declare const LANGUAGE_COOKIE = "sg-language";
6
+ export declare const readLanguageCookie: () => SupportedLanguage | null;
7
+ export declare const sgI18n: i18n;
8
+ type AppResources = Partial<Record<SupportedLanguage, Record<string, unknown>>>;
9
+ /** Adds the app's own texts (default namespace) to the shared instance and makes it the one react-i18next hooks use. */
10
+ export declare const createI18n: ({ resources }: {
11
+ resources: AppResources;
12
+ }) => i18n;
13
+ export {};
package/dist/index.d.ts CHANGED
@@ -17,3 +17,6 @@ export * from './components/FontPicker/FontPicker';
17
17
  export * from './components/Toggle/Toggle';
18
18
  export * from './components/AppLauncher/AppLauncher';
19
19
  export * from './components/AppAccessGate/AppAccessGate';
20
+ export * from './components/LanguageSwitcher/LanguageSwitcher';
21
+ export { createI18n, sgI18n, SUPPORTED_LANGUAGES, LANGUAGE_NAMES, LANGUAGE_COOKIE, readLanguageCookie } from './i18n';
22
+ export type { SupportedLanguage } from './i18n';
package/dist/index.es.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import e, { forwardRef as t, useEffect as n, useId as r, useImperativeHandle as i, useRef as a, useState as o } from "react";
2
2
  import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
3
+ import { setI18n as u, useTranslation as d } from "react-i18next";
4
+ import f from "i18next";
3
5
  //#region src/components/Icon/Icon.tsx
4
- var u = {
6
+ var p = {
5
7
  menu: /* @__PURE__ */ l(s, { children: [
6
8
  /* @__PURE__ */ c("line", {
7
9
  x1: "3",
@@ -228,8 +230,15 @@ var u = {
228
230
  cy: e,
229
231
  r: "2"
230
232
  }, `${t}-${e}`)))
231
- })
232
- }, d = ({ name: e, size: t = 18, ...n }) => /* @__PURE__ */ c("svg", {
233
+ }),
234
+ lock: /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c("rect", {
235
+ x: "5",
236
+ y: "11",
237
+ width: "14",
238
+ height: "10",
239
+ rx: "2"
240
+ }), /* @__PURE__ */ c("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })] })
241
+ }, m = ({ name: e, size: t = 18, ...n }) => /* @__PURE__ */ c("svg", {
233
242
  className: "sg-icon",
234
243
  width: t,
235
244
  height: t,
@@ -241,8 +250,8 @@ var u = {
241
250
  strokeLinejoin: "round",
242
251
  "aria-hidden": "true",
243
252
  ...n,
244
- children: u[e]
245
- }), f = ({ label: e, icon: t, iconOnly: n, className: r, title: i, ...a }) => /* @__PURE__ */ l("button", {
253
+ children: p[e]
254
+ }), h = ({ label: e, icon: t, iconOnly: n, className: r, title: i, ...a }) => /* @__PURE__ */ l("button", {
246
255
  className: [
247
256
  "custom-button",
248
257
  t ? "custom-button-with-icon" : "",
@@ -250,11 +259,11 @@ var u = {
250
259
  ].filter(Boolean).join(" "),
251
260
  title: i ?? (n ? e : void 0),
252
261
  ...a,
253
- children: [t && /* @__PURE__ */ c(d, { name: t }), /* @__PURE__ */ c("span", {
262
+ children: [t && /* @__PURE__ */ c(m, { name: t }), /* @__PURE__ */ c("span", {
254
263
  className: n ? "custom-button-label-visually-hidden" : void 0,
255
264
  children: e
256
265
  })]
257
- }), p = ({ copyright: e = "© 2026 My Company", href: t = "#", links: n = [] }) => /* @__PURE__ */ l("footer", {
266
+ }), g = ({ copyright: e = "© 2026 My Company", href: t = "#", links: n = [] }) => /* @__PURE__ */ l("footer", {
258
267
  className: "footer",
259
268
  children: [/* @__PURE__ */ c("span", { children: /* @__PURE__ */ c("a", {
260
269
  href: t,
@@ -264,17 +273,17 @@ var u = {
264
273
  style: { marginLeft: "16px" },
265
274
  children: e
266
275
  }, t)) })]
267
- }), m = "simonegentili.com-cookie-consent", h = 3600 * 24 * 365;
268
- function g() {
269
- return document.cookie.split("; ").some((e) => e.startsWith(`${m}=`));
276
+ }), _ = "simonegentili.com-cookie-consent", v = 3600 * 24 * 365;
277
+ function y() {
278
+ return document.cookie.split("; ").some((e) => e.startsWith(`${_}=`));
270
279
  }
271
- function _() {
272
- document.cookie = `${m}=accepted; path=/; domain=.simonegentili.com; max-age=${h}; secure; samesite=strict`;
280
+ function b() {
281
+ document.cookie = `${_}=accepted; path=/; domain=.simonegentili.com; max-age=${v}; secure; samesite=strict`;
273
282
  }
274
- var v = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
283
+ var x = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
275
284
  let [t, r] = o(!1);
276
285
  return n(() => {
277
- r(!g());
286
+ r(!y());
278
287
  }, []), t ? /* @__PURE__ */ l("div", {
279
288
  className: "cookie-consent",
280
289
  role: "region",
@@ -292,15 +301,15 @@ var v = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
292
301
  }),
293
302
  "."
294
303
  ]
295
- }), /* @__PURE__ */ c(f, {
304
+ }), /* @__PURE__ */ c(h, {
296
305
  label: "Accetta",
297
306
  onClick: () => {
298
- _(), r(!1);
307
+ b(), r(!1);
299
308
  },
300
309
  className: "cookie-consent-accept"
301
310
  })]
302
311
  }) : null;
303
- }, y = [
312
+ }, S = [
304
313
  {
305
314
  label: "guitar",
306
315
  href: "https://guitar.simonegentili.com"
@@ -321,11 +330,11 @@ var v = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
321
330
  label: "code2image",
322
331
  href: "https://code2image.simonegentili.com"
323
332
  }
324
- ], b = () => /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c(p, {
333
+ ], C = () => /* @__PURE__ */ l(s, { children: [/* @__PURE__ */ c(g, {
325
334
  href: "https://simonegentili.com",
326
335
  copyright: "© 2026 simonegentili.com",
327
- links: y
328
- }), /* @__PURE__ */ c(v, {})] }), x = ({ onNavigate: e, title: t = "Simone Gentili - senior web developer, tech author and chess enthusiast", homePageKey: n = "home", children: r, className: i }) => /* @__PURE__ */ c("header", {
336
+ links: S
337
+ }), /* @__PURE__ */ c(x, {})] }), w = ({ onNavigate: e, title: t = "Simone Gentili - senior web developer, tech author and chess enthusiast", homePageKey: n = "home", children: r, className: i }) => /* @__PURE__ */ c("header", {
329
338
  className: i ? `header ${i}` : "header",
330
339
  children: /* @__PURE__ */ l("div", {
331
340
  className: "header-container",
@@ -343,46 +352,279 @@ var v = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
343
352
  children: r
344
353
  })]
345
354
  })
346
- }), S = ({ onNavigate: e }) => /* @__PURE__ */ c(x, {
355
+ }), T = ({ onNavigate: e }) => /* @__PURE__ */ c(w, {
347
356
  onNavigate: e,
348
357
  title: "Simone Gentili - senior web developer, tech author and chess enthusiast",
349
358
  homePageKey: "home"
350
- }), C = "https://api.simonegentili.com/heimdall", w = (e) => e.endsWith("/") ? e : `${e}/`, T = (e) => {
351
- let t = w(window.location.origin + window.location.pathname);
352
- return e.filter((e) => t.startsWith(w(e.url))).sort((e, t) => t.url.length - e.url.length)[0] ?? null;
353
- }, E = ({ token: e, heimdallUrl: t = C }) => {
354
- let [r, i] = o(null), [a, u] = o(!1), [d, p] = o(!1);
359
+ }), E = {
360
+ auth: {
361
+ login: "Accedi",
362
+ logout: "Esci",
363
+ username: "Username",
364
+ password: "Password"
365
+ },
366
+ modal: { close: "Chiudi" },
367
+ accessGate: {
368
+ title: "Non hai accesso a {{app}}",
369
+ pending: "La tua richiesta è in attesa di approvazione.",
370
+ rejected: "La tua richiesta di accesso è stata rifiutata. Puoi inviarne una nuova.",
371
+ none: "Per usare questa app devi chiedere l’accesso a un amministratore.",
372
+ failed: "Invio non riuscito, riprova.",
373
+ sending: "Invio…",
374
+ request: "Richiedi accesso"
375
+ },
376
+ launcher: {
377
+ label: "App",
378
+ loading: "Caricamento…",
379
+ loadFailed: "Impossibile caricare le app.",
380
+ locked: "accesso su richiesta"
381
+ },
382
+ setPassword: {
383
+ title: "Imposta una nuova password",
384
+ newPassword: "Nuova password",
385
+ confirmPassword: "Conferma password",
386
+ submit: "Salva password",
387
+ errorEmpty: "Inserisci e conferma la nuova password.",
388
+ errorMismatch: "Le password non coincidono."
389
+ },
390
+ languageSwitcher: { label: "Lingua" }
391
+ }, D = {
392
+ auth: {
393
+ login: "Log in",
394
+ logout: "Log out",
395
+ username: "Username",
396
+ password: "Password"
397
+ },
398
+ modal: { close: "Close" },
399
+ accessGate: {
400
+ title: "You don't have access to {{app}}",
401
+ pending: "Your request is awaiting approval.",
402
+ rejected: "Your access request was rejected. You can send a new one.",
403
+ none: "To use this app you need to ask an administrator for access.",
404
+ failed: "Sending failed, please try again.",
405
+ sending: "Sending…",
406
+ request: "Request access"
407
+ },
408
+ launcher: {
409
+ label: "Apps",
410
+ loading: "Loading…",
411
+ loadFailed: "Unable to load the apps.",
412
+ locked: "access on request"
413
+ },
414
+ setPassword: {
415
+ title: "Set a new password",
416
+ newPassword: "New password",
417
+ confirmPassword: "Confirm password",
418
+ submit: "Save password",
419
+ errorEmpty: "Enter and confirm the new password.",
420
+ errorMismatch: "The passwords don't match."
421
+ },
422
+ languageSwitcher: { label: "Language" }
423
+ }, O = {
424
+ auth: {
425
+ login: "Se connecter",
426
+ logout: "Se déconnecter",
427
+ username: "Nom d’utilisateur",
428
+ password: "Mot de passe"
429
+ },
430
+ modal: { close: "Fermer" },
431
+ accessGate: {
432
+ title: "Vous n’avez pas accès à {{app}}",
433
+ pending: "Votre demande est en attente d’approbation.",
434
+ rejected: "Votre demande d’accès a été refusée. Vous pouvez en envoyer une nouvelle.",
435
+ none: "Pour utiliser cette application, vous devez demander l’accès à un administrateur.",
436
+ failed: "Échec de l’envoi, veuillez réessayer.",
437
+ sending: "Envoi…",
438
+ request: "Demander l’accès"
439
+ },
440
+ launcher: {
441
+ label: "Applications",
442
+ loading: "Chargement…",
443
+ loadFailed: "Impossible de charger les applications.",
444
+ locked: "accès sur demande"
445
+ },
446
+ setPassword: {
447
+ title: "Définir un nouveau mot de passe",
448
+ newPassword: "Nouveau mot de passe",
449
+ confirmPassword: "Confirmer le mot de passe",
450
+ submit: "Enregistrer le mot de passe",
451
+ errorEmpty: "Saisissez et confirmez le nouveau mot de passe.",
452
+ errorMismatch: "Les mots de passe ne correspondent pas."
453
+ },
454
+ languageSwitcher: { label: "Langue" }
455
+ }, k = {
456
+ auth: {
457
+ login: "Anmelden",
458
+ logout: "Abmelden",
459
+ username: "Benutzername",
460
+ password: "Passwort"
461
+ },
462
+ modal: { close: "Schließen" },
463
+ accessGate: {
464
+ title: "Du hast keinen Zugriff auf {{app}}",
465
+ pending: "Deine Anfrage wartet auf Freigabe.",
466
+ rejected: "Deine Zugriffsanfrage wurde abgelehnt. Du kannst eine neue senden.",
467
+ none: "Um diese App zu nutzen, musst du einen Administrator um Zugriff bitten.",
468
+ failed: "Senden fehlgeschlagen, bitte versuche es erneut.",
469
+ sending: "Wird gesendet…",
470
+ request: "Zugriff anfragen"
471
+ },
472
+ launcher: {
473
+ label: "Apps",
474
+ loading: "Wird geladen…",
475
+ loadFailed: "Die Apps konnten nicht geladen werden.",
476
+ locked: "Zugriff auf Anfrage"
477
+ },
478
+ setPassword: {
479
+ title: "Neues Passwort festlegen",
480
+ newPassword: "Neues Passwort",
481
+ confirmPassword: "Passwort bestätigen",
482
+ submit: "Passwort speichern",
483
+ errorEmpty: "Gib das neue Passwort ein und bestätige es.",
484
+ errorMismatch: "Die Passwörter stimmen nicht überein."
485
+ },
486
+ languageSwitcher: { label: "Sprache" }
487
+ }, A = {
488
+ auth: {
489
+ login: "Iniciar sesión",
490
+ logout: "Cerrar sesión",
491
+ username: "Usuario",
492
+ password: "Contraseña"
493
+ },
494
+ modal: { close: "Cerrar" },
495
+ accessGate: {
496
+ title: "No tienes acceso a {{app}}",
497
+ pending: "Tu solicitud está pendiente de aprobación.",
498
+ rejected: "Tu solicitud de acceso ha sido rechazada. Puedes enviar una nueva.",
499
+ none: "Para usar esta aplicación debes pedir acceso a un administrador.",
500
+ failed: "No se pudo enviar, inténtalo de nuevo.",
501
+ sending: "Enviando…",
502
+ request: "Solicitar acceso"
503
+ },
504
+ launcher: {
505
+ label: "Aplicaciones",
506
+ loading: "Cargando…",
507
+ loadFailed: "No se pudieron cargar las aplicaciones.",
508
+ locked: "acceso bajo solicitud"
509
+ },
510
+ setPassword: {
511
+ title: "Establecer una nueva contraseña",
512
+ newPassword: "Nueva contraseña",
513
+ confirmPassword: "Confirmar contraseña",
514
+ submit: "Guardar contraseña",
515
+ errorEmpty: "Introduce y confirma la nueva contraseña.",
516
+ errorMismatch: "Las contraseñas no coinciden."
517
+ },
518
+ languageSwitcher: { label: "Idioma" }
519
+ }, ee = {
520
+ auth: {
521
+ login: "ログイン",
522
+ logout: "ログアウト",
523
+ username: "ユーザー名",
524
+ password: "パスワード"
525
+ },
526
+ modal: { close: "閉じる" },
527
+ accessGate: {
528
+ title: "{{app}} へのアクセス権がありません",
529
+ pending: "リクエストは承認待ちです。",
530
+ rejected: "アクセスリクエストは却下されました。新しいリクエストを送信できます。",
531
+ none: "このアプリを使うには、管理者にアクセスを申請してください。",
532
+ failed: "送信に失敗しました。もう一度お試しください。",
533
+ sending: "送信中…",
534
+ request: "アクセスを申請"
535
+ },
536
+ launcher: {
537
+ label: "アプリ",
538
+ loading: "読み込み中…",
539
+ loadFailed: "アプリを読み込めませんでした。",
540
+ locked: "申請によりアクセス可能"
541
+ },
542
+ setPassword: {
543
+ title: "新しいパスワードを設定",
544
+ newPassword: "新しいパスワード",
545
+ confirmPassword: "パスワードの確認",
546
+ submit: "パスワードを保存",
547
+ errorEmpty: "新しいパスワードを入力して確認してください。",
548
+ errorMismatch: "パスワードが一致しません。"
549
+ },
550
+ languageSwitcher: { label: "言語" }
551
+ }, j = [
552
+ "it",
553
+ "en",
554
+ "fr",
555
+ "de",
556
+ "es",
557
+ "ja"
558
+ ], M = {
559
+ it: "Italiano",
560
+ en: "English",
561
+ fr: "Français",
562
+ de: "Deutsch",
563
+ es: "Español",
564
+ ja: "日本語"
565
+ }, N = "sg-language", P = "it", te = (e) => !!e && j.includes(e), F = () => {
566
+ let e = document.cookie.split("; ").find((e) => e.startsWith(`${N}=`))?.slice(12);
567
+ return te(e) ? e : null;
568
+ }, I = (e) => {
569
+ let t = window.location.hostname.endsWith("simonegentili.com") ? "; domain=.simonegentili.com; secure" : "";
570
+ document.cookie = `${N}=${e}; path=/; max-age=31536000; samesite=lax${t}`;
571
+ }, L = f.createInstance();
572
+ L.init({
573
+ resources: {
574
+ it: { sg: E },
575
+ en: { sg: D },
576
+ fr: { sg: O },
577
+ de: { sg: k },
578
+ es: { sg: A },
579
+ ja: { sg: ee }
580
+ },
581
+ lng: F() ?? P,
582
+ fallbackLng: P,
583
+ supportedLngs: [...j],
584
+ ns: ["sg", "translation"],
585
+ defaultNS: "translation",
586
+ initAsync: !1,
587
+ interpolation: { escapeValue: !1 }
588
+ }), L.on("languageChanged", I);
589
+ var ne = ({ resources: e }) => {
590
+ for (let [t, n] of Object.entries(e)) L.addResourceBundle(t, "translation", n, !0, !0);
591
+ return u(L), L;
592
+ }, R = "https://api.simonegentili.com/heimdall", z = (e) => e.endsWith("/") ? e : `${e}/`, re = (e) => {
593
+ let t = z(window.location.origin + window.location.pathname);
594
+ return e.filter((e) => t.startsWith(z(e.url))).sort((e, t) => t.url.length - e.url.length)[0] ?? null;
595
+ }, B = ({ token: e, heimdallUrl: t = R }) => {
596
+ let { t: r } = d("sg", { i18n: L }), [i, a] = o(null), [u, f] = o(!1), [p, m] = o(!1);
355
597
  n(() => {
356
- e && fetch(`${t}/access`, { headers: { Authorization: `Bearer ${e}` } }).then((e) => e.ok ? e.json() : Promise.reject(e.status)).then((t) => i({
598
+ e && fetch(`${t}/access`, { headers: { Authorization: `Bearer ${e}` } }).then((e) => e.ok ? e.json() : Promise.reject(e.status)).then((t) => a({
357
599
  token: e,
358
- app: T(t.apps)
359
- })).catch(() => i({
600
+ app: re(t.apps)
601
+ })).catch(() => a({
360
602
  token: e,
361
603
  app: null
362
604
  }));
363
605
  }, [e, t]);
364
- let m = r?.token === e ? r.app : null;
365
- return !e || m === null || m.access === "open" || m.access === "approved" ? null : /* @__PURE__ */ c("div", {
606
+ let g = i?.token === e ? i.app : null;
607
+ return !e || g === null || g.access === "open" || g.access === "approved" ? null : /* @__PURE__ */ c("div", {
366
608
  className: "sg-access-gate",
367
609
  role: "dialog",
368
610
  "aria-modal": "true",
369
611
  "aria-labelledby": "sg-access-gate-title",
370
612
  children: /* @__PURE__ */ l("div", {
371
613
  className: "sg-access-gate-card",
372
- children: [/* @__PURE__ */ l("h2", {
614
+ children: [/* @__PURE__ */ c("h2", {
373
615
  id: "sg-access-gate-title",
374
- children: ["Non hai accesso a ", m.name]
375
- }), m.access === "pending" ? /* @__PURE__ */ c("p", { children: "La tua richiesta è in attesa di approvazione." }) : /* @__PURE__ */ l(s, { children: [
376
- /* @__PURE__ */ c("p", { children: m.access === "rejected" ? "La tua richiesta di accesso è stata rifiutata. Puoi inviarne una nuova." : "Per usare questa app devi chiedere l’accesso a un amministratore." }),
377
- d && /* @__PURE__ */ c("p", {
616
+ children: r("accessGate.title", { app: g.name })
617
+ }), g.access === "pending" ? /* @__PURE__ */ c("p", { children: r("accessGate.pending") }) : /* @__PURE__ */ l(s, { children: [
618
+ /* @__PURE__ */ c("p", { children: g.access === "rejected" ? r("accessGate.rejected") : r("accessGate.none") }),
619
+ p && /* @__PURE__ */ c("p", {
378
620
  className: "sg-access-gate-error",
379
- children: "Invio non riuscito, riprova."
621
+ children: r("accessGate.failed")
380
622
  }),
381
- /* @__PURE__ */ c(f, {
382
- label: a ? "Invio…" : "Richiedi accesso",
383
- disabled: a,
623
+ /* @__PURE__ */ c(h, {
624
+ label: r(u ? "accessGate.sending" : "accessGate.request"),
625
+ disabled: u,
384
626
  onClick: async () => {
385
- u(!0), p(!1);
627
+ f(!0), m(!1);
386
628
  try {
387
629
  let n = await fetch(`${t}/access-requests`, {
388
630
  method: "POST",
@@ -390,153 +632,174 @@ var v = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
390
632
  Authorization: `Bearer ${e}`,
391
633
  "Content-Type": "application/json"
392
634
  },
393
- body: JSON.stringify({ app: m.slug })
635
+ body: JSON.stringify({ app: g.slug })
394
636
  });
395
- n.ok || n.status === 409 ? i({
637
+ n.ok || n.status === 409 ? a({
396
638
  token: e,
397
639
  app: {
398
- ...m,
640
+ ...g,
399
641
  access: "pending"
400
642
  }
401
- }) : p(!0);
643
+ }) : m(!0);
402
644
  } catch {
403
- p(!0);
645
+ m(!0);
404
646
  } finally {
405
- u(!1);
647
+ f(!1);
406
648
  }
407
649
  }
408
650
  })
409
651
  ] })]
410
652
  })
411
653
  });
412
- }, D = "https://api.simonegentili.com/heimdall/launcher", O = ({ appsUrl: e = D, label: t = "App", token: r = null }) => {
413
- let [i, s] = o(!1), [u, f] = o(null), p = u?.token === r ? u.apps : null, [m, h] = o(!1), g = a(null);
654
+ }, V = "https://api.simonegentili.com/heimdall/launcher", H = ({ appsUrl: e = V, label: t, token: r = null }) => {
655
+ let { t: i } = d("sg", { i18n: L }), s = t ?? i("launcher.label"), [u, f] = o(!1), [p, h] = o(null), g = p?.token === r ? p.apps : null, [_, v] = o(!1), y = a(null);
414
656
  return n(() => {
415
- !i || p !== null || fetch(e, r ? { headers: { Authorization: `Bearer ${r}` } } : void 0).then((e) => e.ok ? e.json() : Promise.reject(e.status)).then((e) => f({
657
+ !u || g !== null || fetch(e, r ? { headers: { Authorization: `Bearer ${r}` } } : void 0).then((e) => e.ok ? e.json() : Promise.reject(e.status)).then((e) => h({
416
658
  token: r,
417
659
  apps: e.apps
418
- })).catch(() => h(!0));
660
+ })).catch(() => v(!0));
419
661
  }, [
420
- i,
421
- p,
662
+ u,
663
+ g,
422
664
  e,
423
665
  r
424
666
  ]), n(() => {
425
- if (!i) return;
667
+ if (!u) return;
426
668
  let e = (e) => {
427
- g.current?.contains(e.target) || s(!1);
669
+ y.current?.contains(e.target) || f(!1);
428
670
  }, t = (e) => {
429
- e.key === "Escape" && s(!1);
671
+ e.key === "Escape" && f(!1);
430
672
  };
431
673
  return document.addEventListener("pointerdown", e), document.addEventListener("keydown", t), () => {
432
674
  document.removeEventListener("pointerdown", e), document.removeEventListener("keydown", t);
433
675
  };
434
- }, [i]), /* @__PURE__ */ l("div", {
676
+ }, [u]), /* @__PURE__ */ l("div", {
435
677
  className: "sg-app-launcher",
436
- ref: g,
678
+ ref: y,
437
679
  children: [/* @__PURE__ */ c("button", {
438
680
  type: "button",
439
681
  className: "sg-app-launcher-toggle",
440
- "aria-label": t,
441
- title: t,
442
- "aria-expanded": i,
682
+ "aria-label": s,
683
+ title: s,
684
+ "aria-expanded": u,
443
685
  onClick: () => {
444
- i || h(!1), s(!i);
686
+ u || v(!1), f(!u);
445
687
  },
446
- children: /* @__PURE__ */ c(d, {
688
+ children: /* @__PURE__ */ c(m, {
447
689
  name: "grid",
448
690
  size: 22
449
691
  })
450
- }), i && /* @__PURE__ */ c("div", {
692
+ }), u && /* @__PURE__ */ c("div", {
451
693
  className: "sg-app-launcher-panel",
452
- children: m ? /* @__PURE__ */ c("p", {
694
+ children: _ ? /* @__PURE__ */ c("p", {
453
695
  className: "sg-app-launcher-message",
454
- children: "Impossibile caricare le app."
455
- }) : p === null ? /* @__PURE__ */ c("p", {
696
+ children: i("launcher.loadFailed")
697
+ }) : g === null ? /* @__PURE__ */ c("p", {
456
698
  className: "sg-app-launcher-message",
457
- children: "Caricamento…"
699
+ children: i("launcher.loading")
458
700
  }) : /* @__PURE__ */ c("ul", {
459
701
  className: "sg-app-launcher-grid",
460
- children: p.map((e) => /* @__PURE__ */ c("li", { children: /* @__PURE__ */ l("a", {
702
+ children: g.map((e) => /* @__PURE__ */ c("li", { children: /* @__PURE__ */ l("a", {
461
703
  href: e.url,
462
- className: new URL(e.url).origin === window.location.origin ? "sg-app-launcher-app current" : "sg-app-launcher-app",
463
- children: [/* @__PURE__ */ c("span", {
704
+ className: [
705
+ "sg-app-launcher-app",
706
+ new URL(e.url).origin === window.location.origin ? "current" : "",
707
+ e.locked ? "locked" : ""
708
+ ].filter(Boolean).join(" "),
709
+ title: e.locked ? `${e.name}: ${i("launcher.locked")}` : void 0,
710
+ children: [/* @__PURE__ */ l("span", {
464
711
  className: "sg-app-launcher-tile",
465
712
  "aria-hidden": "true",
466
- children: e.name.charAt(0).toUpperCase()
467
- }), /* @__PURE__ */ c("span", {
713
+ children: [e.name.charAt(0).toUpperCase(), e.locked && /* @__PURE__ */ c("span", {
714
+ className: "sg-app-launcher-lock",
715
+ children: /* @__PURE__ */ c(m, {
716
+ name: "lock",
717
+ size: 12
718
+ })
719
+ })]
720
+ }), /* @__PURE__ */ l("span", {
468
721
  className: "sg-app-launcher-name",
469
- children: e.name
722
+ children: [e.name, e.locked && /* @__PURE__ */ l("span", {
723
+ className: "sg-visually-hidden",
724
+ children: [
725
+ " (",
726
+ i("launcher.locked"),
727
+ ")"
728
+ ]
729
+ })]
470
730
  })]
471
731
  }) }, e.url))
472
732
  })
473
733
  })]
474
734
  });
475
- }, k = ({ isLoggedIn: e, handleLogin: t, handleLogout: n }) => /* @__PURE__ */ c(s, { children: e ? /* @__PURE__ */ c("button", {
476
- onClick: n,
477
- className: "sg-authenticator-button",
478
- children: "logout"
479
- }) : /* @__PURE__ */ c("button", {
480
- onClick: t,
481
- className: "sg-authenticator-button",
482
- children: "Login"
483
- }) }), A = 220, j = ({ open: t, onClose: r, title: i, icon: a, children: s, buttons: u, footer: d, closeLabel: p = "Close", className: m }) => {
484
- let [h, g] = o(!1), [_, v] = o({
735
+ }, U = ({ isLoggedIn: e, handleLogin: t, handleLogout: n }) => {
736
+ let { t: r } = d("sg", { i18n: L });
737
+ return /* @__PURE__ */ c(s, { children: e ? /* @__PURE__ */ c("button", {
738
+ onClick: n,
739
+ className: "sg-authenticator-button",
740
+ children: r("auth.logout")
741
+ }) : /* @__PURE__ */ c("button", {
742
+ onClick: t,
743
+ className: "sg-authenticator-button",
744
+ children: r("auth.login")
745
+ }) });
746
+ }, ie = 220, W = ({ open: t, onClose: r, title: i, icon: a, children: s, buttons: u, footer: f, closeLabel: p, className: m }) => {
747
+ let { t: g } = d("sg", { i18n: L }), [_, v] = o(!1), [y, b] = o({
485
748
  x: 0,
486
749
  y: 0
487
- }), y = e.useRef(null), [b, x] = o(!1), S = (e) => {
488
- h || (g(!0), setTimeout(() => {
489
- g(!1), e();
490
- }, A));
750
+ }), x = e.useRef(null), [S, C] = o(!1), w = (e) => {
751
+ _ || (v(!0), setTimeout(() => {
752
+ v(!1), e();
753
+ }, ie));
491
754
  };
492
755
  n(() => {
493
756
  if (!t) return;
494
757
  let e = (e) => {
495
- e.key === "Escape" && S(r);
758
+ e.key === "Escape" && w(r);
496
759
  };
497
760
  return window.addEventListener("keydown", e), () => window.removeEventListener("keydown", e);
498
761
  }, [t, r]);
499
- let [C, w] = o(t);
500
- if (t !== C && (w(t), t && v({
762
+ let [T, E] = o(t);
763
+ if (t !== T && (E(t), t && b({
501
764
  x: 0,
502
765
  y: 0
503
766
  })), !t) return null;
504
- function T(e) {
505
- e.target.closest(".modal-close") || (y.current = {
767
+ function D(e) {
768
+ e.target.closest(".modal-close") || (x.current = {
506
769
  pointerX: e.clientX,
507
770
  pointerY: e.clientY,
508
- offsetX: _.x,
509
- offsetY: _.y
510
- }, x(!0), e.currentTarget.setPointerCapture(e.pointerId));
771
+ offsetX: y.x,
772
+ offsetY: y.y
773
+ }, C(!0), e.currentTarget.setPointerCapture(e.pointerId));
511
774
  }
512
- function E(e) {
513
- if (!y.current) return;
514
- let { pointerX: t, pointerY: n, offsetX: r, offsetY: i } = y.current;
515
- v({
775
+ function O(e) {
776
+ if (!x.current) return;
777
+ let { pointerX: t, pointerY: n, offsetX: r, offsetY: i } = x.current;
778
+ b({
516
779
  x: r + (e.clientX - t),
517
780
  y: i + (e.clientY - n)
518
781
  });
519
782
  }
520
- function D() {
521
- y.current = null, x(!1);
783
+ function k() {
784
+ x.current = null, C(!1);
522
785
  }
523
786
  return /* @__PURE__ */ c("div", {
524
- className: `modal-overlay${h ? " modal-overlay-closing" : ""}`,
525
- onClick: () => S(r),
787
+ className: `modal-overlay${_ ? " modal-overlay-closing" : ""}`,
788
+ onClick: () => w(r),
526
789
  children: /* @__PURE__ */ l("div", {
527
- className: `modal-content${m ? ` ${m}` : ""}${h ? " modal-content-closing" : ""}`,
790
+ className: `modal-content${m ? ` ${m}` : ""}${_ ? " modal-content-closing" : ""}`,
528
791
  style: {
529
- "--drag-x": `${_.x}px`,
530
- "--drag-y": `${_.y}px`
792
+ "--drag-x": `${y.x}px`,
793
+ "--drag-y": `${y.y}px`
531
794
  },
532
795
  onClick: (e) => e.stopPropagation(),
533
796
  children: [
534
797
  /* @__PURE__ */ l("div", {
535
- className: `modal-header${b ? " modal-header-dragging" : ""}`,
536
- onPointerDown: T,
537
- onPointerMove: E,
538
- onPointerUp: D,
539
- onPointerCancel: D,
798
+ className: `modal-header${S ? " modal-header-dragging" : ""}`,
799
+ onPointerDown: D,
800
+ onPointerMove: O,
801
+ onPointerUp: k,
802
+ onPointerCancel: k,
540
803
  children: [
541
804
  a && /* @__PURE__ */ c("span", {
542
805
  className: "modal-icon",
@@ -549,9 +812,9 @@ var v = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
549
812
  /* @__PURE__ */ c("button", {
550
813
  type: "button",
551
814
  className: "modal-close",
552
- "aria-label": p,
553
- onClick: () => S(r),
554
- disabled: h,
815
+ "aria-label": p ?? g("modal.close"),
816
+ onClick: () => w(r),
817
+ disabled: _,
555
818
  children: "×"
556
819
  })
557
820
  ]
@@ -562,20 +825,20 @@ var v = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
562
825
  }),
563
826
  u && /* @__PURE__ */ c("div", {
564
827
  className: "modal-footer",
565
- children: u.map((e, t) => /* @__PURE__ */ c(f, {
828
+ children: u.map((e, t) => /* @__PURE__ */ c(h, {
566
829
  label: e.label,
567
- onClick: () => S(e.onClick),
568
- disabled: h
830
+ onClick: () => w(e.onClick),
831
+ disabled: _
569
832
  }, t))
570
833
  }),
571
- d && /* @__PURE__ */ c("div", {
834
+ f && /* @__PURE__ */ c("div", {
572
835
  className: "modal-footer",
573
- children: d
836
+ children: f
574
837
  })
575
838
  ]
576
839
  })
577
840
  });
578
- }, M = ({ label: e, invalid: t, className: n, id: i, ...a }) => {
841
+ }, G = ({ label: e, invalid: t, className: n, id: i, ...a }) => {
579
842
  let o = r(), s = i ?? o;
580
843
  return /* @__PURE__ */ l("div", {
581
844
  className: "input-wrapper",
@@ -593,7 +856,7 @@ var v = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
593
856
  ...a
594
857
  })]
595
858
  });
596
- }, N = ({ label: e, className: t, id: n, ...i }) => {
859
+ }, K = ({ label: e, className: t, id: n, ...i }) => {
597
860
  let a = r(), o = n ?? a;
598
861
  return /* @__PURE__ */ l("div", {
599
862
  className: "input-wrapper",
@@ -608,138 +871,164 @@ var v = ({ privacyPolicyHref: e = "https://simonegentili.com/privacy" }) => {
608
871
  ...i
609
872
  })]
610
873
  });
611
- }, P = ({ open: e, onClose: t, onLogin: n }) => {
612
- let [r, i] = o(""), [a, s] = o("");
613
- return /* @__PURE__ */ c(j, {
874
+ }, q = ({ open: e, onClose: t, onLogin: n }) => {
875
+ let { t: r } = d("sg", { i18n: L }), [i, a] = o(""), [s, u] = o("");
876
+ return /* @__PURE__ */ c(W, {
614
877
  open: e,
615
878
  onClose: t,
616
- title: "Login",
879
+ title: r("auth.login"),
617
880
  children: /* @__PURE__ */ l("form", {
618
881
  onSubmit: (e) => {
619
- e.preventDefault(), n(r, a);
882
+ e.preventDefault(), n(i, s);
620
883
  },
621
884
  children: [
622
- /* @__PURE__ */ c(M, {
623
- label: "Username",
624
- value: r,
625
- onChange: (e) => i(e.target.value),
885
+ /* @__PURE__ */ c(G, {
886
+ label: r("auth.username"),
887
+ value: i,
888
+ onChange: (e) => a(e.target.value),
626
889
  autoFocus: !0
627
890
  }),
628
- /* @__PURE__ */ c(N, {
629
- label: "Password",
630
- value: a,
631
- onChange: (e) => s(e.target.value)
891
+ /* @__PURE__ */ c(K, {
892
+ label: r("auth.password"),
893
+ value: s,
894
+ onChange: (e) => u(e.target.value)
632
895
  }),
633
- /* @__PURE__ */ c(f, {
634
- label: "Login",
896
+ /* @__PURE__ */ c(h, {
897
+ label: r("auth.login"),
635
898
  type: "submit",
636
899
  style: { width: "100%" }
637
900
  })
638
901
  ]
639
902
  })
640
903
  });
641
- }, F = "simonegentili.com-access-token", I = (e) => document.cookie.split("; ").some((t) => t.startsWith(`${e}=`) && t.slice(e.length + 1).length > 0), L = (e) => {
904
+ }, J = "simonegentili.com-access-token", Y = "sg-avatar-change", X = (e) => document.cookie.split("; ").some((t) => t.startsWith(`${e}=`) && t.slice(e.length + 1).length > 0), ae = (e) => {
642
905
  let t = document.cookie.split("; ").find((t) => t.startsWith(`${e}=`));
643
906
  return t ? t.slice(e.length + 1) : null;
644
- }, R = (e, t) => {
907
+ }, oe = (e, t) => {
645
908
  try {
646
909
  return JSON.parse(atob(e.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")))[t] ?? null;
647
910
  } catch {
648
911
  return null;
649
912
  }
650
- }, z = t(({ title: e = "Quadrato", homePageKey: t = "home", onNavigate: r, cookieName: a = F, username: u = null, usernameJwtField: d = "sub", onLogin: f, onLogout: p, onUserAuthenticated: m, appsUrl: h, heimdallUrl: g }, _) => {
651
- let [v, y] = o(() => I(a)), [b, S] = o(!1);
652
- i(_, () => ({ openLoginModal: () => S(!0) }));
653
- let C = v ? L(a) : null, w = u ?? (C ? R(C, d) : null);
913
+ }, Z = t(({ title: e = "Quadrato", homePageKey: t = "home", onNavigate: r, cookieName: a = J, username: u = null, usernameJwtField: d = "sub", onLogin: f, onLogout: p, onUserAuthenticated: m, appsUrl: h, heimdallUrl: g = R }, _) => {
914
+ let [v, y] = o(() => X(a)), [b, x] = o(!1);
915
+ i(_, () => ({ openLoginModal: () => x(!0) }));
916
+ let S = v ? ae(a) : null, C = u ?? (S ? oe(S, d) : null), [T, E] = o(null), D = S && T?.token === S ? T.src : null;
654
917
  return n(() => {
655
- m?.(v, v ? w : null);
918
+ if (!S) return;
919
+ let e = !1;
920
+ return fetch(`${g}/me/avatar`, { headers: { Authorization: `Bearer ${S}` } }).then((e) => e.ok ? e.json() : null).then((t) => {
921
+ e || E({
922
+ token: S,
923
+ src: t?.avatar ?? null
924
+ });
925
+ }).catch(() => {}), () => {
926
+ e = !0;
927
+ };
928
+ }, [S, g]), n(() => {
929
+ if (!S) return;
930
+ let e = (e) => E({
931
+ token: S,
932
+ src: e.detail
933
+ });
934
+ return window.addEventListener(Y, e), () => window.removeEventListener(Y, e);
935
+ }, [S]), n(() => {
936
+ m?.(v, v ? C : null);
656
937
  }, []), /* @__PURE__ */ l(s, { children: [
657
- /* @__PURE__ */ l(x, {
938
+ /* @__PURE__ */ l(w, {
658
939
  onNavigate: r,
659
940
  title: e,
660
941
  homePageKey: t,
661
942
  className: "quadrato-header",
662
- children: [/* @__PURE__ */ c(O, {
943
+ children: [/* @__PURE__ */ c(H, {
663
944
  appsUrl: h,
664
- token: C
945
+ token: S
665
946
  }), /* @__PURE__ */ l("div", {
666
947
  className: "quadrato-header-auth",
667
- children: [v && w && /* @__PURE__ */ c("span", {
668
- className: "quadrato-header-username",
669
- children: w
670
- }), /* @__PURE__ */ c(k, {
671
- isLoggedIn: v,
672
- handleLogin: () => S(!0),
673
- handleLogout: () => {
674
- p?.();
675
- let e = I(a);
676
- y(e), m?.(e, null);
677
- }
678
- })]
948
+ children: [
949
+ v && D && /* @__PURE__ */ c("img", {
950
+ className: "quadrato-header-avatar",
951
+ src: D,
952
+ alt: ""
953
+ }),
954
+ v && C && /* @__PURE__ */ c("span", {
955
+ className: "quadrato-header-username",
956
+ children: C
957
+ }),
958
+ /* @__PURE__ */ c(U, {
959
+ isLoggedIn: v,
960
+ handleLogin: () => x(!0),
961
+ handleLogout: () => {
962
+ p?.();
963
+ let e = X(a);
964
+ y(e), m?.(e, null);
965
+ }
966
+ })
967
+ ]
679
968
  })]
680
969
  }),
681
- /* @__PURE__ */ c(E, {
682
- token: C,
970
+ /* @__PURE__ */ c(B, {
971
+ token: S,
683
972
  heimdallUrl: g
684
973
  }),
685
- /* @__PURE__ */ c(P, {
974
+ /* @__PURE__ */ c(q, {
686
975
  open: b,
687
- onClose: () => S(!1),
976
+ onClose: () => x(!1),
688
977
  onLogin: async (e, t) => {
689
978
  await f(e, t);
690
- let n = I(a);
691
- y(n), S(!1), m?.(n, n ? u ?? e : null);
979
+ let n = X(a);
980
+ y(n), x(!1), m?.(n, n ? u ?? e : null);
692
981
  }
693
982
  })
694
983
  ] });
695
984
  });
696
- z.displayName = "QuadratoHeader";
985
+ Z.displayName = "QuadratoHeader";
697
986
  //#endregion
698
987
  //#region src/components/SetPasswordModal/SetPasswordModal.tsx
699
- var B = ({ open: e, onClose: t, onSubmit: n }) => {
700
- let [r, i] = o(""), [a, s] = o(""), [u, d] = o("");
701
- return /* @__PURE__ */ c(j, {
988
+ var se = ({ open: e, onClose: t, onSubmit: n }) => {
989
+ let { t: r } = d("sg", { i18n: L }), [i, a] = o(""), [s, u] = o(""), [f, p] = o("");
990
+ return /* @__PURE__ */ c(W, {
702
991
  open: e,
703
992
  onClose: t,
704
- title: "Imposta una nuova password",
993
+ title: r("setPassword.title"),
705
994
  children: /* @__PURE__ */ l("form", {
706
995
  className: "set-password-modal-form",
707
996
  onSubmit: (e) => {
708
- if (e.preventDefault(), r === "" || a === "") {
709
- d("Inserisci ed conferma la nuova password.");
997
+ if (e.preventDefault(), i === "" || s === "") {
998
+ p(r("setPassword.errorEmpty"));
710
999
  return;
711
1000
  }
712
- if (r !== a) {
713
- d("Le password non coincidono.");
1001
+ if (i !== s) {
1002
+ p(r("setPassword.errorMismatch"));
714
1003
  return;
715
1004
  }
716
- d(""), n(r);
1005
+ p(""), n(i);
717
1006
  },
718
1007
  children: [
719
- /* @__PURE__ */ c(N, {
720
- label: "Nuova password",
721
- value: r,
722
- onChange: (e) => i(e.target.value),
1008
+ /* @__PURE__ */ c(K, {
1009
+ label: r("setPassword.newPassword"),
1010
+ value: i,
1011
+ onChange: (e) => a(e.target.value),
723
1012
  autoFocus: !0
724
1013
  }),
725
- /* @__PURE__ */ c(N, {
726
- label: "Conferma password",
727
- value: a,
728
- onChange: (e) => s(e.target.value)
1014
+ /* @__PURE__ */ c(K, {
1015
+ label: r("setPassword.confirmPassword"),
1016
+ value: s,
1017
+ onChange: (e) => u(e.target.value)
729
1018
  }),
730
- u && /* @__PURE__ */ c("p", {
1019
+ f && /* @__PURE__ */ c("p", {
731
1020
  className: "set-password-modal-error",
732
- children: u
1021
+ children: f
733
1022
  }),
734
- /* @__PURE__ */ c(f, {
735
- label: "Salva password",
1023
+ /* @__PURE__ */ c(h, {
1024
+ label: r("setPassword.submit"),
736
1025
  type: "submit",
737
1026
  style: { width: "100%" }
738
1027
  })
739
1028
  ]
740
1029
  })
741
1030
  });
742
- }, V = ({ value: e, onChange: t, swatches: r }) => {
1031
+ }, Q = ({ value: e, onChange: t, swatches: r }) => {
743
1032
  let [i, s] = o(!1), u = a(null);
744
1033
  return n(() => {
745
1034
  if (!i) return;
@@ -788,7 +1077,7 @@ var B = ({ open: e, onClose: t, onSubmit: n }) => {
788
1077
  })]
789
1078
  })]
790
1079
  });
791
- }, H = (e) => e.split(",")[0].replace(/'/g, ""), U = ({ value: e, onChange: t, options: r }) => {
1080
+ }, $ = (e) => e.split(",")[0].replace(/'/g, ""), ce = ({ value: e, onChange: t, options: r }) => {
792
1081
  let [i, s] = o(!1), u = a(null);
793
1082
  return n(() => {
794
1083
  if (!i) return;
@@ -810,7 +1099,7 @@ var B = ({ open: e, onClose: t, onSubmit: n }) => {
810
1099
  style: { fontFamily: e },
811
1100
  children: [/* @__PURE__ */ c("span", {
812
1101
  className: "sg-font-picker-toggle-label",
813
- children: H(e)
1102
+ children: $(e)
814
1103
  }), /* @__PURE__ */ c("span", {
815
1104
  className: "sg-font-picker-toggle-caret",
816
1105
  children: i ? "▲" : "▼"
@@ -824,11 +1113,11 @@ var B = ({ open: e, onClose: t, onSubmit: n }) => {
824
1113
  t(n), s(!1);
825
1114
  },
826
1115
  style: { fontFamily: n },
827
- children: H(n)
1116
+ children: $(n)
828
1117
  }) }, n))
829
1118
  })]
830
1119
  });
831
- }, W = ({ checked: e, onChange: t, label: n }) => /* @__PURE__ */ l("label", {
1120
+ }, le = ({ checked: e, onChange: t, label: n }) => /* @__PURE__ */ l("label", {
832
1121
  className: "sg-toggle",
833
1122
  children: [
834
1123
  /* @__PURE__ */ c("input", {
@@ -846,6 +1135,18 @@ var B = ({ open: e, onClose: t, onSubmit: n }) => {
846
1135
  children: n
847
1136
  })
848
1137
  ]
849
- });
1138
+ }), ue = ({ className: e }) => {
1139
+ let { t, i18n: n } = d("sg", { i18n: L });
1140
+ return /* @__PURE__ */ c("select", {
1141
+ className: ["sg-language-switcher", e].filter(Boolean).join(" "),
1142
+ value: n.resolvedLanguage,
1143
+ onChange: (e) => n.changeLanguage(e.target.value),
1144
+ "aria-label": t("languageSwitcher.label"),
1145
+ children: j.map((e) => /* @__PURE__ */ c("option", {
1146
+ value: e,
1147
+ children: M[e]
1148
+ }, e))
1149
+ });
1150
+ };
850
1151
  //#endregion
851
- export { E as AppAccessGate, O as AppLauncher, k as Authenticator, f as Button, V as ColorPicker, v as CookieConsent, D as DEFAULT_APPS_URL, C as DEFAULT_HEIMDALL_URL, U as FontPicker, p as Footer, x as Header, d as Icon, M as Input, j as Modal, N as PasswordInput, z as QuadratoHeader, b as SGFooter, S as SGHeader, B as SetPasswordModal, W as Toggle };
1152
+ export { Y as AVATAR_CHANGE_EVENT, B as AppAccessGate, H as AppLauncher, U as Authenticator, h as Button, Q as ColorPicker, x as CookieConsent, V as DEFAULT_APPS_URL, R as DEFAULT_HEIMDALL_URL, ce as FontPicker, g as Footer, w as Header, m as Icon, G as Input, N as LANGUAGE_COOKIE, M as LANGUAGE_NAMES, ue as LanguageSwitcher, W as Modal, K as PasswordInput, Z as QuadratoHeader, C as SGFooter, T as SGHeader, j as SUPPORTED_LANGUAGES, se as SetPasswordModal, le as Toggle, ne as createI18n, F as readLanguageCookie, L as sgI18n };
@@ -1,2 +1,2 @@
1
- .custom-button{border-radius:var(--button-radius);background:var(--button-bg);color:var(--button-color);cursor:pointer;font-size:var(--primary-font);font-weight:600;font-family:var(--font-family);box-shadow:var(--button-shadow);transition:background .15s var(--button-ease-standard), transform var(--button-ease-bounce) .2s;border:none;padding:8px 20px}.custom-button:hover:not(:disabled){background:var(--button-bg-hover);transform:translateY(-1px)}.custom-button:active:not(:disabled){transform:scale(.97)}.custom-button:disabled{opacity:.6;cursor:default}.custom-button-with-icon{align-items:center;gap:8px;display:inline-flex}.custom-button-label-visually-hidden{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.sg-icon{vertical-align:middle;flex-shrink:0;display:inline-block}.footer{background-color:var(--primary-bg);border-top:1px solid var(--primary-border);font-family:var(--font-family);color:#555;justify-content:space-between;align-items:center;width:100%;padding:16px 32px;font-size:14px;display:flex;position:fixed;bottom:0;left:0}.footer a{color:#1ea7fd;text-decoration:none}.footer a:hover{text-decoration:underline}.cookie-consent{background-color:var(--primary-bg);border-top:1px solid var(--primary-border);font-family:var(--font-family);color:#555;z-index:1000;box-sizing:border-box;flex-wrap:wrap;justify-content:center;align-items:center;gap:16px;width:100%;padding:16px 32px;font-size:14px;display:flex;position:fixed;bottom:0;left:0}.cookie-consent-text{flex:320px;margin:0}.cookie-consent a{color:#1ea7fd;text-decoration:none}.cookie-consent a:hover{text-decoration:underline}.cookie-consent-accept{flex:none}.logo-text{font-size:var(--font-size-h1);font-family:var(--font-family);margin:0;font-weight:400}.header{z-index:100;width:100%;font-size:var(--font-size);background-color:#fff;border-bottom:1px solid #e1e5e9;position:sticky;top:0;box-shadow:0 2px 4px #0000001a}.header-container{justify-content:flex-start;align-items:center;max-width:1200px;margin:0 auto;padding:16px;display:flex}.header-actions{align-items:center;gap:12px;margin-left:auto;display:flex}.logo{cursor:pointer;color:#333;font-size:24px;font-weight:700;transition:color .2s}.logo:hover{color:#007acc}.logo-full{display:inline}.logo-short{display:none}@media (width<=768px){.header-container{padding:12px 16px}.logo{font-size:var(--font-size)}.logo-full{display:none}.logo-short{display:inline}}@media (width<=480px){.header-container{padding:8px 12px}.logo{font-size:var(--font-size)}}.sg-access-gate{z-index:998;box-sizing:border-box;background:var(--primary-bg);font-family:var(--font-family);justify-content:center;align-items:center;padding:16px;display:flex;position:fixed;inset:0}.sg-access-gate-card{border:1px solid var(--primary-border);border-radius:calc(var(--radius) * 3);background:var(--modal-bg);max-width:420px;box-shadow:var(--shadow-2);text-align:center;padding:24px}.sg-access-gate-card h2{font-size:var(--font-size-h1);color:var(--modal-title-color);margin:0 0 12px}.sg-access-gate-card p{color:var(--ink-muted);margin:0 0 16px}.sg-access-gate-card .sg-access-gate-error{color:#c0392b}.sg-app-launcher{font-family:var(--font-family);position:relative}.sg-app-launcher-toggle{width:40px;height:40px;color:var(--ink-muted);cursor:pointer;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;padding:0;display:inline-flex}.sg-app-launcher-toggle:hover,.sg-app-launcher-toggle[aria-expanded=true]{background:var(--primary-hover-bg)}.sg-app-launcher-panel{z-index:1000;box-sizing:border-box;border:1px solid var(--primary-border);border-radius:calc(var(--radius) * 3);background:var(--modal-bg);width:320px;max-width:calc(100vw - 24px);max-height:70vh;box-shadow:var(--shadow-2);padding:12px;position:absolute;top:calc(100% + 8px);right:0;overflow-y:auto}.sg-app-launcher-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:4px;margin:0;padding:0;list-style:none;display:grid}.sg-app-launcher-app{border-radius:var(--radius);color:inherit;flex-direction:column;align-items:center;gap:6px;padding:12px 4px;text-decoration:none;display:flex}.sg-app-launcher-app:hover,.sg-app-launcher-app:focus-visible{background:var(--primary-hover-bg)}.sg-app-launcher-app.current{background:var(--accent-soft)}.sg-app-launcher-tile{border-radius:var(--radius);background:var(--accent);color:#fff;justify-content:center;align-items:center;width:40px;height:40px;font-size:18px;font-weight:600;display:flex}.sg-app-launcher-name{text-overflow:ellipsis;white-space:nowrap;max-width:100%;font-size:13px;overflow:hidden}.sg-app-launcher-message{color:var(--ink-muted);margin:0;padding:8px;font-size:13px}.sg-authenticator-button{font-size:.9rem;font-family:var(--font-family);border:1px solid var(--primary-border);border-radius:var(--radius);background:var(--primary-bg);color:#333;cursor:pointer;padding:.4rem 1rem;transition:background .15s,border-color .15s}.sg-authenticator-button:hover{background:var(--primary-hover-bg);border-color:var(--ink-muted)}.modal-overlay{background:var(--modal-overlay);z-index:1000;justify-content:center;align-items:center;width:100vw;height:100vh;animation:.25s ease-out sg-modal-overlay-fade-in;display:flex;position:fixed;top:0;left:0}.modal-overlay.modal-overlay-closing{animation:.22s ease-in forwards sg-modal-overlay-fade-out}.modal-content{--drag-x:0px;--drag-y:0px;background:var(--modal-bg);border-radius:var(--primary-radius);font-size:var(--primary-font);min-width:320px;max-width:calc(100vw - 40px);max-height:90vh;transform:translate(var(--drag-x), var(--drag-y));flex-direction:column;animation:.3s ease-out sg-modal-slide-in;display:flex;overflow:hidden;box-shadow:0 2px 16px #00000026}.modal-content.modal-content-closing{animation:.22s ease-in forwards sg-modal-slide-out}@keyframes sg-modal-overlay-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sg-modal-overlay-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sg-modal-slide-in{0%{transform:translate(var(--drag-x), calc(var(--drag-y) - 24px)) scale(.97);opacity:0}to{transform:translate(var(--drag-x), var(--drag-y)) scale(1);opacity:1}}@keyframes sg-modal-slide-out{0%{transform:translate(var(--drag-x), var(--drag-y)) scale(1);opacity:1}to{transform:translate(var(--drag-x), calc(var(--drag-y) - 24px)) scale(.97);opacity:0}}.modal-header{background:var(--primary-bg);border-top-left-radius:var(--primary-radius);border-top-right-radius:var(--primary-radius);cursor:grab;touch-action:none;flex:none;align-items:center;gap:8px;padding:16px;display:flex}.modal-header-dragging{cursor:grabbing;-webkit-user-select:none;user-select:none}.modal-icon{color:var(--modal-title-color);display:inline-flex}.modal-title{color:var(--modal-title-color);font-family:var(--font-family);font-size:var(--primary-font);flex:1;margin:0}.modal-close{cursor:pointer;color:var(--modal-title-color);background:0 0;border:none;padding:4px 8px;font-size:20px;line-height:1}.modal-close:disabled{cursor:default;opacity:.6}.modal-children{font-family:var(--font-family);padding:16px;overflow-y:auto}.modal-footer{border-top:1px solid var(--primary-border);flex-wrap:wrap;flex:none;justify-content:flex-end;gap:8px;padding:12px 16px;display:flex}.input-label{font-weight:500;font-family:var(--font-family);font-size:var(--primary-font);margin-bottom:4px;display:block}.input-field--invalid{background-color:#ffe5e5;border-color:#c00}.input-wrapper{margin-bottom:16px}.input-label{font-weight:500;font-size:var(--primary-font);margin-bottom:4px;display:block}.input-field{box-sizing:border-box;border-radius:var(--radius);width:100%;font-size:var(--primary-font);border-style:solid;border-width:1px;padding:8px}.login-modal-form{font-family:var(--font-family);font-size:var(--primary-font);flex-direction:column;gap:16px;display:flex}.header.quadrato-header{z-index:999}.quadrato-header-auth{align-items:center;gap:12px;display:flex}.quadrato-header-username{font-size:var(--font-size);color:#333;transition:color .2s}.quadrato-header-username:hover{color:var(--accent)}form{font-family:var(--font-family)}.set-password-modal-form{font-family:var(--font-family);font-size:var(--primary-font);flex-direction:column;gap:16px;display:flex}.set-password-modal-error{color:#d32f2f;font-size:var(--primary-font);margin:0}.sg-color-picker{font-family:var(--font-family);position:relative}.sg-color-picker-toggle{border-radius:var(--input-radius);border:1px solid var(--input-border);background:var(--input-bg);width:100%;color:inherit;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;font-family:inherit;font-size:13px;display:flex}.sg-color-picker-toggle-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sg-color-picker-swatch{border:1px solid #00000026;border-radius:4px;flex:none;width:18px;height:18px}.sg-color-picker-popover{z-index:30;background:var(--input-bg);border:1px solid var(--input-border);border-radius:var(--input-radius);box-shadow:var(--shadow-1);width:170px;padding:10px;position:absolute;top:calc(100% + 4px);left:0}.sg-color-picker-grid{grid-template-columns:repeat(6,1fr);gap:6px;display:grid}.sg-color-picker-swatch-button{cursor:pointer;border:1px solid #00000026;border-radius:4px;width:20px;height:20px;padding:0}.sg-color-picker-swatch-button.active{border:2px solid var(--accent)}.sg-color-picker-custom{border-top:1px solid var(--input-border);color:var(--ink-muted);cursor:pointer;justify-content:space-between;align-items:center;gap:8px;margin-top:8px;padding-top:8px;font-size:11px;display:flex}.sg-color-picker-custom input[type=color]{cursor:pointer;border:none;width:26px;height:20px;padding:0}.sg-font-picker{font-family:var(--font-family);position:relative}.sg-font-picker-toggle{border-radius:var(--input-radius);border:1px solid var(--input-border);background:var(--input-bg);width:100%;color:inherit;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;font-size:13px;display:flex}.sg-font-picker-toggle-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sg-font-picker-toggle-caret{opacity:.5;flex:none;font-size:10px}.sg-font-picker-list{background:var(--input-bg);border:1px solid var(--input-border);border-radius:var(--input-radius);max-height:260px;box-shadow:var(--shadow-1);z-index:30;margin:0;padding:4px 0;list-style:none;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto}.sg-font-picker-option{text-align:left;width:100%;color:inherit;cursor:pointer;background:0 0;border:none;padding:8px 10px;font-size:15px;display:block}.sg-font-picker-option.active{background:var(--accent-soft)}:root{--unit:3px;--primary-bg:#f5f5f5;--primary-border:#ccc;--radius:calc(var(--unit) * 2);--primary-radius:var(--radius);--primary-font:var(--font-size);--primary-hover-bg:#e0e0e0;--modal-bg:#fff;--modal-overlay:#0000004d;--modal-title-color:#222;--input-bg:#fff;--input-border:#ccc;--input-radius:var(--radius);--input-font:1rem;--font-size:14px;--font-family:Arial, sans-serif;--font-size-h1:calc(var(--font-size) * 1.5);--button-bg:#6f9cf7;--button-bg-hover:#5480e6;--button-color:#fff;--button-radius:999px;--button-shadow:0 1px 2px #5480e61f, 0 1px 3px #5480e61a;--button-ease-standard:cubic-bezier(.4, 0, .2, 1);--button-ease-bounce:cubic-bezier(.34, 1.56, .64, 1);--accent:#3d5fc4;--accent-strong:#2e4aa0;--accent-soft:#e2e7f8;--ink-muted:#647089;--shadow-1:0 1px 2px #0f172a14, 0 1px 3px #0f172a0f;--shadow-2:0 4px 12px #0f172a1f}.theme-dark{--primary-bg:#222;--primary-border:#444;--primary-font:1rem;--font-family:"Inter", Arial, sans-serif;--primary-hover-bg:#333;--modal-bg:#222;--modal-title-color:#fff;--input-bg:#333;--input-border:#555;--input-font:1rem;--button-bg:#6f9cf7;--button-bg-hover:#86aefc;--button-color:#fff;--accent:#6f9cf7;--accent-strong:#86aefc;--accent-soft:#6f9cf72e;--ink-muted:#9aa3b5;--shadow-1:0 1px 2px #0000004d, 0 1px 3px #0000003d;--shadow-2:0 4px 12px #0006}.sg-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--font-family);align-items:center;gap:6px;display:flex}.sg-toggle-input{opacity:0;width:1px;height:1px;position:absolute}.sg-toggle-track{background:var(--input-border);border-radius:999px;flex:none;width:34px;height:20px;transition:background .15s cubic-bezier(.4,0,.2,1);position:relative}.sg-toggle-input:checked+.sg-toggle-track{background:var(--accent)}.sg-toggle-thumb{background:var(--input-bg);width:16px;height:16px;box-shadow:var(--shadow-1);border-radius:50%;transition:left .15s cubic-bezier(.4,0,.2,1);position:absolute;top:2px;left:2px}.sg-toggle-input:checked+.sg-toggle-track .sg-toggle-thumb{left:16px}.sg-toggle-input:focus-visible+.sg-toggle-track{outline:2px solid var(--accent-strong);outline-offset:2px}.sg-toggle-label{color:var(--ink-muted);white-space:nowrap;font-size:13px}
1
+ .custom-button{border-radius:var(--button-radius);background:var(--button-bg);color:var(--button-color);cursor:pointer;font-size:var(--primary-font);font-weight:600;font-family:var(--font-family);box-shadow:var(--button-shadow);transition:background .15s var(--button-ease-standard), transform var(--button-ease-bounce) .2s;border:none;padding:8px 20px}.custom-button:hover:not(:disabled){background:var(--button-bg-hover);transform:translateY(-1px)}.custom-button:active:not(:disabled){transform:scale(.97)}.custom-button:disabled{opacity:.6;cursor:default}.custom-button-with-icon{align-items:center;gap:8px;display:inline-flex}.custom-button-label-visually-hidden{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.sg-icon{vertical-align:middle;flex-shrink:0;display:inline-block}.footer{background-color:var(--primary-bg);border-top:1px solid var(--primary-border);font-family:var(--font-family);color:var(--sg-text-secondary);justify-content:space-between;align-items:center;width:100%;padding:16px 32px;font-size:14px;display:flex;position:fixed;bottom:0;left:0}.footer a{color:var(--sg-link);text-decoration:none}.footer a:hover{text-decoration:underline}.cookie-consent{background-color:var(--primary-bg);border-top:1px solid var(--primary-border);font-family:var(--font-family);color:var(--sg-text-secondary);z-index:1000;box-sizing:border-box;flex-wrap:wrap;justify-content:center;align-items:center;gap:16px;width:100%;padding:16px 32px;font-size:14px;display:flex;position:fixed;bottom:0;left:0}.cookie-consent-text{flex:320px;margin:0}.cookie-consent a{color:var(--sg-link);text-decoration:none}.cookie-consent a:hover{text-decoration:underline}.cookie-consent-accept{flex:none}.logo-text{font-size:var(--font-size-h1);font-family:var(--font-family);margin:0;font-weight:400}.header{background-color:var(--sg-surface);width:100%;color:var(--sg-text);border-bottom:1px solid var(--sg-border-subtle);box-shadow:var(--sg-shadow-1);z-index:100;font-size:var(--font-size);position:sticky;top:0}.header-container{justify-content:flex-start;align-items:center;max-width:1200px;margin:0 auto;padding:16px;display:flex}.header-actions{align-items:center;gap:12px;margin-left:auto;display:flex}.logo{cursor:pointer;color:var(--sg-text);font-size:24px;font-weight:700;transition:color .2s}.logo:hover{color:var(--accent)}.logo-full{display:inline}.logo-short{display:none}@media (width<=768px){.header-container{padding:12px 16px}.logo{font-size:var(--font-size)}.logo-full{display:none}.logo-short{display:inline}}@media (width<=480px){.header-container{padding:8px 12px}.logo{font-size:var(--font-size)}}.sg-access-gate{z-index:998;box-sizing:border-box;background:var(--primary-bg);font-family:var(--font-family);justify-content:center;align-items:center;padding:16px;display:flex;position:fixed;inset:0}.sg-access-gate-card{border:1px solid var(--primary-border);border-radius:calc(var(--radius) * 3);background:var(--modal-bg);max-width:420px;box-shadow:var(--shadow-2);color:var(--sg-text);text-align:center;padding:24px}.sg-access-gate-card h2{font-size:var(--font-size-h1);color:var(--modal-title-color);margin:0 0 12px}.sg-access-gate-card p{color:var(--ink-muted);margin:0 0 16px}.sg-access-gate-card .sg-access-gate-error{color:var(--sg-danger)}.sg-app-launcher{font-family:var(--font-family);position:relative}.sg-app-launcher-toggle{width:40px;height:40px;color:var(--ink-muted);cursor:pointer;background:0 0;border:none;border-radius:50%;justify-content:center;align-items:center;padding:0;display:inline-flex}.sg-app-launcher-toggle:hover,.sg-app-launcher-toggle[aria-expanded=true]{background:var(--primary-hover-bg)}.sg-app-launcher-panel{z-index:1000;box-sizing:border-box;border:1px solid var(--primary-border);border-radius:calc(var(--radius) * 3);background:var(--modal-bg);width:320px;max-width:calc(100vw - 24px);max-height:70vh;color:var(--sg-text);box-shadow:var(--shadow-2);padding:12px;position:absolute;top:calc(100% + 8px);right:0;overflow-y:auto}.sg-app-launcher-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:4px;margin:0;padding:0;list-style:none;display:grid}.sg-app-launcher-app{border-radius:var(--radius);color:inherit;flex-direction:column;align-items:center;gap:6px;padding:12px 4px;text-decoration:none;display:flex}.sg-app-launcher-app:hover,.sg-app-launcher-app:focus-visible{background:var(--primary-hover-bg)}.sg-app-launcher-app.current{background:var(--accent-soft)}.sg-app-launcher-tile{border-radius:var(--radius);background:var(--accent);width:40px;height:40px;color:var(--sg-on-accent);justify-content:center;align-items:center;font-size:18px;font-weight:600;display:flex}.sg-app-launcher-name{text-overflow:ellipsis;white-space:nowrap;max-width:100%;font-size:13px;overflow:hidden}.sg-app-launcher-message{color:var(--ink-muted);margin:0;padding:8px;font-size:13px}.sg-app-launcher-tile{position:relative}.sg-app-launcher-app.locked .sg-app-launcher-tile{background:var(--ink-muted)}.sg-app-launcher-app.locked .sg-app-launcher-name{color:var(--ink-muted)}.sg-app-launcher-lock{border:2px solid var(--modal-bg);background:var(--modal-title-color);width:20px;height:20px;color:var(--modal-bg);border-radius:50%;justify-content:center;align-items:center;display:flex;position:absolute;bottom:-6px;right:-6px}.sg-visually-hidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.sg-authenticator-button{font-size:.9rem;font-family:var(--font-family);border:1px solid var(--primary-border);border-radius:var(--radius);background:var(--primary-bg);color:var(--sg-text);cursor:pointer;padding:.4rem 1rem;transition:background .15s,border-color .15s}.sg-authenticator-button:hover{background:var(--primary-hover-bg);border-color:var(--ink-muted)}.modal-overlay{background:var(--modal-overlay);z-index:1000;justify-content:center;align-items:center;width:100vw;height:100vh;animation:.25s ease-out sg-modal-overlay-fade-in;display:flex;position:fixed;top:0;left:0}.modal-overlay.modal-overlay-closing{animation:.22s ease-in forwards sg-modal-overlay-fade-out}.modal-content{--drag-x:0px;--drag-y:0px;background:var(--modal-bg);border-radius:var(--primary-radius);font-size:var(--primary-font);min-width:320px;max-width:calc(100vw - 40px);max-height:90vh;color:var(--sg-text);box-shadow:var(--sg-shadow-3);transform:translate(var(--drag-x), var(--drag-y));flex-direction:column;animation:.3s ease-out sg-modal-slide-in;display:flex;overflow:hidden}.modal-content.modal-content-closing{animation:.22s ease-in forwards sg-modal-slide-out}@keyframes sg-modal-overlay-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sg-modal-overlay-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sg-modal-slide-in{0%{transform:translate(var(--drag-x), calc(var(--drag-y) - 24px)) scale(.97);opacity:0}to{transform:translate(var(--drag-x), var(--drag-y)) scale(1);opacity:1}}@keyframes sg-modal-slide-out{0%{transform:translate(var(--drag-x), var(--drag-y)) scale(1);opacity:1}to{transform:translate(var(--drag-x), calc(var(--drag-y) - 24px)) scale(.97);opacity:0}}.modal-header{background:var(--primary-bg);border-top-left-radius:var(--primary-radius);border-top-right-radius:var(--primary-radius);cursor:grab;touch-action:none;flex:none;align-items:center;gap:8px;padding:16px;display:flex}.modal-header-dragging{cursor:grabbing;-webkit-user-select:none;user-select:none}.modal-icon{color:var(--modal-title-color);display:inline-flex}.modal-title{color:var(--modal-title-color);font-family:var(--font-family);font-size:var(--primary-font);flex:1;margin:0}.modal-close{cursor:pointer;color:var(--modal-title-color);background:0 0;border:none;padding:4px 8px;font-size:20px;line-height:1}.modal-close:disabled{cursor:default;opacity:.6}.modal-children{font-family:var(--font-family);padding:16px;overflow-y:auto}.modal-footer{border-top:1px solid var(--primary-border);flex-wrap:wrap;flex:none;justify-content:flex-end;gap:8px;padding:12px 16px;display:flex}.input-label{font-weight:500;font-family:var(--font-family);font-size:var(--primary-font);margin-bottom:4px;display:block}.input-field--invalid{background-color:var(--sg-danger-soft);border-color:var(--sg-danger)}.input-wrapper{margin-bottom:16px}.input-label{font-weight:500;font-size:var(--primary-font);margin-bottom:4px;display:block}.input-field{box-sizing:border-box;border-radius:var(--radius);border-style:solid;border-width:1px;border-color:var(--input-border);background-color:var(--input-bg);width:100%;color:var(--sg-text);font-size:var(--primary-font);padding:8px}.login-modal-form{font-family:var(--font-family);font-size:var(--primary-font);flex-direction:column;gap:16px;display:flex}.header.quadrato-header{z-index:999}.quadrato-header-auth{align-items:center;gap:12px;display:flex}.quadrato-header-avatar{object-fit:cover;width:32px;height:32px;box-shadow:var(--sg-shadow-1);border-radius:50%}.quadrato-header-username{font-size:var(--font-size);color:var(--sg-text);transition:color .2s}.quadrato-header-username:hover{color:var(--accent)}form{font-family:var(--font-family)}.set-password-modal-form{font-family:var(--font-family);font-size:var(--primary-font);flex-direction:column;gap:16px;display:flex}.set-password-modal-error{color:var(--sg-danger);font-size:var(--primary-font);margin:0}.sg-color-picker{font-family:var(--font-family);position:relative}.sg-color-picker-toggle{border-radius:var(--input-radius);border:1px solid var(--input-border);background:var(--input-bg);width:100%;color:inherit;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;font-family:inherit;font-size:13px;display:flex}.sg-color-picker-toggle-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sg-color-picker-swatch{border:1px solid var(--sg-border-subtle);border-radius:4px;flex:none;width:18px;height:18px}.sg-color-picker-popover{z-index:30;background:var(--input-bg);border:1px solid var(--input-border);border-radius:var(--input-radius);box-shadow:var(--shadow-1);width:170px;padding:10px;position:absolute;top:calc(100% + 4px);left:0}.sg-color-picker-grid{grid-template-columns:repeat(6,1fr);gap:6px;display:grid}.sg-color-picker-swatch-button{border:1px solid var(--sg-border-subtle);cursor:pointer;border-radius:4px;width:20px;height:20px;padding:0}.sg-color-picker-swatch-button.active{border:2px solid var(--accent)}.sg-color-picker-custom{border-top:1px solid var(--input-border);color:var(--ink-muted);cursor:pointer;justify-content:space-between;align-items:center;gap:8px;margin-top:8px;padding-top:8px;font-size:11px;display:flex}.sg-color-picker-custom input[type=color]{cursor:pointer;border:none;width:26px;height:20px;padding:0}.sg-font-picker{font-family:var(--font-family);position:relative}.sg-font-picker-toggle{border-radius:var(--input-radius);border:1px solid var(--input-border);background:var(--input-bg);width:100%;color:inherit;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;padding:6px 8px;font-size:13px;display:flex}.sg-font-picker-toggle-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sg-font-picker-toggle-caret{opacity:.5;flex:none;font-size:10px}.sg-font-picker-list{background:var(--input-bg);border:1px solid var(--input-border);border-radius:var(--input-radius);max-height:260px;box-shadow:var(--shadow-1);z-index:30;margin:0;padding:4px 0;list-style:none;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto}.sg-font-picker-option{text-align:left;width:100%;color:inherit;cursor:pointer;background:0 0;border:none;padding:8px 10px;font-size:15px;display:block}.sg-font-picker-option.active{background:var(--accent-soft)}.sg-toggle{cursor:pointer;-webkit-user-select:none;user-select:none;font-family:var(--font-family);align-items:center;gap:6px;display:flex}.sg-toggle-input{opacity:0;width:1px;height:1px;position:absolute}.sg-toggle-track{background:var(--input-border);border-radius:999px;flex:none;width:34px;height:20px;transition:background .15s cubic-bezier(.4,0,.2,1);position:relative}.sg-toggle-input:checked+.sg-toggle-track{background:var(--accent)}.sg-toggle-thumb{background:var(--input-bg);width:16px;height:16px;box-shadow:var(--shadow-1);border-radius:50%;transition:left .15s cubic-bezier(.4,0,.2,1);position:absolute;top:2px;left:2px}.sg-toggle-input:checked+.sg-toggle-track .sg-toggle-thumb{left:16px}.sg-toggle-input:focus-visible+.sg-toggle-track{outline:2px solid var(--accent-strong);outline-offset:2px}.sg-toggle-label{color:var(--ink-muted);white-space:nowrap;font-size:13px}:root{--sg-color-white:#fff;--sg-color-black:#000;--sg-color-gray-50:#f5f5f5;--sg-color-gray-100:#e1e5e9;--sg-color-gray-150:#e0e0e0;--sg-color-gray-200:#e6e6e6;--sg-color-gray-300:#ccc;--sg-color-gray-350:#bdbdbd;--sg-color-gray-400:#9aa3b5;--sg-color-gray-500:#647089;--sg-color-gray-600:#555;--sg-color-gray-650:#444;--sg-color-gray-700:#333;--sg-color-gray-750:#2a2a2a;--sg-color-gray-800:#222;--sg-color-gray-900:#1a1a1a;--sg-color-blue-50:#e2e7f8;--sg-color-blue-300:#86aefc;--sg-color-blue-400:#6f9cf7;--sg-color-blue-500:#5480e6;--sg-color-blue-600:#3d5fc4;--sg-color-blue-700:#2e4aa0;--sg-color-sky-500:#1ea7fd;--sg-color-red-50:#ffe5e5;--sg-color-red-400:#e57373;--sg-color-red-600:#c0392b;--sg-bg:var(--sg-color-white);--sg-surface:var(--sg-color-white);--sg-surface-muted:var(--sg-color-gray-50);--sg-surface-hover:var(--sg-color-gray-150);--sg-border:var(--sg-color-gray-300);--sg-border-subtle:var(--sg-color-gray-100);--sg-text:var(--sg-color-gray-700);--sg-text-heading:var(--sg-color-gray-800);--sg-text-secondary:var(--sg-color-gray-600);--sg-text-muted:var(--sg-color-gray-500);--sg-accent:var(--sg-color-blue-600);--sg-accent-strong:var(--sg-color-blue-700);--sg-accent-soft:var(--sg-color-blue-50);--sg-on-accent:var(--sg-color-white);--sg-action:var(--sg-color-blue-400);--sg-action-hover:var(--sg-color-blue-500);--sg-link:var(--sg-color-sky-500);--sg-danger:var(--sg-color-red-600);--sg-danger-soft:var(--sg-color-red-50);--sg-overlay:#0000004d;--sg-shadow-1:0 1px 2px #0f172a14, 0 1px 3px #0f172a0f;--sg-shadow-2:0 4px 12px #0f172a1f;--sg-shadow-3:0 2px 16px #00000026;--sg-action-shadow:0 1px 2px #5480e61f, 0 1px 3px #5480e61a;--unit:3px;--radius:calc(var(--unit) * 2);--font-size:14px;--font-family:Arial, sans-serif;--font-size-h1:calc(var(--font-size) * 1.5);--primary-bg:var(--sg-surface-muted);--primary-border:var(--sg-border);--primary-radius:var(--radius);--primary-font:var(--font-size);--primary-hover-bg:var(--sg-surface-hover);--modal-bg:var(--sg-surface);--modal-overlay:var(--sg-overlay);--modal-title-color:var(--sg-text-heading);--input-bg:var(--sg-surface);--input-border:var(--sg-border);--input-radius:var(--radius);--input-font:1rem;--button-bg:var(--sg-action);--button-bg-hover:var(--sg-action-hover);--button-color:var(--sg-on-accent);--button-radius:999px;--button-shadow:var(--sg-action-shadow);--button-ease-standard:cubic-bezier(.4, 0, .2, 1);--button-ease-bounce:cubic-bezier(.34, 1.56, .64, 1);--accent:var(--sg-accent);--accent-strong:var(--sg-accent-strong);--accent-soft:var(--sg-accent-soft);--ink-muted:var(--sg-text-muted);--shadow-1:var(--sg-shadow-1);--shadow-2:var(--sg-shadow-2)}:root[data-theme=dark]{--sg-bg:var(--sg-color-gray-900);--sg-surface:var(--sg-color-gray-800);--sg-surface-muted:var(--sg-color-gray-750);--sg-surface-hover:var(--sg-color-gray-700);--sg-border:var(--sg-color-gray-600);--sg-border-subtle:var(--sg-color-gray-650);--sg-text:var(--sg-color-gray-200);--sg-text-heading:var(--sg-color-white);--sg-text-secondary:var(--sg-color-gray-350);--sg-text-muted:var(--sg-color-gray-400);--sg-accent:var(--sg-color-blue-400);--sg-accent-strong:var(--sg-color-blue-300);--sg-accent-soft:#6f9cf72e;--sg-action-hover:var(--sg-color-blue-300);--sg-link:var(--sg-color-blue-300);--sg-danger:var(--sg-color-red-400);--sg-danger-soft:#e5737326;--sg-overlay:#0009;--sg-shadow-1:0 1px 2px #0000004d, 0 1px 3px #0000003d;--sg-shadow-2:0 4px 12px #0006;--sg-shadow-3:0 2px 16px #00000080}.sg-language-switcher{border:1px solid var(--input-border);border-radius:var(--input-radius);background:var(--input-bg);color:var(--sg-text);font-family:var(--font-family);font-size:var(--primary-font);cursor:pointer;padding:4px 8px}
2
2
  /*$vite$:1*/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sensorario/sg-components",
3
- "version": "0.0.102",
3
+ "version": "0.0.104",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "scripts": {
@@ -25,12 +25,21 @@
25
25
  "types": "./dist/index.d.ts",
26
26
  "import": "./dist/index.es.js",
27
27
  "style": "./dist/sg-components.css"
28
- }
28
+ },
29
+ "./style.css": "./dist/sg-components.css",
30
+ "./dist/sg-components.css": "./dist/sg-components.css"
29
31
  },
32
+ "sideEffects": [
33
+ "*.css"
34
+ ],
30
35
  "dependencies": {
31
36
  "react": "^19.2.6",
32
37
  "react-dom": "^19.2.6"
33
38
  },
39
+ "peerDependencies": {
40
+ "i18next": ">=26",
41
+ "react-i18next": ">=17"
42
+ },
34
43
  "devDependencies": {
35
44
  "@chromatic-com/storybook": "^5.3.0",
36
45
  "@eslint/js": "^10.0.1",
@@ -54,7 +63,9 @@
54
63
  "eslint-plugin-react-refresh": "^0.5.2",
55
64
  "eslint-plugin-storybook": "^10.5.10",
56
65
  "globals": "^17.6.0",
66
+ "i18next": "^26.4.2",
57
67
  "playwright": "^1.60.0",
68
+ "react-i18next": "^17.0.15",
58
69
  "rollup": "^4.60.4",
59
70
  "rollup-plugin-dts": "^6.4.1",
60
71
  "rollup-plugin-peer-deps-external": "^2.2.4",