@trismegisto/spine 2.1.1 → 3.0.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.
Files changed (78) hide show
  1. package/README.md +105 -19
  2. package/fesm2022/trismegisto-spine-audit.mjs +264 -10
  3. package/fesm2022/trismegisto-spine-audit.mjs.map +1 -1
  4. package/fesm2022/trismegisto-spine-auto-service-apps.technical-BAKdkVdz.mjs +84 -0
  5. package/fesm2022/trismegisto-spine-auto-service-apps.technical-BAKdkVdz.mjs.map +1 -0
  6. package/fesm2022/trismegisto-spine-auto-service-apps.usage-CAXMP5r8.mjs +87 -0
  7. package/fesm2022/trismegisto-spine-auto-service-apps.usage-CAXMP5r8.mjs.map +1 -0
  8. package/fesm2022/{trismegisto-spine-auto-service-audit.technical-C901aIIQ.mjs → trismegisto-spine-auto-service-audit.technical-B75d4mh2.mjs} +6 -5
  9. package/fesm2022/trismegisto-spine-auto-service-audit.technical-B75d4mh2.mjs.map +1 -0
  10. package/fesm2022/{trismegisto-spine-auto-service-audit.usage-GIJ2RwuH.mjs → trismegisto-spine-auto-service-audit.usage-IdXMruv5.mjs} +9 -8
  11. package/fesm2022/trismegisto-spine-auto-service-audit.usage-IdXMruv5.mjs.map +1 -0
  12. package/fesm2022/{trismegisto-spine-auto-service-auto-service-audit.component-CCiz-q8o.mjs → trismegisto-spine-auto-service-auto-service-audit.component-BIL8tH1I.mjs} +4 -4
  13. package/fesm2022/{trismegisto-spine-auto-service-auto-service-audit.component-CCiz-q8o.mjs.map → trismegisto-spine-auto-service-auto-service-audit.component-BIL8tH1I.mjs.map} +1 -1
  14. package/fesm2022/{trismegisto-spine-auto-service-auto-service-client-picker.component-Z0wCANF6.mjs → trismegisto-spine-auto-service-auto-service-client-picker.component-CBt_0Uli.mjs} +3 -3
  15. package/fesm2022/{trismegisto-spine-auto-service-auto-service-client-picker.component-Z0wCANF6.mjs.map → trismegisto-spine-auto-service-auto-service-client-picker.component-CBt_0Uli.mjs.map} +1 -1
  16. package/fesm2022/{trismegisto-spine-auto-service-auto-service-client.component-C7SPavrX.mjs → trismegisto-spine-auto-service-auto-service-client.component-DAxhZC-a.mjs} +123 -27
  17. package/fesm2022/trismegisto-spine-auto-service-auto-service-client.component-DAxhZC-a.mjs.map +1 -0
  18. package/fesm2022/trismegisto-spine-auto-service-auto-service-delete-CoJYTtBU.mjs +87 -0
  19. package/fesm2022/trismegisto-spine-auto-service-auto-service-delete-CoJYTtBU.mjs.map +1 -0
  20. package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-UVMGF7-B.mjs +544 -0
  21. package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-UVMGF7-B.mjs.map +1 -0
  22. package/fesm2022/trismegisto-spine-auto-service-auto-service-login-attempts.component-Boi-kG0G.mjs +37 -0
  23. package/fesm2022/trismegisto-spine-auto-service-auto-service-login-attempts.component-Boi-kG0G.mjs.map +1 -0
  24. package/fesm2022/trismegisto-spine-auto-service-auto-service.component-DmLafUkX.mjs +943 -0
  25. package/fesm2022/trismegisto-spine-auto-service-auto-service.component-DmLafUkX.mjs.map +1 -0
  26. package/fesm2022/{trismegisto-spine-auto-service-client.technical-BHWhpyWv.mjs → trismegisto-spine-auto-service-client.technical-DP5G8i3o.mjs} +24 -3
  27. package/fesm2022/trismegisto-spine-auto-service-client.technical-DP5G8i3o.mjs.map +1 -0
  28. package/fesm2022/{trismegisto-spine-auto-service-client.usage-CM2WB2e2.mjs → trismegisto-spine-auto-service-client.usage-CcJANb20.mjs} +15 -6
  29. package/fesm2022/trismegisto-spine-auto-service-client.usage-CcJANb20.mjs.map +1 -0
  30. package/fesm2022/trismegisto-spine-auto-service-groups.technical-BlktCM70.mjs +65 -0
  31. package/fesm2022/trismegisto-spine-auto-service-groups.technical-BlktCM70.mjs.map +1 -0
  32. package/fesm2022/trismegisto-spine-auto-service-groups.usage-Cz5sCKgn.mjs +81 -0
  33. package/fesm2022/trismegisto-spine-auto-service-groups.usage-Cz5sCKgn.mjs.map +1 -0
  34. package/fesm2022/{trismegisto-spine-auto-service-home.component-vl3BPFnk.mjs → trismegisto-spine-auto-service-home.component-5_aji1hf.mjs} +5 -4
  35. package/fesm2022/trismegisto-spine-auto-service-home.component-5_aji1hf.mjs.map +1 -0
  36. package/fesm2022/trismegisto-spine-auto-service-login-attempts.technical-CasKIIws.mjs +37 -0
  37. package/fesm2022/trismegisto-spine-auto-service-login-attempts.technical-CasKIIws.mjs.map +1 -0
  38. package/fesm2022/trismegisto-spine-auto-service-login-attempts.usage-BMgLNu7W.mjs +60 -0
  39. package/fesm2022/trismegisto-spine-auto-service-login-attempts.usage-BMgLNu7W.mjs.map +1 -0
  40. package/fesm2022/trismegisto-spine-auto-service.mjs +49 -29
  41. package/fesm2022/trismegisto-spine-auto-service.mjs.map +1 -1
  42. package/fesm2022/trismegisto-spine-layout.mjs +1850 -217
  43. package/fesm2022/trismegisto-spine-layout.mjs.map +1 -1
  44. package/fesm2022/trismegisto-spine-shell.mjs +28 -8
  45. package/fesm2022/trismegisto-spine-shell.mjs.map +1 -1
  46. package/fesm2022/trismegisto-spine.mjs +1163 -87
  47. package/fesm2022/trismegisto-spine.mjs.map +1 -1
  48. package/package.json +6 -6
  49. package/types/trismegisto-spine-audit.d.ts +180 -5
  50. package/types/trismegisto-spine-audit.d.ts.map +1 -1
  51. package/types/trismegisto-spine-auto-service.d.ts +100 -7
  52. package/types/trismegisto-spine-auto-service.d.ts.map +1 -1
  53. package/types/trismegisto-spine-layout.d.ts +885 -22
  54. package/types/trismegisto-spine-layout.d.ts.map +1 -1
  55. package/types/trismegisto-spine-shell.d.ts +11 -2
  56. package/types/trismegisto-spine-shell.d.ts.map +1 -1
  57. package/types/trismegisto-spine.d.ts +496 -34
  58. package/types/trismegisto-spine.d.ts.map +1 -1
  59. package/fesm2022/trismegisto-spine-auto-service-apps.technical-ClMKB9cK.mjs +0 -48
  60. package/fesm2022/trismegisto-spine-auto-service-apps.technical-ClMKB9cK.mjs.map +0 -1
  61. package/fesm2022/trismegisto-spine-auto-service-apps.usage-Cf5SG-oQ.mjs +0 -55
  62. package/fesm2022/trismegisto-spine-auto-service-apps.usage-Cf5SG-oQ.mjs.map +0 -1
  63. package/fesm2022/trismegisto-spine-auto-service-audit.technical-C901aIIQ.mjs.map +0 -1
  64. package/fesm2022/trismegisto-spine-auto-service-audit.usage-GIJ2RwuH.mjs.map +0 -1
  65. package/fesm2022/trismegisto-spine-auto-service-auto-service-client.component-C7SPavrX.mjs.map +0 -1
  66. package/fesm2022/trismegisto-spine-auto-service-auto-service-delete-6Bk46MOf.mjs +0 -39
  67. package/fesm2022/trismegisto-spine-auto-service-auto-service-delete-6Bk46MOf.mjs.map +0 -1
  68. package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-CqIVe2LU.mjs +0 -357
  69. package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-CqIVe2LU.mjs.map +0 -1
  70. package/fesm2022/trismegisto-spine-auto-service-auto-service.component-BLN49F4a.mjs +0 -502
  71. package/fesm2022/trismegisto-spine-auto-service-auto-service.component-BLN49F4a.mjs.map +0 -1
  72. package/fesm2022/trismegisto-spine-auto-service-client.technical-BHWhpyWv.mjs.map +0 -1
  73. package/fesm2022/trismegisto-spine-auto-service-client.usage-CM2WB2e2.mjs.map +0 -1
  74. package/fesm2022/trismegisto-spine-auto-service-groups.technical-jQlwaQPn.mjs +0 -39
  75. package/fesm2022/trismegisto-spine-auto-service-groups.technical-jQlwaQPn.mjs.map +0 -1
  76. package/fesm2022/trismegisto-spine-auto-service-groups.usage-Bh7ig2zZ.mjs +0 -54
  77. package/fesm2022/trismegisto-spine-auto-service-groups.usage-Bh7ig2zZ.mjs.map +0 -1
  78. package/fesm2022/trismegisto-spine-auto-service-home.component-vl3BPFnk.mjs.map +0 -1
@@ -1,9 +1,9 @@
1
- import { AlertComponent, ButtonComponent, DrawerComponent, IconButtonComponent, IconComponent, MenuButtonComponent, MenuComponent, ModalComponent, SidebarComponent, SidebarGroupComponent, SidebarLinkComponent, SidebarNodeComponent, ToastStackComponent, HermesThemeService, HERMES_THEME_CONFIG, HERMES_SIDEBAR_CONFIG, HERMES_WORKSPACE_PERSISTENCE_CONFIG, provideHermesIcons, HermesShortcutService, HermesI18nService, liveCatalog } from '@trismegisto/angular';
1
+ import { AlertComponent, ButtonComponent, DrawerComponent, IconButtonComponent, IconComponent, MenuButtonComponent, MenuComponent, ModalComponent, SidebarComponent, SidebarGroupComponent, SidebarLinkComponent, SidebarNodeComponent, ToastStackComponent, HermesThemeService, HERMES_THEME_CONFIG, HERMES_SIDEBAR_CONFIG, HERMES_WORKSPACE_PERSISTENCE_CONFIG, provideHermesIcons, HermesShortcutService, HermesI18nService, HermesZoomService, HermesMotionService, liveCatalog } from '@trismegisto/angular';
2
2
  import { isPlatformBrowser, DOCUMENT } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
4
  import { InjectionToken, inject, effect, PLATFORM_ID, Inject, Optional, Injectable, makeEnvironmentProviders, provideAppInitializer, NgZone, signal, computed, untracked, DestroyRef } from '@angular/core';
5
5
  import { HERMES_ICONS } from '@trismegisto/icons/catalog';
6
- import { extendThemeConfig, DEFAULT_THEME_CONFIG, mergeCatalog } from '@trismegisto/primitives';
6
+ import { extendThemeConfig, DEFAULT_THEME_CONFIG, normalizeTextScale, normalizeMotionPreference, mergeCatalog } from '@trismegisto/primitives';
7
7
  import { AuthorizationService } from '@orfeu/angular/core';
8
8
 
9
9
  /** `router_id` da tela (nó 'page'), aceitando o argos antigo (que só mandava `route_id`, mesmo valor). */
@@ -371,15 +371,30 @@ const APP_PACKAGE_INFO = new InjectionToken('APP_PACKAGE_INFO');
371
371
  const TIME_ZONE_SUFFIX = 'time_zone';
372
372
  const LANGUAGE_SUFFIX = 'language';
373
373
  const CHARACTER_KEYS_SUFFIX = 'shortcut_character_keys';
374
+ const TEXT_SCALE_SUFFIX = 'text_scale';
375
+ const MOTION_SUFFIX = 'motion';
376
+ const HOUR_CYCLE_SUFFIX = 'hour_cycle';
377
+ const WEEK_START_SUFFIX = 'week_start';
378
+ const IDLE_SUFFIX = 'idle_minutes';
379
+ const START_PAGE_SUFFIX = 'start_page';
380
+ const NOTICES_SUFFIX = 'notices';
381
+ const BROWSER_NOTICES_SUFFIX = 'browser_notices';
382
+ /** Minutos de inatividade oferecidos em "Encerrar por inatividade" (0 = nunca). */
383
+ const SPINE_IDLE_OPTIONS = [0, 5, 15, 30, 60];
374
384
  const CHANNEL_BASE = 'spine_preferences';
375
385
  /**
376
386
  * Preferências do usuário que não são de tema (tema/família/densidade continuam no
377
- * `HermesThemeService`; modo do menu lateral no `HermesSidebarService`): fuso horário, idioma e o
378
- * interruptor dos atalhos de tecla única (WCAG 2.1.4) do `HermesShortcutService`.
387
+ * `HermesThemeService`; modo do menu lateral no `HermesSidebarService`): fuso horário, idioma, relógio de
388
+ * 12/24 h e primeiro dia da semana (`HermesI18nService.setRegionalFormat`), tamanho do texto
389
+ * (`HermesZoomService.setTextScale`), movimento (`HermesMotionService.setPreference`) e o interruptor dos
390
+ * atalhos de tecla única (WCAG 2.1.4) do `HermesShortcutService`.
379
391
  *
380
- * Por enquanto vive no navegador (localStorage, sincronizado entre abas). A sincronização com o
381
- * servidor (argos-admin `/me/preferences`) entra depois sem mudar esta API: quem lê `timeZone()`
382
- * não precisa saber de onde o valor veio.
392
+ * Também guarda as preferências dos recursos opcionais (`SpineFeatures`): minutos de inatividade, tela inicial,
393
+ * tipos de aviso ligados e o aviso do navegador.
394
+ *
395
+ * Vive no navegador (localStorage, sincronizado entre abas). Com `SpineFeatures.accountPreferences`, o
396
+ * `SpineAccountPreferencesService` (`@trismegisto/spine/layout`) espelha estes valores na conta do Argos e os aplica
397
+ * pelos mesmos `set*` no login: quem lê `timeZone()` não precisa saber de onde o valor veio.
383
398
  */
384
399
  class SpinePreferencesService {
385
400
  constructor() {
@@ -389,8 +404,14 @@ class SpinePreferencesService {
389
404
  this.timeZoneKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${TIME_ZONE_SUFFIX}`;
390
405
  this.languageKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${LANGUAGE_SUFFIX}`;
391
406
  this.characterKeysKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${CHARACTER_KEYS_SUFFIX}`;
407
+ this.textScaleKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${TEXT_SCALE_SUFFIX}`;
408
+ this.motionKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${MOTION_SUFFIX}`;
409
+ this.hourCycleKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${HOUR_CYCLE_SUFFIX}`;
410
+ this.weekStartKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${WEEK_START_SUFFIX}`;
392
411
  this.shortcuts = inject(HermesShortcutService);
393
412
  this.i18n = inject(HermesI18nService);
413
+ this.zoom = inject(HermesZoomService);
414
+ this.motionService = inject(HermesMotionService);
394
415
  /** Idioma com que o app subiu (`provideHermesI18n`) — vale quando a pessoa não escolheu nenhum. */
395
416
  this.appLanguage = this.i18n.locale();
396
417
  this.channel = this.browser && typeof BroadcastChannel !== 'undefined'
@@ -409,6 +430,27 @@ class SpinePreferencesService {
409
430
  * sem escolha salva, vale o padrão do `HermesShortcutService` (`provideHermesShortcuts`).
410
431
  */
411
432
  this.characterKeys = signal(this.readCharacterKeys(), ...(ngDevMode ? [{ debugName: "characterKeys" }] : /* istanbul ignore next */ []));
433
+ /** Tamanho do texto (1 = 100%; os oferecidos em `HERMES_TEXT_SCALES`). */
434
+ this.textScale = signal(normalizeTextScale(this.read(this.textScaleKey)), ...(ngDevMode ? [{ debugName: "textScale" }] : /* istanbul ignore next */ []));
435
+ /** Movimento: `system` segue o `prefers-reduced-motion`; `reduce` sempre reduz; `full` nunca. */
436
+ this.motion = signal(normalizeMotionPreference(this.read(this.motionKey)), ...(ngDevMode ? [{ debugName: "motion" }] : /* istanbul ignore next */ []));
437
+ /** Relógio: `h23` (24 horas), `h12` (12 horas) ou vazio (o do idioma). */
438
+ this.hourCycle = signal(toHourCycle(this.read(this.hourCycleKey)), ...(ngDevMode ? [{ debugName: "hourCycle" }] : /* istanbul ignore next */ []));
439
+ /** Primeiro dia da semana (0 = domingo, 1 = segunda) ou `null` (o do idioma). */
440
+ this.weekStart = signal(toWeekStart(this.read(this.weekStartKey)), ...(ngDevMode ? [{ debugName: "weekStart" }] : /* istanbul ignore next */ []));
441
+ this.idleKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${IDLE_SUFFIX}`;
442
+ this.startPageKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${START_PAGE_SUFFIX}`;
443
+ this.noticesKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${NOTICES_SUFFIX}`;
444
+ this.browserNoticesKey = `${this.ns ? `hermes_${this.ns}` : 'hermes'}_${BROWSER_NOTICES_SUFFIX}`;
445
+ /** Encerrar a sessão depois de tantos minutos sem uso (0 = nunca). Os limites do app (`SpineFeatures.idleTimeout`)
446
+ * são aplicados por quem usa o valor (`SpineIdleService`), não aqui. */
447
+ this.idleMinutes = signal(toIdleMinutes(this.read(this.idleKey)), ...(ngDevMode ? [{ debugName: "idleMinutes" }] : /* istanbul ignore next */ []));
448
+ /** Tela que abre depois do login. */
449
+ this.startPage = signal(toStartPage(this.read(this.startPageKey)), ...(ngDevMode ? [{ debugName: "startPage" }] : /* istanbul ignore next */ []));
450
+ /** Tipos de aviso que a pessoa ligou/desligou (`id` → ligado); tipo ausente segue o `defaultEnabled` dele. */
451
+ this.noticeTypes = signal(toNoticeTypes(this.read(this.noticesKey)), ...(ngDevMode ? [{ debugName: "noticeTypes" }] : /* istanbul ignore next */ []));
452
+ /** Aviso do navegador (Notification API) quando a aba está em segundo plano. */
453
+ this.browserNotices = signal(this.read(this.browserNoticesKey) === 'on', ...(ngDevMode ? [{ debugName: "browserNotices" }] : /* istanbul ignore next */ []));
412
454
  /** Idiomas oferecidos na configuração, cada um com o nome no próprio idioma (padrão de mercado). */
413
455
  this.languages = [
414
456
  // check-strings: off — cada idioma escrito no próprio idioma (padrão de mercado), igual em todo catálogo.
@@ -425,11 +467,33 @@ class SpinePreferencesService {
425
467
  this.applyLanguage(e.data.value ?? '');
426
468
  if (e.data?.type === 'CHARACTER_KEYS_CHANGED')
427
469
  this.applyCharacterKeys(e.data.value !== false);
470
+ if (e.data?.type === 'TEXT_SCALE_CHANGED')
471
+ this.applyTextScale(normalizeTextScale(e.data.value));
472
+ if (e.data?.type === 'MOTION_CHANGED')
473
+ this.applyMotion(normalizeMotionPreference(e.data.value));
474
+ if (e.data?.type === 'HOUR_CYCLE_CHANGED')
475
+ this.applyRegionalFormat(toHourCycle(e.data.value), this.weekStart());
476
+ if (e.data?.type === 'WEEK_START_CHANGED')
477
+ this.applyRegionalFormat(this.hourCycle(), toWeekStart(e.data.value));
478
+ if (e.data?.type === 'IDLE_CHANGED')
479
+ this.idleMinutes.set(toIdleMinutes(e.data.value));
480
+ if (e.data?.type === 'START_PAGE_CHANGED')
481
+ this.startPage.set(toStartPage(e.data.value));
482
+ if (e.data?.type === 'NOTICES_CHANGED')
483
+ this.noticeTypes.set(toNoticeTypes(JSON.stringify(e.data.value ?? {})));
484
+ if (e.data?.type === 'BROWSER_NOTICES_CHANGED')
485
+ this.browserNotices.set(e.data.value === true);
428
486
  });
429
487
  }
430
488
  // Escolha salva vale desde a primeira tela (o layout injeta este serviço no boot).
431
489
  if (this.language())
432
490
  this.i18n.setLocale(this.language());
491
+ if (this.textScale() !== 1)
492
+ this.zoom.setTextScale(this.textScale());
493
+ if (this.motion() !== 'system')
494
+ this.motionService.setPreference(this.motion());
495
+ if (this.hourCycle() || this.weekStart() !== null)
496
+ this.applyRegionalFormat(this.hourCycle(), this.weekStart());
433
497
  if (this.characterKeys() !== this.shortcuts.characterKeys())
434
498
  this.shortcuts.setCharacterKeys(this.characterKeys());
435
499
  // O interruptor do hm-shortcut-list fala direto com o HermesShortcutService: a mudança que vier
@@ -468,6 +532,78 @@ class SpinePreferencesService {
468
532
  this.language.set(value);
469
533
  this.i18n.setLocale(value || this.appLanguage);
470
534
  }
535
+ /** Tamanho do texto na hora (1 = 100%), guarda e avisa as outras abas. */
536
+ setTextScale(value) {
537
+ const scale = normalizeTextScale(value);
538
+ this.applyTextScale(scale);
539
+ this.write(this.textScaleKey, scale === 1 ? '' : String(scale));
540
+ this.channel?.postMessage({ type: 'TEXT_SCALE_CHANGED', value: scale });
541
+ }
542
+ applyTextScale(scale) {
543
+ this.textScale.set(scale);
544
+ this.zoom.setTextScale(scale);
545
+ }
546
+ /** Movimento na hora (`system`/`reduce`/`full`), guarda e avisa as outras abas. */
547
+ setMotion(value) {
548
+ const motion = normalizeMotionPreference(value);
549
+ this.applyMotion(motion);
550
+ this.write(this.motionKey, motion === 'system' ? '' : motion);
551
+ this.channel?.postMessage({ type: 'MOTION_CHANGED', value: motion });
552
+ }
553
+ applyMotion(motion) {
554
+ this.motion.set(motion);
555
+ this.motionService.setPreference(motion);
556
+ }
557
+ /** Relógio na hora (`h23`/`h12`; vazio = o do idioma), guarda e avisa as outras abas. */
558
+ setHourCycle(value) {
559
+ const cycle = toHourCycle(value);
560
+ this.applyRegionalFormat(cycle, this.weekStart());
561
+ this.write(this.hourCycleKey, cycle);
562
+ this.channel?.postMessage({ type: 'HOUR_CYCLE_CHANGED', value: cycle });
563
+ }
564
+ /** Primeiro dia da semana na hora (0 = domingo, 1 = segunda; `null` = o do idioma), guarda e avisa. */
565
+ setWeekStart(value) {
566
+ const day = toWeekStart(value);
567
+ this.applyRegionalFormat(this.hourCycle(), day);
568
+ this.write(this.weekStartKey, day === null ? '' : String(day));
569
+ this.channel?.postMessage({ type: 'WEEK_START_CHANGED', value: day });
570
+ }
571
+ applyRegionalFormat(hourCycle, weekStart) {
572
+ this.hourCycle.set(hourCycle);
573
+ this.weekStart.set(weekStart);
574
+ this.i18n.setRegionalFormat({ hourCycle: hourCycle || null, firstDayOfWeek: weekStart });
575
+ }
576
+ /** Minutos sem uso até encerrar a sessão (0 = nunca), guarda e avisa as outras abas. */
577
+ setIdleMinutes(value) {
578
+ const minutes = toIdleMinutes(value);
579
+ this.idleMinutes.set(minutes);
580
+ this.write(this.idleKey, minutes ? String(minutes) : '');
581
+ this.channel?.postMessage({ type: 'IDLE_CHANGED', value: minutes });
582
+ }
583
+ /** Tela inicial depois do login, guarda e avisa as outras abas. */
584
+ setStartPage(value) {
585
+ const page = toStartPage(value);
586
+ this.startPage.set(page);
587
+ this.write(this.startPageKey, page === 'home' ? '' : page);
588
+ this.channel?.postMessage({ type: 'START_PAGE_CHANGED', value: page });
589
+ }
590
+ /** Liga/desliga um tipo de aviso, guarda e avisa as outras abas. */
591
+ setNoticeType(id, enabled) {
592
+ this.setNoticeTypes({ ...this.noticeTypes(), [id]: enabled });
593
+ }
594
+ /** Troca o mapa inteiro de tipos de aviso (restaurar, sincronização com a conta). */
595
+ setNoticeTypes(value) {
596
+ const types = toNoticeTypes(JSON.stringify(value ?? {}));
597
+ this.noticeTypes.set(types);
598
+ this.write(this.noticesKey, Object.keys(types).length ? JSON.stringify(types) : '');
599
+ this.channel?.postMessage({ type: 'NOTICES_CHANGED', value: types });
600
+ }
601
+ /** Liga/desliga o aviso do navegador, guarda e avisa as outras abas (a permissão é pedida por quem chama). */
602
+ setBrowserNotices(enabled) {
603
+ this.browserNotices.set(enabled);
604
+ this.write(this.browserNoticesKey, enabled ? 'on' : '');
605
+ this.channel?.postMessage({ type: 'BROWSER_NOTICES_CHANGED', value: enabled });
606
+ }
471
607
  setTimeZone(value) {
472
608
  this.timeZone.set(value);
473
609
  this.write(this.timeZoneKey, value);
@@ -511,6 +647,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
511
647
  type: Injectable,
512
648
  args: [{ providedIn: 'root' }]
513
649
  }], ctorParameters: () => [] });
650
+ function toIdleMinutes(value) {
651
+ const minutes = Number(value);
652
+ return Number.isInteger(minutes) && minutes > 0 && minutes <= 24 * 60 ? minutes : 0;
653
+ }
654
+ function toStartPage(value) {
655
+ if (value === 'last')
656
+ return 'last';
657
+ if (typeof value === 'string' && value.startsWith('fav:') && value.length > 4 && value.length <= 500)
658
+ return value;
659
+ return 'home';
660
+ }
661
+ /** Texto JSON salvo → mapa `id` → booleano (o resto é descartado). */
662
+ function toNoticeTypes(raw) {
663
+ let parsed;
664
+ try {
665
+ parsed = raw ? JSON.parse(raw) : {};
666
+ }
667
+ catch {
668
+ return {};
669
+ }
670
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
671
+ return {};
672
+ const out = {};
673
+ for (const [id, enabled] of Object.entries(parsed)) {
674
+ if (typeof enabled === 'boolean' && /^[\w.:-]{1,64}$/.test(id))
675
+ out[id] = enabled;
676
+ }
677
+ return out;
678
+ }
679
+ function toHourCycle(value) {
680
+ return value === 'h12' || value === 'h23' ? value : '';
681
+ }
682
+ /** `0`…`6` (número ou texto salvo) → dia da semana; o resto, `null`. */
683
+ function toWeekStart(value) {
684
+ if (value === null || value === undefined || value === '')
685
+ return null;
686
+ const day = Number(value);
687
+ return Number.isInteger(day) && day >= 0 && day <= 6 ? day : null;
688
+ }
514
689
 
515
690
  /**
516
691
  * Base da API do argos-admin-server (ex.: `https://exemplo.com.br/argos/admin/api`) —
@@ -526,17 +701,22 @@ const SPINE_USER_STORAGE_KEYS = {
526
701
  favorites: 'nav_favorites',
527
702
  recents: 'nav_recent',
528
703
  recentSearches: 'header_recent_searches',
704
+ /** Última tela visitada, guardada só com a tela inicial "Última tela visitada" (`SpineStartPageService`). */
705
+ lastPage: 'start_last_page',
529
706
  };
530
707
  /** Históricos (o que a pessoa visitou/buscou): apagados no logout. Favoritos ficam (preferência do usuário). */
531
708
  const HISTORY_KEYS = [SPINE_USER_STORAGE_KEYS.recents, SPINE_USER_STORAGE_KEYS.recentSearches];
709
+ // A última tela (`lastPage`) fica no logout de propósito: é ela que a tela inicial "Última tela visitada" abre no
710
+ // próximo login. Só é gravada quando a pessoa escolheu essa opção.
532
711
  /**
533
712
  * Chaves de `localStorage` do shell por usuário: `<clientId>_<lista>_<sub>`, com o `sub` do token da sessão.
534
713
  * Num computador compartilhado, uma pessoa não vê os recentes/buscas/favoritos de quem entrou antes.
535
714
  *
536
715
  * - Sem `sub` (sem sessão, ou app sem `@orfeu/angular`) a chave é a antiga, `<clientId>_<lista>`.
537
716
  * - Com `sub`, a chave antiga sem usuário é apagada (era compartilhada entre todas as contas do navegador).
538
- * - No logout (`clearHistory()`, chamado pelo spine antes do `logout()`, e o `isAuth$` indo para `false`, que
539
- * cobre o logout vindo de outra aba) os HISTÓRICOS do usuário saem; os favoritos ficam.
717
+ * - No logout (`clearHistory()`, chamado pelo `SpineLogoutService` só quando o `logout()` deu certo, e o `isAuth$`
718
+ * indo para `false`, que cobre o logout vindo de outra aba) os HISTÓRICOS do usuário saem; os favoritos ficam.
719
+ * Logout que falhou no Argos (`@orfeu/angular` 1.3.x) mantém a sessão e os históricos.
540
720
  */
541
721
  class SpineUserStorage {
542
722
  constructor() {
@@ -596,6 +776,34 @@ class SpineUserStorage {
596
776
  }
597
777
  this.notify();
598
778
  }
779
+ /**
780
+ * Apaga uma lista do usuário atual (Configurações › Dados do navegador): a chave dele, e também a antiga sem
781
+ * usuário. Quem guarda a lista em memória relê (`onChange`).
782
+ */
783
+ clear(list) {
784
+ if (!this.browser)
785
+ return;
786
+ try {
787
+ localStorage.removeItem(this.key(list));
788
+ localStorage.removeItem(`${this.ns}_${list}`);
789
+ }
790
+ catch {
791
+ /* storage indisponível */
792
+ }
793
+ this.notify();
794
+ }
795
+ /** Quantos itens a lista do usuário atual tem guardados (0 sem nada ou com valor ilegível). */
796
+ count(list) {
797
+ if (!this.browser)
798
+ return 0;
799
+ try {
800
+ const parsed = JSON.parse(localStorage.getItem(this.key(list)) ?? '[]');
801
+ return Array.isArray(parsed) ? parsed.length : 0;
802
+ }
803
+ catch {
804
+ return 0;
805
+ }
806
+ }
599
807
  /** Avisa quando a sessão muda ou o histórico é limpo (quem guarda a lista em memória relê). */
600
808
  onChange(listener) {
601
809
  this.listeners.add(listener);
@@ -688,6 +896,18 @@ class SpineQuickAccessService {
688
896
  const next = current.some((f) => f.id === item.id) ? current.filter((f) => f.id !== item.id) : [...current, item];
689
897
  this.commit(this.favoriteList, this.favoritesKey, next);
690
898
  }
899
+ /** Troca a lista de favoritos inteira (sincronização com a conta). Itens inválidos ou repetidos são descartados. */
900
+ setFavorites(items) {
901
+ const seen = new Set();
902
+ const next = items.flatMap((entry) => {
903
+ const item = normalize(entry);
904
+ if (!item || seen.has(item.id))
905
+ return [];
906
+ seen.add(item.id);
907
+ return [item];
908
+ });
909
+ this.commit(this.favoriteList, this.favoritesKey, next);
910
+ }
691
911
  recordVisit(item) {
692
912
  const current = this.recentList();
693
913
  const top = current[0];
@@ -933,7 +1153,7 @@ const SPINE_MESSAGES_PT_BR = {
933
1153
  appearance: 'Aparência',
934
1154
  appearanceKeywords: 'visual cores tema layout',
935
1155
  theme: 'Tema',
936
- themeDescription: 'Claro, escuro ou acompanhando o sistema operacional.',
1156
+ themeDescription: 'Claro, escuro ou o do sistema operacional.',
937
1157
  themeKeywords: 'modo escuro dark light noturno',
938
1158
  themeLight: 'Claro',
939
1159
  themeDark: 'Escuro',
@@ -942,7 +1162,7 @@ const SPINE_MESSAGES_PT_BR = {
942
1162
  accentDescription: 'Família de cores do cabeçalho, dos botões e dos destaques.',
943
1163
  accentKeywords: 'família paleta cor accent',
944
1164
  density: 'Densidade',
945
- densityDescription: 'Compacta aproxima linhas e controles para caber mais informação na tela.',
1165
+ densityDescription: 'Compacta mostra mais informação na tela.',
946
1166
  densityKeywords: 'espaçamento tamanho compacto',
947
1167
  comfortable: 'Confortável',
948
1168
  compact: 'Compacta',
@@ -950,64 +1170,121 @@ const SPINE_MESSAGES_PT_BR = {
950
1170
  fontDescription: 'Família de letra de todo o aplicativo.',
951
1171
  fontKeywords: 'tipografia letra texto roboto rubik figtree geist',
952
1172
  sidebar: 'Menu lateral',
953
- sidebarDescription: 'Automático abre ao aproximar o ponteiro; fixo fica sempre aberto; compacto mostra só os ícones.',
1173
+ sidebarDescription: 'Como o menu lateral abre e fecha.',
954
1174
  sidebarKeywords: 'sidebar navegação barra lateral',
1175
+ sidebarHelp: 'Automático: abre ao aproximar o ponteiro. Fixo: fica sempre aberto. Compacto: mostra só os ícones.',
955
1176
  sidebarAuto: 'Automático',
956
1177
  sidebarFixed: 'Fixo',
957
1178
  sidebarCompact: 'Compacto',
958
1179
  region: 'Região',
959
- regionKeywords: 'idioma língua localização horário data',
1180
+ regionKeywords: 'idioma língua localização horário data hora semana',
960
1181
  language: 'Idioma',
961
- languageDescription: 'Idioma dos textos do sistema. Vale na hora, sem recarregar.',
1182
+ languageDescription: 'Vale na hora, sem recarregar.',
962
1183
  languageKeywords: 'língua language idioma tradução inglês espanhol português',
963
1184
  languageAuto: 'Padrão do sistema ({language})',
964
1185
  timeZone: 'Fuso horário',
965
1186
  timeZoneKeywords: 'timezone hora utc gmt',
966
- timeZoneHint: 'Usado para exibir datas e horários. Agora: {now}',
1187
+ timeZoneHint: 'Para datas e horários. Agora: {now}',
967
1188
  timeZoneAuto: 'Automático ({zone})',
1189
+ hourCycle: 'Formato de hora',
1190
+ hourCycleHint: 'Agora: {now}',
1191
+ hourCycleKeywords: 'relógio 24h 12h am pm horário',
1192
+ hourCycleAuto: 'Automático',
1193
+ hourCycle24: '24 horas',
1194
+ hourCycle12: '12 horas',
1195
+ weekStart: 'Primeiro dia da semana',
1196
+ weekStartDescription: 'Para calendários e seletores de data.',
1197
+ weekStartKeywords: 'semana calendário domingo segunda',
1198
+ weekStartAuto: 'Automático',
968
1199
  shortcuts: 'Atalhos',
969
1200
  shortcutsDescription: 'Pressione ? em qualquer tela para ver esta lista.',
970
- shortcutsKeywords: 'teclado teclas tour ajuda',
1201
+ shortcutsKeywords: 'teclado teclas',
971
1202
  tour: 'Tour guiado',
972
- tourDescription: 'Mostra de novo, passo a passo, onde fica cada parte da tela.',
1203
+ tourDescription: 'Mostra de novo onde fica cada parte da tela.',
973
1204
  tourKeywords: 'onboarding ajuda apresentação',
974
1205
  replayTour: 'Refazer tour',
1206
+ accessibility: 'Acessibilidade',
1207
+ accessibilityKeywords: 'acessibilidade leitura visão',
1208
+ textSize: 'Tamanho do texto',
1209
+ textSizeDescription: 'Aumenta o texto e os controles de todas as telas.',
1210
+ textSizeKeywords: 'zoom fonte letra maior ampliar',
1211
+ motion: 'Reduzir animações',
1212
+ motionDescription: 'Sistema segue o computador ou o celular.',
1213
+ motionKeywords: 'animação movimento transição reduced motion enjoo tontura',
1214
+ motionSystem: 'Sistema',
1215
+ motionReduce: 'Sempre',
1216
+ motionFull: 'Nunca',
1217
+ help: 'Ajuda',
1218
+ helpKeywords: 'ajuda novidades tour apresentação',
1219
+ news: 'Novidades',
1220
+ newsDescription: 'O que mudou nas últimas versões.',
1221
+ newsKeywords: 'novidades versão lançamento changelog',
1222
+ showNews: 'Ver novidades',
1223
+ browserData: 'Dados do navegador',
1224
+ browserDataDescription: 'Guardados só neste navegador, para a sua conta.',
1225
+ browserDataKeywords: 'privacidade limpar apagar histórico dados',
1226
+ recentPages: 'Páginas recentes',
1227
+ recentPagesKeywords: 'histórico visitadas recentes',
1228
+ recentSearches: 'Buscas recentes',
1229
+ recentSearchesKeywords: 'busca pesquisa histórico',
1230
+ favorites: 'Favoritos',
1231
+ favoritesKeywords: 'favoritos estrela',
1232
+ pagesCount: { zero: 'Nenhuma página guardada', one: '{count} página guardada', other: '{count} páginas guardadas' },
1233
+ searchesCount: { zero: 'Nenhuma busca guardada', one: '{count} busca guardada', other: '{count} buscas guardadas' },
1234
+ clear: 'Limpar',
1235
+ confirmClear: 'Confirmar',
1236
+ cancel: 'Cancelar',
1237
+ recentPagesCleared: 'Páginas recentes apagadas.',
1238
+ recentSearchesCleared: 'Buscas recentes apagadas.',
1239
+ favoritesCleared: 'Favoritos apagados.',
1240
+ restorePreferences: 'Preferências',
1241
+ restorePreferencesDescription: 'Aparência, acessibilidade e formatos voltam ao padrão.',
1242
+ restorePreferencesHelp: 'Voltam ao padrão: tema, cor principal, densidade, fonte, menu lateral, tamanho do texto, animações, formato de hora e primeiro dia da semana. Idioma, fuso horário e atalhos de tecla única ficam como estão.',
1243
+ restorePreferencesKeywords: 'restaurar padrão redefinir reset preferências',
1244
+ restore: 'Restaurar padrão',
1245
+ confirmRestore: 'Confirmar',
1246
+ preferencesRestored: 'Preferências restauradas.',
975
1247
  account: 'Conta',
976
- accountKeywords: 'perfil usuário sessão login',
1248
+ accountKeywords: 'perfil usuário login nível sair',
977
1249
  user: 'Usuário',
978
1250
  accessLevel: 'Nível de acesso',
979
1251
  accessLevelKeywords: 'crachá badge perfil permissão grupo',
980
1252
  session: 'Sessão',
981
- sessionDescription: 'A sessão é renovada sozinha enquanto a aplicação estiver aberta.',
1253
+ sessionDescription: 'Renovada sozinha enquanto a aplicação estiver aberta.',
982
1254
  sessionKeywords: 'expiração token login',
983
1255
  sessionUnknown: 'Não informado',
984
1256
  sessionExpired: 'Expirada às {time}',
985
1257
  sessionExpiresIn: 'Expira em {minutes} min (às {time})',
986
1258
  sessionExpiresAt: 'Expira às {time}',
987
1259
  accessToken: 'Token de acesso',
988
- accessTokenDescription: 'Para testar uma API em seu nome. Dá acesso à sua conta até expirar: nunca compartilhe.',
1260
+ accessTokenDescription: 'Para testar uma API em seu nome. Não compartilhe.',
989
1261
  accessTokenKeywords: 'jwt bearer api desenvolvedor',
990
1262
  copy: 'Copiar',
1263
+ moreAbout: 'Mais sobre {label}',
991
1264
  appTokens: 'Tokens de aplicação',
992
- appTokensDescription: 'Para usar esta aplicação fora da interface em seu nome — um MCP, um script. O token vale só para esta aplicação, com as permissões do nível de acesso escolhido, e expira em no máximo 1 ano. Envie no cabeçalho x-app-token.',
1265
+ appTokensDescription: 'Para usar a aplicação fora da interface: um MCP, um script.',
993
1266
  appTokensKeywords: 'token app mcp api chave pat x-app-token automação script integração',
1267
+ appTokensHelp: 'O token vale só para esta aplicação, com as permissões do nível de acesso escolhido, e expira em no máximo 1 ano. Envie no cabeçalho x-app-token.',
994
1268
  logout: 'Sair',
995
1269
  logoutDescription: 'Encerra a sessão neste navegador.',
996
1270
  logoutKeywords: 'logout desconectar',
997
- security: 'Conta e segurança',
998
- securityDescription: 'Verificação em duas etapas, códigos de recuperação e sessões da sua conta no Argos, o login das aplicações.',
999
- securityKeywords: 'segurança argos senha autenticação mfa 2fa dispositivos',
1271
+ mfa: 'MFA',
1272
+ mfaDescription: 'Verificação em duas etapas da sua conta no Argos.',
1273
+ mfaKeywords: 'segurança argos senha autenticação mfa 2fa dois fatores duas etapas verificação',
1000
1274
  signedInAccount: 'Conta no Argos',
1001
1275
  signedInRealm: 'Realm {realm}',
1002
1276
  twoFactor: 'Verificação em duas etapas',
1003
- twoFactorDescription: 'Adicione ou remova métodos (aplicativo autenticador ou e-mail) e troque o aparelho do autenticador.',
1277
+ twoFactorDescription: 'Um código além da senha para entrar.',
1004
1278
  twoFactorKeywords: 'mfa 2fa totp autenticador authenticator código e-mail método aparelho celular',
1005
1279
  recoveryCodes: 'Códigos de recuperação',
1006
- recoveryCodesDescription: 'Servem para entrar se você perder o aparelho. Gerar novos códigos invalida os anteriores.',
1280
+ recoveryCodesDescription: 'Para entrar se você perder o aparelho.',
1007
1281
  recoveryCodesKeywords: 'backup recuperação perdi celular aparelho mfa 2fa',
1008
1282
  sessions: 'Sessões e dispositivos',
1009
- sessionsDescription: 'Veja onde a sua conta está conectada (navegador, sistema, IP, último uso) e encerre uma sessão ou todas as outras.',
1283
+ sessionsDescription: 'Onde a sua conta está conectada.',
1010
1284
  sessionsKeywords: 'dispositivos navegador ip encerrar desconectar sair aparelhos conectados',
1285
+ sessionsSection: 'Sessões',
1286
+ sessionsSectionDescription: 'Esta sessão, os tokens de aplicação e os dispositivos conectados.',
1287
+ sessionsSectionKeywords: 'sessão sessões token tokens dispositivos login segurança',
1011
1288
  about: 'Sobre',
1012
1289
  aboutKeywords: 'versão build ambiente dependências suporte',
1013
1290
  application: 'Aplicação',
@@ -1025,7 +1302,7 @@ const SPINE_MESSAGES_PT_BR = {
1025
1302
  dependenciesDescription: 'Versões declaradas no package.json desta aplicação.',
1026
1303
  dependenciesKeywords: 'angular hermes rxjs apollo graphql',
1027
1304
  supportInfo: 'Informações para suporte',
1028
- supportInfoDescription: 'Copia versão, ambiente, navegador, página e preferências — cole no chamado.',
1305
+ supportInfoDescription: 'Versão, ambiente, navegador e preferências, para colar no chamado.',
1029
1306
  supportInfoKeywords: 'ajuda chamado diagnóstico',
1030
1307
  noSessionToken: 'Nenhum token de sessão disponível.',
1031
1308
  tokenCopied: 'Token copiado. Não compartilhe: ele dá acesso à sua conta até expirar.',
@@ -1107,7 +1384,7 @@ const SPINE_MESSAGES_PT_BR = {
1107
1384
  removeHint: 'Você deixa de poder usar este método para entrar.',
1108
1385
  removeLastHint: 'Sem nenhum método, os códigos de recuperação também deixam de valer.',
1109
1386
  reauthTitle: 'Confirme sua identidade',
1110
- reauthHint: 'Para mudar a verificação em duas etapas ou gerar códigos, prove de novo quem é você. Vale por 5 minutos.',
1387
+ reauthHint: 'Por segurança, confirme antes de continuar. Vale por 5 minutos.',
1111
1388
  reauthHow: 'Como confirmar',
1112
1389
  reauthPassword: 'Senha',
1113
1390
  reauthRecoveryCode: 'Código de recuperação',
@@ -1131,13 +1408,13 @@ const SPINE_MESSAGES_PT_BR = {
1131
1408
  sendCode: 'Enviar código',
1132
1409
  emailCodeTitle: 'Confirme o e-mail',
1133
1410
  emailCodeHint: 'Digite o código de 6 dígitos enviado para {email}.',
1134
- codesRemaining: { zero: 'Nenhum código disponível.', one: '1 código disponível.', other: '{count} códigos disponíveis.' },
1135
- codesHint: 'Cada um vale uma vez para entrar se você perder o seu método de verificação.',
1411
+ codesRemaining: { zero: 'Nenhum código disponível', one: '1 código disponível', other: '{count} códigos disponíveis' },
1412
+ codesHint: 'Cada um vale uma vez. Gerar novos invalida os anteriores.',
1136
1413
  codesNeedMethod: 'Cadastre um método de verificação para gerar códigos de recuperação.',
1137
1414
  generateCodes: 'Gerar códigos',
1138
1415
  regenerateCodes: 'Gerar novos códigos',
1139
1416
  codesTitle: 'Seus códigos de recuperação',
1140
- codesShownOnce: 'Guarde estes códigos num lugar seguro. Cada um vale uma única vez para entrar se você perder o acesso ao seu método de verificação. Eles não serão mostrados de novo.',
1417
+ codesShownOnce: 'Guarde num lugar seguro: eles não serão mostrados de novo. Cada um vale uma vez.',
1141
1418
  copyCodes: 'Copiar',
1142
1419
  downloadCodes: 'Baixar .txt',
1143
1420
  codesCopied: 'Códigos copiados.',
@@ -1216,6 +1493,7 @@ const SPINE_MESSAGES_PT_BR = {
1216
1493
  signInAgain: 'Entrar novamente',
1217
1494
  callbackFailedHeading: 'Não foi possível concluir a entrada',
1218
1495
  callbackFailedText: 'O retorno do login veio incompleto ou expirou. Entre novamente para continuar.',
1496
+ logoutFailed: 'Não foi possível sair agora. Verifique a conexão e tente de novo.',
1219
1497
  },
1220
1498
  routing: {
1221
1499
  notPublished: 'Este endereço existe no seu acesso, mas a tela dele ainda não foi publicada.',
@@ -1261,10 +1539,10 @@ const SPINE_MESSAGES_PT_BR = {
1261
1539
  tabRoutes: 'Rotas',
1262
1540
  tabRouters: 'Roteadores',
1263
1541
  tabAccess: 'Acesso',
1264
- noClients: 'Você ainda não administra nenhum client por aqui. É preciso (a) ter acesso concedido a um client e (b) estar logado com o crachá <strong>Administrador</strong> ativo. Troque de crachá no menu do seu perfil, se disponível, ou peça acesso a um administrador central.',
1542
+ noClients: 'Você não administra este app. É preciso (a) estar no grupo de administração do projeto dele e (b) estar logado com o crachá <strong>Administrador</strong> ativo. Troque de crachá no menu do seu perfil, se disponível, ou peça acesso a quem administra o projeto.',
1265
1543
  pickClient: 'Selecione o client que deseja administrar',
1266
1544
  groupsTitle: 'Grupos',
1267
- groupsIntro: 'Gerencie os membros dos grupos deste client — sem depender do painel central.',
1545
+ groupsIntro: 'Crie, edite e exclua os grupos deste client e gerencie os membros — sem depender do painel central.',
1268
1546
  groupsFixed: 'Os 5 grupos abaixo são fixos deste client e não podem ser renomeados, criados ou excluídos — consulte um para gerenciar seus membros.',
1269
1547
  pickGroup: 'Selecione um grupo',
1270
1548
  noDescription: 'Sem descrição cadastrada.',
@@ -1350,6 +1628,9 @@ const SPINE_MESSAGES_PT_BR = {
1350
1628
  colPage: 'Página',
1351
1629
  colBuiltPath: 'Caminho montado',
1352
1630
  colVisibility: 'Visibilidade',
1631
+ colAllowedBadges: 'Crachás permitidos',
1632
+ anyBadge: 'Qualquer crachá',
1633
+ allowedBadgesHint: 'Vazio = qualquer crachá. Com um ou mais escolhidos, só os grupos com um desses crachás podem receber acesso a esta tela na aba Acesso.',
1353
1634
  routesTitle: 'Rotas',
1354
1635
  newRoute: 'Nova rota',
1355
1636
  routesCaption: 'Rotas do client',
@@ -1368,6 +1649,19 @@ const SPINE_MESSAGES_PT_BR = {
1368
1649
  pathPlaceholder: 'ex.: dashboard',
1369
1650
  pathHint: 'Um trecho só do endereço: letras minúsculas e hífen. O "/" entre níveis é o roteador que monta.',
1370
1651
  pathInvalid: 'Use só letras minúsculas (a–z) e hífen, sem "/", espaço, número ou acento, e sem hífen no início ou no fim.',
1652
+ rotateSecret: 'Gerar novo segredo',
1653
+ confirmRotateSecretHeading: 'Gerar um novo segredo?',
1654
+ confirmRotateSecret: 'O segredo atual deixa de funcionar na hora; atualize o APP_SECRET do serviço que usa esta aplicação. Tokens já emitidos valem até expirar (até 1 hora).',
1655
+ rotatedSecretHeading: 'Novo segredo de {name}',
1656
+ rotatedSecretWarning: 'Copie agora: o segredo não será mostrado de novo.',
1657
+ rotatedSecretNote: 'O servidor guarda só um hash do segredo. Ao fechar esta janela não há como consultá-lo; se ele se perder, gere outro.',
1658
+ rotatedSecretLabel: 'Segredo da aplicação {name}',
1659
+ copySecret: 'Copiar',
1660
+ secretCopied: 'Segredo copiado.',
1661
+ rotatedSecretDone: 'Concluir',
1662
+ rotateSecretError: 'Não foi possível gerar o novo segredo. Tente de novo.',
1663
+ rotateSecretNotFound: 'Aplicação não encontrada neste client.',
1664
+ rotateSecretForbidden: 'Você não administra este client.',
1371
1665
  confirmDeleteAppHeading: 'Excluir aplicação?',
1372
1666
  confirmDeleteApp: 'A aplicação {name} será excluída e a credencial dela para de funcionar. Esta ação não pode ser desfeita.',
1373
1667
  confirmDeleteRouteHeading: 'Excluir rota?',
@@ -1402,6 +1696,89 @@ const SPINE_MESSAGES_PT_BR = {
1402
1696
  reorderLoadError: 'Não foi possível carregar o menu. Tente de novo.',
1403
1697
  reorderSaveError: 'Não foi possível salvar a ordem. Nada foi perdido: tente de novo.',
1404
1698
  reorderSave: 'Salvar ordem',
1699
+ newGroup: 'Novo grupo',
1700
+ editGroup: 'Editar grupo',
1701
+ groupsCaption: 'Grupos do projeto',
1702
+ noGroups: 'Nenhum grupo cadastrado ainda.',
1703
+ colStatus: 'Situação',
1704
+ groupName: 'Nome',
1705
+ groupType: 'Tipo',
1706
+ groupTypePlaceholder: 'Selecione o tipo',
1707
+ groupDescription: 'Descrição (opcional)',
1708
+ openGroup: 'Abrir o grupo {name}',
1709
+ adminGroupTag: 'Administração',
1710
+ adminGroupNote: 'Grupo de administração do projeto: quem está nele administra este app no auto-serviço. É o único do projeto, não pode ser excluído, renomeado, desativado nem mudar de tipo, e não pode ficar sem membros.',
1711
+ adminGroupEditNote: 'Grupo de administração do projeto: só a descrição pode ser alterada.',
1712
+ adminGroupLastMemberWarning: 'O grupo de administração não pode ficar sem membros: inclua outra pessoa ou desfaça uma remoção.',
1713
+ confirmDeleteGroupHeading: 'Excluir grupo?',
1714
+ confirmDeleteGroup: 'O grupo {name} será excluído junto com os membros, os acessos a telas e as permissões em aplicações. Esta ação não pode ser desfeita.',
1715
+ groupSaveError: 'Não foi possível salvar o grupo. Tente de novo.',
1716
+ membersSaveError: 'Não foi possível salvar todas as alterações dos membros. Confira a lista e tente de novo.',
1717
+ appsCaption: 'Aplicações do projeto',
1718
+ noApps: 'Nenhuma aplicação cadastrada ainda.',
1719
+ colGrants: 'Grants',
1720
+ openApp: 'Abrir a aplicação {name}',
1721
+ tabTokens: 'Tokens',
1722
+ tokensCaption: 'Tokens da aplicação',
1723
+ noTokens: 'Nenhum token criado para esta aplicação ainda.',
1724
+ newToken: 'Novo token',
1725
+ colTokenPrefix: 'Início do token',
1726
+ colExpiresAt: 'Expira em',
1727
+ colLastUsed: 'Último uso',
1728
+ tokenExpired: 'Expirado',
1729
+ tokenRevoked: 'Revogado',
1730
+ neverUsed: 'Nunca',
1731
+ revokeToken: 'Revogar',
1732
+ confirmRevokeTokenHeading: 'Revogar token?',
1733
+ confirmRevokeToken: 'O token {name} deixa de funcionar na hora. A revogação não pode ser desfeita.',
1734
+ tokenRevokedDone: 'Token {name} revogado.',
1735
+ tokenRevokeError: 'Não foi possível revogar o token. Tente de novo.',
1736
+ tokensLoadError: 'Não foi possível carregar os tokens da aplicação.',
1737
+ tokenName: 'Nome',
1738
+ tokenDescription: 'Descrição (opcional)',
1739
+ tokenExpiresAt: 'Expira em',
1740
+ tokenExpiresHint: 'De amanhã até 365 dias a partir de hoje.',
1741
+ tokenExpiresInvalid: 'Escolha uma data entre amanhã e 365 dias a partir de hoje.',
1742
+ tokenOperations: 'Operações liberadas ao token',
1743
+ tokenOperationsNote: 'Marque as operações protected que o token pode chamar. As public e private já valem para ele e aparecem marcadas.',
1744
+ freeForAll: 'liberada para todos',
1745
+ freeForAuthenticated: 'liberada para autenticados',
1746
+ freeNoPermission: 'não depende de permissão',
1747
+ createToken: 'Criar token',
1748
+ tokenSaveError: 'Não foi possível criar o token. Tente de novo.',
1749
+ tokenCreatedHeading: 'Token {name} criado',
1750
+ tokenCreatedWarning: 'Copie agora: o token não será mostrado de novo.',
1751
+ tokenCreatedNote: 'Envie no cabeçalho x-api-token. O servidor guarda só um hash do token; se ele se perder, revogue e crie outro.',
1752
+ tokenValueLabel: 'Token {name}',
1753
+ copyToken: 'Copiar',
1754
+ tokenCopied: 'Token copiado.',
1755
+ hierarchyHint: 'De cima para baixo: Navegação só com Header, Menu só com Navegação, Item só com Menu e Submenu só com Item. Limpar um nível limpa os de baixo.',
1756
+ routerSaveError: 'Não foi possível salvar o roteador. Tente de novo.',
1757
+ permissionsSaveError: 'Não foi possível salvar todas as alterações das operações. Reabra o painel e confira.',
1758
+ tokenErrors: {
1759
+ invalidExpiresAt: 'Data de validade inválida. Escolha outra data.',
1760
+ invalidValidity: 'A validade precisa ficar entre amanhã e 365 dias a partir de hoje.',
1761
+ invalidOperationIds: 'Alguma operação escolhida não existe mais nesta aplicação. Reabra o formulário e escolha de novo.',
1762
+ operationNotProtected: 'Só operações protected podem ser liberadas ao token; as public e private já valem para ele.',
1763
+ appNotFound: 'Aplicação não encontrada neste client. Ela pode ter sido excluída.',
1764
+ tokenNotFound: 'Token não encontrado nesta aplicação. Ele pode já ter sido revogado.',
1765
+ },
1766
+ routerErrors: {
1767
+ hierarchyGap: 'A hierarquia tem um nível vazio no meio: cada nível precisa do de cima (Header → Navegação → Menu → Item → Submenu). Preencha o nível que falta ou limpe os de baixo.',
1768
+ invalidAllowedTypes: 'Algum crachá escolhido não existe mais. Revise os crachás permitidos.',
1769
+ },
1770
+ groupErrors: {
1771
+ invalidType: 'Esse tipo de grupo não existe mais. Escolha outro tipo.',
1772
+ adminTypeReserved: 'O tipo Administrador é só do grupo de administração do projeto.',
1773
+ emptyUpdate: 'Nada para salvar: nenhum campo foi alterado.',
1774
+ groupProjectImmutable: 'O grupo não muda de projeto.',
1775
+ groupNameTaken: 'Já existe um grupo com esse nome neste projeto.',
1776
+ adminGroupImmutable: 'O grupo de administração não muda de nome, de tipo nem é desativado.',
1777
+ adminGroupProtected: 'O grupo de administração do projeto não pode ser excluído.',
1778
+ adminGroupLastMember: 'O grupo de administração não pode ficar sem membros.',
1779
+ adminGroupAccessProtected: 'O acesso do grupo de administração a este app não pode ser removido.',
1780
+ groupNotFound: 'Grupo não encontrado neste projeto. Ele pode ter sido excluído.',
1781
+ },
1405
1782
  },
1406
1783
  autoServiceAudit: {
1407
1784
  title: 'Auditoria',
@@ -1571,6 +1948,132 @@ const SPINE_MESSAGES_PT_BR = {
1571
1948
  spansSummary: '{count} spans em {duration}',
1572
1949
  spanRow: '{app}, {operation}: {outcome}, começa em +{offset}, dura {duration}',
1573
1950
  },
1951
+ loginAttempts: {
1952
+ refresh: 'Atualizar',
1953
+ user: 'Usuário',
1954
+ userPlaceholder: 'Usuário, e-mail ou identificador digitado',
1955
+ period: 'Período',
1956
+ result: 'Resultado',
1957
+ method: 'Método',
1958
+ all: 'Todos',
1959
+ clear: 'Limpar',
1960
+ advancedFilters: 'Filtros avançados',
1961
+ ip: 'IP ou rede',
1962
+ ipPlaceholder: '10.0.0.5 ou 10.0.0.0/8',
1963
+ ipHint: 'IP exato ou rede em notação CIDR.',
1964
+ reason: 'Motivo',
1965
+ caption: 'Tentativas de login',
1966
+ pageItems: 'tentativas',
1967
+ pageSize: 'Tentativas por página',
1968
+ colDate: 'Data/hora',
1969
+ colResult: 'Resultado',
1970
+ colMethod: 'Método',
1971
+ colUser: 'Usuário',
1972
+ colReason: 'Motivo',
1973
+ colIp: 'IP',
1974
+ colRealm: 'Realm',
1975
+ colClient: 'Client',
1976
+ colUserAgent: 'Navegador',
1977
+ filterByIp: 'Filtrar por este IP',
1978
+ filterByUser: 'Filtrar por este usuário',
1979
+ noMatchingUser: 'sem usuário correspondente',
1980
+ methods: { local: 'Senha', mfa: 'MFA', github: 'GitHub', google: 'Google', keycloak: 'Keycloak', entraid: 'Entra ID' },
1981
+ results: { success: 'Sucesso', failure: 'Falha', cancelled: 'Cancelada' },
1982
+ reasons: {
1983
+ bad_password: 'Senha incorreta',
1984
+ unknown_user: 'Usuário inexistente ou inativo',
1985
+ locked: 'Conta bloqueada por tentativas',
1986
+ mfa_failed: 'Código de verificação incorreto',
1987
+ mfa_expired: 'Código de verificação vencido',
1988
+ mfa_not_allowed: 'Método de verificação não permitido',
1989
+ mfa_invalid_method: 'Método de verificação desconhecido',
1990
+ recovery_failed: 'Código de recuperação incorreto ou já usado',
1991
+ recovery_refused: 'Código de recuperação recusado',
1992
+ not_linked: 'Conta externa não vinculada',
1993
+ wrong_realm: 'Usuário de outro realm',
1994
+ },
1995
+ forbiddenAdmin: 'Sua conta não pode consultar as tentativas de login. Peça a um administrador a operação admin:attempts:list.',
1996
+ forbiddenClient: 'Só o grupo de administração do projeto, com o crachá Administrador ativo, vê as tentativas de login desta aplicação.',
1997
+ loadErrorDetail: 'Não foi possível carregar as tentativas: {detail}',
1998
+ loadError: 'Não foi possível carregar as tentativas de login.',
1999
+ },
2000
+ accountFeatures: {
2001
+ aiAgent: 'Conectar um agente de IA',
2002
+ aiAgentDescription: 'Gera o token e a configuração MCP para colar no Claude Code.',
2003
+ aiAgentKeywords: 'ia agente claude mcp assistente llm automação',
2004
+ aiAgentHelp: 'O agente usa um token de aplicação pessoal (x-app-token) com as permissões do nível de acesso escolhido. Revogue em "Tokens de aplicação" quando não precisar mais.',
2005
+ aiAgentStart: 'Conectar agente',
2006
+ aiAgentServers: 'Servidores MCP',
2007
+ aiAgentAccess: 'Acesso do agente',
2008
+ aiAgentReadOnly: 'Só leitura',
2009
+ aiAgentReadWrite: 'Leitura e escrita',
2010
+ aiAgentWriteHint: 'O agente poderá alterar dados em seu nome. Prefira só leitura quando bastar.',
2011
+ aiAgentReadOnlyServer: '{name} (só leitura)',
2012
+ aiAgentTokenName: 'Agente de IA ({date})',
2013
+ aiAgentGenerate: 'Gerar configuração',
2014
+ aiAgentSelectServer: 'Escolha pelo menos um servidor.',
2015
+ aiAgentResult: 'Configuração pronta',
2016
+ aiAgentResultHint: 'Cole em .mcp.json, na raiz do projeto (Claude Code). O token aparece só agora: copie antes de concluir. Vale até {date}.',
2017
+ aiAgentCopy: 'Copiar configuração',
2018
+ aiAgentCopied: 'Configuração copiada. Não compartilhe: ela contém o seu token.',
2019
+ aiAgentDone: 'Concluir',
2020
+ aiAgentCreateError: 'Não foi possível gerar o token do agente.',
2021
+ aiAgentUnavailable: 'Indisponível neste aplicativo: ele não está ligado ao argos-admin.',
2022
+ activity: 'Atividade da conta',
2023
+ activityDescription: 'Entradas e tentativas que falharam nos últimos 90 dias.',
2024
+ activityKeywords: 'login entrada acesso histórico tentativa falha ip navegador segurança',
2025
+ activityFilter: 'Mostrar',
2026
+ activityAll: 'Todas',
2027
+ activityFailures: 'Só falhas',
2028
+ activityDetail: '{date} · IP {ip}',
2029
+ activityApp: 'Aplicativo: {app}',
2030
+ activityEmpty: 'Nada nos últimos 90 dias.',
2031
+ activityMore: 'Carregar mais',
2032
+ activityShowing: '{shown} de {total}',
2033
+ activityLoadError: 'Não foi possível carregar a atividade da conta.',
2034
+ unknownIp: 'desconhecido',
2035
+ unknownApp: 'sem nome',
2036
+ idle: 'Encerrar por inatividade',
2037
+ idleDescription: 'Sai sozinho depois de um tempo sem uso, com um aviso antes.',
2038
+ idleKeywords: 'inatividade ocioso tempo sair automático logout segurança bloqueio',
2039
+ idleNever: 'Nunca',
2040
+ idleOption: '{minutes} min',
2041
+ idleLimitHint: 'Esta aplicação encerra a sessão em no máximo {minutes} min sem uso.',
2042
+ idleWarningHeading: 'A sessão vai ser encerrada',
2043
+ idleWarningText: { one: 'Nenhuma atividade há algum tempo. A sessão termina em {count} segundo.', other: 'Nenhuma atividade há algum tempo. A sessão termina em {count} segundos.' },
2044
+ idleStay: 'Continuar conectado',
2045
+ idleLogout: 'Sair agora',
2046
+ startPage: 'Tela inicial',
2047
+ startPageDescription: 'Onde a aplicação abre depois do login.',
2048
+ startPageKeywords: 'início home abertura login favorito última tela',
2049
+ startHome: 'Início',
2050
+ startLast: 'Última tela visitada',
2051
+ startFavorite: 'Favorito: {name}',
2052
+ startPageHint: 'Marque telas como favoritas para abrir numa delas.',
2053
+ defaultBadge: 'Crachá padrão',
2054
+ defaultBadgeDescription: 'Nível de acesso com que você entra nesta aplicação, em qualquer navegador.',
2055
+ defaultBadgeKeywords: 'crachá badge nível acesso perfil padrão login',
2056
+ defaultBadgeAuto: 'Automático (o de menor nível)',
2057
+ defaultBadgeSaved: 'Crachá padrão salvo. Vale a partir do próximo login.',
2058
+ defaultBadgeError: 'Não foi possível salvar o crachá padrão.',
2059
+ defaultBadgeLoadError: 'Não foi possível carregar os seus crachás.',
2060
+ notices: 'Notificações',
2061
+ noticesDescription: 'Os avisos que esta aplicação mostra.',
2062
+ noticesKeywords: 'notificação aviso alerta navegador push',
2063
+ browserNotices: 'Avisos do navegador',
2064
+ browserNoticesDescription: 'Aviso do sistema quando a aba está em segundo plano.',
2065
+ browserNoticesDenied: 'O navegador bloqueou as notificações deste site. Libere nas configurações do navegador.',
2066
+ browserNoticesUnsupported: 'Este navegador não mostra notificações.',
2067
+ noticeOpen: 'Abrir',
2068
+ sync: 'Preferências na conta',
2069
+ syncDescription: 'Aparência, região, favoritos e as demais preferências acompanham você em qualquer navegador.',
2070
+ syncKeywords: 'sincronizar nuvem conta navegador preferências backup',
2071
+ syncSynced: 'Sincronizado às {time}',
2072
+ syncSaving: 'Salvando…',
2073
+ syncLocal: 'Só neste navegador: a conta não está disponível.',
2074
+ syncError: 'Não foi possível sincronizar. Tente de novo.',
2075
+ syncNow: 'Sincronizar agora',
2076
+ },
1574
2077
  };
1575
2078
 
1576
2079
  const SPINE_MESSAGES_EN = {
@@ -1695,7 +2198,7 @@ const SPINE_MESSAGES_EN = {
1695
2198
  appearance: 'Appearance',
1696
2199
  appearanceKeywords: 'visual colors theme layout',
1697
2200
  theme: 'Theme',
1698
- themeDescription: 'Light, dark or following the operating system.',
2201
+ themeDescription: 'Light, dark or the operating system one.',
1699
2202
  themeKeywords: 'dark mode light night',
1700
2203
  themeLight: 'Light',
1701
2204
  themeDark: 'Dark',
@@ -1704,7 +2207,7 @@ const SPINE_MESSAGES_EN = {
1704
2207
  accentDescription: 'Color family for the header, buttons and highlights.',
1705
2208
  accentKeywords: 'family palette color accent',
1706
2209
  density: 'Density',
1707
- densityDescription: 'Compact brings rows and controls closer to fit more information on screen.',
2210
+ densityDescription: 'Compact shows more information on screen.',
1708
2211
  densityKeywords: 'spacing size compact',
1709
2212
  comfortable: 'Comfortable',
1710
2213
  compact: 'Compact',
@@ -1712,64 +2215,121 @@ const SPINE_MESSAGES_EN = {
1712
2215
  fontDescription: 'Typeface for the whole application.',
1713
2216
  fontKeywords: 'typography typeface text roboto rubik figtree geist',
1714
2217
  sidebar: 'Side menu',
1715
- sidebarDescription: 'Automatic opens when the pointer approaches; fixed stays open; compact shows only icons.',
2218
+ sidebarDescription: 'How the side menu opens and closes.',
1716
2219
  sidebarKeywords: 'sidebar navigation side bar',
2220
+ sidebarHelp: 'Automatic: opens when the pointer approaches. Fixed: always open. Compact: shows only the icons.',
1717
2221
  sidebarAuto: 'Automatic',
1718
2222
  sidebarFixed: 'Fixed',
1719
2223
  sidebarCompact: 'Compact',
1720
2224
  region: 'Region',
1721
- regionKeywords: 'language locale time date',
2225
+ regionKeywords: 'language locale time date hour week',
1722
2226
  language: 'Language',
1723
- languageDescription: 'Language of the system texts. Applies immediately, no reload.',
2227
+ languageDescription: 'Applies immediately, no reload.',
1724
2228
  languageKeywords: 'language locale translation english spanish portuguese',
1725
2229
  languageAuto: 'System default ({language})',
1726
2230
  timeZone: 'Time zone',
1727
2231
  timeZoneKeywords: 'timezone hour utc gmt',
1728
- timeZoneHint: 'Used to display dates and times. Now: {now}',
2232
+ timeZoneHint: 'For dates and times. Now: {now}',
1729
2233
  timeZoneAuto: 'Automatic ({zone})',
2234
+ hourCycle: 'Time format',
2235
+ hourCycleHint: 'Now: {now}',
2236
+ hourCycleKeywords: 'clock 24h 12h am pm hour',
2237
+ hourCycleAuto: 'Automatic',
2238
+ hourCycle24: '24-hour',
2239
+ hourCycle12: '12-hour',
2240
+ weekStart: 'First day of the week',
2241
+ weekStartDescription: 'For calendars and date pickers.',
2242
+ weekStartKeywords: 'week calendar sunday monday',
2243
+ weekStartAuto: 'Automatic',
1730
2244
  shortcuts: 'Shortcuts',
1731
2245
  shortcutsDescription: 'Press ? on any screen to see this list.',
1732
- shortcutsKeywords: 'keyboard keys tour help',
2246
+ shortcutsKeywords: 'keyboard keys',
1733
2247
  tour: 'Guided tour',
1734
- tourDescription: 'Shows again, step by step, where each part of the screen is.',
2248
+ tourDescription: 'Shows again where each part of the screen is.',
1735
2249
  tourKeywords: 'onboarding help introduction',
1736
2250
  replayTour: 'Replay tour',
2251
+ accessibility: 'Accessibility',
2252
+ accessibilityKeywords: 'accessibility reading vision',
2253
+ textSize: 'Text size',
2254
+ textSizeDescription: 'Makes text and controls larger on every screen.',
2255
+ textSizeKeywords: 'zoom font letter bigger enlarge',
2256
+ motion: 'Reduce motion',
2257
+ motionDescription: 'System follows your computer or phone.',
2258
+ motionKeywords: 'animation motion transition reduced dizziness',
2259
+ motionSystem: 'System',
2260
+ motionReduce: 'Always',
2261
+ motionFull: 'Never',
2262
+ help: 'Help',
2263
+ helpKeywords: "help what's new tour introduction",
2264
+ news: "What's new",
2265
+ newsDescription: 'What changed in the latest versions.',
2266
+ newsKeywords: 'news version release changelog',
2267
+ showNews: "See what's new",
2268
+ browserData: 'Browser data',
2269
+ browserDataDescription: 'Stored only in this browser, for your account.',
2270
+ browserDataKeywords: 'privacy clear delete history data',
2271
+ recentPages: 'Recent pages',
2272
+ recentPagesKeywords: 'history visited recent',
2273
+ recentSearches: 'Recent searches',
2274
+ recentSearchesKeywords: 'search query history',
2275
+ favorites: 'Favorites',
2276
+ favoritesKeywords: 'favorites star bookmarks',
2277
+ pagesCount: { zero: 'No pages stored', one: '{count} page stored', other: '{count} pages stored' },
2278
+ searchesCount: { zero: 'No searches stored', one: '{count} search stored', other: '{count} searches stored' },
2279
+ clear: 'Clear',
2280
+ confirmClear: 'Confirm',
2281
+ cancel: 'Cancel',
2282
+ recentPagesCleared: 'Recent pages cleared.',
2283
+ recentSearchesCleared: 'Recent searches cleared.',
2284
+ favoritesCleared: 'Favorites cleared.',
2285
+ restorePreferences: 'Preferences',
2286
+ restorePreferencesDescription: 'Appearance, accessibility and formats go back to the defaults.',
2287
+ restorePreferencesHelp: 'Back to the defaults: theme, main color, density, font, side menu, text size, animations, time format and first day of the week. Language, time zone and single-key shortcuts stay as they are.',
2288
+ restorePreferencesKeywords: 'restore default reset preferences',
2289
+ restore: 'Restore defaults',
2290
+ confirmRestore: 'Confirm',
2291
+ preferencesRestored: 'Preferences restored.',
1737
2292
  account: 'Account',
1738
- accountKeywords: 'profile user session login',
2293
+ accountKeywords: 'profile user login level sign out',
1739
2294
  user: 'User',
1740
2295
  accessLevel: 'Access level',
1741
2296
  accessLevelKeywords: 'badge profile permission group',
1742
2297
  session: 'Session',
1743
- sessionDescription: 'The session renews itself while the application is open.',
2298
+ sessionDescription: 'Renews itself while the application is open.',
1744
2299
  sessionKeywords: 'expiration token login',
1745
2300
  sessionUnknown: 'Not provided',
1746
2301
  sessionExpired: 'Expired at {time}',
1747
2302
  sessionExpiresIn: 'Expires in {minutes} min (at {time})',
1748
2303
  sessionExpiresAt: 'Expires at {time}',
1749
2304
  accessToken: 'Access token',
1750
- accessTokenDescription: 'To test an API on your behalf. It gives access to your account until it expires: never share it.',
2305
+ accessTokenDescription: "To test an API on your behalf. Don't share it.",
1751
2306
  accessTokenKeywords: 'jwt bearer api developer',
1752
2307
  copy: 'Copy',
2308
+ moreAbout: 'More about {label}',
1753
2309
  appTokens: 'Application tokens',
1754
- appTokensDescription: 'To use this application outside the interface on your behalf — an MCP, a script. The token is valid only for this application, with the permissions of the chosen access level, and expires in at most 1 year. Send it in the x-app-token header.',
2310
+ appTokensDescription: 'To use the application outside the interface: an MCP, a script.',
1755
2311
  appTokensKeywords: 'token app mcp api key pat x-app-token automation script integration',
2312
+ appTokensHelp: 'The token is valid only for this application, with the permissions of the chosen access level, and expires in at most 1 year. Send it in the x-app-token header.',
1756
2313
  logout: 'Sign out',
1757
2314
  logoutDescription: 'Ends the session in this browser.',
1758
2315
  logoutKeywords: 'logout disconnect',
1759
- security: 'Account and security',
1760
- securityDescription: 'Two-step verification, recovery codes and sessions of your Argos account, the sign-in for the applications.',
1761
- securityKeywords: 'security argos password authentication mfa 2fa devices',
2316
+ mfa: 'MFA',
2317
+ mfaDescription: 'Two-step verification of your Argos account.',
2318
+ mfaKeywords: 'security argos password authentication mfa 2fa two-factor two-step verification',
1762
2319
  signedInAccount: 'Argos account',
1763
2320
  signedInRealm: 'Realm {realm}',
1764
2321
  twoFactor: 'Two-step verification',
1765
- twoFactorDescription: 'Add or remove methods (authenticator app or email) and switch the authenticator to a new device.',
2322
+ twoFactorDescription: 'A code on top of the password to sign in.',
1766
2323
  twoFactorKeywords: 'mfa 2fa totp authenticator code email method device phone',
1767
2324
  recoveryCodes: 'Recovery codes',
1768
- recoveryCodesDescription: 'Use them to sign in if you lose your device. Generating new codes invalidates the previous ones.',
2325
+ recoveryCodesDescription: 'To sign in if you lose your device.',
1769
2326
  recoveryCodesKeywords: 'backup recovery lost phone device mfa 2fa',
1770
2327
  sessions: 'Sessions and devices',
1771
- sessionsDescription: 'See where your account is signed in (browser, system, IP, last used) and end one session or all the others.',
2328
+ sessionsDescription: 'Where your account is signed in.',
1772
2329
  sessionsKeywords: 'devices browser ip end sign out disconnect connected',
2330
+ sessionsSection: 'Sessions',
2331
+ sessionsSectionDescription: 'This session, the application tokens and the signed-in devices.',
2332
+ sessionsSectionKeywords: 'session sessions token tokens devices login security',
1773
2333
  about: 'About',
1774
2334
  aboutKeywords: 'version build environment dependencies support',
1775
2335
  application: 'Application',
@@ -1787,7 +2347,7 @@ const SPINE_MESSAGES_EN = {
1787
2347
  dependenciesDescription: "Versions declared in this application's package.json.",
1788
2348
  dependenciesKeywords: 'angular hermes rxjs apollo graphql',
1789
2349
  supportInfo: 'Support information',
1790
- supportInfoDescription: 'Copies version, environment, browser, page and preferences — paste it into the ticket.',
2350
+ supportInfoDescription: 'Version, environment, browser and preferences, to paste into the ticket.',
1791
2351
  supportInfoKeywords: 'help ticket diagnostics',
1792
2352
  noSessionToken: 'No session token available.',
1793
2353
  tokenCopied: "Token copied. Don't share it: it gives access to your account until it expires.",
@@ -1869,7 +2429,7 @@ const SPINE_MESSAGES_EN = {
1869
2429
  removeHint: "You won't be able to use this method to sign in anymore.",
1870
2430
  removeLastHint: 'With no method left, the recovery codes stop working too.',
1871
2431
  reauthTitle: 'Confirm your identity',
1872
- reauthHint: 'To change two-step verification or generate codes, prove who you are again. It lasts 5 minutes.',
2432
+ reauthHint: 'For security, confirm before continuing. It lasts 5 minutes.',
1873
2433
  reauthHow: 'How to confirm',
1874
2434
  reauthPassword: 'Password',
1875
2435
  reauthRecoveryCode: 'Recovery code',
@@ -1893,13 +2453,13 @@ const SPINE_MESSAGES_EN = {
1893
2453
  sendCode: 'Send code',
1894
2454
  emailCodeTitle: 'Confirm the email',
1895
2455
  emailCodeHint: 'Enter the 6-digit code sent to {email}.',
1896
- codesRemaining: { zero: 'No codes available.', one: '1 code available.', other: '{count} codes available.' },
1897
- codesHint: 'Each one signs you in once if you lose your verification method.',
2456
+ codesRemaining: { zero: 'No codes available', one: '1 code available', other: '{count} codes available' },
2457
+ codesHint: 'Each one works once. Generating new ones invalidates the previous ones.',
1898
2458
  codesNeedMethod: 'Set up a verification method to generate recovery codes.',
1899
2459
  generateCodes: 'Generate codes',
1900
2460
  regenerateCodes: 'Generate new codes',
1901
2461
  codesTitle: 'Your recovery codes',
1902
- codesShownOnce: "Keep these codes somewhere safe. Each one signs you in once if you lose access to your verification method. They won't be shown again.",
2462
+ codesShownOnce: "Keep them somewhere safe: they won't be shown again. Each one works once.",
1903
2463
  copyCodes: 'Copy',
1904
2464
  downloadCodes: 'Download .txt',
1905
2465
  codesCopied: 'Codes copied.',
@@ -1978,6 +2538,7 @@ const SPINE_MESSAGES_EN = {
1978
2538
  signInAgain: 'Sign in again',
1979
2539
  callbackFailedHeading: "Couldn't complete sign-in",
1980
2540
  callbackFailedText: 'The sign-in response was incomplete or expired. Sign in again to continue.',
2541
+ logoutFailed: "Couldn't sign out right now. Check your connection and try again.",
1981
2542
  },
1982
2543
  routing: {
1983
2544
  notPublished: "This address is in your access, but its screen hasn't been published yet.",
@@ -2023,10 +2584,10 @@ const SPINE_MESSAGES_EN = {
2023
2584
  tabRoutes: 'Routes',
2024
2585
  tabRouters: 'Routers',
2025
2586
  tabAccess: 'Access',
2026
- noClients: "You don't manage any client here yet. You need to (a) have been granted access to a client and (b) be signed in with the <strong>Administrator</strong> badge active. Switch badges in your profile menu, if available, or ask a central administrator for access.",
2587
+ noClients: "You don't manage this app. You need to (a) be in its project's administration group and (b) be signed in with the <strong>Administrator</strong> badge active. Switch badges in your profile menu, if available, or ask whoever manages the project for access.",
2027
2588
  pickClient: 'Select the client you want to manage',
2028
2589
  groupsTitle: 'Groups',
2029
- groupsIntro: "Manage the members of this client's groups — without relying on the central panel.",
2590
+ groupsIntro: "Create, edit and delete this client's groups and manage their members — without relying on the central panel.",
2030
2591
  groupsFixed: "The 5 groups below are fixed for this client and can't be renamed, created or deleted — look one up to manage its members.",
2031
2592
  pickGroup: 'Select a group',
2032
2593
  noDescription: 'No description provided.',
@@ -2112,6 +2673,9 @@ const SPINE_MESSAGES_EN = {
2112
2673
  colPage: 'Page',
2113
2674
  colBuiltPath: 'Built path',
2114
2675
  colVisibility: 'Visibility',
2676
+ colAllowedBadges: 'Allowed badges',
2677
+ anyBadge: 'Any badge',
2678
+ allowedBadgesHint: 'Empty = any badge. With one or more chosen, only groups with one of those badges can be given access to this screen in the Access tab.',
2115
2679
  routesTitle: 'Routes',
2116
2680
  newRoute: 'New route',
2117
2681
  routesCaption: 'Client routes',
@@ -2130,6 +2694,19 @@ const SPINE_MESSAGES_EN = {
2130
2694
  pathPlaceholder: 'e.g.: dashboard',
2131
2695
  pathHint: 'A single piece of the address: lowercase letters and hyphens. The router adds the "/" between levels.',
2132
2696
  pathInvalid: 'Use only lowercase letters (a–z) and hyphens — no "/", spaces, digits or accents, and no hyphen at the start or end.',
2697
+ rotateSecret: 'Generate new secret',
2698
+ confirmRotateSecretHeading: 'Generate a new secret?',
2699
+ confirmRotateSecret: 'The current secret stops working right away; update the APP_SECRET of the service that uses this application. Tokens already issued stay valid until they expire (up to 1 hour).',
2700
+ rotatedSecretHeading: 'New secret for {name}',
2701
+ rotatedSecretWarning: "Copy it now: the secret won't be shown again.",
2702
+ rotatedSecretNote: 'The server keeps only a hash of the secret. Once this window is closed there is no way to look it up; if it gets lost, generate another one.',
2703
+ rotatedSecretLabel: 'Secret of application {name}',
2704
+ copySecret: 'Copy',
2705
+ secretCopied: 'Secret copied.',
2706
+ rotatedSecretDone: 'Done',
2707
+ rotateSecretError: "Couldn't generate the new secret. Try again.",
2708
+ rotateSecretNotFound: 'Application not found in this client.',
2709
+ rotateSecretForbidden: "You don't manage this client.",
2133
2710
  confirmDeleteAppHeading: 'Delete application?',
2134
2711
  confirmDeleteApp: 'The application {name} will be deleted and its credential will stop working. This cannot be undone.',
2135
2712
  confirmDeleteRouteHeading: 'Delete route?',
@@ -2164,6 +2741,89 @@ const SPINE_MESSAGES_EN = {
2164
2741
  reorderLoadError: 'Could not load the menu. Try again.',
2165
2742
  reorderSaveError: 'Could not save the order. Nothing was lost: try again.',
2166
2743
  reorderSave: 'Save order',
2744
+ newGroup: 'New group',
2745
+ editGroup: 'Edit group',
2746
+ groupsCaption: 'Project groups',
2747
+ noGroups: 'No groups yet.',
2748
+ colStatus: 'Status',
2749
+ groupName: 'Name',
2750
+ groupType: 'Type',
2751
+ groupTypePlaceholder: 'Select the type',
2752
+ groupDescription: 'Description (optional)',
2753
+ openGroup: 'Open the {name} group',
2754
+ adminGroupTag: 'Administration',
2755
+ adminGroupNote: "The project's administration group: its members manage this app in self-service. It is the only one in the project and can't be deleted, renamed, disabled or change type, nor be left without members.",
2756
+ adminGroupEditNote: "The project's administration group: only the description can change.",
2757
+ adminGroupLastMemberWarning: "The administration group can't be left without members: add someone else or undo a removal.",
2758
+ confirmDeleteGroupHeading: 'Delete group?',
2759
+ confirmDeleteGroup: 'The {name} group will be deleted along with its members, screen access and application permissions. This cannot be undone.',
2760
+ groupSaveError: 'Could not save the group. Try again.',
2761
+ membersSaveError: 'Could not save all member changes. Check the list and try again.',
2762
+ appsCaption: 'Project applications',
2763
+ noApps: 'No applications registered yet.',
2764
+ colGrants: 'Grants',
2765
+ openApp: 'Open the application {name}',
2766
+ tabTokens: 'Tokens',
2767
+ tokensCaption: 'Application tokens',
2768
+ noTokens: 'No tokens created for this application yet.',
2769
+ newToken: 'New token',
2770
+ colTokenPrefix: 'Token prefix',
2771
+ colExpiresAt: 'Expires',
2772
+ colLastUsed: 'Last used',
2773
+ tokenExpired: 'Expired',
2774
+ tokenRevoked: 'Revoked',
2775
+ neverUsed: 'Never',
2776
+ revokeToken: 'Revoke',
2777
+ confirmRevokeTokenHeading: 'Revoke token?',
2778
+ confirmRevokeToken: 'The token {name} stops working right away. Revoking cannot be undone.',
2779
+ tokenRevokedDone: 'Token {name} revoked.',
2780
+ tokenRevokeError: 'Could not revoke the token. Try again.',
2781
+ tokensLoadError: 'Could not load the application tokens.',
2782
+ tokenName: 'Name',
2783
+ tokenDescription: 'Description (optional)',
2784
+ tokenExpiresAt: 'Expires on',
2785
+ tokenExpiresHint: 'From tomorrow up to 365 days from today.',
2786
+ tokenExpiresInvalid: 'Pick a date between tomorrow and 365 days from today.',
2787
+ tokenOperations: 'Operations granted to the token',
2788
+ tokenOperationsNote: 'Check the protected operations the token may call. Public and private ones already apply to it and show as checked.',
2789
+ freeForAll: 'open to everyone',
2790
+ freeForAuthenticated: 'open to authenticated callers',
2791
+ freeNoPermission: 'does not depend on permission',
2792
+ createToken: 'Create token',
2793
+ tokenSaveError: 'Could not create the token. Try again.',
2794
+ tokenCreatedHeading: 'Token {name} created',
2795
+ tokenCreatedWarning: 'Copy it now: the token will not be shown again.',
2796
+ tokenCreatedNote: 'Send it in the x-api-token header. The server keeps only a hash of the token; if it is lost, revoke it and create another.',
2797
+ tokenValueLabel: 'Token {name}',
2798
+ copyToken: 'Copy',
2799
+ tokenCopied: 'Token copied.',
2800
+ hierarchyHint: 'Top-down: Navigation only with a Header, Menu only with a Navigation, Item only with a Menu and Submenu only with an Item. Clearing a level clears the ones below.',
2801
+ routerSaveError: 'Could not save the router. Try again.',
2802
+ permissionsSaveError: 'Could not save all operation changes. Reopen the panel and check.',
2803
+ tokenErrors: {
2804
+ invalidExpiresAt: 'Invalid expiration date. Pick another date.',
2805
+ invalidValidity: 'The expiration must be between tomorrow and 365 days from today.',
2806
+ invalidOperationIds: 'Some selected operation no longer exists in this application. Reopen the form and choose again.',
2807
+ operationNotProtected: 'Only protected operations can be granted to the token; public and private ones already apply to it.',
2808
+ appNotFound: 'Application not found in this client. It may have been deleted.',
2809
+ tokenNotFound: 'Token not found in this application. It may have been revoked already.',
2810
+ },
2811
+ routerErrors: {
2812
+ hierarchyGap: 'The hierarchy has an empty level in the middle: each level needs the one above it (Header → Navigation → Menu → Item → Submenu). Fill in the missing level or clear the ones below.',
2813
+ invalidAllowedTypes: 'A chosen badge no longer exists. Review the allowed badges.',
2814
+ },
2815
+ groupErrors: {
2816
+ invalidType: 'That group type no longer exists. Choose another type.',
2817
+ adminTypeReserved: "The Administrator type belongs only to the project's administration group.",
2818
+ emptyUpdate: 'Nothing to save: no field was changed.',
2819
+ groupProjectImmutable: "A group can't move to another project.",
2820
+ groupNameTaken: 'A group with this name already exists in this project.',
2821
+ adminGroupImmutable: "The administration group can't be renamed, change type or be disabled.",
2822
+ adminGroupProtected: "The project's administration group can't be deleted.",
2823
+ adminGroupLastMember: "The administration group can't be left without members.",
2824
+ adminGroupAccessProtected: "The administration group's access to this app can't be removed.",
2825
+ groupNotFound: 'Group not found in this project. It may have been deleted.',
2826
+ },
2167
2827
  },
2168
2828
  autoServiceAudit: {
2169
2829
  title: 'Audit',
@@ -2333,6 +2993,132 @@ const SPINE_MESSAGES_EN = {
2333
2993
  spansSummary: '{count} spans in {duration}',
2334
2994
  spanRow: '{app}, {operation}: {outcome}, starts at +{offset}, takes {duration}',
2335
2995
  },
2996
+ loginAttempts: {
2997
+ refresh: 'Refresh',
2998
+ user: 'User',
2999
+ userPlaceholder: 'User, e-mail or typed identifier',
3000
+ period: 'Period',
3001
+ result: 'Result',
3002
+ method: 'Method',
3003
+ all: 'All',
3004
+ clear: 'Clear',
3005
+ advancedFilters: 'Advanced filters',
3006
+ ip: 'IP or network',
3007
+ ipPlaceholder: '10.0.0.5 or 10.0.0.0/8',
3008
+ ipHint: 'Exact IP or a network in CIDR notation.',
3009
+ reason: 'Reason',
3010
+ caption: 'Login attempts',
3011
+ pageItems: 'attempts',
3012
+ pageSize: 'Attempts per page',
3013
+ colDate: 'Date/time',
3014
+ colResult: 'Result',
3015
+ colMethod: 'Method',
3016
+ colUser: 'User',
3017
+ colReason: 'Reason',
3018
+ colIp: 'IP',
3019
+ colRealm: 'Realm',
3020
+ colClient: 'Client',
3021
+ colUserAgent: 'Browser',
3022
+ filterByIp: 'Filter by this IP',
3023
+ filterByUser: 'Filter by this user',
3024
+ noMatchingUser: 'no matching user',
3025
+ methods: { local: 'Password', mfa: 'MFA', github: 'GitHub', google: 'Google', keycloak: 'Keycloak', entraid: 'Entra ID' },
3026
+ results: { success: 'Success', failure: 'Failure', cancelled: 'Cancelled' },
3027
+ reasons: {
3028
+ bad_password: 'Wrong password',
3029
+ unknown_user: 'Unknown or inactive user',
3030
+ locked: 'Account locked after failed attempts',
3031
+ mfa_failed: 'Wrong verification code',
3032
+ mfa_expired: 'Expired verification code',
3033
+ mfa_not_allowed: 'Verification method not allowed',
3034
+ mfa_invalid_method: 'Unknown verification method',
3035
+ recovery_failed: 'Recovery code wrong or already used',
3036
+ recovery_refused: 'Recovery code refused',
3037
+ not_linked: 'External account not linked',
3038
+ wrong_realm: 'User from another realm',
3039
+ },
3040
+ forbiddenAdmin: 'Your account cannot read login attempts. Ask an administrator for the admin:attempts:list operation.',
3041
+ forbiddenClient: 'Only the project administration group, with the Administrator badge active, sees the login attempts of this application.',
3042
+ loadErrorDetail: 'Could not load the attempts: {detail}',
3043
+ loadError: 'Could not load the login attempts.',
3044
+ },
3045
+ accountFeatures: {
3046
+ aiAgent: 'Connect an AI agent',
3047
+ aiAgentDescription: 'Generates the token and the MCP configuration to paste into Claude Code.',
3048
+ aiAgentKeywords: 'ai agent claude mcp assistant llm automation',
3049
+ aiAgentHelp: 'The agent uses a personal application token (x-app-token) with the permissions of the chosen access level. Revoke it under "Application tokens" when you no longer need it.',
3050
+ aiAgentStart: 'Connect agent',
3051
+ aiAgentServers: 'MCP servers',
3052
+ aiAgentAccess: 'Agent access',
3053
+ aiAgentReadOnly: 'Read only',
3054
+ aiAgentReadWrite: 'Read and write',
3055
+ aiAgentWriteHint: 'The agent will be able to change data on your behalf. Prefer read only when it is enough.',
3056
+ aiAgentReadOnlyServer: '{name} (read only)',
3057
+ aiAgentTokenName: 'AI agent ({date})',
3058
+ aiAgentGenerate: 'Generate configuration',
3059
+ aiAgentSelectServer: 'Choose at least one server.',
3060
+ aiAgentResult: 'Configuration ready',
3061
+ aiAgentResultHint: 'Paste into .mcp.json at the project root (Claude Code). The token is shown only now: copy it before finishing. Valid until {date}.',
3062
+ aiAgentCopy: 'Copy configuration',
3063
+ aiAgentCopied: 'Configuration copied. Do not share it: it contains your token.',
3064
+ aiAgentDone: 'Finish',
3065
+ aiAgentCreateError: 'Could not generate the agent token.',
3066
+ aiAgentUnavailable: 'Unavailable in this application: it is not connected to argos-admin.',
3067
+ activity: 'Account activity',
3068
+ activityDescription: 'Sign-ins and failed attempts in the last 90 days.',
3069
+ activityKeywords: 'login sign-in access history attempt failure ip browser security',
3070
+ activityFilter: 'Show',
3071
+ activityAll: 'All',
3072
+ activityFailures: 'Failures only',
3073
+ activityDetail: '{date} · IP {ip}',
3074
+ activityApp: 'Application: {app}',
3075
+ activityEmpty: 'Nothing in the last 90 days.',
3076
+ activityMore: 'Load more',
3077
+ activityShowing: '{shown} of {total}',
3078
+ activityLoadError: 'Could not load the account activity.',
3079
+ unknownIp: 'unknown',
3080
+ unknownApp: 'unnamed',
3081
+ idle: 'Sign out when idle',
3082
+ idleDescription: 'Signs out by itself after a while without use, with a warning first.',
3083
+ idleKeywords: 'idle inactivity timeout sign out automatic logout security lock',
3084
+ idleNever: 'Never',
3085
+ idleOption: '{minutes} min',
3086
+ idleLimitHint: 'This application signs out after at most {minutes} min without use.',
3087
+ idleWarningHeading: 'Your session is about to end',
3088
+ idleWarningText: { one: 'No activity for a while. The session ends in {count} second.', other: 'No activity for a while. The session ends in {count} seconds.' },
3089
+ idleStay: 'Stay signed in',
3090
+ idleLogout: 'Sign out now',
3091
+ startPage: 'Start page',
3092
+ startPageDescription: 'Where the application opens after signing in.',
3093
+ startPageKeywords: 'home opening login favorite last page',
3094
+ startHome: 'Home',
3095
+ startLast: 'Last visited page',
3096
+ startFavorite: 'Favorite: {name}',
3097
+ startPageHint: 'Mark pages as favorites to open on one of them.',
3098
+ defaultBadge: 'Default badge',
3099
+ defaultBadgeDescription: 'Access level you sign in with in this application, in any browser.',
3100
+ defaultBadgeKeywords: 'badge access level profile default login',
3101
+ defaultBadgeAuto: 'Automatic (the lowest level)',
3102
+ defaultBadgeSaved: 'Default badge saved. It applies from the next sign-in.',
3103
+ defaultBadgeError: 'Could not save the default badge.',
3104
+ defaultBadgeLoadError: 'Could not load your badges.',
3105
+ notices: 'Notifications',
3106
+ noticesDescription: 'The notices this application shows.',
3107
+ noticesKeywords: 'notification notice alert browser push',
3108
+ browserNotices: 'Browser notifications',
3109
+ browserNoticesDescription: 'System notification when the tab is in the background.',
3110
+ browserNoticesDenied: 'The browser blocked notifications for this site. Allow them in the browser settings.',
3111
+ browserNoticesUnsupported: 'This browser does not show notifications.',
3112
+ noticeOpen: 'Open',
3113
+ sync: 'Preferences in your account',
3114
+ syncDescription: 'Appearance, region, favorites and the other preferences follow you in any browser.',
3115
+ syncKeywords: 'sync cloud account browser preferences backup',
3116
+ syncSynced: 'Synced at {time}',
3117
+ syncSaving: 'Saving…',
3118
+ syncLocal: 'Only in this browser: the account is not available.',
3119
+ syncError: 'Could not sync. Try again.',
3120
+ syncNow: 'Sync now',
3121
+ },
2336
3122
  };
2337
3123
 
2338
3124
  const SPINE_MESSAGES_ES = {
@@ -2457,7 +3243,7 @@ const SPINE_MESSAGES_ES = {
2457
3243
  appearance: 'Apariencia',
2458
3244
  appearanceKeywords: 'visual colores tema diseño',
2459
3245
  theme: 'Tema',
2460
- themeDescription: 'Claro, oscuro o siguiendo el sistema operativo.',
3246
+ themeDescription: 'Claro, oscuro o el del sistema operativo.',
2461
3247
  themeKeywords: 'modo oscuro dark light nocturno',
2462
3248
  themeLight: 'Claro',
2463
3249
  themeDark: 'Oscuro',
@@ -2466,7 +3252,7 @@ const SPINE_MESSAGES_ES = {
2466
3252
  accentDescription: 'Familia de colores del encabezado, los botones y los destacados.',
2467
3253
  accentKeywords: 'familia paleta color acento',
2468
3254
  density: 'Densidad',
2469
- densityDescription: 'Compacta acerca filas y controles para que quepa más información en la pantalla.',
3255
+ densityDescription: 'Compacta muestra más información en pantalla.',
2470
3256
  densityKeywords: 'espaciado tamaño compacto',
2471
3257
  comfortable: 'Cómoda',
2472
3258
  compact: 'Compacta',
@@ -2474,64 +3260,121 @@ const SPINE_MESSAGES_ES = {
2474
3260
  fontDescription: 'Tipografía de toda la aplicación.',
2475
3261
  fontKeywords: 'tipografía letra texto roboto rubik figtree geist',
2476
3262
  sidebar: 'Menú lateral',
2477
- sidebarDescription: 'Automático se abre al acercar el puntero; fijo queda siempre abierto; compacto muestra solo los íconos.',
3263
+ sidebarDescription: 'Cómo se abre y se cierra el menú lateral.',
2478
3264
  sidebarKeywords: 'sidebar navegación barra lateral',
3265
+ sidebarHelp: 'Automático: se abre al acercar el puntero. Fijo: siempre abierto. Compacto: muestra solo los íconos.',
2479
3266
  sidebarAuto: 'Automático',
2480
3267
  sidebarFixed: 'Fijo',
2481
3268
  sidebarCompact: 'Compacto',
2482
3269
  region: 'Región',
2483
- regionKeywords: 'idioma localización horario fecha',
3270
+ regionKeywords: 'idioma localización horario fecha hora semana',
2484
3271
  language: 'Idioma',
2485
- languageDescription: 'Idioma de los textos del sistema. Se aplica al instante, sin recargar.',
3272
+ languageDescription: 'Se aplica al instante, sin recargar.',
2486
3273
  languageKeywords: 'idioma lengua traducción inglés español portugués',
2487
3274
  languageAuto: 'Predeterminado del sistema ({language})',
2488
3275
  timeZone: 'Zona horaria',
2489
3276
  timeZoneKeywords: 'timezone hora utc gmt',
2490
- timeZoneHint: 'Se usa para mostrar fechas y horas. Ahora: {now}',
3277
+ timeZoneHint: 'Para fechas y horas. Ahora: {now}',
2491
3278
  timeZoneAuto: 'Automático ({zone})',
3279
+ hourCycle: 'Formato de hora',
3280
+ hourCycleHint: 'Ahora: {now}',
3281
+ hourCycleKeywords: 'reloj 24h 12h am pm horario',
3282
+ hourCycleAuto: 'Automático',
3283
+ hourCycle24: '24 horas',
3284
+ hourCycle12: '12 horas',
3285
+ weekStart: 'Primer día de la semana',
3286
+ weekStartDescription: 'Para calendarios y selectores de fecha.',
3287
+ weekStartKeywords: 'semana calendario domingo lunes',
3288
+ weekStartAuto: 'Automático',
2492
3289
  shortcuts: 'Atajos',
2493
3290
  shortcutsDescription: 'Pulsa ? en cualquier pantalla para ver esta lista.',
2494
- shortcutsKeywords: 'teclado teclas recorrido ayuda',
3291
+ shortcutsKeywords: 'teclado teclas',
2495
3292
  tour: 'Recorrido guiado',
2496
- tourDescription: 'Muestra de nuevo, paso a paso, dónde está cada parte de la pantalla.',
3293
+ tourDescription: 'Muestra de nuevo dónde está cada parte de la pantalla.',
2497
3294
  tourKeywords: 'onboarding ayuda presentación',
2498
3295
  replayTour: 'Repetir recorrido',
3296
+ accessibility: 'Accesibilidad',
3297
+ accessibilityKeywords: 'accesibilidad lectura visión',
3298
+ textSize: 'Tamaño del texto',
3299
+ textSizeDescription: 'Agranda el texto y los controles de todas las pantallas.',
3300
+ textSizeKeywords: 'zoom fuente letra más grande ampliar',
3301
+ motion: 'Reducir animaciones',
3302
+ motionDescription: 'Sistema sigue la computadora o el celular.',
3303
+ motionKeywords: 'animación movimiento transición reducido mareo',
3304
+ motionSystem: 'Sistema',
3305
+ motionReduce: 'Siempre',
3306
+ motionFull: 'Nunca',
3307
+ help: 'Ayuda',
3308
+ helpKeywords: 'ayuda novedades recorrido presentación',
3309
+ news: 'Novedades',
3310
+ newsDescription: 'Lo que cambió en las últimas versiones.',
3311
+ newsKeywords: 'novedades versión lanzamiento changelog',
3312
+ showNews: 'Ver novedades',
3313
+ browserData: 'Datos del navegador',
3314
+ browserDataDescription: 'Guardados solo en este navegador, para tu cuenta.',
3315
+ browserDataKeywords: 'privacidad limpiar borrar historial datos',
3316
+ recentPages: 'Páginas recientes',
3317
+ recentPagesKeywords: 'historial visitadas recientes',
3318
+ recentSearches: 'Búsquedas recientes',
3319
+ recentSearchesKeywords: 'búsqueda historial',
3320
+ favorites: 'Favoritos',
3321
+ favoritesKeywords: 'favoritos estrella',
3322
+ pagesCount: { zero: 'Ninguna página guardada', one: '{count} página guardada', other: '{count} páginas guardadas' },
3323
+ searchesCount: { zero: 'Ninguna búsqueda guardada', one: '{count} búsqueda guardada', other: '{count} búsquedas guardadas' },
3324
+ clear: 'Limpiar',
3325
+ confirmClear: 'Confirmar',
3326
+ cancel: 'Cancelar',
3327
+ recentPagesCleared: 'Páginas recientes borradas.',
3328
+ recentSearchesCleared: 'Búsquedas recientes borradas.',
3329
+ favoritesCleared: 'Favoritos borrados.',
3330
+ restorePreferences: 'Preferencias',
3331
+ restorePreferencesDescription: 'Apariencia, accesibilidad y formatos vuelven a los valores predeterminados.',
3332
+ restorePreferencesHelp: 'Vuelven a lo predeterminado: tema, color principal, densidad, fuente, menú lateral, tamaño del texto, animaciones, formato de hora y primer día de la semana. El idioma, la zona horaria y los atajos de una sola tecla se mantienen.',
3333
+ restorePreferencesKeywords: 'restaurar predeterminado restablecer reset preferencias',
3334
+ restore: 'Restaurar',
3335
+ confirmRestore: 'Confirmar',
3336
+ preferencesRestored: 'Preferencias restauradas.',
2499
3337
  account: 'Cuenta',
2500
- accountKeywords: 'perfil usuario sesión login',
3338
+ accountKeywords: 'perfil usuario login nivel salir',
2501
3339
  user: 'Usuario',
2502
3340
  accessLevel: 'Nivel de acceso',
2503
3341
  accessLevelKeywords: 'credencial badge perfil permiso grupo',
2504
3342
  session: 'Sesión',
2505
- sessionDescription: 'La sesión se renueva sola mientras la aplicación esté abierta.',
3343
+ sessionDescription: 'Se renueva sola mientras la aplicación esté abierta.',
2506
3344
  sessionKeywords: 'expiración token login',
2507
3345
  sessionUnknown: 'No informado',
2508
3346
  sessionExpired: 'Expiró a las {time}',
2509
3347
  sessionExpiresIn: 'Expira en {minutes} min (a las {time})',
2510
3348
  sessionExpiresAt: 'Expira a las {time}',
2511
3349
  accessToken: 'Token de acceso',
2512
- accessTokenDescription: 'Para probar una API en tu nombre. Da acceso a tu cuenta hasta que expire: nunca lo compartas.',
3350
+ accessTokenDescription: 'Para probar una API en tu nombre. No lo compartas.',
2513
3351
  accessTokenKeywords: 'jwt bearer api desarrollador',
2514
3352
  copy: 'Copiar',
3353
+ moreAbout: 'Más sobre {label}',
2515
3354
  appTokens: 'Tokens de aplicación',
2516
- appTokensDescription: 'Para usar esta aplicación fuera de la interfaz en tu nombre — un MCP, un script. El token vale solo para esta aplicación, con los permisos del nivel de acceso elegido, y expira en un máximo de 1 año. Envíalo en el encabezado x-app-token.',
3355
+ appTokensDescription: 'Para usar la aplicación fuera de la interfaz: un MCP, un script.',
2517
3356
  appTokensKeywords: 'token app mcp api clave pat x-app-token automatización script integración',
3357
+ appTokensHelp: 'El token vale solo para esta aplicación, con los permisos del nivel de acceso elegido, y vence en como máximo 1 año. Envíalo en el encabezado x-app-token.',
2518
3358
  logout: 'Salir',
2519
3359
  logoutDescription: 'Cierra la sesión en este navegador.',
2520
3360
  logoutKeywords: 'logout desconectar',
2521
- security: 'Cuenta y seguridad',
2522
- securityDescription: 'Verificación en dos pasos, códigos de recuperación y sesiones de tu cuenta de Argos, el inicio de sesión de las aplicaciones.',
2523
- securityKeywords: 'seguridad argos contraseña autenticación mfa 2fa dispositivos',
3361
+ mfa: 'MFA',
3362
+ mfaDescription: 'Verificación en dos pasos de tu cuenta en Argos.',
3363
+ mfaKeywords: 'seguridad argos contraseña autenticación mfa 2fa dos factores dos pasos verificación',
2524
3364
  signedInAccount: 'Cuenta de Argos',
2525
3365
  signedInRealm: 'Realm {realm}',
2526
3366
  twoFactor: 'Verificación en dos pasos',
2527
- twoFactorDescription: 'Agrega o quita métodos (aplicación de autenticación o correo electrónico) y cambia el dispositivo del autenticador.',
3367
+ twoFactorDescription: 'Un código además de la contraseña para entrar.',
2528
3368
  twoFactorKeywords: 'mfa 2fa totp autenticador authenticator código correo método dispositivo celular',
2529
3369
  recoveryCodes: 'Códigos de recuperación',
2530
- recoveryCodesDescription: 'Sirven para iniciar sesión si pierdes el dispositivo. Generar códigos nuevos invalida los anteriores.',
3370
+ recoveryCodesDescription: 'Para entrar si pierdes el dispositivo.',
2531
3371
  recoveryCodesKeywords: 'respaldo recuperación perdí celular dispositivo mfa 2fa',
2532
3372
  sessions: 'Sesiones y dispositivos',
2533
- sessionsDescription: 'Ve dónde está conectada tu cuenta (navegador, sistema, IP, último uso) y cierra una sesión o todas las demás.',
3373
+ sessionsDescription: 'Dónde está conectada tu cuenta.',
2534
3374
  sessionsKeywords: 'dispositivos navegador ip cerrar desconectar salir conectados',
3375
+ sessionsSection: 'Sesiones',
3376
+ sessionsSectionDescription: 'Esta sesión, los tokens de aplicación y los dispositivos conectados.',
3377
+ sessionsSectionKeywords: 'sesión sesiones token tokens dispositivos login seguridad',
2535
3378
  about: 'Acerca de',
2536
3379
  aboutKeywords: 'versión build entorno dependencias soporte',
2537
3380
  application: 'Aplicación',
@@ -2549,7 +3392,7 @@ const SPINE_MESSAGES_ES = {
2549
3392
  dependenciesDescription: 'Versiones declaradas en el package.json de esta aplicación.',
2550
3393
  dependenciesKeywords: 'angular hermes rxjs apollo graphql',
2551
3394
  supportInfo: 'Información para soporte',
2552
- supportInfoDescription: 'Copia versión, entorno, navegador, página y preferencias — pégala en el ticket.',
3395
+ supportInfoDescription: 'Versión, entorno, navegador y preferencias, para pegar en el ticket.',
2553
3396
  supportInfoKeywords: 'ayuda ticket diagnóstico',
2554
3397
  noSessionToken: 'No hay ningún token de sesión disponible.',
2555
3398
  tokenCopied: 'Token copiado. No lo compartas: da acceso a tu cuenta hasta que expire.',
@@ -2631,7 +3474,7 @@ const SPINE_MESSAGES_ES = {
2631
3474
  removeHint: 'Ya no podrás usar este método para iniciar sesión.',
2632
3475
  removeLastHint: 'Sin ningún método, los códigos de recuperación también dejan de valer.',
2633
3476
  reauthTitle: 'Confirma tu identidad',
2634
- reauthHint: 'Para cambiar la verificación en dos pasos o generar códigos, demuestra de nuevo quién eres. Vale por 5 minutos.',
3477
+ reauthHint: 'Por seguridad, confirma antes de continuar. Vale por 5 minutos.',
2635
3478
  reauthHow: 'Cómo confirmar',
2636
3479
  reauthPassword: 'Contraseña',
2637
3480
  reauthRecoveryCode: 'Código de recuperación',
@@ -2655,13 +3498,13 @@ const SPINE_MESSAGES_ES = {
2655
3498
  sendCode: 'Enviar código',
2656
3499
  emailCodeTitle: 'Confirma el correo electrónico',
2657
3500
  emailCodeHint: 'Escribe el código de 6 dígitos enviado a {email}.',
2658
- codesRemaining: { zero: 'Ningún código disponible.', one: '1 código disponible.', other: '{count} códigos disponibles.' },
2659
- codesHint: 'Cada uno vale una vez para iniciar sesión si pierdes tu método de verificación.',
3501
+ codesRemaining: { zero: 'Ningún código disponible', one: '1 código disponible', other: '{count} códigos disponibles' },
3502
+ codesHint: 'Cada uno vale una vez. Generar nuevos invalida los anteriores.',
2660
3503
  codesNeedMethod: 'Registra un método de verificación para generar códigos de recuperación.',
2661
3504
  generateCodes: 'Generar códigos',
2662
3505
  regenerateCodes: 'Generar códigos nuevos',
2663
3506
  codesTitle: 'Tus códigos de recuperación',
2664
- codesShownOnce: 'Guarda estos códigos en un lugar seguro. Cada uno vale una sola vez para iniciar sesión si pierdes el acceso a tu método de verificación. No se volverán a mostrar.',
3507
+ codesShownOnce: 'Guárdalos en un lugar seguro: no se volverán a mostrar. Cada uno vale una vez.',
2665
3508
  copyCodes: 'Copiar',
2666
3509
  downloadCodes: 'Descargar .txt',
2667
3510
  codesCopied: 'Códigos copiados.',
@@ -2740,6 +3583,7 @@ const SPINE_MESSAGES_ES = {
2740
3583
  signInAgain: 'Iniciar sesión de nuevo',
2741
3584
  callbackFailedHeading: 'No se pudo completar el inicio de sesión',
2742
3585
  callbackFailedText: 'La respuesta del inicio de sesión llegó incompleta o caducó. Vuelve a iniciar sesión para continuar.',
3586
+ logoutFailed: 'No se pudo cerrar la sesión ahora. Verifica la conexión e inténtalo de nuevo.',
2743
3587
  },
2744
3588
  routing: {
2745
3589
  notPublished: 'Esta dirección está en tu acceso, pero su pantalla todavía no fue publicada.',
@@ -2785,10 +3629,10 @@ const SPINE_MESSAGES_ES = {
2785
3629
  tabRoutes: 'Rutas',
2786
3630
  tabRouters: 'Enrutadores',
2787
3631
  tabAccess: 'Acceso',
2788
- noClients: 'Todavía no administras ningún client aquí. Necesitas (a) tener acceso concedido a un client y (b) haber iniciado sesión con la credencial <strong>Administrador</strong> activa. Cambia de credencial en el menú de tu perfil, si está disponible, o pide acceso a un administrador central.',
3632
+ noClients: 'No administras esta app. Necesitas (a) estar en el grupo de administración de su proyecto y (b) haber iniciado sesión con la credencial <strong>Administrador</strong> activa. Cambia de credencial en el menú de tu perfil, si está disponible, o pide acceso a quien administra el proyecto.',
2789
3633
  pickClient: 'Selecciona el client que quieres administrar',
2790
3634
  groupsTitle: 'Grupos',
2791
- groupsIntro: 'Gestiona los miembros de los grupos de este client — sin depender del panel central.',
3635
+ groupsIntro: 'Crea, edita y elimina los grupos de este client y gestiona sus miembros — sin depender del panel central.',
2792
3636
  groupsFixed: 'Los 5 grupos de abajo son fijos de este client y no se pueden renombrar, crear ni eliminar — consulta uno para gestionar sus miembros.',
2793
3637
  pickGroup: 'Selecciona un grupo',
2794
3638
  noDescription: 'Sin descripción registrada.',
@@ -2874,6 +3718,9 @@ const SPINE_MESSAGES_ES = {
2874
3718
  colPage: 'Página',
2875
3719
  colBuiltPath: 'Ruta armada',
2876
3720
  colVisibility: 'Visibilidad',
3721
+ colAllowedBadges: 'Credenciales permitidas',
3722
+ anyBadge: 'Cualquier credencial',
3723
+ allowedBadgesHint: 'Vacío = cualquier credencial. Con una o más elegidas, solo los grupos con una de esas credenciales pueden recibir acceso a esta pantalla en la pestaña Acceso.',
2877
3724
  routesTitle: 'Rutas',
2878
3725
  newRoute: 'Nueva ruta',
2879
3726
  routesCaption: 'Rutas del client',
@@ -2892,6 +3739,19 @@ const SPINE_MESSAGES_ES = {
2892
3739
  pathPlaceholder: 'ej.: dashboard',
2893
3740
  pathHint: 'Un solo tramo de la dirección: letras minúsculas y guion. El "/" entre niveles lo arma el enrutador.',
2894
3741
  pathInvalid: 'Usa solo letras minúsculas (a–z) y guion — sin "/", espacios, números ni acentos, y sin guion al inicio o al final.',
3742
+ rotateSecret: 'Generar nuevo secreto',
3743
+ confirmRotateSecretHeading: '¿Generar un nuevo secreto?',
3744
+ confirmRotateSecret: 'El secreto actual deja de funcionar de inmediato; actualice el APP_SECRET del servicio que usa esta aplicación. Los tokens ya emitidos valen hasta que expiren (hasta 1 hora).',
3745
+ rotatedSecretHeading: 'Nuevo secreto de {name}',
3746
+ rotatedSecretWarning: 'Cópielo ahora: el secreto no se volverá a mostrar.',
3747
+ rotatedSecretNote: 'El servidor guarda solo un hash del secreto. Al cerrar esta ventana no hay forma de consultarlo; si se pierde, genere otro.',
3748
+ rotatedSecretLabel: 'Secreto de la aplicación {name}',
3749
+ copySecret: 'Copiar',
3750
+ secretCopied: 'Secreto copiado.',
3751
+ rotatedSecretDone: 'Listo',
3752
+ rotateSecretError: 'No se pudo generar el nuevo secreto. Inténtelo de nuevo.',
3753
+ rotateSecretNotFound: 'Aplicación no encontrada en este client.',
3754
+ rotateSecretForbidden: 'Usted no administra este client.',
2895
3755
  confirmDeleteAppHeading: '¿Eliminar aplicación?',
2896
3756
  confirmDeleteApp: 'La aplicación {name} se eliminará y su credencial dejará de funcionar. Esta acción no se puede deshacer.',
2897
3757
  confirmDeleteRouteHeading: '¿Eliminar ruta?',
@@ -2926,6 +3786,89 @@ const SPINE_MESSAGES_ES = {
2926
3786
  reorderLoadError: 'No se pudo cargar el menú. Inténtelo de nuevo.',
2927
3787
  reorderSaveError: 'No se pudo guardar el orden. No se perdió nada: inténtelo de nuevo.',
2928
3788
  reorderSave: 'Guardar orden',
3789
+ newGroup: 'Nuevo grupo',
3790
+ editGroup: 'Editar grupo',
3791
+ groupsCaption: 'Grupos del proyecto',
3792
+ noGroups: 'Aún no hay grupos registrados.',
3793
+ colStatus: 'Estado',
3794
+ groupName: 'Nombre',
3795
+ groupType: 'Tipo',
3796
+ groupTypePlaceholder: 'Selecciona el tipo',
3797
+ groupDescription: 'Descripción (opcional)',
3798
+ openGroup: 'Abrir el grupo {name}',
3799
+ adminGroupTag: 'Administración',
3800
+ adminGroupNote: 'Grupo de administración del proyecto: sus miembros administran esta app en el autoservicio. Es el único del proyecto, no se puede eliminar, renombrar, desactivar ni cambiar de tipo, y no puede quedarse sin miembros.',
3801
+ adminGroupEditNote: 'Grupo de administración del proyecto: solo se puede cambiar la descripción.',
3802
+ adminGroupLastMemberWarning: 'El grupo de administración no puede quedarse sin miembros: agrega a otra persona o deshaz una eliminación.',
3803
+ confirmDeleteGroupHeading: '¿Eliminar grupo?',
3804
+ confirmDeleteGroup: 'El grupo {name} se eliminará junto con sus miembros, los accesos a pantallas y los permisos en aplicaciones. Esta acción no se puede deshacer.',
3805
+ groupSaveError: 'No se pudo guardar el grupo. Inténtalo de nuevo.',
3806
+ membersSaveError: 'No se pudieron guardar todos los cambios de los miembros. Revisa la lista e inténtalo de nuevo.',
3807
+ appsCaption: 'Aplicaciones del proyecto',
3808
+ noApps: 'Ninguna aplicación registrada todavía.',
3809
+ colGrants: 'Grants',
3810
+ openApp: 'Abrir la aplicación {name}',
3811
+ tabTokens: 'Tokens',
3812
+ tokensCaption: 'Tokens de la aplicación',
3813
+ noTokens: 'Ningún token creado para esta aplicación todavía.',
3814
+ newToken: 'Nuevo token',
3815
+ colTokenPrefix: 'Inicio del token',
3816
+ colExpiresAt: 'Vence',
3817
+ colLastUsed: 'Último uso',
3818
+ tokenExpired: 'Vencido',
3819
+ tokenRevoked: 'Revocado',
3820
+ neverUsed: 'Nunca',
3821
+ revokeToken: 'Revocar',
3822
+ confirmRevokeTokenHeading: '¿Revocar token?',
3823
+ confirmRevokeToken: 'El token {name} deja de funcionar de inmediato. La revocación no se puede deshacer.',
3824
+ tokenRevokedDone: 'Token {name} revocado.',
3825
+ tokenRevokeError: 'No se pudo revocar el token. Inténtalo de nuevo.',
3826
+ tokensLoadError: 'No se pudieron cargar los tokens de la aplicación.',
3827
+ tokenName: 'Nombre',
3828
+ tokenDescription: 'Descripción (opcional)',
3829
+ tokenExpiresAt: 'Vence el',
3830
+ tokenExpiresHint: 'Desde mañana hasta 365 días a partir de hoy.',
3831
+ tokenExpiresInvalid: 'Elige una fecha entre mañana y 365 días a partir de hoy.',
3832
+ tokenOperations: 'Operaciones concedidas al token',
3833
+ tokenOperationsNote: 'Marca las operaciones protected que el token puede llamar. Las public y private ya valen para él y aparecen marcadas.',
3834
+ freeForAll: 'abierta a todos',
3835
+ freeForAuthenticated: 'abierta a autenticados',
3836
+ freeNoPermission: 'no depende de permiso',
3837
+ createToken: 'Crear token',
3838
+ tokenSaveError: 'No se pudo crear el token. Inténtalo de nuevo.',
3839
+ tokenCreatedHeading: 'Token {name} creado',
3840
+ tokenCreatedWarning: 'Cópialo ahora: el token no se mostrará de nuevo.',
3841
+ tokenCreatedNote: 'Envíalo en el encabezado x-api-token. El servidor guarda solo un hash del token; si se pierde, revócalo y crea otro.',
3842
+ tokenValueLabel: 'Token {name}',
3843
+ copyToken: 'Copiar',
3844
+ tokenCopied: 'Token copiado.',
3845
+ hierarchyHint: 'De arriba hacia abajo: Navegación solo con Header, Menú solo con Navegación, Ítem solo con Menú y Submenú solo con Ítem. Borrar un nivel borra los de abajo.',
3846
+ routerSaveError: 'No se pudo guardar el enrutador. Inténtalo de nuevo.',
3847
+ permissionsSaveError: 'No se pudieron guardar todos los cambios de las operaciones. Vuelve a abrir el panel y revisa.',
3848
+ tokenErrors: {
3849
+ invalidExpiresAt: 'Fecha de vencimiento no válida. Elige otra fecha.',
3850
+ invalidValidity: 'El vencimiento debe estar entre mañana y 365 días a partir de hoy.',
3851
+ invalidOperationIds: 'Alguna operación elegida ya no existe en esta aplicación. Vuelve a abrir el formulario y elige de nuevo.',
3852
+ operationNotProtected: 'Solo las operaciones protected se pueden conceder al token; las public y private ya valen para él.',
3853
+ appNotFound: 'Aplicación no encontrada en este client. Puede haber sido eliminada.',
3854
+ tokenNotFound: 'Token no encontrado en esta aplicación. Puede que ya haya sido revocado.',
3855
+ },
3856
+ routerErrors: {
3857
+ hierarchyGap: 'La jerarquía tiene un nivel vacío en el medio: cada nivel necesita el de arriba (Header → Navegación → Menú → Ítem → Submenú). Completa el nivel que falta o borra los de abajo.',
3858
+ invalidAllowedTypes: 'Alguna credencial elegida ya no existe. Revisa las credenciales permitidas.',
3859
+ },
3860
+ groupErrors: {
3861
+ invalidType: 'Ese tipo de grupo ya no existe. Elige otro tipo.',
3862
+ adminTypeReserved: 'El tipo Administrador es solo del grupo de administración del proyecto.',
3863
+ emptyUpdate: 'Nada que guardar: no se cambió ningún campo.',
3864
+ groupProjectImmutable: 'El grupo no cambia de proyecto.',
3865
+ groupNameTaken: 'Ya existe un grupo con ese nombre en este proyecto.',
3866
+ adminGroupImmutable: 'El grupo de administración no cambia de nombre ni de tipo, ni se desactiva.',
3867
+ adminGroupProtected: 'El grupo de administración del proyecto no se puede eliminar.',
3868
+ adminGroupLastMember: 'El grupo de administración no puede quedarse sin miembros.',
3869
+ adminGroupAccessProtected: 'El acceso del grupo de administración a esta app no se puede quitar.',
3870
+ groupNotFound: 'Grupo no encontrado en este proyecto. Puede que se haya eliminado.',
3871
+ },
2929
3872
  },
2930
3873
  autoServiceAudit: {
2931
3874
  title: 'Auditoría',
@@ -3095,6 +4038,132 @@ const SPINE_MESSAGES_ES = {
3095
4038
  spansSummary: '{count} spans en {duration}',
3096
4039
  spanRow: '{app}, {operation}: {outcome}, empieza en +{offset}, dura {duration}',
3097
4040
  },
4041
+ loginAttempts: {
4042
+ refresh: 'Actualizar',
4043
+ user: 'Usuario',
4044
+ userPlaceholder: 'Usuario, correo o identificador escrito',
4045
+ period: 'Período',
4046
+ result: 'Resultado',
4047
+ method: 'Método',
4048
+ all: 'Todos',
4049
+ clear: 'Limpiar',
4050
+ advancedFilters: 'Filtros avanzados',
4051
+ ip: 'IP o red',
4052
+ ipPlaceholder: '10.0.0.5 o 10.0.0.0/8',
4053
+ ipHint: 'IP exacta o red en notación CIDR.',
4054
+ reason: 'Motivo',
4055
+ caption: 'Intentos de inicio de sesión',
4056
+ pageItems: 'intentos',
4057
+ pageSize: 'Intentos por página',
4058
+ colDate: 'Fecha/hora',
4059
+ colResult: 'Resultado',
4060
+ colMethod: 'Método',
4061
+ colUser: 'Usuario',
4062
+ colReason: 'Motivo',
4063
+ colIp: 'IP',
4064
+ colRealm: 'Realm',
4065
+ colClient: 'Client',
4066
+ colUserAgent: 'Navegador',
4067
+ filterByIp: 'Filtrar por esta IP',
4068
+ filterByUser: 'Filtrar por este usuario',
4069
+ noMatchingUser: 'sin usuario correspondiente',
4070
+ methods: { local: 'Contraseña', mfa: 'MFA', github: 'GitHub', google: 'Google', keycloak: 'Keycloak', entraid: 'Entra ID' },
4071
+ results: { success: 'Éxito', failure: 'Fallo', cancelled: 'Cancelado' },
4072
+ reasons: {
4073
+ bad_password: 'Contraseña incorrecta',
4074
+ unknown_user: 'Usuario inexistente o inactivo',
4075
+ locked: 'Cuenta bloqueada por intentos',
4076
+ mfa_failed: 'Código de verificación incorrecto',
4077
+ mfa_expired: 'Código de verificación vencido',
4078
+ mfa_not_allowed: 'Método de verificación no permitido',
4079
+ mfa_invalid_method: 'Método de verificación desconocido',
4080
+ recovery_failed: 'Código de recuperación incorrecto o ya usado',
4081
+ recovery_refused: 'Código de recuperación rechazado',
4082
+ not_linked: 'Cuenta externa no vinculada',
4083
+ wrong_realm: 'Usuario de otro realm',
4084
+ },
4085
+ forbiddenAdmin: 'Tu cuenta no puede consultar los intentos de inicio de sesión. Pide a un administrador la operación admin:attempts:list.',
4086
+ forbiddenClient: 'Solo el grupo de administración del proyecto, con la insignia Administrador activa, ve los intentos de inicio de sesión de esta aplicación.',
4087
+ loadErrorDetail: 'No se pudieron cargar los intentos: {detail}',
4088
+ loadError: 'No se pudieron cargar los intentos de inicio de sesión.',
4089
+ },
4090
+ accountFeatures: {
4091
+ aiAgent: 'Conectar un agente de IA',
4092
+ aiAgentDescription: 'Genera el token y la configuración MCP para pegar en Claude Code.',
4093
+ aiAgentKeywords: 'ia agente claude mcp asistente llm automatización',
4094
+ aiAgentHelp: 'El agente usa un token de aplicación personal (x-app-token) con los permisos del nivel de acceso elegido. Revóquelo en "Tokens de aplicación" cuando ya no lo necesite.',
4095
+ aiAgentStart: 'Conectar agente',
4096
+ aiAgentServers: 'Servidores MCP',
4097
+ aiAgentAccess: 'Acceso del agente',
4098
+ aiAgentReadOnly: 'Solo lectura',
4099
+ aiAgentReadWrite: 'Lectura y escritura',
4100
+ aiAgentWriteHint: 'El agente podrá cambiar datos en su nombre. Prefiera solo lectura cuando sea suficiente.',
4101
+ aiAgentReadOnlyServer: '{name} (solo lectura)',
4102
+ aiAgentTokenName: 'Agente de IA ({date})',
4103
+ aiAgentGenerate: 'Generar configuración',
4104
+ aiAgentSelectServer: 'Elija al menos un servidor.',
4105
+ aiAgentResult: 'Configuración lista',
4106
+ aiAgentResultHint: 'Péguelo en .mcp.json, en la raíz del proyecto (Claude Code). El token aparece solo ahora: cópielo antes de terminar. Válido hasta {date}.',
4107
+ aiAgentCopy: 'Copiar configuración',
4108
+ aiAgentCopied: 'Configuración copiada. No la comparta: contiene su token.',
4109
+ aiAgentDone: 'Terminar',
4110
+ aiAgentCreateError: 'No fue posible generar el token del agente.',
4111
+ aiAgentUnavailable: 'No disponible en esta aplicación: no está conectada a argos-admin.',
4112
+ activity: 'Actividad de la cuenta',
4113
+ activityDescription: 'Inicios de sesión e intentos fallidos en los últimos 90 días.',
4114
+ activityKeywords: 'login acceso historial intento falla ip navegador seguridad',
4115
+ activityFilter: 'Mostrar',
4116
+ activityAll: 'Todas',
4117
+ activityFailures: 'Solo fallas',
4118
+ activityDetail: '{date} · IP {ip}',
4119
+ activityApp: 'Aplicación: {app}',
4120
+ activityEmpty: 'Nada en los últimos 90 días.',
4121
+ activityMore: 'Cargar más',
4122
+ activityShowing: '{shown} de {total}',
4123
+ activityLoadError: 'No fue posible cargar la actividad de la cuenta.',
4124
+ unknownIp: 'desconocido',
4125
+ unknownApp: 'sin nombre',
4126
+ idle: 'Cerrar por inactividad',
4127
+ idleDescription: 'Sale solo después de un tiempo sin uso, con un aviso antes.',
4128
+ idleKeywords: 'inactividad tiempo salir automático logout seguridad bloqueo',
4129
+ idleNever: 'Nunca',
4130
+ idleOption: '{minutes} min',
4131
+ idleLimitHint: 'Esta aplicación cierra la sesión en un máximo de {minutes} min sin uso.',
4132
+ idleWarningHeading: 'La sesión se va a cerrar',
4133
+ idleWarningText: { one: 'Sin actividad desde hace un tiempo. La sesión termina en {count} segundo.', other: 'Sin actividad desde hace un tiempo. La sesión termina en {count} segundos.' },
4134
+ idleStay: 'Seguir conectado',
4135
+ idleLogout: 'Salir ahora',
4136
+ startPage: 'Pantalla inicial',
4137
+ startPageDescription: 'Dónde se abre la aplicación después del inicio de sesión.',
4138
+ startPageKeywords: 'inicio apertura login favorito última pantalla',
4139
+ startHome: 'Inicio',
4140
+ startLast: 'Última pantalla visitada',
4141
+ startFavorite: 'Favorito: {name}',
4142
+ startPageHint: 'Marque pantallas como favoritas para abrir en una de ellas.',
4143
+ defaultBadge: 'Credencial predeterminada',
4144
+ defaultBadgeDescription: 'Nivel de acceso con el que entra en esta aplicación, en cualquier navegador.',
4145
+ defaultBadgeKeywords: 'credencial badge nivel acceso perfil predeterminado login',
4146
+ defaultBadgeAuto: 'Automático (el de menor nivel)',
4147
+ defaultBadgeSaved: 'Credencial predeterminada guardada. Vale desde el próximo inicio de sesión.',
4148
+ defaultBadgeError: 'No fue posible guardar la credencial predeterminada.',
4149
+ defaultBadgeLoadError: 'No fue posible cargar sus credenciales.',
4150
+ notices: 'Notificaciones',
4151
+ noticesDescription: 'Los avisos que muestra esta aplicación.',
4152
+ noticesKeywords: 'notificación aviso alerta navegador push',
4153
+ browserNotices: 'Avisos del navegador',
4154
+ browserNoticesDescription: 'Aviso del sistema cuando la pestaña está en segundo plano.',
4155
+ browserNoticesDenied: 'El navegador bloqueó las notificaciones de este sitio. Permítalas en la configuración del navegador.',
4156
+ browserNoticesUnsupported: 'Este navegador no muestra notificaciones.',
4157
+ noticeOpen: 'Abrir',
4158
+ sync: 'Preferencias en la cuenta',
4159
+ syncDescription: 'Apariencia, región, favoritos y las demás preferencias lo acompañan en cualquier navegador.',
4160
+ syncKeywords: 'sincronizar nube cuenta navegador preferencias respaldo',
4161
+ syncSynced: 'Sincronizado a las {time}',
4162
+ syncSaving: 'Guardando…',
4163
+ syncLocal: 'Solo en este navegador: la cuenta no está disponible.',
4164
+ syncError: 'No fue posible sincronizar. Inténtelo de nuevo.',
4165
+ syncNow: 'Sincronizar ahora',
4166
+ },
3098
4167
  };
3099
4168
 
3100
4169
  const SPINE_I18N_CONFIG = new InjectionToken('SPINE_I18N_CONFIG', {
@@ -3121,6 +4190,10 @@ function currentSpineMessages() {
3121
4190
  * Textos do spine no idioma ativo da lib (`HermesI18nService.locale`): uma troca de idioma com
3122
4191
  * `setLocale` atualiza a lib e o spine juntos, sem recarregar. Leitura igual à do `HERMES_MESSAGES`
3123
4192
  * (`inject(SPINE_MESSAGES).header.settings`), viva também em template.
4193
+ *
4194
+ * `meta.locale` é o de formatação da lib (`HermesI18nService.formatLocale`) quando a língua é a mesma: a
4195
+ * variante regional (`en-GB`) e o relógio/primeiro dia da semana escolhidos em Região valem também nas
4196
+ * datas e horas que o spine formata.
3124
4197
  */
3125
4198
  const SPINE_MESSAGES = new InjectionToken('SPINE_MESSAGES', {
3126
4199
  providedIn: 'root',
@@ -3129,8 +4202,11 @@ const SPINE_MESSAGES = new InjectionToken('SPINE_MESSAGES', {
3129
4202
  const config = inject(SPINE_I18N_CONFIG);
3130
4203
  const catalogs = { ...BUILT_IN, ...config.catalogs };
3131
4204
  const active = computed(() => {
3132
- const base = i18n.pick(catalogs, SPINE_MESSAGES_PT_BR);
3133
- return config.override ? mergeCatalog(config.override, base) : base;
4205
+ const picked = i18n.pick(catalogs, SPINE_MESSAGES_PT_BR);
4206
+ const base = config.override ? mergeCatalog(config.override, picked) : picked;
4207
+ const locale = i18n.formatLocale();
4208
+ const sameLanguage = locale.split('-')[0].toLowerCase() === base.meta.locale.split('-')[0].toLowerCase();
4209
+ return sameLanguage && locale !== base.meta.locale ? { ...base, meta: { ...base.meta, locale } } : base;
3134
4210
  }, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
3135
4211
  current = liveCatalog(() => active());
3136
4212
  return current;
@@ -3145,5 +4221,5 @@ function provideSpineMessages(config) {
3145
4221
  * Generated bundle index. Do not edit.
3146
4222
  */
3147
4223
 
3148
- export { APP_PACKAGE_INFO, DensityService, HERMES_DS_IMPORTS, SPINE_ADMIN_API, SPINE_CONFIG, SPINE_DEFAULT_LOGO, SPINE_I18N_CONFIG, SPINE_MESSAGES, SPINE_MESSAGES_EN, SPINE_MESSAGES_ES, SPINE_MESSAGES_PT_BR, SPINE_NEWS, SPINE_USER_STORAGE_KEYS, SpinePreferencesService, SpineQuickAccessService, SpineUserStorage, ThemePresetService, ThemeService, applyRuntimeEnvironment, applySpineFavicon, currentSpineMessages, mergeRouteTree, provideSpineMessages, provideSpineNews, provideUi, resolveSpineLogo, routerIdOf, spineRoutesOf };
4224
+ export { APP_PACKAGE_INFO, DensityService, HERMES_DS_IMPORTS, SPINE_ADMIN_API, SPINE_CONFIG, SPINE_DEFAULT_LOGO, SPINE_I18N_CONFIG, SPINE_IDLE_OPTIONS, SPINE_MESSAGES, SPINE_MESSAGES_EN, SPINE_MESSAGES_ES, SPINE_MESSAGES_PT_BR, SPINE_NEWS, SPINE_USER_STORAGE_KEYS, SpinePreferencesService, SpineQuickAccessService, SpineUserStorage, ThemePresetService, ThemeService, applyRuntimeEnvironment, applySpineFavicon, currentSpineMessages, mergeRouteTree, provideSpineMessages, provideSpineNews, provideUi, resolveSpineLogo, routerIdOf, spineRoutesOf };
3149
4225
  //# sourceMappingURL=trismegisto-spine.mjs.map