@trismegisto/spine 2.1.1 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -3
- package/fesm2022/trismegisto-spine-layout.mjs +118 -27
- package/fesm2022/trismegisto-spine-layout.mjs.map +1 -1
- package/fesm2022/trismegisto-spine-shell.mjs +12 -8
- package/fesm2022/trismegisto-spine-shell.mjs.map +1 -1
- package/fesm2022/trismegisto-spine.mjs +6 -2
- package/fesm2022/trismegisto-spine.mjs.map +1 -1
- package/package.json +6 -6
- package/types/trismegisto-spine-layout.d.ts +53 -10
- package/types/trismegisto-spine-layout.d.ts.map +1 -1
- package/types/trismegisto-spine-shell.d.ts +3 -2
- package/types/trismegisto-spine-shell.d.ts.map +1 -1
- package/types/trismegisto-spine.d.ts +5 -2
- package/types/trismegisto-spine.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -55,8 +55,8 @@ export const appConfig: ApplicationConfig = {
|
|
|
55
55
|
`isDevMode()`).
|
|
56
56
|
- **Recentes, buscas recentes e favoritos** ficam no `localStorage` por usuário (`SpineUserStorage`: chave
|
|
57
57
|
`<clientId>_<lista>_<sub>`, com o `sub` do token). No logout (pelo spine ou vindo de outra aba, via `isAuth$`) os
|
|
58
|
-
históricos são apagados; os favoritos ficam. Logout próprio do app:
|
|
59
|
-
|
|
58
|
+
históricos são apagados; os favoritos ficam. Logout próprio do app: use `SpineLogoutService.logout()` (veja
|
|
59
|
+
"Sair (logout)" abaixo).
|
|
60
60
|
- **`APP_ROUTES`** traz login, callback, saída e as rotas privadas sob o `spine-layout`
|
|
61
61
|
(`LayoutComponent`). O `/callback` usa o `handleCallback()` do `@orfeu/angular`: confere state/verifier, volta
|
|
62
62
|
para a URL pedida antes do login e, no `login_required` da reautorização silenciosa, segue sozinho para o login
|
|
@@ -84,6 +84,35 @@ export const components: ComponentRoute[] = [
|
|
|
84
84
|
];
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
### Sair (logout)
|
|
88
|
+
|
|
89
|
+
Todas as saídas do spine (item "Sair" do menu do perfil, botão "Sair" do painel de configurações, atalho
|
|
90
|
+
`Mod+Shift+L` e a ação da tela `/blocked`) passam pelo **`SpineLogoutService`** (`@trismegisto/spine/layout`). O app não
|
|
91
|
+
precisa de código próprio:
|
|
92
|
+
|
|
93
|
+
- **Falha no Argos** (`@orfeu/angular` 1.3.x: o `logout()` repete uma vez e, falhando de novo, mantém a sessão e emite
|
|
94
|
+
`logoutFailed$`): o serviço assina `logoutFailed$` uma vez e mostra um aviso de erro (`hm-alert` de erro,
|
|
95
|
+
`role="alert"`, pelo `NotificationService`) com `SpineMessages.session.logoutFailed` — "Não foi possível sair agora.
|
|
96
|
+
Verifique a conexão e tente de novo." A pessoa fica na tela, a sessão continua e os botões voltam ao normal para
|
|
97
|
+
tentar de novo. Vale também para um `AuthorizationService.logout()` chamado direto pelo app.
|
|
98
|
+
- **Sucesso**: só então os históricos (recentes e buscas recentes) são apagados; o `@orfeu/angular` navega para o
|
|
99
|
+
`/logoff`. Com falha, nada é apagado.
|
|
100
|
+
- `logout()` devolve `Promise<boolean>` (`true` = saiu; nunca rejeita) e `pending` (signal) fica `true` durante a
|
|
101
|
+
saída: o painel de configurações mostra o botão carregando, o item do menu fica desabilitado e o painel só fecha
|
|
102
|
+
quando a saída deu certo. Cliques repetidos reaproveitam a mesma tentativa.
|
|
103
|
+
- **`@orfeu/angular` 1.0.x** (sem `logoutFailed$`, `logout()` sem retorno): funciona como antes — a saída conta como
|
|
104
|
+
concluída e os históricos saem.
|
|
105
|
+
|
|
106
|
+
Logout num botão do próprio app:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const saiu = await inject(SpineLogoutService).logout(); // false: o aviso já apareceu; a pessoa continua aqui
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
O `spine-layout` também monta os avisos: o `spine-notification` (`NotificationService`) e um `hm-toast-stack`
|
|
113
|
+
(`HermesToastService`, usado nas configurações, em Conta e segurança e no auto-serviço). O app não monta outro; se
|
|
114
|
+
montar, os avisos não aparecem em dobro (só uma pilha desenha).
|
|
115
|
+
|
|
87
116
|
## Logo e favicon
|
|
88
117
|
|
|
89
118
|
O spine não traz marca nenhuma: o logo do cabeçalho e o favicon são arquivos do app.
|
|
@@ -145,7 +174,7 @@ Argos guarda essa prova por 5 minutos.
|
|
|
145
174
|
| `@trismegisto/spine` | `provideUi`, `SpineConfig`, `ThemeService`, `DensityService`, `SpinePreferencesService`, `provideSpineNews`, textos (`SPINE_MESSAGES`, `provideSpineMessages`) |
|
|
146
175
|
| `@trismegisto/spine/shell` | `APP_ROUTES`, `PUBLIC_ROUTES`, `PRIVATE_ROUTES`, `provideShell`, telas de login e de estado |
|
|
147
176
|
| `@trismegisto/spine/routing` | `ComponentRoute`, `COMPONENT_ROUTES`, `ViewGuard`, `RouteIdInterceptor`, resolução de rota |
|
|
148
|
-
| `@trismegisto/spine/layout` | `LayoutComponent` (`spine-layout`), cabeçalho, menu lateral, onboarding, atalhos, configurações, novidades, documentação |
|
|
177
|
+
| `@trismegisto/spine/layout` | `LayoutComponent` (`spine-layout`), cabeçalho, menu lateral, onboarding, atalhos, configurações, novidades, documentação, `SpineLogoutService` |
|
|
149
178
|
| `@trismegisto/spine/modal` | `ModalService` (`open(config)` devolve um `Observable` com o resultado), `ModalContent` |
|
|
150
179
|
| `@trismegisto/spine/side-panel` | `SidePanelService`, `SidePanelContent` |
|
|
151
180
|
| `@trismegisto/spine/notification` | `NotificationService` (`addNotification`) |
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ChangeDetectorRef, ChangeDetectionStrategy, Component, InjectionToken, input, signal, computed, Optional, Inject, Injectable, Injector, afterNextRender, ViewChild, effect, HostListener, DOCUMENT, Directive, isDevMode,
|
|
2
|
+
import { inject, ChangeDetectorRef, ChangeDetectionStrategy, Component, InjectionToken, input, signal, computed, Optional, Inject, Injectable, DestroyRef, Injector, afterNextRender, ViewChild, effect, HostListener, DOCUMENT, Directive, isDevMode, output, ElementRef, ViewEncapsulation } from '@angular/core';
|
|
3
3
|
import * as i3 from '@angular/router';
|
|
4
4
|
import { RouterLink, Router, NavigationEnd, NavigationSkipped, RouterOutlet } from '@angular/router';
|
|
5
5
|
import { BehaviorSubject, filter, take, Subject, firstValueFrom } from 'rxjs';
|
|
6
|
-
import { IconComponent, IconButtonComponent, LoadingSquareComponent, HermesShortcutService, HERMES_MESSAGES, HeaderComponent as HeaderComponent$1, InputComponent, MenuButtonComponent, AccountAvatarComponent, ModalComponent, createPersistedWorkspaceStore, HERMES_WORKSPACE_PERSISTENCE_CONFIG, findPanelTabs, SidebarComponent as SidebarComponent$1, SidebarNodeComponent, ButtonComponent, CarouselComponent, ShortcutListComponent, CodeInputComponent, RadioComponent, AlertComponent, HermesToastService, TagComponent, SegmentedControlComponent, HermesThemeService, HermesSidebarService, ComboboxComponent, SettingsDialogComponent, SettingsRowComponent, SettingsSectionComponent, SelectComponent, ThemePickerComponent, BreadcrumbComponent, TooltipDirective, SkeletonComponent, WorkspaceComponent } from '@trismegisto/angular';
|
|
6
|
+
import { IconComponent, IconButtonComponent, LoadingSquareComponent, HermesShortcutService, HERMES_MESSAGES, HeaderComponent as HeaderComponent$1, InputComponent, MenuButtonComponent, AccountAvatarComponent, ModalComponent, createPersistedWorkspaceStore, HERMES_WORKSPACE_PERSISTENCE_CONFIG, findPanelTabs, SidebarComponent as SidebarComponent$1, SidebarNodeComponent, ButtonComponent, CarouselComponent, ShortcutListComponent, CodeInputComponent, RadioComponent, AlertComponent, HermesToastService, TagComponent, SegmentedControlComponent, HermesThemeService, HermesSidebarService, ComboboxComponent, SettingsDialogComponent, SettingsRowComponent, SettingsSectionComponent, SelectComponent, ThemePickerComponent, BreadcrumbComponent, TooltipDirective, SkeletonComponent, WorkspaceComponent, ToastStackComponent } from '@trismegisto/angular';
|
|
7
7
|
import { ModalComponent as ModalComponent$1 } from '@trismegisto/spine/modal';
|
|
8
|
-
import { NotificationComponent } from '@trismegisto/spine/notification';
|
|
8
|
+
import { NotificationService, AlertType, NotificationComponent } from '@trismegisto/spine/notification';
|
|
9
9
|
import { SidePanelComponent } from '@trismegisto/spine/side-panel';
|
|
10
10
|
import * as i2$1 from '@trismegisto/spine';
|
|
11
11
|
import { SPINE_MESSAGES, SPINE_CONFIG, ThemeService, resolveSpineLogo, SPINE_NEWS, SpineUserStorage, SPINE_USER_STORAGE_KEYS, spineRoutesOf, routerIdOf, SpinePreferencesService, SPINE_ADMIN_API, ThemePresetService, DensityService, APP_PACKAGE_INFO, SpineQuickAccessService } from '@trismegisto/spine';
|
|
@@ -14,7 +14,7 @@ import { CommonModule, NgTemplateOutlet, NgComponentOutlet } from '@angular/comm
|
|
|
14
14
|
import * as i1$1 from '@angular/forms';
|
|
15
15
|
import { FormsModule, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
16
16
|
import * as i1 from '@orfeu/angular/core';
|
|
17
|
-
import {
|
|
17
|
+
import { AuthorizationService, ViewService, RealmAppsService, AUTH_CONFIG } from '@orfeu/angular/core';
|
|
18
18
|
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
19
19
|
import { formatMessage, pluralMessage, urlPath, formatMessageHtml, HERMES_FONTS } from '@trismegisto/primitives';
|
|
20
20
|
import { resolveEffectiveRouteChain, ComponentDistributorService, componentRouterId, LoggerService } from '@trismegisto/spine/routing';
|
|
@@ -474,6 +474,88 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
474
474
|
args: [{ providedIn: 'root' }]
|
|
475
475
|
}], ctorParameters: () => [] });
|
|
476
476
|
|
|
477
|
+
/** Tempo do aviso de falha na tela (segundos). */
|
|
478
|
+
const LOGOUT_FAILED_SECONDS = 8;
|
|
479
|
+
/**
|
|
480
|
+
* Saída da sessão pelo shell: todo botão/atalho de sair do spine passa por aqui (menu do perfil, painel de
|
|
481
|
+
* configurações, `Mod+Shift+L`, tela `/blocked`).
|
|
482
|
+
*
|
|
483
|
+
* - Assina `logoutFailed$` do `@orfeu/angular` (1.3.x) uma vez só: quando o logout no Argos falha (a lib repete uma
|
|
484
|
+
* vez e mantém a sessão), mostra o aviso de erro `session.logoutFailed` (`hm-alert` de erro, `role="alert"`). Vale
|
|
485
|
+
* também para um `logout()` chamado direto pelo app.
|
|
486
|
+
* - `logout()` aguarda o resultado: só apaga os históricos do usuário (`SpineUserStorage.clearHistory()`) quando a
|
|
487
|
+
* saída deu certo. Com falha, nada muda: a pessoa continua na tela, com a sessão e os recentes.
|
|
488
|
+
* - `pending` fica `true` enquanto a saída está em andamento e sempre volta a `false` (os botões não travam).
|
|
489
|
+
*
|
|
490
|
+
* Com `@orfeu/angular` 1.0.x (sem `logoutFailed$`, `logout()` sem retorno) a saída conta como concluída, como antes.
|
|
491
|
+
*/
|
|
492
|
+
class SpineLogoutService {
|
|
493
|
+
constructor() {
|
|
494
|
+
this.auth = inject(AuthorizationService);
|
|
495
|
+
this.userStorage = inject(SpineUserStorage);
|
|
496
|
+
this.notifications = inject(NotificationService);
|
|
497
|
+
this.messages = inject(SPINE_MESSAGES);
|
|
498
|
+
this.inFlight = null;
|
|
499
|
+
/** Saída em andamento (o botão mostra "saindo…" / fica desabilitado). */
|
|
500
|
+
this.pending = signal(false, ...(ngDevMode ? [{ debugName: "pending" }] : /* istanbul ignore next */ []));
|
|
501
|
+
const failed$ = this.auth.logoutFailed$;
|
|
502
|
+
this.failureStream = typeof failed$?.subscribe === 'function';
|
|
503
|
+
if (!this.failureStream)
|
|
504
|
+
return;
|
|
505
|
+
const sub = failed$.subscribe(() => this.notifyFailure());
|
|
506
|
+
inject(DestroyRef).onDestroy(() => sub.unsubscribe());
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Sai da sessão. Resolve `true` quando saiu (o `@orfeu/angular` já navega para o `/logoff`) e `false` quando o
|
|
510
|
+
* Argos não confirmou: a sessão continua e o aviso de erro aparece. Nunca rejeita. Cliques repetidos durante a
|
|
511
|
+
* saída reaproveitam a mesma tentativa.
|
|
512
|
+
*/
|
|
513
|
+
logout() {
|
|
514
|
+
this.inFlight ??= this.run().finally(() => {
|
|
515
|
+
this.inFlight = null;
|
|
516
|
+
this.pending.set(false);
|
|
517
|
+
});
|
|
518
|
+
return this.inFlight;
|
|
519
|
+
}
|
|
520
|
+
async run() {
|
|
521
|
+
this.pending.set(true);
|
|
522
|
+
// Guarda de quem é a sessão antes de sair: depois do logout o token some, e a limpeza precisa do usuário.
|
|
523
|
+
this.userStorage.userId();
|
|
524
|
+
let ok;
|
|
525
|
+
let rejected = false;
|
|
526
|
+
try {
|
|
527
|
+
// 1.3.x: Promise<boolean>; 1.0.x: void (a saída local sempre acontecia).
|
|
528
|
+
ok = (await this.auth.logout()) !== false;
|
|
529
|
+
}
|
|
530
|
+
catch {
|
|
531
|
+
ok = false;
|
|
532
|
+
rejected = true;
|
|
533
|
+
}
|
|
534
|
+
if (ok) {
|
|
535
|
+
this.userStorage.clearHistory();
|
|
536
|
+
}
|
|
537
|
+
else if (rejected || !this.failureStream) {
|
|
538
|
+
// Sem `logoutFailed$` para avisar (ou a chamada nem chegou a resolver): o próprio resultado avisa.
|
|
539
|
+
this.notifyFailure();
|
|
540
|
+
}
|
|
541
|
+
return ok;
|
|
542
|
+
}
|
|
543
|
+
notifyFailure() {
|
|
544
|
+
this.notifications.addNotification({
|
|
545
|
+
timer: LOGOUT_FAILED_SECONDS,
|
|
546
|
+
hermesAlertType: AlertType.DANGER,
|
|
547
|
+
message: this.messages.session.logoutFailed,
|
|
548
|
+
hermesAlertClosable: true,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineLogoutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
552
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineLogoutService, providedIn: 'root' }); }
|
|
553
|
+
}
|
|
554
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineLogoutService, decorators: [{
|
|
555
|
+
type: Injectable,
|
|
556
|
+
args: [{ providedIn: 'root' }]
|
|
557
|
+
}], ctorParameters: () => [] });
|
|
558
|
+
|
|
477
559
|
/** Seções que o próprio spine já traz. `key` é também o valor de `?settings=` na URL. */
|
|
478
560
|
const SPINE_SETTINGS_KEYS = {
|
|
479
561
|
appearance: 'aparencia',
|
|
@@ -698,6 +780,7 @@ class HeaderComponent {
|
|
|
698
780
|
this.settings = inject(SpineSettingsService);
|
|
699
781
|
this.news = inject(SpineNewsService);
|
|
700
782
|
this.userStorage = inject(SpineUserStorage);
|
|
783
|
+
this.logoutService = inject(SpineLogoutService);
|
|
701
784
|
this.quickQuery = '';
|
|
702
785
|
this.quickOpen = false;
|
|
703
786
|
this.quickResults = [];
|
|
@@ -776,9 +859,9 @@ class HeaderComponent {
|
|
|
776
859
|
async switchBadge(badgeTypeId) {
|
|
777
860
|
await this.authService.switchBadge(badgeTypeId);
|
|
778
861
|
}
|
|
862
|
+
/** Sai pelo `SpineLogoutService`: com falha no Argos a pessoa fica aqui, com aviso, e o histórico fica. */
|
|
779
863
|
logout() {
|
|
780
|
-
this.
|
|
781
|
-
this.authService.logout();
|
|
864
|
+
return this.logoutService.logout();
|
|
782
865
|
}
|
|
783
866
|
/** Reabre o tour guiado de onboarding. */
|
|
784
867
|
startTour() { this.onboarding.restart(); }
|
|
@@ -805,7 +888,8 @@ class HeaderComponent {
|
|
|
805
888
|
// Só quando o app registrou alguma novidade vigente (provideSpineNews).
|
|
806
889
|
if (this.news.hasAny())
|
|
807
890
|
items.push({ id: 'news', label: this.text.news, icon: 'star' });
|
|
808
|
-
|
|
891
|
+
// Desabilitado só enquanto a saída está em andamento; volta sozinho se ela falhar.
|
|
892
|
+
items.push({ id: 'logout', label: this.text.logout, icon: 'logout', danger: true, separatorAbove: true, disabled: this.logoutService.pending() });
|
|
809
893
|
return items;
|
|
810
894
|
}
|
|
811
895
|
onProfileMenuChosen(id) {
|
|
@@ -831,7 +915,7 @@ class HeaderComponent {
|
|
|
831
915
|
this.settings.open(SPINE_SETTINGS_KEYS.appearance);
|
|
832
916
|
return;
|
|
833
917
|
case 'logout':
|
|
834
|
-
this.logout();
|
|
918
|
+
void this.logout();
|
|
835
919
|
return;
|
|
836
920
|
}
|
|
837
921
|
}
|
|
@@ -2592,7 +2676,9 @@ class SettingsPanelComponent {
|
|
|
2592
2676
|
// ── Atalhos ─────────────────────────────────────────────────────────────────
|
|
2593
2677
|
/** Atalhos registrados (favoritos numa linha só) + os que outra peça dispara — os mesmos do painel "?". */
|
|
2594
2678
|
this.shortcutItems = this.shortcutService.panelItems;
|
|
2595
|
-
this.
|
|
2679
|
+
this.logoutService = inject(SpineLogoutService);
|
|
2680
|
+
/** "Saindo…" no botão; volta a `false` também quando a saída falha. */
|
|
2681
|
+
this.logoutPending = this.logoutService.pending;
|
|
2596
2682
|
// ── Conta e segurança ───────────────────────────────────────────────────────
|
|
2597
2683
|
/** API da conta no Argos (Bearer com o token da sessão), a partir do `AUTH_CONFIG`. Sem ela (sem
|
|
2598
2684
|
* `authority`/`realmId`, ou com `CHANGE_ME`) a seção não aparece. */
|
|
@@ -2650,10 +2736,10 @@ class SettingsPanelComponent {
|
|
|
2650
2736
|
}
|
|
2651
2737
|
await this.copy(token, this.text.tokenCopied);
|
|
2652
2738
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
this.
|
|
2656
|
-
|
|
2739
|
+
/** O painel fica aberto até a saída dar certo: com falha, a pessoa continua aqui, com o aviso de erro. */
|
|
2740
|
+
async logout() {
|
|
2741
|
+
if (await this.logoutService.logout())
|
|
2742
|
+
this.settings.close();
|
|
2657
2743
|
}
|
|
2658
2744
|
/** Login da pessoa (`preferred_username` do token, ou o e-mail), para dizer QUAL conta do Argos. */
|
|
2659
2745
|
get accountLogin() {
|
|
@@ -2704,7 +2790,7 @@ class SettingsPanelComponent {
|
|
|
2704
2790
|
this.settings.close();
|
|
2705
2791
|
}
|
|
2706
2792
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SettingsPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2707
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: SettingsPanelComponent, isStandalone: true, selector: "spine-settings-panel", ngImport: i0, template: "<!-- Montado s\u00F3 enquanto aberto: nada disto (lista de fusos, leitura do token...) custa no\n carregamento de toda p\u00E1gina. -->\n@if (settings.isOpen()) {\n <hm-settings-dialog\n [heading]=\"text.heading\"\n [open]=\"true\"\n (openChange)=\"onOpenChange($event)\"\n [activeSection]=\"settings.section()\"\n (activeSectionChange)=\"settings.section.set($event)\"\n >\n <!-- \u2500\u2500 Conta \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.account\" [label]=\"text.account\" icon=\"user\" [keywords]=\"text.accountKeywords\">\n <div class=\"sp-settings__profile\">\n <hm-account-avatar [name]=\"profile.name ?? text.user\" [imageUrl]=\"profile.picture\" />\n <span class=\"sp-settings__profile-text\">\n <strong>{{ profile.name ?? text.user }}</strong>\n @if (profile.role) { <span>{{ profile.role }}</span> }\n </span>\n </div>\n\n <!-- S\u00F3 informativo: a troca de n\u00EDvel fica no menu do perfil, perto de onde se trabalha. -->\n @if (badge) {\n <hm-settings-row [label]=\"text.accessLevel\" [keywords]=\"text.accessLevelKeywords\">\n <span class=\"sp-settings__value\">{{ badge.name }}</span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.session\" [description]=\"text.sessionDescription\" [keywords]=\"text.sessionKeywords\">\n <span class=\"sp-settings__value\">{{ sessionExpiry }}</span>\n </hm-settings-row>\n\n <!-- S\u00F3 com SpineConfig.technicalDocs (padr\u00E3o: isDevMode()): token da sess\u00E3o no clipboard \u00E9 ferramenta de dev. -->\n @if (showAccessToken) {\n <hm-settings-row [label]=\"text.accessToken\" [description]=\"text.accessTokenDescription\" [keywords]=\"text.accessTokenKeywords\">\n <hm-button emphasis=\"medium\" icon=\"copy\" (clicked)=\"copyToken()\">{{ text.copy }}</hm-button>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.appTokens\" [stacked]=\"true\"\n [description]=\"text.appTokensDescription\"\n [keywords]=\"text.appTokensKeywords\">\n <spine-app-tokens />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.logout\" [description]=\"text.logoutDescription\" [keywords]=\"text.logoutKeywords\">\n <hm-button emphasis=\"medium\" intent=\"danger\" icon=\"logout\" (clicked)=\"logout()\">{{ text.logout }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Conta e seguran\u00E7a \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- API da conta do Argos com o token da sess\u00E3o (Bearer): MFA, c\u00F3digos de recupera\u00E7\u00E3o e sess\u00F5es aqui\n mesmo, sem abrir o Argos. O spineAccountSecurity d\u00E1 \u00E0s linhas o mesmo estado (reautentica\u00E7\u00E3o,\n cadastro em andamento), que morre quando o painel fecha. -->\n @if (accountSecurity) {\n <hm-settings-section spineAccountSecurity [key]=\"keys.security\" [label]=\"text.security\" icon=\"shield-check\" [description]=\"text.securityDescription\" [keywords]=\"text.securityKeywords\">\n @if (accountLogin || accountRealm) {\n <hm-settings-row [label]=\"text.signedInAccount\">\n @if (accountRealm) { <span rowDescription class=\"sp-settings__hint\">{{ fmt(text.signedInRealm, { realm: accountRealm }) }}</span> }\n <span class=\"sp-settings__value\">{{ profile.name ?? text.user }}@if (accountLogin) { <span class=\"sp-settings__muted\"> \u00B7 {{ accountLogin }}</span> }</span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.twoFactor\" [description]=\"text.twoFactorDescription\" [keywords]=\"text.twoFactorKeywords\" [stacked]=\"true\">\n <spine-account-mfa />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.recoveryCodes\" [description]=\"text.recoveryCodesDescription\" [keywords]=\"text.recoveryCodesKeywords\" [stacked]=\"true\">\n <spine-account-recovery-codes />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.sessions\" [description]=\"text.sessionsDescription\" [keywords]=\"text.sessionsKeywords\" [stacked]=\"true\">\n <spine-account-sessions />\n </hm-settings-row>\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Apar\u00EAncia \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.appearance\" [label]=\"text.appearance\" icon=\"color-palette\" [keywords]=\"text.appearanceKeywords\">\n <hm-settings-row [label]=\"text.theme\" [description]=\"text.themeDescription\" [keywords]=\"text.themeKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.theme\" [options]=\"themeOptions()\" [value]=\"themeMode()\" (valueChange)=\"setThemeMode($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.accent\" [description]=\"text.accentDescription\" [keywords]=\"text.accentKeywords\" [stacked]=\"true\">\n <hm-theme-picker\n [families]=\"families\"\n [family]=\"family()\"\n [theme]=\"appliedTheme()\"\n [showMode]=\"false\"\n (familyChosen)=\"setFamily($event)\"\n />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.density\" [description]=\"text.densityDescription\" [keywords]=\"text.densityKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.density\" [options]=\"densityOptions()\" [value]=\"densityValue()\" (valueChange)=\"setDensity($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.font\" [description]=\"text.fontDescription\" [keywords]=\"text.fontKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.font\" [options]=\"fontOptions\" [value]=\"fontValue()\" (valueChange)=\"setFont($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.sidebar\" [description]=\"text.sidebarDescription\" [keywords]=\"text.sidebarKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.sidebar\" [options]=\"sidebarOptions()\" [value]=\"sidebar.mode()\" (valueChange)=\"setSidebarMode($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Regi\u00E3o \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.region\" [label]=\"text.region\" icon=\"clock\" [keywords]=\"text.regionKeywords\">\n <hm-settings-row [label]=\"text.language\" [description]=\"text.languageDescription\" [keywords]=\"text.languageKeywords\">\n <hm-select\n class=\"sp-settings__language\"\n [label]=\"text.language\"\n [labelHidden]=\"true\"\n [value]=\"preferences.language()\"\n (valueChange)=\"preferences.setLanguage($event ?? '')\"\n >\n @for (option of languageOptions(); track option.value) {\n <option [value]=\"option.value\" [attr.lang]=\"option.value || null\">{{ option.label }}</option>\n }\n </hm-select>\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.timeZone\" [keywords]=\"text.timeZoneKeywords\">\n <span rowDescription class=\"sp-settings__hint\">{{ fmt(text.timeZoneHint, { now: timeZonePreview() }) }}</span>\n <hm-combobox\n class=\"sp-settings__timezone\"\n [label]=\"text.timeZone\"\n [labelHidden]=\"true\"\n icon=\"world\"\n [options]=\"timeZoneOptions()\"\n [value]=\"preferences.timeZone()\"\n (valueChange)=\"preferences.setTimeZone($event)\"\n />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Atalhos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.shortcuts\" [label]=\"text.shortcuts\" icon=\"keyboard\" [description]=\"text.shortcutsDescription\" [keywords]=\"text.shortcutsKeywords\">\n <!-- Mesma lista do painel \"?\", sem busca (a do di\u00E1logo de configura\u00E7\u00F5es j\u00E1 cobre a se\u00E7\u00E3o);\n o interruptor de tecla \u00FAnica (WCAG 2.1.4) persiste no SpinePreferencesService. -->\n <hm-shortcut-list characterKeysToggle [registered]=\"false\" [items]=\"shortcutItems()\" [headingLevel]=\"4\" />\n <hm-settings-row [label]=\"text.tour\" [description]=\"text.tourDescription\" [keywords]=\"text.tourKeywords\">\n <hm-button emphasis=\"medium\" icon=\"play\" (clicked)=\"replayTour()\">{{ text.replayTour }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Se\u00E7\u00F5es do app (provideSettingsSection) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (s of appSections; track s.key) {\n <hm-settings-section [key]=\"s.key\" [label]=\"s.label\" [icon]=\"s.icon ?? 'cog'\" [description]=\"s.description ?? ''\" [keywords]=\"s.keywords ?? ''\">\n <ng-container *ngComponentOutlet=\"s.component\" />\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Sobre \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.about\" [label]=\"text.about\" icon=\"info-circle\" [keywords]=\"text.aboutKeywords\">\n <hm-settings-row [label]=\"text.application\">\n <span class=\"sp-settings__value\">{{ appName }}@if (packageInfo.name) { <span class=\"sp-settings__muted\"> \u00B7 {{ packageInfo.name }}</span> }</span>\n </hm-settings-row>\n <hm-settings-row [label]=\"text.version\" [keywords]=\"text.versionKeywords\">\n <span class=\"sp-settings__value sp-settings__mono\">{{ packageInfo.version ?? '\u2014' }}</span>\n </hm-settings-row>\n @if (buildDate) {\n <hm-settings-row [label]=\"text.buildDate\">\n <span class=\"sp-settings__value\">{{ buildDate }}</span>\n </hm-settings-row>\n }\n @if (packageInfo.environment) {\n <hm-settings-row [label]=\"text.environment\" [keywords]=\"text.environmentKeywords\">\n <span class=\"sp-settings__value\">{{ packageInfo.environment }}</span>\n </hm-settings-row>\n }\n @if (packageInfo.repository?.url || packageInfo.docs || packageInfo.issues) {\n <hm-settings-row [label]=\"text.links\" [keywords]=\"text.linksKeywords\">\n <span class=\"sp-settings__links\">\n @if (packageInfo.docs) { <hm-button emphasis=\"minimal\" icon=\"book\" [link]=\"packageInfo.docs\" [newTab]=\"true\">{{ text.docs }}</hm-button> }\n @if (packageInfo.issues) { <hm-button emphasis=\"minimal\" icon=\"bug\" [link]=\"packageInfo.issues\" [newTab]=\"true\">{{ text.reportIssue }}</hm-button> }\n @if (packageInfo.repository?.url) { <hm-button emphasis=\"minimal\" icon=\"link\" [link]=\"packageInfo.repository!.url!\" [newTab]=\"true\">{{ text.repository }}</hm-button> }\n </span>\n </hm-settings-row>\n }\n @if (dependencies.length) {\n <hm-settings-row [label]=\"text.dependencies\" [description]=\"text.dependenciesDescription\" [keywords]=\"text.dependenciesKeywords\" [stacked]=\"true\">\n <dl class=\"sp-settings__deps\">\n @for (d of dependencies; track d.name) {\n <div><dt>{{ d.label }}</dt><dd class=\"sp-settings__mono\">{{ d.version }}</dd></div>\n }\n </dl>\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"text.supportInfo\" [description]=\"text.supportInfoDescription\" [keywords]=\"text.supportInfoKeywords\">\n <hm-button emphasis=\"medium\" icon=\"clipboard\" (clicked)=\"copySupportInfo()\">{{ text.copy }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n </hm-settings-dialog>\n}\n", styles: ["@charset \"UTF-8\";.sp-settings__hint{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-settings__timezone{display:block;width:min(320px,100%)}.sp-settings__profile{display:flex;align-items:center;gap:var(--hm-space-xxxs);padding:var(--hm-space-xxxs) 0 var(--hm-space-sm)}.sp-settings__profile-text{display:flex;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text)}.sp-settings__profile-text span{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-settings__value{color:var(--hm-text);font-size:var(--hm-font-size-sm);text-align:end}.sp-settings__muted{color:var(--hm-muted)}.sp-settings__mono{font-family:Geist Mono,ui-monospace,monospace}.sp-settings__links{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--hm-space-nano)}.sp-settings__deps{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0}.sp-settings__deps div{display:flex;justify-content:space-between;gap:var(--hm-space-xxxs);padding:var(--hm-space-nano) var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-hover)}.sp-settings__deps dt{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-settings__deps dd{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-xs)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AppTokensComponent, selector: "spine-app-tokens" }, { kind: "component", type: AccountMfaComponent, selector: "spine-account-mfa" }, { kind: "component", type: AccountRecoveryCodesComponent, selector: "spine-account-recovery-codes" }, { kind: "component", type: AccountSessionsComponent, selector: "spine-account-sessions" }, { kind: "directive", type: SpineAccountSecurityScopeDirective, selector: "[spineAccountSecurity]" }, { kind: "component", type: AccountAvatarComponent, selector: "hm-account-avatar", inputs: ["name", "imageUrl", "size", "loading"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "busy", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: ComboboxComponent, selector: "hm-combobox", inputs: ["label", "options", "value", "placeholder", "icon", "hint", "help", "size", "labelHidden", "clearable", "requiredManual", "loading", "allowCustomValue"], outputs: ["valueChange"] }, { kind: "component", type: SegmentedControlComponent, selector: "hm-segmented-control", inputs: ["options", "value", "label", "size", "emphasis"], outputs: ["valueChange"] }, { kind: "component", type: SettingsDialogComponent, selector: "hm-settings-dialog", inputs: ["open", "heading", "activeSection", "closeHandler", "actions", "submitLabel", "cancelLabel"], outputs: ["openChange", "activeSectionChange", "closed", "submitted"] }, { kind: "component", type: SettingsRowComponent, selector: "hm-settings-row", inputs: ["label", "description", "keywords", "stacked"] }, { kind: "component", type: SettingsSectionComponent, selector: "hm-settings-section", inputs: ["key", "label", "icon", "description", "keywords", "control", "invalid"] }, { kind: "component", type: SelectComponent, selector: "hm-select", inputs: ["value", "label", "labelHidden", "hint"], outputs: ["valueChange"] }, { kind: "component", type: ShortcutListComponent, selector: "hm-shortcut-list", inputs: ["items", "registered", "searchable", "characterKeysToggle", "headingLevel"] }, { kind: "component", type: ThemePickerComponent, selector: "hm-theme-picker", inputs: ["families", "family", "theme", "showMode"], outputs: ["familyChosen", "themeChosen"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2793
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: SettingsPanelComponent, isStandalone: true, selector: "spine-settings-panel", ngImport: i0, template: "<!-- Montado s\u00F3 enquanto aberto: nada disto (lista de fusos, leitura do token...) custa no\n carregamento de toda p\u00E1gina. -->\n@if (settings.isOpen()) {\n <hm-settings-dialog\n [heading]=\"text.heading\"\n [open]=\"true\"\n (openChange)=\"onOpenChange($event)\"\n [activeSection]=\"settings.section()\"\n (activeSectionChange)=\"settings.section.set($event)\"\n >\n <!-- \u2500\u2500 Conta \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.account\" [label]=\"text.account\" icon=\"user\" [keywords]=\"text.accountKeywords\">\n <div class=\"sp-settings__profile\">\n <hm-account-avatar [name]=\"profile.name ?? text.user\" [imageUrl]=\"profile.picture\" />\n <span class=\"sp-settings__profile-text\">\n <strong>{{ profile.name ?? text.user }}</strong>\n @if (profile.role) { <span>{{ profile.role }}</span> }\n </span>\n </div>\n\n <!-- S\u00F3 informativo: a troca de n\u00EDvel fica no menu do perfil, perto de onde se trabalha. -->\n @if (badge) {\n <hm-settings-row [label]=\"text.accessLevel\" [keywords]=\"text.accessLevelKeywords\">\n <span class=\"sp-settings__value\">{{ badge.name }}</span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.session\" [description]=\"text.sessionDescription\" [keywords]=\"text.sessionKeywords\">\n <span class=\"sp-settings__value\">{{ sessionExpiry }}</span>\n </hm-settings-row>\n\n <!-- S\u00F3 com SpineConfig.technicalDocs (padr\u00E3o: isDevMode()): token da sess\u00E3o no clipboard \u00E9 ferramenta de dev. -->\n @if (showAccessToken) {\n <hm-settings-row [label]=\"text.accessToken\" [description]=\"text.accessTokenDescription\" [keywords]=\"text.accessTokenKeywords\">\n <hm-button emphasis=\"medium\" icon=\"copy\" (clicked)=\"copyToken()\">{{ text.copy }}</hm-button>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.appTokens\" [stacked]=\"true\"\n [description]=\"text.appTokensDescription\"\n [keywords]=\"text.appTokensKeywords\">\n <spine-app-tokens />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.logout\" [description]=\"text.logoutDescription\" [keywords]=\"text.logoutKeywords\">\n <hm-button emphasis=\"medium\" intent=\"danger\" icon=\"logout\" [loading]=\"logoutPending()\" (clicked)=\"logout()\">{{ text.logout }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Conta e seguran\u00E7a \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- API da conta do Argos com o token da sess\u00E3o (Bearer): MFA, c\u00F3digos de recupera\u00E7\u00E3o e sess\u00F5es aqui\n mesmo, sem abrir o Argos. O spineAccountSecurity d\u00E1 \u00E0s linhas o mesmo estado (reautentica\u00E7\u00E3o,\n cadastro em andamento), que morre quando o painel fecha. -->\n @if (accountSecurity) {\n <hm-settings-section spineAccountSecurity [key]=\"keys.security\" [label]=\"text.security\" icon=\"shield-check\" [description]=\"text.securityDescription\" [keywords]=\"text.securityKeywords\">\n @if (accountLogin || accountRealm) {\n <hm-settings-row [label]=\"text.signedInAccount\">\n @if (accountRealm) { <span rowDescription class=\"sp-settings__hint\">{{ fmt(text.signedInRealm, { realm: accountRealm }) }}</span> }\n <span class=\"sp-settings__value\">{{ profile.name ?? text.user }}@if (accountLogin) { <span class=\"sp-settings__muted\"> \u00B7 {{ accountLogin }}</span> }</span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.twoFactor\" [description]=\"text.twoFactorDescription\" [keywords]=\"text.twoFactorKeywords\" [stacked]=\"true\">\n <spine-account-mfa />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.recoveryCodes\" [description]=\"text.recoveryCodesDescription\" [keywords]=\"text.recoveryCodesKeywords\" [stacked]=\"true\">\n <spine-account-recovery-codes />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.sessions\" [description]=\"text.sessionsDescription\" [keywords]=\"text.sessionsKeywords\" [stacked]=\"true\">\n <spine-account-sessions />\n </hm-settings-row>\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Apar\u00EAncia \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.appearance\" [label]=\"text.appearance\" icon=\"color-palette\" [keywords]=\"text.appearanceKeywords\">\n <hm-settings-row [label]=\"text.theme\" [description]=\"text.themeDescription\" [keywords]=\"text.themeKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.theme\" [options]=\"themeOptions()\" [value]=\"themeMode()\" (valueChange)=\"setThemeMode($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.accent\" [description]=\"text.accentDescription\" [keywords]=\"text.accentKeywords\" [stacked]=\"true\">\n <hm-theme-picker\n [families]=\"families\"\n [family]=\"family()\"\n [theme]=\"appliedTheme()\"\n [showMode]=\"false\"\n (familyChosen)=\"setFamily($event)\"\n />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.density\" [description]=\"text.densityDescription\" [keywords]=\"text.densityKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.density\" [options]=\"densityOptions()\" [value]=\"densityValue()\" (valueChange)=\"setDensity($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.font\" [description]=\"text.fontDescription\" [keywords]=\"text.fontKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.font\" [options]=\"fontOptions\" [value]=\"fontValue()\" (valueChange)=\"setFont($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.sidebar\" [description]=\"text.sidebarDescription\" [keywords]=\"text.sidebarKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.sidebar\" [options]=\"sidebarOptions()\" [value]=\"sidebar.mode()\" (valueChange)=\"setSidebarMode($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Regi\u00E3o \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.region\" [label]=\"text.region\" icon=\"clock\" [keywords]=\"text.regionKeywords\">\n <hm-settings-row [label]=\"text.language\" [description]=\"text.languageDescription\" [keywords]=\"text.languageKeywords\">\n <hm-select\n class=\"sp-settings__language\"\n [label]=\"text.language\"\n [labelHidden]=\"true\"\n [value]=\"preferences.language()\"\n (valueChange)=\"preferences.setLanguage($event ?? '')\"\n >\n @for (option of languageOptions(); track option.value) {\n <option [value]=\"option.value\" [attr.lang]=\"option.value || null\">{{ option.label }}</option>\n }\n </hm-select>\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.timeZone\" [keywords]=\"text.timeZoneKeywords\">\n <span rowDescription class=\"sp-settings__hint\">{{ fmt(text.timeZoneHint, { now: timeZonePreview() }) }}</span>\n <hm-combobox\n class=\"sp-settings__timezone\"\n [label]=\"text.timeZone\"\n [labelHidden]=\"true\"\n icon=\"world\"\n [options]=\"timeZoneOptions()\"\n [value]=\"preferences.timeZone()\"\n (valueChange)=\"preferences.setTimeZone($event)\"\n />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Atalhos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.shortcuts\" [label]=\"text.shortcuts\" icon=\"keyboard\" [description]=\"text.shortcutsDescription\" [keywords]=\"text.shortcutsKeywords\">\n <!-- Mesma lista do painel \"?\", sem busca (a do di\u00E1logo de configura\u00E7\u00F5es j\u00E1 cobre a se\u00E7\u00E3o);\n o interruptor de tecla \u00FAnica (WCAG 2.1.4) persiste no SpinePreferencesService. -->\n <hm-shortcut-list characterKeysToggle [registered]=\"false\" [items]=\"shortcutItems()\" [headingLevel]=\"4\" />\n <hm-settings-row [label]=\"text.tour\" [description]=\"text.tourDescription\" [keywords]=\"text.tourKeywords\">\n <hm-button emphasis=\"medium\" icon=\"play\" (clicked)=\"replayTour()\">{{ text.replayTour }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Se\u00E7\u00F5es do app (provideSettingsSection) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (s of appSections; track s.key) {\n <hm-settings-section [key]=\"s.key\" [label]=\"s.label\" [icon]=\"s.icon ?? 'cog'\" [description]=\"s.description ?? ''\" [keywords]=\"s.keywords ?? ''\">\n <ng-container *ngComponentOutlet=\"s.component\" />\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Sobre \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.about\" [label]=\"text.about\" icon=\"info-circle\" [keywords]=\"text.aboutKeywords\">\n <hm-settings-row [label]=\"text.application\">\n <span class=\"sp-settings__value\">{{ appName }}@if (packageInfo.name) { <span class=\"sp-settings__muted\"> \u00B7 {{ packageInfo.name }}</span> }</span>\n </hm-settings-row>\n <hm-settings-row [label]=\"text.version\" [keywords]=\"text.versionKeywords\">\n <span class=\"sp-settings__value sp-settings__mono\">{{ packageInfo.version ?? '\u2014' }}</span>\n </hm-settings-row>\n @if (buildDate) {\n <hm-settings-row [label]=\"text.buildDate\">\n <span class=\"sp-settings__value\">{{ buildDate }}</span>\n </hm-settings-row>\n }\n @if (packageInfo.environment) {\n <hm-settings-row [label]=\"text.environment\" [keywords]=\"text.environmentKeywords\">\n <span class=\"sp-settings__value\">{{ packageInfo.environment }}</span>\n </hm-settings-row>\n }\n @if (packageInfo.repository?.url || packageInfo.docs || packageInfo.issues) {\n <hm-settings-row [label]=\"text.links\" [keywords]=\"text.linksKeywords\">\n <span class=\"sp-settings__links\">\n @if (packageInfo.docs) { <hm-button emphasis=\"minimal\" icon=\"book\" [link]=\"packageInfo.docs\" [newTab]=\"true\">{{ text.docs }}</hm-button> }\n @if (packageInfo.issues) { <hm-button emphasis=\"minimal\" icon=\"bug\" [link]=\"packageInfo.issues\" [newTab]=\"true\">{{ text.reportIssue }}</hm-button> }\n @if (packageInfo.repository?.url) { <hm-button emphasis=\"minimal\" icon=\"link\" [link]=\"packageInfo.repository!.url!\" [newTab]=\"true\">{{ text.repository }}</hm-button> }\n </span>\n </hm-settings-row>\n }\n @if (dependencies.length) {\n <hm-settings-row [label]=\"text.dependencies\" [description]=\"text.dependenciesDescription\" [keywords]=\"text.dependenciesKeywords\" [stacked]=\"true\">\n <dl class=\"sp-settings__deps\">\n @for (d of dependencies; track d.name) {\n <div><dt>{{ d.label }}</dt><dd class=\"sp-settings__mono\">{{ d.version }}</dd></div>\n }\n </dl>\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"text.supportInfo\" [description]=\"text.supportInfoDescription\" [keywords]=\"text.supportInfoKeywords\">\n <hm-button emphasis=\"medium\" icon=\"clipboard\" (clicked)=\"copySupportInfo()\">{{ text.copy }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n </hm-settings-dialog>\n}\n", styles: ["@charset \"UTF-8\";.sp-settings__hint{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-settings__timezone{display:block;width:min(320px,100%)}.sp-settings__profile{display:flex;align-items:center;gap:var(--hm-space-xxxs);padding:var(--hm-space-xxxs) 0 var(--hm-space-sm)}.sp-settings__profile-text{display:flex;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text)}.sp-settings__profile-text span{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-settings__value{color:var(--hm-text);font-size:var(--hm-font-size-sm);text-align:end}.sp-settings__muted{color:var(--hm-muted)}.sp-settings__mono{font-family:Geist Mono,ui-monospace,monospace}.sp-settings__links{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--hm-space-nano)}.sp-settings__deps{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0}.sp-settings__deps div{display:flex;justify-content:space-between;gap:var(--hm-space-xxxs);padding:var(--hm-space-nano) var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-hover)}.sp-settings__deps dt{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-settings__deps dd{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-xs)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AppTokensComponent, selector: "spine-app-tokens" }, { kind: "component", type: AccountMfaComponent, selector: "spine-account-mfa" }, { kind: "component", type: AccountRecoveryCodesComponent, selector: "spine-account-recovery-codes" }, { kind: "component", type: AccountSessionsComponent, selector: "spine-account-sessions" }, { kind: "directive", type: SpineAccountSecurityScopeDirective, selector: "[spineAccountSecurity]" }, { kind: "component", type: AccountAvatarComponent, selector: "hm-account-avatar", inputs: ["name", "imageUrl", "size", "loading"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "busy", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: ComboboxComponent, selector: "hm-combobox", inputs: ["label", "options", "value", "placeholder", "icon", "hint", "help", "size", "labelHidden", "clearable", "requiredManual", "loading", "allowCustomValue"], outputs: ["valueChange"] }, { kind: "component", type: SegmentedControlComponent, selector: "hm-segmented-control", inputs: ["options", "value", "label", "size", "emphasis"], outputs: ["valueChange"] }, { kind: "component", type: SettingsDialogComponent, selector: "hm-settings-dialog", inputs: ["open", "heading", "activeSection", "closeHandler", "actions", "submitLabel", "cancelLabel"], outputs: ["openChange", "activeSectionChange", "closed", "submitted"] }, { kind: "component", type: SettingsRowComponent, selector: "hm-settings-row", inputs: ["label", "description", "keywords", "stacked"] }, { kind: "component", type: SettingsSectionComponent, selector: "hm-settings-section", inputs: ["key", "label", "icon", "description", "keywords", "control", "invalid"] }, { kind: "component", type: SelectComponent, selector: "hm-select", inputs: ["value", "label", "labelHidden", "hint"], outputs: ["valueChange"] }, { kind: "component", type: ShortcutListComponent, selector: "hm-shortcut-list", inputs: ["items", "registered", "searchable", "characterKeysToggle", "headingLevel"] }, { kind: "component", type: ThemePickerComponent, selector: "hm-theme-picker", inputs: ["families", "family", "theme", "showMode"], outputs: ["familyChosen", "themeChosen"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2708
2794
|
}
|
|
2709
2795
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SettingsPanelComponent, decorators: [{
|
|
2710
2796
|
type: Component,
|
|
@@ -2714,7 +2800,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
2714
2800
|
AccountAvatarComponent, ButtonComponent, ComboboxComponent, IconComponent,
|
|
2715
2801
|
SegmentedControlComponent, SettingsDialogComponent, SettingsRowComponent, SettingsSectionComponent,
|
|
2716
2802
|
SelectComponent, ShortcutListComponent, ThemePickerComponent,
|
|
2717
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Montado s\u00F3 enquanto aberto: nada disto (lista de fusos, leitura do token...) custa no\n carregamento de toda p\u00E1gina. -->\n@if (settings.isOpen()) {\n <hm-settings-dialog\n [heading]=\"text.heading\"\n [open]=\"true\"\n (openChange)=\"onOpenChange($event)\"\n [activeSection]=\"settings.section()\"\n (activeSectionChange)=\"settings.section.set($event)\"\n >\n <!-- \u2500\u2500 Conta \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.account\" [label]=\"text.account\" icon=\"user\" [keywords]=\"text.accountKeywords\">\n <div class=\"sp-settings__profile\">\n <hm-account-avatar [name]=\"profile.name ?? text.user\" [imageUrl]=\"profile.picture\" />\n <span class=\"sp-settings__profile-text\">\n <strong>{{ profile.name ?? text.user }}</strong>\n @if (profile.role) { <span>{{ profile.role }}</span> }\n </span>\n </div>\n\n <!-- S\u00F3 informativo: a troca de n\u00EDvel fica no menu do perfil, perto de onde se trabalha. -->\n @if (badge) {\n <hm-settings-row [label]=\"text.accessLevel\" [keywords]=\"text.accessLevelKeywords\">\n <span class=\"sp-settings__value\">{{ badge.name }}</span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.session\" [description]=\"text.sessionDescription\" [keywords]=\"text.sessionKeywords\">\n <span class=\"sp-settings__value\">{{ sessionExpiry }}</span>\n </hm-settings-row>\n\n <!-- S\u00F3 com SpineConfig.technicalDocs (padr\u00E3o: isDevMode()): token da sess\u00E3o no clipboard \u00E9 ferramenta de dev. -->\n @if (showAccessToken) {\n <hm-settings-row [label]=\"text.accessToken\" [description]=\"text.accessTokenDescription\" [keywords]=\"text.accessTokenKeywords\">\n <hm-button emphasis=\"medium\" icon=\"copy\" (clicked)=\"copyToken()\">{{ text.copy }}</hm-button>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.appTokens\" [stacked]=\"true\"\n [description]=\"text.appTokensDescription\"\n [keywords]=\"text.appTokensKeywords\">\n <spine-app-tokens />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.logout\" [description]=\"text.logoutDescription\" [keywords]=\"text.logoutKeywords\">\n <hm-button emphasis=\"medium\" intent=\"danger\" icon=\"logout\" (clicked)=\"logout()\">{{ text.logout }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Conta e seguran\u00E7a \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- API da conta do Argos com o token da sess\u00E3o (Bearer): MFA, c\u00F3digos de recupera\u00E7\u00E3o e sess\u00F5es aqui\n mesmo, sem abrir o Argos. O spineAccountSecurity d\u00E1 \u00E0s linhas o mesmo estado (reautentica\u00E7\u00E3o,\n cadastro em andamento), que morre quando o painel fecha. -->\n @if (accountSecurity) {\n <hm-settings-section spineAccountSecurity [key]=\"keys.security\" [label]=\"text.security\" icon=\"shield-check\" [description]=\"text.securityDescription\" [keywords]=\"text.securityKeywords\">\n @if (accountLogin || accountRealm) {\n <hm-settings-row [label]=\"text.signedInAccount\">\n @if (accountRealm) { <span rowDescription class=\"sp-settings__hint\">{{ fmt(text.signedInRealm, { realm: accountRealm }) }}</span> }\n <span class=\"sp-settings__value\">{{ profile.name ?? text.user }}@if (accountLogin) { <span class=\"sp-settings__muted\"> \u00B7 {{ accountLogin }}</span> }</span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.twoFactor\" [description]=\"text.twoFactorDescription\" [keywords]=\"text.twoFactorKeywords\" [stacked]=\"true\">\n <spine-account-mfa />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.recoveryCodes\" [description]=\"text.recoveryCodesDescription\" [keywords]=\"text.recoveryCodesKeywords\" [stacked]=\"true\">\n <spine-account-recovery-codes />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.sessions\" [description]=\"text.sessionsDescription\" [keywords]=\"text.sessionsKeywords\" [stacked]=\"true\">\n <spine-account-sessions />\n </hm-settings-row>\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Apar\u00EAncia \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.appearance\" [label]=\"text.appearance\" icon=\"color-palette\" [keywords]=\"text.appearanceKeywords\">\n <hm-settings-row [label]=\"text.theme\" [description]=\"text.themeDescription\" [keywords]=\"text.themeKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.theme\" [options]=\"themeOptions()\" [value]=\"themeMode()\" (valueChange)=\"setThemeMode($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.accent\" [description]=\"text.accentDescription\" [keywords]=\"text.accentKeywords\" [stacked]=\"true\">\n <hm-theme-picker\n [families]=\"families\"\n [family]=\"family()\"\n [theme]=\"appliedTheme()\"\n [showMode]=\"false\"\n (familyChosen)=\"setFamily($event)\"\n />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.density\" [description]=\"text.densityDescription\" [keywords]=\"text.densityKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.density\" [options]=\"densityOptions()\" [value]=\"densityValue()\" (valueChange)=\"setDensity($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.font\" [description]=\"text.fontDescription\" [keywords]=\"text.fontKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.font\" [options]=\"fontOptions\" [value]=\"fontValue()\" (valueChange)=\"setFont($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.sidebar\" [description]=\"text.sidebarDescription\" [keywords]=\"text.sidebarKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.sidebar\" [options]=\"sidebarOptions()\" [value]=\"sidebar.mode()\" (valueChange)=\"setSidebarMode($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Regi\u00E3o \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.region\" [label]=\"text.region\" icon=\"clock\" [keywords]=\"text.regionKeywords\">\n <hm-settings-row [label]=\"text.language\" [description]=\"text.languageDescription\" [keywords]=\"text.languageKeywords\">\n <hm-select\n class=\"sp-settings__language\"\n [label]=\"text.language\"\n [labelHidden]=\"true\"\n [value]=\"preferences.language()\"\n (valueChange)=\"preferences.setLanguage($event ?? '')\"\n >\n @for (option of languageOptions(); track option.value) {\n <option [value]=\"option.value\" [attr.lang]=\"option.value || null\">{{ option.label }}</option>\n }\n </hm-select>\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.timeZone\" [keywords]=\"text.timeZoneKeywords\">\n <span rowDescription class=\"sp-settings__hint\">{{ fmt(text.timeZoneHint, { now: timeZonePreview() }) }}</span>\n <hm-combobox\n class=\"sp-settings__timezone\"\n [label]=\"text.timeZone\"\n [labelHidden]=\"true\"\n icon=\"world\"\n [options]=\"timeZoneOptions()\"\n [value]=\"preferences.timeZone()\"\n (valueChange)=\"preferences.setTimeZone($event)\"\n />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Atalhos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.shortcuts\" [label]=\"text.shortcuts\" icon=\"keyboard\" [description]=\"text.shortcutsDescription\" [keywords]=\"text.shortcutsKeywords\">\n <!-- Mesma lista do painel \"?\", sem busca (a do di\u00E1logo de configura\u00E7\u00F5es j\u00E1 cobre a se\u00E7\u00E3o);\n o interruptor de tecla \u00FAnica (WCAG 2.1.4) persiste no SpinePreferencesService. -->\n <hm-shortcut-list characterKeysToggle [registered]=\"false\" [items]=\"shortcutItems()\" [headingLevel]=\"4\" />\n <hm-settings-row [label]=\"text.tour\" [description]=\"text.tourDescription\" [keywords]=\"text.tourKeywords\">\n <hm-button emphasis=\"medium\" icon=\"play\" (clicked)=\"replayTour()\">{{ text.replayTour }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Se\u00E7\u00F5es do app (provideSettingsSection) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (s of appSections; track s.key) {\n <hm-settings-section [key]=\"s.key\" [label]=\"s.label\" [icon]=\"s.icon ?? 'cog'\" [description]=\"s.description ?? ''\" [keywords]=\"s.keywords ?? ''\">\n <ng-container *ngComponentOutlet=\"s.component\" />\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Sobre \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.about\" [label]=\"text.about\" icon=\"info-circle\" [keywords]=\"text.aboutKeywords\">\n <hm-settings-row [label]=\"text.application\">\n <span class=\"sp-settings__value\">{{ appName }}@if (packageInfo.name) { <span class=\"sp-settings__muted\"> \u00B7 {{ packageInfo.name }}</span> }</span>\n </hm-settings-row>\n <hm-settings-row [label]=\"text.version\" [keywords]=\"text.versionKeywords\">\n <span class=\"sp-settings__value sp-settings__mono\">{{ packageInfo.version ?? '\u2014' }}</span>\n </hm-settings-row>\n @if (buildDate) {\n <hm-settings-row [label]=\"text.buildDate\">\n <span class=\"sp-settings__value\">{{ buildDate }}</span>\n </hm-settings-row>\n }\n @if (packageInfo.environment) {\n <hm-settings-row [label]=\"text.environment\" [keywords]=\"text.environmentKeywords\">\n <span class=\"sp-settings__value\">{{ packageInfo.environment }}</span>\n </hm-settings-row>\n }\n @if (packageInfo.repository?.url || packageInfo.docs || packageInfo.issues) {\n <hm-settings-row [label]=\"text.links\" [keywords]=\"text.linksKeywords\">\n <span class=\"sp-settings__links\">\n @if (packageInfo.docs) { <hm-button emphasis=\"minimal\" icon=\"book\" [link]=\"packageInfo.docs\" [newTab]=\"true\">{{ text.docs }}</hm-button> }\n @if (packageInfo.issues) { <hm-button emphasis=\"minimal\" icon=\"bug\" [link]=\"packageInfo.issues\" [newTab]=\"true\">{{ text.reportIssue }}</hm-button> }\n @if (packageInfo.repository?.url) { <hm-button emphasis=\"minimal\" icon=\"link\" [link]=\"packageInfo.repository!.url!\" [newTab]=\"true\">{{ text.repository }}</hm-button> }\n </span>\n </hm-settings-row>\n }\n @if (dependencies.length) {\n <hm-settings-row [label]=\"text.dependencies\" [description]=\"text.dependenciesDescription\" [keywords]=\"text.dependenciesKeywords\" [stacked]=\"true\">\n <dl class=\"sp-settings__deps\">\n @for (d of dependencies; track d.name) {\n <div><dt>{{ d.label }}</dt><dd class=\"sp-settings__mono\">{{ d.version }}</dd></div>\n }\n </dl>\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"text.supportInfo\" [description]=\"text.supportInfoDescription\" [keywords]=\"text.supportInfoKeywords\">\n <hm-button emphasis=\"medium\" icon=\"clipboard\" (clicked)=\"copySupportInfo()\">{{ text.copy }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n </hm-settings-dialog>\n}\n", styles: ["@charset \"UTF-8\";.sp-settings__hint{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-settings__timezone{display:block;width:min(320px,100%)}.sp-settings__profile{display:flex;align-items:center;gap:var(--hm-space-xxxs);padding:var(--hm-space-xxxs) 0 var(--hm-space-sm)}.sp-settings__profile-text{display:flex;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text)}.sp-settings__profile-text span{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-settings__value{color:var(--hm-text);font-size:var(--hm-font-size-sm);text-align:end}.sp-settings__muted{color:var(--hm-muted)}.sp-settings__mono{font-family:Geist Mono,ui-monospace,monospace}.sp-settings__links{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--hm-space-nano)}.sp-settings__deps{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0}.sp-settings__deps div{display:flex;justify-content:space-between;gap:var(--hm-space-xxxs);padding:var(--hm-space-nano) var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-hover)}.sp-settings__deps dt{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-settings__deps dd{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-xs)}\n"] }]
|
|
2803
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Montado s\u00F3 enquanto aberto: nada disto (lista de fusos, leitura do token...) custa no\n carregamento de toda p\u00E1gina. -->\n@if (settings.isOpen()) {\n <hm-settings-dialog\n [heading]=\"text.heading\"\n [open]=\"true\"\n (openChange)=\"onOpenChange($event)\"\n [activeSection]=\"settings.section()\"\n (activeSectionChange)=\"settings.section.set($event)\"\n >\n <!-- \u2500\u2500 Conta \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.account\" [label]=\"text.account\" icon=\"user\" [keywords]=\"text.accountKeywords\">\n <div class=\"sp-settings__profile\">\n <hm-account-avatar [name]=\"profile.name ?? text.user\" [imageUrl]=\"profile.picture\" />\n <span class=\"sp-settings__profile-text\">\n <strong>{{ profile.name ?? text.user }}</strong>\n @if (profile.role) { <span>{{ profile.role }}</span> }\n </span>\n </div>\n\n <!-- S\u00F3 informativo: a troca de n\u00EDvel fica no menu do perfil, perto de onde se trabalha. -->\n @if (badge) {\n <hm-settings-row [label]=\"text.accessLevel\" [keywords]=\"text.accessLevelKeywords\">\n <span class=\"sp-settings__value\">{{ badge.name }}</span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.session\" [description]=\"text.sessionDescription\" [keywords]=\"text.sessionKeywords\">\n <span class=\"sp-settings__value\">{{ sessionExpiry }}</span>\n </hm-settings-row>\n\n <!-- S\u00F3 com SpineConfig.technicalDocs (padr\u00E3o: isDevMode()): token da sess\u00E3o no clipboard \u00E9 ferramenta de dev. -->\n @if (showAccessToken) {\n <hm-settings-row [label]=\"text.accessToken\" [description]=\"text.accessTokenDescription\" [keywords]=\"text.accessTokenKeywords\">\n <hm-button emphasis=\"medium\" icon=\"copy\" (clicked)=\"copyToken()\">{{ text.copy }}</hm-button>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.appTokens\" [stacked]=\"true\"\n [description]=\"text.appTokensDescription\"\n [keywords]=\"text.appTokensKeywords\">\n <spine-app-tokens />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.logout\" [description]=\"text.logoutDescription\" [keywords]=\"text.logoutKeywords\">\n <hm-button emphasis=\"medium\" intent=\"danger\" icon=\"logout\" [loading]=\"logoutPending()\" (clicked)=\"logout()\">{{ text.logout }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Conta e seguran\u00E7a \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <!-- API da conta do Argos com o token da sess\u00E3o (Bearer): MFA, c\u00F3digos de recupera\u00E7\u00E3o e sess\u00F5es aqui\n mesmo, sem abrir o Argos. O spineAccountSecurity d\u00E1 \u00E0s linhas o mesmo estado (reautentica\u00E7\u00E3o,\n cadastro em andamento), que morre quando o painel fecha. -->\n @if (accountSecurity) {\n <hm-settings-section spineAccountSecurity [key]=\"keys.security\" [label]=\"text.security\" icon=\"shield-check\" [description]=\"text.securityDescription\" [keywords]=\"text.securityKeywords\">\n @if (accountLogin || accountRealm) {\n <hm-settings-row [label]=\"text.signedInAccount\">\n @if (accountRealm) { <span rowDescription class=\"sp-settings__hint\">{{ fmt(text.signedInRealm, { realm: accountRealm }) }}</span> }\n <span class=\"sp-settings__value\">{{ profile.name ?? text.user }}@if (accountLogin) { <span class=\"sp-settings__muted\"> \u00B7 {{ accountLogin }}</span> }</span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.twoFactor\" [description]=\"text.twoFactorDescription\" [keywords]=\"text.twoFactorKeywords\" [stacked]=\"true\">\n <spine-account-mfa />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.recoveryCodes\" [description]=\"text.recoveryCodesDescription\" [keywords]=\"text.recoveryCodesKeywords\" [stacked]=\"true\">\n <spine-account-recovery-codes />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.sessions\" [description]=\"text.sessionsDescription\" [keywords]=\"text.sessionsKeywords\" [stacked]=\"true\">\n <spine-account-sessions />\n </hm-settings-row>\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Apar\u00EAncia \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.appearance\" [label]=\"text.appearance\" icon=\"color-palette\" [keywords]=\"text.appearanceKeywords\">\n <hm-settings-row [label]=\"text.theme\" [description]=\"text.themeDescription\" [keywords]=\"text.themeKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.theme\" [options]=\"themeOptions()\" [value]=\"themeMode()\" (valueChange)=\"setThemeMode($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.accent\" [description]=\"text.accentDescription\" [keywords]=\"text.accentKeywords\" [stacked]=\"true\">\n <hm-theme-picker\n [families]=\"families\"\n [family]=\"family()\"\n [theme]=\"appliedTheme()\"\n [showMode]=\"false\"\n (familyChosen)=\"setFamily($event)\"\n />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.density\" [description]=\"text.densityDescription\" [keywords]=\"text.densityKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.density\" [options]=\"densityOptions()\" [value]=\"densityValue()\" (valueChange)=\"setDensity($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.font\" [description]=\"text.fontDescription\" [keywords]=\"text.fontKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.font\" [options]=\"fontOptions\" [value]=\"fontValue()\" (valueChange)=\"setFont($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.sidebar\" [description]=\"text.sidebarDescription\" [keywords]=\"text.sidebarKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.sidebar\" [options]=\"sidebarOptions()\" [value]=\"sidebar.mode()\" (valueChange)=\"setSidebarMode($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Regi\u00E3o \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.region\" [label]=\"text.region\" icon=\"clock\" [keywords]=\"text.regionKeywords\">\n <hm-settings-row [label]=\"text.language\" [description]=\"text.languageDescription\" [keywords]=\"text.languageKeywords\">\n <hm-select\n class=\"sp-settings__language\"\n [label]=\"text.language\"\n [labelHidden]=\"true\"\n [value]=\"preferences.language()\"\n (valueChange)=\"preferences.setLanguage($event ?? '')\"\n >\n @for (option of languageOptions(); track option.value) {\n <option [value]=\"option.value\" [attr.lang]=\"option.value || null\">{{ option.label }}</option>\n }\n </hm-select>\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.timeZone\" [keywords]=\"text.timeZoneKeywords\">\n <span rowDescription class=\"sp-settings__hint\">{{ fmt(text.timeZoneHint, { now: timeZonePreview() }) }}</span>\n <hm-combobox\n class=\"sp-settings__timezone\"\n [label]=\"text.timeZone\"\n [labelHidden]=\"true\"\n icon=\"world\"\n [options]=\"timeZoneOptions()\"\n [value]=\"preferences.timeZone()\"\n (valueChange)=\"preferences.setTimeZone($event)\"\n />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Atalhos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.shortcuts\" [label]=\"text.shortcuts\" icon=\"keyboard\" [description]=\"text.shortcutsDescription\" [keywords]=\"text.shortcutsKeywords\">\n <!-- Mesma lista do painel \"?\", sem busca (a do di\u00E1logo de configura\u00E7\u00F5es j\u00E1 cobre a se\u00E7\u00E3o);\n o interruptor de tecla \u00FAnica (WCAG 2.1.4) persiste no SpinePreferencesService. -->\n <hm-shortcut-list characterKeysToggle [registered]=\"false\" [items]=\"shortcutItems()\" [headingLevel]=\"4\" />\n <hm-settings-row [label]=\"text.tour\" [description]=\"text.tourDescription\" [keywords]=\"text.tourKeywords\">\n <hm-button emphasis=\"medium\" icon=\"play\" (clicked)=\"replayTour()\">{{ text.replayTour }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Se\u00E7\u00F5es do app (provideSettingsSection) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @for (s of appSections; track s.key) {\n <hm-settings-section [key]=\"s.key\" [label]=\"s.label\" [icon]=\"s.icon ?? 'cog'\" [description]=\"s.description ?? ''\" [keywords]=\"s.keywords ?? ''\">\n <ng-container *ngComponentOutlet=\"s.component\" />\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Sobre \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <hm-settings-section [key]=\"keys.about\" [label]=\"text.about\" icon=\"info-circle\" [keywords]=\"text.aboutKeywords\">\n <hm-settings-row [label]=\"text.application\">\n <span class=\"sp-settings__value\">{{ appName }}@if (packageInfo.name) { <span class=\"sp-settings__muted\"> \u00B7 {{ packageInfo.name }}</span> }</span>\n </hm-settings-row>\n <hm-settings-row [label]=\"text.version\" [keywords]=\"text.versionKeywords\">\n <span class=\"sp-settings__value sp-settings__mono\">{{ packageInfo.version ?? '\u2014' }}</span>\n </hm-settings-row>\n @if (buildDate) {\n <hm-settings-row [label]=\"text.buildDate\">\n <span class=\"sp-settings__value\">{{ buildDate }}</span>\n </hm-settings-row>\n }\n @if (packageInfo.environment) {\n <hm-settings-row [label]=\"text.environment\" [keywords]=\"text.environmentKeywords\">\n <span class=\"sp-settings__value\">{{ packageInfo.environment }}</span>\n </hm-settings-row>\n }\n @if (packageInfo.repository?.url || packageInfo.docs || packageInfo.issues) {\n <hm-settings-row [label]=\"text.links\" [keywords]=\"text.linksKeywords\">\n <span class=\"sp-settings__links\">\n @if (packageInfo.docs) { <hm-button emphasis=\"minimal\" icon=\"book\" [link]=\"packageInfo.docs\" [newTab]=\"true\">{{ text.docs }}</hm-button> }\n @if (packageInfo.issues) { <hm-button emphasis=\"minimal\" icon=\"bug\" [link]=\"packageInfo.issues\" [newTab]=\"true\">{{ text.reportIssue }}</hm-button> }\n @if (packageInfo.repository?.url) { <hm-button emphasis=\"minimal\" icon=\"link\" [link]=\"packageInfo.repository!.url!\" [newTab]=\"true\">{{ text.repository }}</hm-button> }\n </span>\n </hm-settings-row>\n }\n @if (dependencies.length) {\n <hm-settings-row [label]=\"text.dependencies\" [description]=\"text.dependenciesDescription\" [keywords]=\"text.dependenciesKeywords\" [stacked]=\"true\">\n <dl class=\"sp-settings__deps\">\n @for (d of dependencies; track d.name) {\n <div><dt>{{ d.label }}</dt><dd class=\"sp-settings__mono\">{{ d.version }}</dd></div>\n }\n </dl>\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"text.supportInfo\" [description]=\"text.supportInfoDescription\" [keywords]=\"text.supportInfoKeywords\">\n <hm-button emphasis=\"medium\" icon=\"clipboard\" (clicked)=\"copySupportInfo()\">{{ text.copy }}</hm-button>\n </hm-settings-row>\n </hm-settings-section>\n </hm-settings-dialog>\n}\n", styles: ["@charset \"UTF-8\";.sp-settings__hint{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-settings__timezone{display:block;width:min(320px,100%)}.sp-settings__profile{display:flex;align-items:center;gap:var(--hm-space-xxxs);padding:var(--hm-space-xxxs) 0 var(--hm-space-sm)}.sp-settings__profile-text{display:flex;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text)}.sp-settings__profile-text span{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-settings__value{color:var(--hm-text);font-size:var(--hm-font-size-sm);text-align:end}.sp-settings__muted{color:var(--hm-muted)}.sp-settings__mono{font-family:Geist Mono,ui-monospace,monospace}.sp-settings__links{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--hm-space-nano)}.sp-settings__deps{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0}.sp-settings__deps div{display:flex;justify-content:space-between;gap:var(--hm-space-xxxs);padding:var(--hm-space-nano) var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-hover)}.sp-settings__deps dt{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-settings__deps dd{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-xs)}\n"] }]
|
|
2718
2804
|
}] });
|
|
2719
2805
|
/** "UTC−03:00" para um fuso IANA. */
|
|
2720
2806
|
function offsetOf(timeZone) {
|
|
@@ -3620,7 +3706,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
3620
3706
|
* (`hm-sidebar` fixa + `.main` com `hm-workspace`). **Sem barra de breadcrumb própria** — isso
|
|
3621
3707
|
* era resíduo do spine antigo (`spine-navigation`), removido: o docs não tem uma, a navegação
|
|
3622
3708
|
* inteira mora na sidebar. Entre o header e o corpo fica o `spine-connection-banner` (aviso de conexão
|
|
3623
|
-
* do `@orfeu/angular`, vazio quando `online`).
|
|
3709
|
+
* do `@orfeu/angular`, vazio quando `online`). Os avisos ficam no `spine-notification` (`NotificationService`) e
|
|
3710
|
+
* no `hm-toast-stack` (`HermesToastService`), os dois montados aqui: o app não monta nenhum.
|
|
3624
3711
|
*
|
|
3625
3712
|
* O `<router-outlet>` que sobra aqui é só pra `/no-permission` (a única rota do shell com
|
|
3626
3713
|
* `component` de verdade — o resto da árvore do backend nunca passa pelo `Router` pra renderizar,
|
|
@@ -3630,10 +3717,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
3630
3717
|
* corressem na mesma volta de detecção de mudanças.
|
|
3631
3718
|
*/
|
|
3632
3719
|
class LayoutComponent {
|
|
3633
|
-
constructor(router, theme,
|
|
3720
|
+
constructor(router, theme, shortcuts, onboarding, news) {
|
|
3634
3721
|
this.router = router;
|
|
3635
3722
|
this.theme = theme;
|
|
3636
|
-
this.auth = auth;
|
|
3637
3723
|
this.shortcuts = shortcuts;
|
|
3638
3724
|
this.onboarding = onboarding;
|
|
3639
3725
|
this.news = news;
|
|
@@ -3641,7 +3727,8 @@ class LayoutComponent {
|
|
|
3641
3727
|
this.preferences = inject(SpinePreferencesService);
|
|
3642
3728
|
this.workspace = inject(SpineWorkspaceNavService);
|
|
3643
3729
|
this.settings = inject(SpineSettingsService);
|
|
3644
|
-
|
|
3730
|
+
/** Também liga, uma vez, o aviso de falha do logout (`logoutFailed$` do `@orfeu/angular`). */
|
|
3731
|
+
this.logoutService = inject(SpineLogoutService);
|
|
3645
3732
|
this.isNoPermission = this.matchesNoPermission(this.router.url);
|
|
3646
3733
|
this.router.events.pipe(filter((e) => e instanceof NavigationEnd))
|
|
3647
3734
|
.subscribe(e => { this.isNoPermission = this.matchesNoPermission(e.urlAfterRedirects); });
|
|
@@ -3655,10 +3742,7 @@ class LayoutComponent {
|
|
|
3655
3742
|
navigate: (url) => this.router.navigateByUrl(url),
|
|
3656
3743
|
toggleTheme: () => this.theme.setTheme(this.theme.current === 'light' ? 'dark' : 'light'),
|
|
3657
3744
|
toggleFullscreen: () => this.toggleFullscreen(),
|
|
3658
|
-
logout: () =>
|
|
3659
|
-
this.userStorage.clearHistory();
|
|
3660
|
-
this.auth.logout();
|
|
3661
|
-
},
|
|
3745
|
+
logout: () => void this.logoutService.logout(),
|
|
3662
3746
|
openSettings: () => this.settings.toggle(),
|
|
3663
3747
|
});
|
|
3664
3748
|
// Link direto pra uma seção do painel de configurações: `?settings=aparencia` (ou só
|
|
@@ -3688,7 +3772,7 @@ class LayoutComponent {
|
|
|
3688
3772
|
document.exitFullscreen?.();
|
|
3689
3773
|
}
|
|
3690
3774
|
}
|
|
3691
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: LayoutComponent, deps: [{ token: i3.Router }, { token: i2$1.ThemeService }, { token:
|
|
3775
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: LayoutComponent, deps: [{ token: i3.Router }, { token: i2$1.ThemeService }, { token: ShortcutService }, { token: OnboardingService }, { token: SpineNewsService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3692
3776
|
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: LayoutComponent, isStandalone: true, selector: "spine-layout", ngImport: i0, template: `
|
|
3693
3777
|
<div class="shell">
|
|
3694
3778
|
<spine-header></spine-header>
|
|
@@ -3717,6 +3801,9 @@ class LayoutComponent {
|
|
|
3717
3801
|
</div>
|
|
3718
3802
|
</div>
|
|
3719
3803
|
<spine-notification></spine-notification>
|
|
3804
|
+
<!-- Avisos do HermesToastService (configurações, conta e segurança, auto-serviço…). Se o app também montar
|
|
3805
|
+
uma, só uma desenha (o serviço escolhe a pilha ativa): nunca em dobro. -->
|
|
3806
|
+
<hm-toast-stack />
|
|
3720
3807
|
<spine-side-panel />
|
|
3721
3808
|
<spine-modal />
|
|
3722
3809
|
<spine-shortcuts-cheatsheet></spine-shortcuts-cheatsheet>
|
|
@@ -3724,7 +3811,7 @@ class LayoutComponent {
|
|
|
3724
3811
|
<spine-docs />
|
|
3725
3812
|
<spine-news />
|
|
3726
3813
|
<spine-onboarding-overlay></spine-onboarding-overlay>
|
|
3727
|
-
`, isInline: true, styles: [":host{display:block}.shell{display:flex;flex-direction:column;height:100vh;height:100dvh;background:var(--hm-surface);color:var(--hm-text)}.body{position:relative;display:flex;flex:1;min-height:0;overflow:hidden}.main{display:flex;flex:1;min-width:0;flex-direction:column}.workspace-area{flex:1;min-height:0;display:flex;flex-direction:column;--hm-tab-strip-bg: var(--hm-surface);--hm-tab-active-bg: var(--hm-surface-raised)}hm-workspace{flex:1;min-height:0}.panel-content{display:flex;flex-direction:column;height:100%;min-height:0}\n"], dependencies: [{ kind: "component", type: SpineNewsComponent, selector: "spine-news" }, { kind: "component", type: SpineDocsComponent, selector: "spine-docs" }, { kind: "component", type: HeaderComponent, selector: "spine-header" }, { kind: "component", type: SidebarComponent, selector: "spine-sidebar" }, { kind: "component", type: SpineConnectionBannerComponent, selector: "spine-connection-banner" }, { kind: "component", type: SpinePagePanelComponent, selector: "spine-page-panel", inputs: ["url", "isFocusedPanel"] }, { kind: "directive", type: PanelLocalLinkDirective, selector: "[appPanelLocalLink]", inputs: ["appPanelLocalLink"], outputs: ["localNavigate"] }, { kind: "component", type: WorkspaceComponent, selector: "hm-workspace", inputs: ["initialTabs", "initialActiveId", "keepAlive", "label", "providedStore"], outputs: ["stateChange", "newTabRequested"] }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: ModalComponent$1, selector: "spine-modal" }, { kind: "component", type: NotificationComponent, selector: "spine-notification" }, { kind: "component", type: SidePanelComponent, selector: "spine-side-panel" }, { kind: "component", type: OnboardingOverlayComponent, selector: "spine-onboarding-overlay" }, { kind: "component", type: ShortcutsCheatsheetComponent, selector: "spine-shortcuts-cheatsheet" }, { kind: "component", type: SettingsPanelComponent, selector: "spine-settings-panel" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3814
|
+
`, isInline: true, styles: [":host{display:block}.shell{display:flex;flex-direction:column;height:100vh;height:100dvh;background:var(--hm-surface);color:var(--hm-text)}.body{position:relative;display:flex;flex:1;min-height:0;overflow:hidden}.main{display:flex;flex:1;min-width:0;flex-direction:column}.workspace-area{flex:1;min-height:0;display:flex;flex-direction:column;--hm-tab-strip-bg: var(--hm-surface);--hm-tab-active-bg: var(--hm-surface-raised)}hm-workspace{flex:1;min-height:0}.panel-content{display:flex;flex-direction:column;height:100%;min-height:0}\n"], dependencies: [{ kind: "component", type: SpineNewsComponent, selector: "spine-news" }, { kind: "component", type: SpineDocsComponent, selector: "spine-docs" }, { kind: "component", type: HeaderComponent, selector: "spine-header" }, { kind: "component", type: SidebarComponent, selector: "spine-sidebar" }, { kind: "component", type: SpineConnectionBannerComponent, selector: "spine-connection-banner" }, { kind: "component", type: SpinePagePanelComponent, selector: "spine-page-panel", inputs: ["url", "isFocusedPanel"] }, { kind: "directive", type: PanelLocalLinkDirective, selector: "[appPanelLocalLink]", inputs: ["appPanelLocalLink"], outputs: ["localNavigate"] }, { kind: "component", type: WorkspaceComponent, selector: "hm-workspace", inputs: ["initialTabs", "initialActiveId", "keepAlive", "label", "providedStore"], outputs: ["stateChange", "newTabRequested"] }, { kind: "component", type: ToastStackComponent, selector: "hm-toast-stack" }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: ModalComponent$1, selector: "spine-modal" }, { kind: "component", type: NotificationComponent, selector: "spine-notification" }, { kind: "component", type: SidePanelComponent, selector: "spine-side-panel" }, { kind: "component", type: OnboardingOverlayComponent, selector: "spine-onboarding-overlay" }, { kind: "component", type: ShortcutsCheatsheetComponent, selector: "spine-shortcuts-cheatsheet" }, { kind: "component", type: SettingsPanelComponent, selector: "spine-settings-panel" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3728
3815
|
}
|
|
3729
3816
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: LayoutComponent, decorators: [{
|
|
3730
3817
|
type: Component,
|
|
@@ -3737,6 +3824,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
3737
3824
|
SpinePagePanelComponent,
|
|
3738
3825
|
PanelLocalLinkDirective,
|
|
3739
3826
|
WorkspaceComponent,
|
|
3827
|
+
ToastStackComponent,
|
|
3740
3828
|
RouterOutlet,
|
|
3741
3829
|
ModalComponent$1,
|
|
3742
3830
|
NotificationComponent,
|
|
@@ -3772,6 +3860,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
3772
3860
|
</div>
|
|
3773
3861
|
</div>
|
|
3774
3862
|
<spine-notification></spine-notification>
|
|
3863
|
+
<!-- Avisos do HermesToastService (configurações, conta e segurança, auto-serviço…). Se o app também montar
|
|
3864
|
+
uma, só uma desenha (o serviço escolhe a pilha ativa): nunca em dobro. -->
|
|
3865
|
+
<hm-toast-stack />
|
|
3775
3866
|
<spine-side-panel />
|
|
3776
3867
|
<spine-modal />
|
|
3777
3868
|
<spine-shortcuts-cheatsheet></spine-shortcuts-cheatsheet>
|
|
@@ -3780,11 +3871,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
3780
3871
|
<spine-news />
|
|
3781
3872
|
<spine-onboarding-overlay></spine-onboarding-overlay>
|
|
3782
3873
|
`, styles: [":host{display:block}.shell{display:flex;flex-direction:column;height:100vh;height:100dvh;background:var(--hm-surface);color:var(--hm-text)}.body{position:relative;display:flex;flex:1;min-height:0;overflow:hidden}.main{display:flex;flex:1;min-width:0;flex-direction:column}.workspace-area{flex:1;min-height:0;display:flex;flex-direction:column;--hm-tab-strip-bg: var(--hm-surface);--hm-tab-active-bg: var(--hm-surface-raised)}hm-workspace{flex:1;min-height:0}.panel-content{display:flex;flex-direction:column;height:100%;min-height:0}\n"] }]
|
|
3783
|
-
}], ctorParameters: () => [{ type: i3.Router }, { type: i2$1.ThemeService }, { type:
|
|
3874
|
+
}], ctorParameters: () => [{ type: i3.Router }, { type: i2$1.ThemeService }, { type: ShortcutService }, { type: OnboardingService }, { type: SpineNewsService }] });
|
|
3784
3875
|
|
|
3785
3876
|
/**
|
|
3786
3877
|
* Generated bundle index. Do not edit.
|
|
3787
3878
|
*/
|
|
3788
3879
|
|
|
3789
|
-
export { AppLauncherComponent, HeaderComponent, LayoutComponent, OnboardingOverlayComponent, OnboardingService, SPINE_BRAND, SPINE_MARKDOWN_PURIFY_CONFIG, SPINE_SETTINGS_KEYS, SPINE_SETTINGS_SECTIONS, SettingsPanelComponent, ShortcutService, ShortcutsCheatsheetComponent, SidebarComponent, SpineAccountSecurityService, SpineAppTokensService, SpineBrandComponent, SpineConnectionBannerComponent, SpineDocsComponent, SpineDocsService, SpineMarkdownComponent, SpineNewsComponent, SpineNewsService, SpineSettingsService, accountErrorCode, argosAccountApi, findTourTarget, provideSettingsSection, sanitizeSpineMarkdownHtml };
|
|
3880
|
+
export { AppLauncherComponent, HeaderComponent, LayoutComponent, OnboardingOverlayComponent, OnboardingService, SPINE_BRAND, SPINE_MARKDOWN_PURIFY_CONFIG, SPINE_SETTINGS_KEYS, SPINE_SETTINGS_SECTIONS, SettingsPanelComponent, ShortcutService, ShortcutsCheatsheetComponent, SidebarComponent, SpineAccountSecurityService, SpineAppTokensService, SpineBrandComponent, SpineConnectionBannerComponent, SpineDocsComponent, SpineDocsService, SpineLogoutService, SpineMarkdownComponent, SpineNewsComponent, SpineNewsService, SpineSettingsService, accountErrorCode, argosAccountApi, findTourTarget, provideSettingsSection, sanitizeSpineMarkdownHtml };
|
|
3790
3881
|
//# sourceMappingURL=trismegisto-spine-layout.mjs.map
|