@trismegisto/spine 3.3.3 → 3.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +15 -2
  2. package/fesm2022/trismegisto-spine-audit.mjs +6 -6
  3. package/fesm2022/trismegisto-spine-audit.mjs.map +1 -1
  4. package/fesm2022/{trismegisto-spine-auto-service-auto-service-client.component-BAfeLyTB.mjs → trismegisto-spine-auto-service-auto-service-client.component-kAXaCM5W.mjs} +8 -8
  5. package/fesm2022/{trismegisto-spine-auto-service-auto-service-client.component-BAfeLyTB.mjs.map → trismegisto-spine-auto-service-auto-service-client.component-kAXaCM5W.mjs.map} +1 -1
  6. package/fesm2022/{trismegisto-spine-auto-service-auto-service-groups.component-BYjIQ9ID.mjs → trismegisto-spine-auto-service-auto-service-groups.component-DE2QIGRH.mjs} +251 -85
  7. package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-DE2QIGRH.mjs.map +1 -0
  8. package/fesm2022/{trismegisto-spine-auto-service-auto-service-news-page.component-CBemH4hr.mjs → trismegisto-spine-auto-service-auto-service-news-page.component-BW7BJ0oD.mjs} +2 -2
  9. package/fesm2022/{trismegisto-spine-auto-service-auto-service-news-page.component-CBemH4hr.mjs.map → trismegisto-spine-auto-service-auto-service-news-page.component-BW7BJ0oD.mjs.map} +1 -1
  10. package/fesm2022/{trismegisto-spine-auto-service-auto-service-ratings-page.component-C_THGTkw.mjs → trismegisto-spine-auto-service-auto-service-ratings-page.component-CHPU-QLJ.mjs} +2 -2
  11. package/fesm2022/{trismegisto-spine-auto-service-auto-service-ratings-page.component-C_THGTkw.mjs.map → trismegisto-spine-auto-service-auto-service-ratings-page.component-CHPU-QLJ.mjs.map} +1 -1
  12. package/fesm2022/trismegisto-spine-auto-service-auto-service-suggestions-page.component-CkiCTmXD.mjs +3733 -0
  13. package/fesm2022/trismegisto-spine-auto-service-auto-service-suggestions-page.component-CkiCTmXD.mjs.map +1 -0
  14. package/fesm2022/{trismegisto-spine-auto-service-auto-service.component-QsIQ61w2.mjs → trismegisto-spine-auto-service-auto-service.component-D58Ht9PH.mjs} +7 -7
  15. package/fesm2022/{trismegisto-spine-auto-service-auto-service.component-QsIQ61w2.mjs.map → trismegisto-spine-auto-service-auto-service.component-D58Ht9PH.mjs.map} +1 -1
  16. package/fesm2022/{trismegisto-spine-auto-service-groups.technical-_jQrg1Qh.mjs → trismegisto-spine-auto-service-groups.technical-CB1D3ANC.mjs} +12 -3
  17. package/fesm2022/trismegisto-spine-auto-service-groups.technical-CB1D3ANC.mjs.map +1 -0
  18. package/fesm2022/{trismegisto-spine-auto-service-groups.usage-OvWjNeeT.mjs → trismegisto-spine-auto-service-groups.usage-De2MhlE7.mjs} +7 -2
  19. package/fesm2022/{trismegisto-spine-auto-service-groups.usage-OvWjNeeT.mjs.map → trismegisto-spine-auto-service-groups.usage-De2MhlE7.mjs.map} +1 -1
  20. package/fesm2022/{trismegisto-spine-auto-service-suggestions.technical-BtUK6sKD.mjs → trismegisto-spine-auto-service-suggestions.technical-CGBBzAR8.mjs} +14 -1
  21. package/fesm2022/{trismegisto-spine-auto-service-suggestions.technical-BtUK6sKD.mjs.map → trismegisto-spine-auto-service-suggestions.technical-CGBBzAR8.mjs.map} +1 -1
  22. package/fesm2022/{trismegisto-spine-auto-service-suggestions.usage-BwO9Hjsx.mjs → trismegisto-spine-auto-service-suggestions.usage-DnAJS-Z0.mjs} +26 -3
  23. package/fesm2022/trismegisto-spine-auto-service-suggestions.usage-DnAJS-Z0.mjs.map +1 -0
  24. package/fesm2022/trismegisto-spine-auto-service.mjs +24 -11
  25. package/fesm2022/trismegisto-spine-auto-service.mjs.map +1 -1
  26. package/fesm2022/{trismegisto-spine-layout-suggestions-page.component-xI2gDMok.mjs → trismegisto-spine-layout-suggestions-page.component-CEKMqera.mjs} +3 -3
  27. package/fesm2022/{trismegisto-spine-layout-suggestions-page.component-xI2gDMok.mjs.map → trismegisto-spine-layout-suggestions-page.component-CEKMqera.mjs.map} +1 -1
  28. package/fesm2022/{trismegisto-spine-layout-trismegisto-spine-layout-Clphrsgc.mjs → trismegisto-spine-layout-trismegisto-spine-layout-DiAfxIGP.mjs} +428 -93
  29. package/fesm2022/trismegisto-spine-layout-trismegisto-spine-layout-DiAfxIGP.mjs.map +1 -0
  30. package/fesm2022/trismegisto-spine-layout.mjs +1 -1
  31. package/fesm2022/trismegisto-spine-side-panel.mjs +1 -1
  32. package/fesm2022/trismegisto-spine-side-panel.mjs.map +1 -1
  33. package/fesm2022/trismegisto-spine.mjs +528 -10
  34. package/fesm2022/trismegisto-spine.mjs.map +1 -1
  35. package/package.json +6 -6
  36. package/types/trismegisto-spine-auto-service.d.ts +20 -4
  37. package/types/trismegisto-spine-auto-service.d.ts.map +1 -1
  38. package/types/trismegisto-spine-layout.d.ts +327 -63
  39. package/types/trismegisto-spine-layout.d.ts.map +1 -1
  40. package/types/trismegisto-spine.d.ts +221 -3
  41. package/types/trismegisto-spine.d.ts.map +1 -1
  42. package/fesm2022/trismegisto-spine-auto-service-auto-service-groups.component-BYjIQ9ID.mjs.map +0 -1
  43. package/fesm2022/trismegisto-spine-auto-service-auto-service-suggestions-page.component-mMjE3JOL.mjs +0 -2179
  44. package/fesm2022/trismegisto-spine-auto-service-auto-service-suggestions-page.component-mMjE3JOL.mjs.map +0 -1
  45. package/fesm2022/trismegisto-spine-auto-service-groups.technical-_jQrg1Qh.mjs.map +0 -1
  46. package/fesm2022/trismegisto-spine-auto-service-suggestions.usage-BwO9Hjsx.mjs.map +0 -1
  47. package/fesm2022/trismegisto-spine-layout-trismegisto-spine-layout-Clphrsgc.mjs.map +0 -1
@@ -3,18 +3,19 @@ import { inject, ChangeDetectorRef, ChangeDetectionStrategy, Component, Injectio
3
3
  import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
4
4
  import * as i3 from '@angular/router';
5
5
  import { RouterLink, Router, NavigationEnd, NavigationSkipped, RouterOutlet } from '@angular/router';
6
- import { firstValueFrom, filter, Subject, take, debounceTime, distinctUntilChanged } from 'rxjs';
7
- import { MenuButtonComponent, HermesThemeService, HERMES_THEME_CONFIG, HermesSidebarService, HermesToastService, IconComponent, ButtonComponent, SkeletonComponent, TextareaComponent, FloatingWindowComponent, IconButtonComponent, AccountAvatarComponent, BadgeComponent, PopoverComponent, PopoverTriggerDirective, HermesShortcutService, HeaderComponent as HeaderComponent$1, CommandPaletteComponent, createPersistedWorkspaceStore, HERMES_WORKSPACE_PERSISTENCE_CONFIG, findPanelTabs, SidebarComponent as SidebarComponent$1, SidebarNodeComponent, HermesI18nService, SegmentedControlComponent, ThemePickerComponent, AlertComponent, BreadcrumbComponent, DrawerComponent, ModalComponent, PaginatorComponent, RichTextComponent, SearchFieldComponent, ShortcutListComponent, TagComponent, CodeInputComponent, InputComponent, RadioComponent, CheckboxComponent, CodeViewerComponent, SelectComponent, AvatarGroupComponent, HERMES_SIDEBAR_CONFIG, ComboboxComponent, SettingsDialogComponent, SettingsRowComponent, SettingsSectionComponent, ToggletipComponent, TooltipDirective, LoadingSquareComponent, WorkspaceComponent, ToastStackComponent, MessageScrollerComponent, BubbleComponent, MessageComponent, MessageDividerComponent } from '@trismegisto/angular';
6
+ import { firstValueFrom, filter, Subject, take, Subscription, debounceTime, distinctUntilChanged } from 'rxjs';
7
+ import { MenuButtonComponent, HermesThemeService, HERMES_THEME_CONFIG, HermesSidebarService, HermesToastService, IconComponent, ButtonComponent, SkeletonComponent, TextareaComponent, FloatingWindowComponent, IconButtonComponent, AccountAvatarComponent, BadgeComponent, PopoverComponent, PopoverTriggerDirective, HermesShortcutService, HeaderComponent as HeaderComponent$1, CommandPaletteComponent, panelIdsInOrder, createPersistedWorkspaceStore, HERMES_WORKSPACE_PERSISTENCE_CONFIG, findPanelTabs, canSplitPanel, cyclicPanelNeighbor, ContextMenuDirective, SidebarComponent as SidebarComponent$1, SidebarNodeComponent, HermesI18nService, SegmentedControlComponent, ThemePickerComponent, AlertComponent, BreadcrumbComponent, DrawerComponent, ModalComponent, PaginatorComponent, RichTextComponent, SearchFieldComponent, ShortcutListComponent, TagComponent, CodeInputComponent, InputComponent, RadioComponent, CheckboxComponent, CodeViewerComponent, SelectComponent, AvatarGroupComponent, HERMES_SIDEBAR_CONFIG, ComboboxComponent, SettingsDialogComponent, SettingsRowComponent, SettingsSectionComponent, ToggletipComponent, TooltipDirective, LoadingSquareComponent, registerUnsavedChanges, HermesUnsavedChangesService, WorkspaceComponent, ToastStackComponent, SkipLinkComponent, MessageScrollerComponent, BubbleComponent, MessageComponent, MessageDividerComponent } from '@trismegisto/angular';
8
+ import { HermesSidebarService as HermesSidebarService$1 } from '@trismegisto/angular/sidebar';
8
9
  import { ModalComponent as ModalComponent$1 } from '@trismegisto/spine/modal';
9
10
  import { SidePanelComponent } from '@trismegisto/spine/side-panel';
10
11
  import * as i2$1 from '@trismegisto/spine';
11
12
  import { SPINE_MESSAGES, SPINE_CONFIG, ThemeService, resolveSpineLogo, SpinePreferencesService, SpineQuickAccessService, DensityService, ThemePresetService, SPINE_ADMIN_API, SpineUserStorage, SPINE_USER_STORAGE_KEYS, spineRoutesOf, routerIdOf, SPINE_IDLE_OPTIONS, APP_PACKAGE_INFO, registerSpineBuiltinPage } from '@trismegisto/spine';
12
13
  import * as i2 from '@angular/common';
13
- import { CommonModule, NgTemplateOutlet, NgComponentOutlet, isPlatformBrowser } from '@angular/common';
14
+ import { CommonModule, NgTemplateOutlet, NgComponentOutlet, isPlatformBrowser, Location } from '@angular/common';
14
15
  import * as i1 from '@orfeu/angular/core';
15
16
  import { AuthorizationService, AUTH_CONFIG, ViewService, RealmAppsService } from '@orfeu/angular/core';
16
17
  import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http';
17
- import { urlPath, formatMessage, pluralMessage, tabUrl, HERMES_FONTS, HERMES_TEXT_SCALES, detectShortcutPlatform, formatShortcut, formatMessageHtml, weekdayNames, FALLBACK_FAMILY, isMessageContinuation } from '@trismegisto/primitives';
18
+ import { urlPath, formatMessage, pluralMessage, detectShortcutPlatform, tabUrl, HERMES_FONTS, HERMES_TEXT_SCALES, formatShortcut, formatMessageHtml, weekdayNames, FALLBACK_FAMILY, findPanelTabs as findPanelTabs$1, isMessageContinuation } from '@trismegisto/primitives';
18
19
  import { NotificationService, AlertType } from '@trismegisto/spine/notification';
19
20
  import { resolveEffectiveRouteChain, ComponentDistributorService, componentRouterId, LoggerService } from '@trismegisto/spine/routing';
20
21
  import * as i1$1 from '@angular/forms';
@@ -334,8 +335,8 @@ const READY_TIMEOUT_MS = 2000;
334
335
  const SETUP_DONE_SUFFIX = '_setup_done';
335
336
  /**
336
337
  * Chaves que são da pessoa, não do app: com o Argos que tem `{api}/preferences/global`, vão no documento global e
337
- * valem em todos os apps (escolhidas uma vez). O resto (menu lateral, atalhos, inatividade, tela inicial, avisos,
338
- * favoritos) continua no documento do app.
338
+ * valem em todos os apps (escolhidas uma vez). O resto (menu lateral, dividir abas, atalhos, inatividade, tela inicial,
339
+ * avisos, favoritos) continua no documento do app.
339
340
  */
340
341
  const SPINE_GLOBAL_PREFERENCE_KEYS = [
341
342
  'themeMode', 'family', 'density', 'font', 'textScale', 'motion', 'language', 'timeZone', 'hourCycle', 'weekStart',
@@ -343,8 +344,8 @@ const SPINE_GLOBAL_PREFERENCE_KEYS = [
343
344
  /**
344
345
  * Preferências na conta (`SpineFeatures.accountPreferences`): espelha no Argos (`GET/PUT {api}/preferences`, por
345
346
  * usuário e aplicação) o que o painel de configurações guarda no navegador — tema, cor, densidade, fonte, menu
346
- * lateral, idioma, fuso, relógio, primeiro dia da semana, tamanho do texto, animações, atalhos de tecla única,
347
- * inatividade, tela inicial, tipos de aviso e favoritos. As telas Aparência, Região etc. não mudam: o
347
+ * lateral, dividir abas, idioma, fuso, relógio, primeiro dia da semana, tamanho do texto, animações, atalhos de tecla
348
+ * única, inatividade, tela inicial, tipos de aviso e favoritos. As telas Aparência, Região etc. não mudam: o
348
349
  * armazenamento local continua sendo a fonte de cada tela, e este serviço só sincroniza.
349
350
  *
350
351
  * - No início da sessão (`start()`, chamado pelo layout e pelo `/callback`), lê a conta e aplica o que estiver lá
@@ -400,6 +401,7 @@ class SpineAccountPreferencesService {
400
401
  startPage: p.startPage(),
401
402
  notices: { ...p.noticeTypes() },
402
403
  browserNotices: p.browserNotices(),
404
+ splitTabs: p.splitTabs(),
403
405
  favorites: this.quickAccess.favorites().slice(0, MAX_FAVORITES).map((f) => ({ ...f })),
404
406
  };
405
407
  }, ...(ngDevMode ? [{ debugName: "snapshot" }] : /* istanbul ignore next */ []));
@@ -553,6 +555,8 @@ class SpineAccountPreferencesService {
553
555
  // O aviso do navegador depende da permissão DESTE navegador: a conta só desliga, nunca liga sozinha.
554
556
  if (doc.browserNotices === false && p.browserNotices())
555
557
  p.setBrowserNotices(false);
558
+ if (typeof doc.splitTabs === 'boolean' && doc.splitTabs !== p.splitTabs())
559
+ p.setSplitTabs(doc.splitTabs);
556
560
  if (Array.isArray(doc.favorites)) {
557
561
  const remote = doc.favorites;
558
562
  const ids = new Set(remote.map((f) => f?.id));
@@ -1406,7 +1410,7 @@ class SpineRatingFormComponent {
1406
1410
  </div>
1407
1411
  }
1408
1412
  </div>
1409
- `, isInline: true, styles: [":host{display:block}.spine-rating{display:flex;flex-direction:column;gap:var(--hm-space-sm);inline-size:min(22rem,100%)}.spine-rating__lead{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm, 14px)}.spine-rating__about{margin:calc(-1 * var(--hm-space-xs)) 0 0;overflow:hidden;color:var(--hm-muted);font-size:var(--hm-font-size-xs, 12px);text-overflow:ellipsis;white-space:nowrap}.spine-rating__score{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-xxs, 4px)}.spine-rating__label{color:var(--hm-text);font-size:var(--hm-font-size-sm, 14px);font-weight:var(--hm-font-weight-semibold, 600)}.spine-rating__hint{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs, 12px)}.spine-rating__error{margin:0;color:var(--hm-danger);font-size:var(--hm-font-size-xs, 12px)}.spine-rating__previous{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs, 12px)}.spine-rating__actions{display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: SkeletonComponent, selector: "hm-skeleton", inputs: ["shape", "count", "label"] }, { kind: "component", type: TextareaComponent, selector: "hm-textarea", inputs: ["value", "label", "hint", "placeholder", "maxLength", "rows", "autoGrow", "labelHidden", "loading", "maxRows", "submitKey"], outputs: ["valueChange", "submitted"] }, { kind: "component", type: SpineRatingStarsComponent, selector: "spine-rating-stars", inputs: ["value", "readonly", "showValue", "showName", "size", "label", "labelledBy", "describedBy", "invalid"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1413
+ `, isInline: true, styles: [":host{display:block}.spine-rating{display:flex;flex-direction:column;gap:var(--hm-space-sm);inline-size:min(22rem,100%)}.spine-rating__lead{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm, 14px)}.spine-rating__about{margin:calc(-1 * var(--hm-space-xs)) 0 0;overflow:hidden;color:var(--hm-muted);font-size:var(--hm-font-size-xs, 12px);text-overflow:ellipsis;white-space:nowrap}.spine-rating__score{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-xxs, 4px)}.spine-rating__label{color:var(--hm-text);font-size:var(--hm-font-size-sm, 14px);font-weight:var(--hm-font-weight-semibold, 600)}.spine-rating__hint{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs, 12px)}.spine-rating__error{margin:0;color:var(--hm-danger);font-size:var(--hm-font-size-xs, 12px)}.spine-rating__previous{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs, 12px)}.spine-rating__actions{display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: SkeletonComponent, selector: "hm-skeleton", inputs: ["shape", "count", "label"] }, { kind: "component", type: TextareaComponent, selector: "hm-textarea", inputs: ["value", "label", "hint", "placeholder", "maxLength", "rows", "autoGrow", "labelHidden", "loading", "maxRows", "submitKey", "mentionCandidates", "mentionTrigger", "texts"], outputs: ["valueChange", "submitted", "mentioned"] }, { kind: "component", type: SpineRatingStarsComponent, selector: "spine-rating-stars", inputs: ["value", "readonly", "showValue", "showName", "size", "label", "labelledBy", "describedBy", "invalid"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1410
1414
  }
1411
1415
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineRatingFormComponent, decorators: [{
1412
1416
  type: Component,
@@ -1803,7 +1807,7 @@ class SpineInboxButtonComponent {
1803
1807
  }
1804
1808
  </hm-popover>
1805
1809
  }
1806
- `, isInline: true, styles: [":host{display:contents}.spine-inbox{position:relative;display:inline-flex}.spine-inbox__badge{position:absolute;inset-block-start:0;inset-inline-end:0;pointer-events:none;transform:translate(25%,-15%)}.spine-inbox__list{display:flex;flex-direction:column;inline-size:min(21rem,calc(100vw - 4rem));max-block-size:min(28rem,70dvh);margin:0;padding:0;overflow-x:hidden;overflow-y:auto;list-style:none}.spine-inbox__item{display:flex;gap:var(--hm-space-sm);inline-size:100%;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:0;border-radius:var(--hm-radius);background:none;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}.spine-inbox__item:hover{background:var(--hm-hover)}.spine-inbox__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-focus-color);outline-offset:calc(var(--hm-border-thin) * -1)}.spine-inbox__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;inline-size:2rem;block-size:2rem;border-radius:50%;background:var(--hm-accent-subtle);color:var(--hm-accent)}.spine-inbox__text{display:flex;flex:1;flex-direction:column;gap:var(--hm-space-micro);min-inline-size:0}.spine-inbox__source{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.spine-inbox__title{overflow:hidden;font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold);text-overflow:ellipsis;white-space:nowrap}.spine-inbox__body{display:-webkit-box;overflow:hidden;color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2}.spine-inbox__meta{display:flex;gap:var(--hm-space-micro);color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.spine-inbox__empty{margin:0;padding-block:var(--hm-space-sm);color:var(--hm-muted);font-size:var(--hm-font-size-sm)}\n"], dependencies: [{ kind: "component", type: AccountAvatarComponent, selector: "hm-account-avatar", inputs: ["name", "imageUrl", "size", "loading"] }, { kind: "component", type: BadgeComponent, selector: "hm-badge", inputs: ["value", "max", "dot", "intent", "label"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconButtonComponent, selector: "hm-icon-button", inputs: ["label", "icon", "solid", "iconDirection", "emphasis", "intent", "size", "disabled", "toggle", "pressed", "keyShortcuts"], outputs: ["pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: PopoverComponent, selector: "hm-popover", inputs: ["open", "heading", "headingLevel", "label", "side", "align", "arrow", "closable", "closeLabel", "closeOnOutsideClick", "openOn", "openDelay", "closeDelay"], outputs: ["openChange"] }, { kind: "directive", type: PopoverTriggerDirective, selector: "[hmPopoverTrigger]", inputs: ["hmPopoverTrigger"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1810
+ `, isInline: true, styles: [":host{display:contents}.spine-inbox{position:relative;display:inline-flex}.spine-inbox__badge{position:absolute;inset-block-start:0;inset-inline-end:0;pointer-events:none;transform:translate(25%,-15%)}.spine-inbox__list{display:flex;flex-direction:column;inline-size:min(21rem,calc(100vw - 4rem));max-block-size:min(28rem,70dvh);margin:0;padding:0;overflow-x:hidden;overflow-y:auto;list-style:none}.spine-inbox__item{display:flex;gap:var(--hm-space-sm);inline-size:100%;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:0;border-radius:var(--hm-radius);background:none;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}.spine-inbox__item:hover{background:var(--hm-hover)}.spine-inbox__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-focus-color);outline-offset:calc(var(--hm-border-thin) * -1)}.spine-inbox__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;inline-size:2rem;block-size:2rem;border-radius:50%;background:var(--hm-accent-subtle);color:var(--hm-accent)}.spine-inbox__text{display:flex;flex:1;flex-direction:column;gap:var(--hm-space-micro);min-inline-size:0}.spine-inbox__source{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.spine-inbox__title{overflow:hidden;font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold);text-overflow:ellipsis;white-space:nowrap}.spine-inbox__body{display:-webkit-box;overflow:hidden;color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2}.spine-inbox__meta{display:flex;gap:var(--hm-space-micro);color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.spine-inbox__empty{margin:0;padding-block:var(--hm-space-sm);color:var(--hm-muted);font-size:var(--hm-font-size-sm)}\n"], dependencies: [{ kind: "component", type: AccountAvatarComponent, selector: "hm-account-avatar", inputs: ["name", "imageUrl", "size", "loading", "presence", "texts"] }, { kind: "component", type: BadgeComponent, selector: "hm-badge", inputs: ["value", "max", "dot", "intent", "label"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconButtonComponent, selector: "hm-icon-button", inputs: ["label", "icon", "solid", "iconDirection", "emphasis", "intent", "size", "disabled", "toggle", "pressed", "keyShortcuts"], outputs: ["pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: PopoverComponent, selector: "hm-popover", inputs: ["open", "heading", "headingLevel", "label", "describedBy", "side", "align", "arrow", "closable", "closeLabel", "closeOnOutsideClick", "openOn", "openDelay", "closeDelay"], outputs: ["openChange"] }, { kind: "directive", type: PopoverTriggerDirective, selector: "[hmPopoverTrigger]", inputs: ["hmPopoverTrigger"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1807
1811
  }
1808
1812
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineInboxButtonComponent, decorators: [{
1809
1813
  type: Component,
@@ -1856,7 +1860,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
1856
1860
  const LOGOUT_FAILED_SECONDS = 8;
1857
1861
  /**
1858
1862
  * Saída da sessão pelo shell: todo botão/atalho de sair do spine passa por aqui (menu do perfil, painel de
1859
- * configurações, `Mod+Shift+L`, tela `/blocked`).
1863
+ * configurações, tela `/blocked`).
1860
1864
  *
1861
1865
  * - Assina `logoutFailed$` do `@orfeu/angular` (1.3.x) uma vez só: quando o logout no Argos falha (a lib repete uma
1862
1866
  * vez e mantém a sessão), mostra o aviso de erro `session.logoutFailed` (`hm-alert` de erro, `role="alert"`). Vale
@@ -2009,14 +2013,17 @@ const FAVORITE_DISPLAY_KEYS = 'G 1…9';
2009
2013
  * Atalhos de teclado globais do sistema, sobre o `HermesShortcutService` da lib (um listener só,
2010
2014
  * sequência `G H`, conflito, campo editável, interruptor de tecla única da WCAG 2.1.4).
2011
2015
  *
2012
- * `setActions` registra os atalhos do shell: `G H` (Home), `G 1`…`G 9` (favoritos), `Mod+,`
2013
- * (configurações), `Mod+.` (tema), `Mod+Shift+F` (tela cheia), `Mod+Shift+L` (sair) e `?` (painel
2014
- * de atalhos, via `help$`). Os combos com `Mod` valem também com o foco num campo; `G …` e `?`, não.
2016
+ * `setActions` registra os atalhos do shell: `G H` (Home), `G 1`…`G 9` (favoritos), `G T` (tema), `Mod+B` (menu
2017
+ * lateral), `Mod+,` (configurações) e `?` (painel de atalhos, via `help$`). `Mod+,` vale também com o foco num campo;
2018
+ * os demais, não (`Mod+B` num editor continua sendo negrito).
2019
+ *
2020
+ * Sem atalho de propósito: sair (um deslize perderia o que estava sendo digitado; fica no menu do perfil) e tela cheia
2021
+ * (o navegador já tem `F11`, e o botão fica no cabeçalho).
2015
2022
  *
2016
2023
  * O painel (`spine-shortcuts-cheatsheet` e a seção Atalhos das configurações) usa o
2017
2024
  * `hm-shortcut-list` com `panelItems()`: tudo o que está registrado no `HermesShortcutService`
2018
2025
  * (inclusive o que o app liga com `[hmShortcut]`) mais o que o spine só exibe e outra peça dispara
2019
- * (`Mod+K` do `hm-header`, `Alt+←/→` do navegador, `Esc` dos diálogos).
2026
+ * (`Mod+K` e `/` do `hm-header`, voltar/avançar do navegador, `Esc` dos diálogos).
2020
2027
  */
2021
2028
  class ShortcutService {
2022
2029
  constructor() {
@@ -2052,10 +2059,9 @@ class ShortcutService {
2052
2059
  return [
2053
2060
  { keys: ['G H'], describe: () => t.goHome, group: 'navigation', handler: () => this.navigate('/home') },
2054
2061
  { keys: FAVORITE_KEYS, describe: () => t.goFavorite, group: 'navigation', handler: (e) => this.goToFavorite(e) },
2062
+ { keys: ['Mod+B'], describe: () => t.toggleSidebar, group: 'navigation', handler: () => this.call(this.actions.toggleSidebar) },
2055
2063
  { keys: ['Mod+,'], describe: () => t.openSettings, group: 'actions', allowInInputs: true, handler: () => this.call(this.actions.openSettings) },
2056
- { keys: ['Mod+.'], describe: () => t.toggleTheme, group: 'actions', allowInInputs: true, handler: () => this.call(this.actions.toggleTheme) },
2057
- { keys: ['Mod+Shift+F'], describe: () => t.fullscreen, group: 'actions', allowInInputs: true, handler: () => this.call(this.actions.toggleFullscreen) },
2058
- { keys: ['Mod+Shift+L'], describe: () => t.logout, group: 'actions', allowInInputs: true, handler: () => this.call(this.actions.logout) },
2064
+ { keys: ['G T'], describe: () => t.toggleTheme, group: 'actions', handler: () => this.call(this.actions.toggleTheme) },
2059
2065
  { keys: ['?'], describe: () => t.showHelp, group: 'general', handler: () => this.help$.next() },
2060
2066
  ];
2061
2067
  }
@@ -2063,8 +2069,9 @@ class ShortcutService {
2063
2069
  displayOnly() {
2064
2070
  const t = this.text;
2065
2071
  return [
2066
- { keys: 'Mod+K', description: t.openSearch, group: 'navigation' },
2067
- { keys: ['Alt+Left', 'Alt+Right'], description: t.history, group: 'navigation' },
2072
+ { keys: ['Mod+K', '/'], description: t.openSearch, group: 'navigation' },
2073
+ // O atalho é do navegador, e no Mac ele é outro.
2074
+ { keys: detectShortcutPlatform() === 'mac' ? ['Mod+[', 'Mod+]'] : ['Alt+Left', 'Alt+Right'], description: t.history, group: 'navigation' },
2068
2075
  { keys: 'Esc', description: t.closeOverlay, group: 'general' },
2069
2076
  ];
2070
2077
  }
@@ -2329,7 +2336,7 @@ class HeaderComponent {
2329
2336
  }
2330
2337
  return out;
2331
2338
  }
2332
- /** Chamado pelo `(searchRequested)` do `hm-header` (clique no campo ou `Ctrl K`, já tratado por ele): abre a paleta. */
2339
+ /** Chamado pelo `(searchRequested)` do `hm-header` (clique no campo, `Ctrl K` ou `/`, já tratados por ele): abre a paleta. */
2333
2340
  onSearchRequested() {
2334
2341
  this.searchCommands = this.buildSearchCommands();
2335
2342
  this.quickOpen = true;
@@ -2408,7 +2415,7 @@ class HeaderComponent {
2408
2415
  catch { /* storage cheio/indisponível */ }
2409
2416
  }
2410
2417
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: HeaderComponent, deps: [{ token: i1.AuthorizationService }, { token: i2$1.ThemeService }, { token: i2$1.ThemePresetService }, { token: i2$1.DensityService }, { token: i1.ViewService }, { token: i3.Router }, { token: ShortcutService }, { token: SPINE_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
2411
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: HeaderComponent, isStandalone: true, selector: "spine-header", ngImport: i0, template: "<hm-header [class.header-blocked]=\"headerBlocked\" (searchRequested)=\"onSearchRequested()\">\n <div *ngIf=\"headerBlocked\" class=\"header-overlay\"></div>\n\n <spine-app-launcher></spine-app-launcher>\n\n <!-- Marca: logo do app (SpineConfig.logo, padr\u00E3o assets/images/logo.svg) + SpineConfig.appName,\n levando \u00E0 primeira rota naveg\u00E1vel. Ver SpineBrandComponent. -->\n <spine-brand [homeRoute]=\"homeRoute\" [label]=\"text.home\" />\n\n <div actions class=\"h-right\">\n <!-- central de notifica\u00E7\u00F5es: o sino, com as novidades das fontes registradas (sem fonte, n\u00E3o aparece) -->\n <spine-inbox-button />\n\n <!-- tema claro/escuro -->\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"setTheme(themeService.current == 'light' ? 'dark' : 'light')\"\n [icon]=\"themeService.current == 'light' ? 'moon' : 'sun'\"\n [label]=\"themeService.current == 'light' ? text.themeDark : text.themeLight\">\n </hm-icon-button>\n\n <!-- densidade -->\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"setDensity()\"\n [icon]=\"isCompactDensity ? 'density-comfortable' : 'density-compact'\"\n [label]=\"isCompactDensity ? text.densityComfortable : text.densityCompact\">\n </hm-icon-button>\n\n <!-- Avaliar o {app} (SpineFeatures.ratings): a estrela abre o di\u00E1logo e fica cheia depois de avaliar. Toma o\n lugar do bot\u00E3o de tela cheia (o atalho de teclado continua); sem avalia\u00E7\u00E3o, a tela cheia volta aqui. -->\n @if (showFullscreenButton) {\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"toggleFullscreen()\"\n [icon]=\"isFullscreen ? 'shrink' : 'resize'\"\n [label]=\"isFullscreen ? text.exitFullscreen : text.fullscreen\">\n </hm-icon-button>\n } @else {\n <spine-rate-app-button />\n }\n\n <span class=\"h-divider\" aria-hidden=\"true\"></span>\n\n <!-- perfil: crach\u00E1, ajuda, tema e sair ficam dentro do menu -->\n <hm-menu-button class=\"h-profile\" [label]=\"profile.name ?? text.user\" align=\"end\" [items]=\"profileMenuItems\" (chosen)=\"onProfileMenuChosen($event)\">\n <hm-account-avatar [name]=\"profile.name ?? text.user\" [imageUrl]=\"profile.picture\"></hm-account-avatar>\n <span class=\"h-profile-info\">\n <span class=\"h-profile-name\">{{ profile.name ?? text.user }}</span>\n <span class=\"h-profile-role\" *ngIf=\"badge\">{{ badge?.name }}</span>\n </span>\n </hm-menu-button>\n </div>\n</hm-header>\n\n<!-- Busca global: paleta ancorada logo abaixo do cabe\u00E7alho. Campo vazio = favoritos, abertos h\u00E1 pouco e\n m\u00F3dulos; com texto, busca em todas as telas. Setas/Enter navegam, Esc fecha. -->\n<hm-command-palette\n [(open)]=\"quickOpen\"\n [commands]=\"searchCommands\"\n [browseGroups]=\"searchBrowse\"\n [groupIcons]=\"searchIcons\"\n [label]=\"text.search\"\n [placeholder]=\"text.searchPlaceholder\"\n/>\n\n<!-- A janela de avalia\u00E7\u00E3o (flutuante, canto inferior direito): \"Avaliar o {app}\" da estrela do cabe\u00E7alho e de\n Configura\u00E7\u00F5es \u203A Sobre, e \"Avaliar esta tela\" da barra da p\u00E1gina (SpineFeatures.ratings). S\u00F3 desenha com o recurso\n ligado e aberta. -->\n<spine-rating-window />\n", styles: ["@charset \"UTF-8\";:host{display:contents}.header-overlay{position:absolute;inset:0;z-index:9999;cursor:not-allowed}.h-btn{display:inline-flex;align-items:center;justify-content:center;gap:3px;height:40px;min-width:40px;padding:0 10px;border:none;background:transparent;cursor:pointer;border-radius:9px;text-decoration:none;color:var(--hm-navbar-text);outline:none}.h-btn:focus-visible,.h-btn:hover{background:var(--hm-navbar-hover)}.h-btn.active{background:var(--hm-navbar-hover)}.h-right{display:flex;align-items:center;gap:var(--hm-header-action-gap)}.h-divider{width:1px;height:24px;margin:0 4px;background:var(--hm-navbar-hover)}.h-profile{display:inline-flex;align-items:center;gap:6px;height:40px;padding:0 4px 0 2px}.h-profile hm-account-avatar{background:color-mix(in srgb,var(--hm-navbar-text) 18%,transparent);color:var(--hm-navbar-text)}.h-profile-info{display:flex;flex-direction:column;min-width:0;max-width:140px;line-height:1.2;text-align:left}.h-profile-name{font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-profile-role{color:color-mix(in srgb,var(--hm-navbar-text) 65%,transparent);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: HeaderComponent$1, selector: "hm-header", inputs: ["searchPlaceholder", "searchLabel"], outputs: ["searchRequested"] }, { kind: "component", type: IconButtonComponent, selector: "hm-icon-button", inputs: ["label", "icon", "solid", "iconDirection", "emphasis", "intent", "size", "disabled", "toggle", "pressed", "keyShortcuts"], outputs: ["pressedChange", "clicked"] }, { kind: "component", type: MenuButtonComponent, selector: "hm-menu-button", inputs: ["label", "icon", "items", "showArrow", "align", "side", "size", "shape", "layer", "closeOnCheck", "disabled"], outputs: ["chosen", "checkedChange"] }, { kind: "component", type: AccountAvatarComponent, selector: "hm-account-avatar", inputs: ["name", "imageUrl", "size", "loading"] }, { kind: "component", type: CommandPaletteComponent, selector: "hm-command-palette", inputs: ["open", "commands", "shortcut", "label", "placeholder", "recentLimit", "recentIds", "closeOnRun", "browseGroups", "groupIcons"], outputs: ["openChange", "recentIdsChange", "executed"] }, { kind: "component", type: AppLauncherComponent, selector: "spine-app-launcher" }, { kind: "component", type: SpineBrandComponent, selector: "spine-brand", inputs: ["homeRoute", "label"] }, { kind: "component", type: SpineRatingWindowComponent, selector: "spine-rating-window" }, { kind: "component", type: SpineRateAppButtonComponent, selector: "spine-rate-app-button" }, { kind: "component", type: SpineInboxButtonComponent, selector: "spine-inbox-button" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2418
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: HeaderComponent, isStandalone: true, selector: "spine-header", ngImport: i0, template: "<hm-header [class.header-blocked]=\"headerBlocked\" (searchRequested)=\"onSearchRequested()\">\n <div *ngIf=\"headerBlocked\" class=\"header-overlay\"></div>\n\n <spine-app-launcher></spine-app-launcher>\n\n <!-- Marca: logo do app (SpineConfig.logo, padr\u00E3o assets/images/logo.svg) + SpineConfig.appName,\n levando \u00E0 primeira rota naveg\u00E1vel. Ver SpineBrandComponent. -->\n <spine-brand [homeRoute]=\"homeRoute\" [label]=\"text.home\" />\n\n <div actions class=\"h-right\">\n <!-- central de notifica\u00E7\u00F5es: o sino, com as novidades das fontes registradas (sem fonte, n\u00E3o aparece) -->\n <spine-inbox-button />\n\n <!-- tema claro/escuro -->\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"setTheme(themeService.current == 'light' ? 'dark' : 'light')\"\n [icon]=\"themeService.current == 'light' ? 'moon' : 'sun'\"\n [label]=\"themeService.current == 'light' ? text.themeDark : text.themeLight\">\n </hm-icon-button>\n\n <!-- densidade -->\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"setDensity()\"\n [icon]=\"isCompactDensity ? 'density-comfortable' : 'density-compact'\"\n [label]=\"isCompactDensity ? text.densityComfortable : text.densityCompact\">\n </hm-icon-button>\n\n <!-- Avaliar o {app} (SpineFeatures.ratings): a estrela abre o di\u00E1logo e fica cheia depois de avaliar. Toma o\n lugar do bot\u00E3o de tela cheia (o atalho de teclado continua); sem avalia\u00E7\u00E3o, a tela cheia volta aqui. -->\n @if (showFullscreenButton) {\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"toggleFullscreen()\"\n [icon]=\"isFullscreen ? 'shrink' : 'resize'\"\n [label]=\"isFullscreen ? text.exitFullscreen : text.fullscreen\">\n </hm-icon-button>\n } @else {\n <spine-rate-app-button />\n }\n\n <span class=\"h-divider\" aria-hidden=\"true\"></span>\n\n <!-- perfil: crach\u00E1, ajuda, tema e sair ficam dentro do menu -->\n <hm-menu-button class=\"h-profile\" [label]=\"profile.name ?? text.user\" align=\"end\" [items]=\"profileMenuItems\" (chosen)=\"onProfileMenuChosen($event)\">\n <hm-account-avatar [name]=\"profile.name ?? text.user\" [imageUrl]=\"profile.picture\"></hm-account-avatar>\n <span class=\"h-profile-info\">\n <span class=\"h-profile-name\">{{ profile.name ?? text.user }}</span>\n <span class=\"h-profile-role\" *ngIf=\"badge\">{{ badge?.name }}</span>\n </span>\n </hm-menu-button>\n </div>\n</hm-header>\n\n<!-- Busca global: paleta ancorada logo abaixo do cabe\u00E7alho. Campo vazio = favoritos, abertos h\u00E1 pouco e\n m\u00F3dulos; com texto, busca em todas as telas. Setas/Enter navegam, Esc fecha. -->\n<hm-command-palette\n [(open)]=\"quickOpen\"\n [commands]=\"searchCommands\"\n [browseGroups]=\"searchBrowse\"\n [groupIcons]=\"searchIcons\"\n [label]=\"text.search\"\n [placeholder]=\"text.searchPlaceholder\"\n/>\n\n<!-- A janela de avalia\u00E7\u00E3o (flutuante, canto inferior direito): \"Avaliar o {app}\" da estrela do cabe\u00E7alho e de\n Configura\u00E7\u00F5es \u203A Sobre, e \"Avaliar esta tela\" da barra da p\u00E1gina (SpineFeatures.ratings). S\u00F3 desenha com o recurso\n ligado e aberta. -->\n<spine-rating-window />\n", styles: ["@charset \"UTF-8\";:host{display:contents}.header-overlay{position:absolute;inset:0;z-index:9999;cursor:not-allowed}.h-btn{display:inline-flex;align-items:center;justify-content:center;gap:3px;height:40px;min-width:40px;padding:0 10px;border:none;background:transparent;cursor:pointer;border-radius:9px;text-decoration:none;color:var(--hm-navbar-text);outline:none}.h-btn:focus-visible,.h-btn:hover{background:var(--hm-navbar-hover)}.h-btn.active{background:var(--hm-navbar-hover)}.h-right{display:flex;align-items:center;gap:var(--hm-header-action-gap)}.h-divider{width:1px;height:24px;margin:0 4px;background:var(--hm-navbar-hover)}.h-profile{display:inline-flex;align-items:center;gap:6px;height:40px;padding:0 4px 0 2px}.h-profile hm-account-avatar{background:color-mix(in srgb,var(--hm-navbar-text) 18%,transparent);color:var(--hm-navbar-text)}.h-profile-info{display:flex;flex-direction:column;min-width:0;max-width:140px;line-height:1.2;text-align:left}.h-profile-name{font-size:var(--hm-font-size-label);font-weight:var(--hm-font-weight-semibold);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-profile-role{color:color-mix(in srgb,var(--hm-navbar-text) 65%,transparent);font-size:var(--hm-font-size-xs);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: HeaderComponent$1, selector: "hm-header", inputs: ["searchPlaceholder", "searchLabel"], outputs: ["searchRequested"] }, { kind: "component", type: IconButtonComponent, selector: "hm-icon-button", inputs: ["label", "icon", "solid", "iconDirection", "emphasis", "intent", "size", "disabled", "toggle", "pressed", "keyShortcuts"], outputs: ["pressedChange", "clicked"] }, { kind: "component", type: MenuButtonComponent, selector: "hm-menu-button", inputs: ["label", "icon", "items", "showArrow", "align", "side", "size", "shape", "layer", "closeOnCheck", "disabled"], outputs: ["chosen", "checkedChange"] }, { kind: "component", type: AccountAvatarComponent, selector: "hm-account-avatar", inputs: ["name", "imageUrl", "size", "loading", "presence", "texts"] }, { kind: "component", type: CommandPaletteComponent, selector: "hm-command-palette", inputs: ["open", "commands", "shortcut", "label", "placeholder", "recentLimit", "recentIds", "closeOnRun", "browseGroups", "groupIcons"], outputs: ["openChange", "recentIdsChange", "executed"] }, { kind: "component", type: AppLauncherComponent, selector: "spine-app-launcher" }, { kind: "component", type: SpineBrandComponent, selector: "spine-brand", inputs: ["homeRoute", "label"] }, { kind: "component", type: SpineRatingWindowComponent, selector: "spine-rating-window" }, { kind: "component", type: SpineRateAppButtonComponent, selector: "spine-rate-app-button" }, { kind: "component", type: SpineInboxButtonComponent, selector: "spine-inbox-button" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2412
2419
  }
2413
2420
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: HeaderComponent, decorators: [{
2414
2421
  type: Component,
@@ -2416,7 +2423,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
2416
2423
  CommonModule,
2417
2424
  HeaderComponent$1, IconButtonComponent, IconComponent, MenuButtonComponent, AccountAvatarComponent, CommandPaletteComponent,
2418
2425
  AppLauncherComponent, SpineBrandComponent, SpineRatingWindowComponent, SpineRateAppButtonComponent, SpineInboxButtonComponent,
2419
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<hm-header [class.header-blocked]=\"headerBlocked\" (searchRequested)=\"onSearchRequested()\">\n <div *ngIf=\"headerBlocked\" class=\"header-overlay\"></div>\n\n <spine-app-launcher></spine-app-launcher>\n\n <!-- Marca: logo do app (SpineConfig.logo, padr\u00E3o assets/images/logo.svg) + SpineConfig.appName,\n levando \u00E0 primeira rota naveg\u00E1vel. Ver SpineBrandComponent. -->\n <spine-brand [homeRoute]=\"homeRoute\" [label]=\"text.home\" />\n\n <div actions class=\"h-right\">\n <!-- central de notifica\u00E7\u00F5es: o sino, com as novidades das fontes registradas (sem fonte, n\u00E3o aparece) -->\n <spine-inbox-button />\n\n <!-- tema claro/escuro -->\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"setTheme(themeService.current == 'light' ? 'dark' : 'light')\"\n [icon]=\"themeService.current == 'light' ? 'moon' : 'sun'\"\n [label]=\"themeService.current == 'light' ? text.themeDark : text.themeLight\">\n </hm-icon-button>\n\n <!-- densidade -->\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"setDensity()\"\n [icon]=\"isCompactDensity ? 'density-comfortable' : 'density-compact'\"\n [label]=\"isCompactDensity ? text.densityComfortable : text.densityCompact\">\n </hm-icon-button>\n\n <!-- Avaliar o {app} (SpineFeatures.ratings): a estrela abre o di\u00E1logo e fica cheia depois de avaliar. Toma o\n lugar do bot\u00E3o de tela cheia (o atalho de teclado continua); sem avalia\u00E7\u00E3o, a tela cheia volta aqui. -->\n @if (showFullscreenButton) {\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"toggleFullscreen()\"\n [icon]=\"isFullscreen ? 'shrink' : 'resize'\"\n [label]=\"isFullscreen ? text.exitFullscreen : text.fullscreen\">\n </hm-icon-button>\n } @else {\n <spine-rate-app-button />\n }\n\n <span class=\"h-divider\" aria-hidden=\"true\"></span>\n\n <!-- perfil: crach\u00E1, ajuda, tema e sair ficam dentro do menu -->\n <hm-menu-button class=\"h-profile\" [label]=\"profile.name ?? text.user\" align=\"end\" [items]=\"profileMenuItems\" (chosen)=\"onProfileMenuChosen($event)\">\n <hm-account-avatar [name]=\"profile.name ?? text.user\" [imageUrl]=\"profile.picture\"></hm-account-avatar>\n <span class=\"h-profile-info\">\n <span class=\"h-profile-name\">{{ profile.name ?? text.user }}</span>\n <span class=\"h-profile-role\" *ngIf=\"badge\">{{ badge?.name }}</span>\n </span>\n </hm-menu-button>\n </div>\n</hm-header>\n\n<!-- Busca global: paleta ancorada logo abaixo do cabe\u00E7alho. Campo vazio = favoritos, abertos h\u00E1 pouco e\n m\u00F3dulos; com texto, busca em todas as telas. Setas/Enter navegam, Esc fecha. -->\n<hm-command-palette\n [(open)]=\"quickOpen\"\n [commands]=\"searchCommands\"\n [browseGroups]=\"searchBrowse\"\n [groupIcons]=\"searchIcons\"\n [label]=\"text.search\"\n [placeholder]=\"text.searchPlaceholder\"\n/>\n\n<!-- A janela de avalia\u00E7\u00E3o (flutuante, canto inferior direito): \"Avaliar o {app}\" da estrela do cabe\u00E7alho e de\n Configura\u00E7\u00F5es \u203A Sobre, e \"Avaliar esta tela\" da barra da p\u00E1gina (SpineFeatures.ratings). S\u00F3 desenha com o recurso\n ligado e aberta. -->\n<spine-rating-window />\n", styles: ["@charset \"UTF-8\";:host{display:contents}.header-overlay{position:absolute;inset:0;z-index:9999;cursor:not-allowed}.h-btn{display:inline-flex;align-items:center;justify-content:center;gap:3px;height:40px;min-width:40px;padding:0 10px;border:none;background:transparent;cursor:pointer;border-radius:9px;text-decoration:none;color:var(--hm-navbar-text);outline:none}.h-btn:focus-visible,.h-btn:hover{background:var(--hm-navbar-hover)}.h-btn.active{background:var(--hm-navbar-hover)}.h-right{display:flex;align-items:center;gap:var(--hm-header-action-gap)}.h-divider{width:1px;height:24px;margin:0 4px;background:var(--hm-navbar-hover)}.h-profile{display:inline-flex;align-items:center;gap:6px;height:40px;padding:0 4px 0 2px}.h-profile hm-account-avatar{background:color-mix(in srgb,var(--hm-navbar-text) 18%,transparent);color:var(--hm-navbar-text)}.h-profile-info{display:flex;flex-direction:column;min-width:0;max-width:140px;line-height:1.2;text-align:left}.h-profile-name{font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-profile-role{color:color-mix(in srgb,var(--hm-navbar-text) 65%,transparent);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
2426
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<hm-header [class.header-blocked]=\"headerBlocked\" (searchRequested)=\"onSearchRequested()\">\n <div *ngIf=\"headerBlocked\" class=\"header-overlay\"></div>\n\n <spine-app-launcher></spine-app-launcher>\n\n <!-- Marca: logo do app (SpineConfig.logo, padr\u00E3o assets/images/logo.svg) + SpineConfig.appName,\n levando \u00E0 primeira rota naveg\u00E1vel. Ver SpineBrandComponent. -->\n <spine-brand [homeRoute]=\"homeRoute\" [label]=\"text.home\" />\n\n <div actions class=\"h-right\">\n <!-- central de notifica\u00E7\u00F5es: o sino, com as novidades das fontes registradas (sem fonte, n\u00E3o aparece) -->\n <spine-inbox-button />\n\n <!-- tema claro/escuro -->\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"setTheme(themeService.current == 'light' ? 'dark' : 'light')\"\n [icon]=\"themeService.current == 'light' ? 'moon' : 'sun'\"\n [label]=\"themeService.current == 'light' ? text.themeDark : text.themeLight\">\n </hm-icon-button>\n\n <!-- densidade -->\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"setDensity()\"\n [icon]=\"isCompactDensity ? 'density-comfortable' : 'density-compact'\"\n [label]=\"isCompactDensity ? text.densityComfortable : text.densityCompact\">\n </hm-icon-button>\n\n <!-- Avaliar o {app} (SpineFeatures.ratings): a estrela abre o di\u00E1logo e fica cheia depois de avaliar. Toma o\n lugar do bot\u00E3o de tela cheia (o atalho de teclado continua); sem avalia\u00E7\u00E3o, a tela cheia volta aqui. -->\n @if (showFullscreenButton) {\n <hm-icon-button emphasis=\"subtle\" (clicked)=\"toggleFullscreen()\"\n [icon]=\"isFullscreen ? 'shrink' : 'resize'\"\n [label]=\"isFullscreen ? text.exitFullscreen : text.fullscreen\">\n </hm-icon-button>\n } @else {\n <spine-rate-app-button />\n }\n\n <span class=\"h-divider\" aria-hidden=\"true\"></span>\n\n <!-- perfil: crach\u00E1, ajuda, tema e sair ficam dentro do menu -->\n <hm-menu-button class=\"h-profile\" [label]=\"profile.name ?? text.user\" align=\"end\" [items]=\"profileMenuItems\" (chosen)=\"onProfileMenuChosen($event)\">\n <hm-account-avatar [name]=\"profile.name ?? text.user\" [imageUrl]=\"profile.picture\"></hm-account-avatar>\n <span class=\"h-profile-info\">\n <span class=\"h-profile-name\">{{ profile.name ?? text.user }}</span>\n <span class=\"h-profile-role\" *ngIf=\"badge\">{{ badge?.name }}</span>\n </span>\n </hm-menu-button>\n </div>\n</hm-header>\n\n<!-- Busca global: paleta ancorada logo abaixo do cabe\u00E7alho. Campo vazio = favoritos, abertos h\u00E1 pouco e\n m\u00F3dulos; com texto, busca em todas as telas. Setas/Enter navegam, Esc fecha. -->\n<hm-command-palette\n [(open)]=\"quickOpen\"\n [commands]=\"searchCommands\"\n [browseGroups]=\"searchBrowse\"\n [groupIcons]=\"searchIcons\"\n [label]=\"text.search\"\n [placeholder]=\"text.searchPlaceholder\"\n/>\n\n<!-- A janela de avalia\u00E7\u00E3o (flutuante, canto inferior direito): \"Avaliar o {app}\" da estrela do cabe\u00E7alho e de\n Configura\u00E7\u00F5es \u203A Sobre, e \"Avaliar esta tela\" da barra da p\u00E1gina (SpineFeatures.ratings). S\u00F3 desenha com o recurso\n ligado e aberta. -->\n<spine-rating-window />\n", styles: ["@charset \"UTF-8\";:host{display:contents}.header-overlay{position:absolute;inset:0;z-index:9999;cursor:not-allowed}.h-btn{display:inline-flex;align-items:center;justify-content:center;gap:3px;height:40px;min-width:40px;padding:0 10px;border:none;background:transparent;cursor:pointer;border-radius:9px;text-decoration:none;color:var(--hm-navbar-text);outline:none}.h-btn:focus-visible,.h-btn:hover{background:var(--hm-navbar-hover)}.h-btn.active{background:var(--hm-navbar-hover)}.h-right{display:flex;align-items:center;gap:var(--hm-header-action-gap)}.h-divider{width:1px;height:24px;margin:0 4px;background:var(--hm-navbar-hover)}.h-profile{display:inline-flex;align-items:center;gap:6px;height:40px;padding:0 4px 0 2px}.h-profile hm-account-avatar{background:color-mix(in srgb,var(--hm-navbar-text) 18%,transparent);color:var(--hm-navbar-text)}.h-profile-info{display:flex;flex-direction:column;min-width:0;max-width:140px;line-height:1.2;text-align:left}.h-profile-name{font-size:var(--hm-font-size-label);font-weight:var(--hm-font-weight-semibold);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.h-profile-role{color:color-mix(in srgb,var(--hm-navbar-text) 65%,transparent);font-size:var(--hm-font-size-xs);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n"] }]
2420
2427
  }], ctorParameters: () => [{ type: i1.AuthorizationService }, { type: i2$1.ThemeService }, { type: i2$1.ThemePresetService }, { type: i2$1.DensityService }, { type: i1.ViewService }, { type: i3.Router }, { type: ShortcutService }, { type: undefined, decorators: [{
2421
2428
  type: Optional
2422
2429
  }, {
@@ -2428,6 +2435,20 @@ const HOME_PATH = '/home';
2428
2435
  function leavesOf(node) {
2429
2436
  return node.kind === 'panel' ? [node] : [...leavesOf(node.a), ...leavesOf(node.b)];
2430
2437
  }
2438
+ /** Limite da grade de painéis do shell: no máximo 2 lado a lado e 2 empilhados (os quatro cantos da tela). */
2439
+ const SPINE_WORKSPACE_LIMITS = { maxColumns: 2, maxRows: 2 };
2440
+ /** O painel do outro lado da divisão lado a lado mais próxima de `panelId` (o primeiro dele, na ordem da árvore). */
2441
+ function besidePanel(node, panelId) {
2442
+ if (node.kind === 'panel')
2443
+ return null;
2444
+ const inA = panelIdsInOrder(node.a).includes(panelId);
2445
+ if (!inA && !panelIdsInOrder(node.b).includes(panelId))
2446
+ return null;
2447
+ const deeper = besidePanel(inA ? node.a : node.b, panelId);
2448
+ if (deeper)
2449
+ return deeper;
2450
+ return node.direction === 'row' ? panelIdsInOrder(inA ? node.b : node.a)[0] : null;
2451
+ }
2431
2452
  /** O caminho de um endereço (sem query nem fragmento). */
2432
2453
  function spinePathOf(url) {
2433
2454
  return url.split(/[?#]/)[0] || HOME_PATH;
@@ -2481,11 +2502,20 @@ function keepsIdentity(id, url) {
2481
2502
  * 3. outra aba do painel já está nesse endereço → ela é ativada;
2482
2503
  * 4. senão → a aba ativa passa a ser a da tela nova (o comportamento de sempre).
2483
2504
  * `openTab` abre (ou foca, em qualquer painel) a aba de um endereço com parâmetros.
2505
+ *
2506
+ * **Dividir abas é preferência da pessoa** (`SpinePreferencesService.splitTabs`, desligada por padrão): o shell só
2507
+ * deixa dividir a área em painéis com ela ligada (`split`). Desligada — ao carregar ou ao desligar —, um arranjo com
2508
+ * vários painéis (salvo no navegador ou feito antes) é fundido num painel só, sem perder aba (`mergePanels`).
2484
2509
  */
2485
2510
  class SpineWorkspaceNavService {
2486
2511
  constructor() {
2487
2512
  this.router = inject(Router);
2488
2513
  this.viewService = inject(ViewService);
2514
+ this.preferences = inject(SpinePreferencesService);
2515
+ /** A pessoa pode dividir a área de abas em painéis (a preferência "Dividir abas"). */
2516
+ this.split = computed(() => this.preferences.splitTabs(), ...(ngDevMode ? [{ debugName: "split" }] : /* istanbul ignore next */ []));
2517
+ /** Até onde dá para dividir (o shell passa para o `hm-workspace`; `openTab` com `beside` respeita). */
2518
+ this.limits = SPINE_WORKSPACE_LIMITS;
2489
2519
  this.store = createPersistedWorkspaceStore({ tabs: [this.tabFor(HOME_PATH)], activeId: HOME_PATH }, (id, saved) => this.restoreTab(id, saved), inject(HERMES_WORKSPACE_PERSISTENCE_CONFIG));
2490
2520
  this.state = signal(this.store.getState(), ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
2491
2521
  this.root = computed(() => this.state().root, ...(ngDevMode ? [{ debugName: "root" }] : /* istanbul ignore next */ []));
@@ -2502,6 +2532,13 @@ class SpineWorkspaceNavService {
2502
2532
  /** O último endereço que o `Router` mostrou (normalizado, com a query): o `effect` abaixo não navega para ele de novo. */
2503
2533
  this.lastKnownUrl = '';
2504
2534
  this.store.subscribe((s) => this.state.set(s));
2535
+ // Já antes da primeira tela: o arranjo salvo com vários painéis volta num painel só se a divisão está desligada.
2536
+ if (!this.split())
2537
+ this.mergePanels();
2538
+ effect(() => {
2539
+ if (!this.split())
2540
+ untracked(() => this.mergePanels());
2541
+ });
2505
2542
  this.router.events.subscribe((event) => {
2506
2543
  if (!(event instanceof NavigationEnd))
2507
2544
  return;
@@ -2629,6 +2666,18 @@ class SpineWorkspaceNavService {
2629
2666
  label: (id.includes('?') && cleanLabel(saved?.label)) || fresh.label,
2630
2667
  };
2631
2668
  }
2669
+ /**
2670
+ * Junta todos os painéis no focado, sem perder aba: as abas ficam na ordem dos painéis (da esquerda para a direita,
2671
+ * de cima para baixo), uma aba repetida em dois painéis vira uma só (vale a do focado), e a aba ativa do painel focado
2672
+ * continua a ativa (`WorkspaceStore.mergePanels`). Com um painel só, não faz nada.
2673
+ */
2674
+ mergePanels() {
2675
+ const { root, focusedPanelId } = this.store.getState();
2676
+ if (root.kind === 'panel')
2677
+ return;
2678
+ const ids = panelIdsInOrder(root);
2679
+ this.store.mergePanels(ids.includes(focusedPanelId) ? focusedPanelId : ids[0]);
2680
+ }
2632
2681
  /** O `+` de um painel: abre a home ali, como um navegador. */
2633
2682
  openNewTab(panelId) {
2634
2683
  this.store.openTab(panelId, this.tabFor(HOME_PATH));
@@ -2637,7 +2686,7 @@ class SpineWorkspaceNavService {
2637
2686
  * Abre (ou foca) a aba de um endereço com parâmetros — um registro por aba na mesma tela:
2638
2687
  * `openTab('/vendas/pedidos?pedido=123&secao=itens', { label: 'Pedido 123', match: ['pedido'] })`.
2639
2688
  * Se alguma aba (em qualquer painel) já tem essa identidade, ela é focada e passa a esse endereço; senão abre uma
2640
- * no painel focado (ou num painel novo ao lado, com `beside`). Só abre o que a view da pessoa permite (a mesma
2689
+ * no painel focado (ou num painel novo ao lado, com `beside` e a divisão ligada). Só abre o que a view da pessoa permite (a mesma
2641
2690
  * regra do menu e do `ViewGuard`); fora dela, não faz nada.
2642
2691
  */
2643
2692
  openTab(url, options = {}) {
@@ -2659,10 +2708,21 @@ class SpineWorkspaceNavService {
2659
2708
  }
2660
2709
  const fresh = this.tabFor(target);
2661
2710
  const tab = { ...fresh, id, ...patch, label: label ?? fresh.label, icon: options.icon ?? fresh.icon };
2662
- if (options.beside)
2663
- this.store.splitPanel(this.state().focusedPanelId, 'row', tab);
2664
- else
2665
- this.store.openTab(this.state().focusedPanelId, tab);
2711
+ const { root, focusedPanelId } = this.state();
2712
+ if (options.beside && this.split()) {
2713
+ if (canSplitPanel(root, focusedPanelId, 'row', this.limits)) {
2714
+ this.store.splitPanel(focusedPanelId, 'row', tab);
2715
+ return;
2716
+ }
2717
+ // No limite de colunas: abre no painel que já está ao lado (o do outro lado da divisão), sem dividir.
2718
+ const beside = besidePanel(root, focusedPanelId) ?? cyclicPanelNeighbor(panelIdsInOrder(root), focusedPanelId, 1);
2719
+ if (beside) {
2720
+ this.store.openTab(beside, tab);
2721
+ this.store.focusPanel(beside);
2722
+ return;
2723
+ }
2724
+ }
2725
+ this.store.openTab(focusedPanelId, tab);
2666
2726
  }
2667
2727
  /** O nome (e o ícone) de uma aba, dado pela tela (`SPINE_TAB.setLabel`). */
2668
2728
  setTabLabel(panelId, tabId, label, icon) {
@@ -2737,6 +2797,27 @@ function toSidebarNode(route) {
2737
2797
  * atual muda (navegação real feita, mesma UX do `leaveModule()` do docs).
2738
2798
  */
2739
2799
  class SidebarComponent {
2800
+ /** Botão direito (ou Shift+F10/tecla Menu, que o navegador traduz em `contextmenu`) numa página do menu. Grupo e
2801
+ * entrada de módulo não são páginas: ali o menu nativo continua valendo. */
2802
+ onContextMenu(event, menu) {
2803
+ const link = event.target.closest('a.hm-sidebar-link:not(.hm-sidebar-link--opens-menu)');
2804
+ const href = link?.getAttribute('href');
2805
+ if (!link || !href)
2806
+ return;
2807
+ event.preventDefault();
2808
+ this.menuPage = this.location.normalize(href);
2809
+ // No modo automático a coluna fecharia: o ponteiro vai para o menu, que fica fora dela.
2810
+ this.column.holdOpen(true);
2811
+ // Pelo teclado o evento pode vir sem coordenadas: abre colado no link.
2812
+ const box = link.getBoundingClientRect();
2813
+ menu.openAt(event.clientX || box.left, event.clientY || box.bottom);
2814
+ }
2815
+ onPageMenuClosed() {
2816
+ this.column.holdOpen(false);
2817
+ }
2818
+ openPageInNewTab() {
2819
+ this.workspace.openTab(this.menuPage);
2820
+ }
2740
2821
  constructor() {
2741
2822
  this.text = inject(SPINE_MESSAGES).sidebar;
2742
2823
  this.fmt = formatMessage;
@@ -2744,6 +2825,11 @@ class SidebarComponent {
2744
2825
  this.viewService = inject(ViewService);
2745
2826
  this.authService = inject(AuthorizationService);
2746
2827
  this.realmApps = inject(RealmAppsService);
2828
+ this.location = inject(Location);
2829
+ this.column = inject(HermesSidebarService);
2830
+ this.pageMenu = [{ id: 'new-tab', label: this.text.openInNewTab, icon: 'plus' }];
2831
+ /** Endereço da página em que o menu de contexto foi aberto. */
2832
+ this.menuPage = '';
2747
2833
  /** Aplicações do realm (o app atual já vem excluído pelo backend) — exibidas só no mobile. */
2748
2834
  this.apps = signal([], ...(ngDevMode ? [{ debugName: "apps" }] : /* istanbul ignore next */ []));
2749
2835
  this.currentView = toSignal(this.viewService.currentView$, { initialValue: this.viewService.currentView });
@@ -2801,11 +2887,11 @@ class SidebarComponent {
2801
2887
  this.wantsModules.set(true);
2802
2888
  }
2803
2889
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2804
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: SidebarComponent, isStandalone: true, selector: "spine-sidebar", ngImport: i0, template: "<hm-sidebar\n *ngIf=\"modules().length != 0 || menus().length != 0\"\n [label]=\"!viewingModules() && currentModule() ? fmt(text.moduleNavigation, { module: currentModule()!.route_display_name ?? '' }) : text.modules\"\n [heading]=\"!viewingModules() ? (currentModule()?.route_display_name ?? '') : ''\"\n [headingIcon]=\"currentModule()?.route_icon ?? 'grid'\"\n [backLabel]=\"!viewingModules() && modules().length > 1 ? text.modules : ''\"\n [direction]=\"viewingModules() ? 'back' : 'forward'\"\n (back)=\"showModules()\"\n>\n <!-- S\u00F3 no mobile: o seletor de aplica\u00E7\u00F5es do header some e vem para c\u00E1. -->\n @if (apps().length) {\n <section class=\"spine-sidebar-apps\" [attr.aria-label]=\"text.apps\">\n <p class=\"spine-sidebar-apps__title\"><hm-icon shape=\"grid\" /> {{ text.apps }}</p>\n @for (app of apps(); track app.app_url) {\n <a class=\"spine-sidebar-apps__link\" [href]=\"app.app_url\" target=\"_blank\" rel=\"noopener noreferrer\">\n <hm-icon shape=\"applications\" />\n <span>{{ app.name }}</span>\n </a>\n }\n </section>\n }\n @if (viewingModules()) {\n @for (m of modules(); track m.id) {\n <hm-sidebar-node [node]=\"m\" [depth]=\"0\" />\n }\n } @else {\n @for (menu of menus(); track menu.id) {\n <!-- Infinity, n\u00E3o um n\u00FAmero fixo: o backend do spine j\u00E1 usa `route_type` pra limitar a\n \u00E1rvore a 6 n\u00EDveis (header\u2192navigation\u2192menu\u2192item\u2192submenu\u2192page \u2014 ver `route.model.ts`),\n ent\u00E3o n\u00E3o h\u00E1 necessidade de um segundo teto aqui. Um teto baixo (achado ao vivo,\n 2026-09-28: `depth=2`) for\u00E7ava o pen\u00FAltimo n\u00EDvel a virar link plano mesmo tendo\n filhos reais (ex.: \"submenu\" com p\u00E1ginas embaixo sumia da \u00E1rvore vis\u00EDvel). -->\n <hm-sidebar-node [node]=\"menu\" [depth]=\"maxDepth\" />\n }\n }\n</hm-sidebar>\n", styles: [".spine-sidebar-apps{display:none}@media(max-width:767px){.spine-sidebar-apps{display:flex;flex-direction:column;gap:var(--hm-space-micro);margin-bottom:var(--hm-space-sm);padding-bottom:var(--hm-space-sm);border-bottom:var(--hm-border-hairline) solid var(--hm-divider)}.spine-sidebar-apps__title{display:flex;align-items:center;gap:var(--hm-space-nano);margin:0 var(--hm-space-xxxs);color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.04em;text-transform:uppercase}.spine-sidebar-apps__link{display:flex;align-items:center;gap:var(--hm-space-nano);min-height:var(--hm-space-2xl);padding:var(--hm-space-nano) var(--hm-space-xxxs);border-radius:var(--hm-radius-control);color:inherit;font-size:var(--hm-font-size-sm);text-decoration:none}.spine-sidebar-apps__link:hover,.spine-sidebar-apps__link:focus-visible{background:var(--hm-hover)}}:host{display:flex;flex-direction:column;height:100%}:host ::ng-deep .hm-sidebar{height:100%;flex-grow:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SidebarComponent$1, selector: "hm-sidebar", inputs: ["label", "heading", "headingIcon", "backLabel", "direction", "collapsible"], outputs: ["back"] }, { kind: "component", type: SidebarNodeComponent, selector: "hm-sidebar-node", inputs: ["node", "depth"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2890
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: SidebarComponent, isStandalone: true, selector: "spine-sidebar", ngImport: i0, template: "<span hidden [hmContextMenu]=\"pageMenu\" #pageContextMenu=\"hmContextMenu\" (chosen)=\"openPageInNewTab()\" (closed)=\"onPageMenuClosed()\"></span>\n<hm-sidebar\n *ngIf=\"modules().length != 0 || menus().length != 0\"\n [label]=\"!viewingModules() && currentModule() ? fmt(text.moduleNavigation, { module: currentModule()!.route_display_name ?? '' }) : text.modules\"\n [heading]=\"!viewingModules() ? (currentModule()?.route_display_name ?? '') : ''\"\n [headingIcon]=\"currentModule()?.route_icon ?? 'grid'\"\n [backLabel]=\"!viewingModules() && modules().length > 1 ? text.modules : ''\"\n [direction]=\"viewingModules() ? 'back' : 'forward'\"\n (back)=\"showModules()\"\n (contextmenu)=\"onContextMenu($event, pageContextMenu)\"\n>\n <!-- S\u00F3 no mobile: o seletor de aplica\u00E7\u00F5es do header some e vem para c\u00E1. -->\n @if (apps().length) {\n <section class=\"spine-sidebar-apps\" [attr.aria-label]=\"text.apps\">\n <p class=\"spine-sidebar-apps__title\"><hm-icon shape=\"grid\" /> {{ text.apps }}</p>\n @for (app of apps(); track app.app_url) {\n <a class=\"spine-sidebar-apps__link\" [href]=\"app.app_url\" target=\"_blank\" rel=\"noopener noreferrer\">\n <hm-icon shape=\"applications\" />\n <span>{{ app.name }}</span>\n </a>\n }\n </section>\n }\n @if (viewingModules()) {\n @for (m of modules(); track m.id) {\n <hm-sidebar-node [node]=\"m\" [depth]=\"0\" />\n }\n } @else {\n @for (menu of menus(); track menu.id) {\n <!-- Infinity, n\u00E3o um n\u00FAmero fixo: o backend do spine j\u00E1 usa `route_type` pra limitar a\n \u00E1rvore a 6 n\u00EDveis (header\u2192navigation\u2192menu\u2192item\u2192submenu\u2192page \u2014 ver `route.model.ts`),\n ent\u00E3o n\u00E3o h\u00E1 necessidade de um segundo teto aqui. Um teto baixo (achado ao vivo,\n 2026-09-28: `depth=2`) for\u00E7ava o pen\u00FAltimo n\u00EDvel a virar link plano mesmo tendo\n filhos reais (ex.: \"submenu\" com p\u00E1ginas embaixo sumia da \u00E1rvore vis\u00EDvel). -->\n <hm-sidebar-node [node]=\"menu\" [depth]=\"maxDepth\" />\n }\n }\n</hm-sidebar>\n", styles: [".spine-sidebar-apps{display:none}@media(max-width:767px){.spine-sidebar-apps{display:flex;flex-direction:column;gap:var(--hm-space-micro);margin-bottom:var(--hm-space-sm);padding-bottom:var(--hm-space-sm);border-bottom:var(--hm-border-hairline) solid var(--hm-divider)}.spine-sidebar-apps__title{display:flex;align-items:center;gap:var(--hm-space-nano);margin:0 var(--hm-space-xxxs);color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.04em;text-transform:uppercase}.spine-sidebar-apps__link{display:flex;align-items:center;gap:var(--hm-space-nano);min-height:var(--hm-space-2xl);padding:var(--hm-space-nano) var(--hm-space-xxxs);border-radius:var(--hm-radius-control);color:inherit;font-size:var(--hm-font-size-sm);text-decoration:none}.spine-sidebar-apps__link:hover,.spine-sidebar-apps__link:focus-visible{background:var(--hm-hover)}}:host{display:flex;flex-direction:column;height:100%}:host ::ng-deep .hm-sidebar{height:100%;flex-grow:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: ContextMenuDirective, selector: "[hmContextMenu]", inputs: ["hmContextMenu", "hmContextMenuLabel", "hmContextMenuDisabled", "hmContextMenuLayer", "hmContextMenuCloseOnCheck"], outputs: ["chosen", "closed", "checkedChange"], exportAs: ["hmContextMenu"] }, { kind: "component", type: SidebarComponent$1, selector: "hm-sidebar", inputs: ["label", "heading", "headingIcon", "backLabel", "direction", "collapsible"], outputs: ["back"] }, { kind: "component", type: SidebarNodeComponent, selector: "hm-sidebar-node", inputs: ["node", "depth"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2805
2891
  }
2806
2892
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SidebarComponent, decorators: [{
2807
2893
  type: Component,
2808
- args: [{ selector: 'spine-sidebar', imports: [CommonModule, SidebarComponent$1, SidebarNodeComponent, IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<hm-sidebar\n *ngIf=\"modules().length != 0 || menus().length != 0\"\n [label]=\"!viewingModules() && currentModule() ? fmt(text.moduleNavigation, { module: currentModule()!.route_display_name ?? '' }) : text.modules\"\n [heading]=\"!viewingModules() ? (currentModule()?.route_display_name ?? '') : ''\"\n [headingIcon]=\"currentModule()?.route_icon ?? 'grid'\"\n [backLabel]=\"!viewingModules() && modules().length > 1 ? text.modules : ''\"\n [direction]=\"viewingModules() ? 'back' : 'forward'\"\n (back)=\"showModules()\"\n>\n <!-- S\u00F3 no mobile: o seletor de aplica\u00E7\u00F5es do header some e vem para c\u00E1. -->\n @if (apps().length) {\n <section class=\"spine-sidebar-apps\" [attr.aria-label]=\"text.apps\">\n <p class=\"spine-sidebar-apps__title\"><hm-icon shape=\"grid\" /> {{ text.apps }}</p>\n @for (app of apps(); track app.app_url) {\n <a class=\"spine-sidebar-apps__link\" [href]=\"app.app_url\" target=\"_blank\" rel=\"noopener noreferrer\">\n <hm-icon shape=\"applications\" />\n <span>{{ app.name }}</span>\n </a>\n }\n </section>\n }\n @if (viewingModules()) {\n @for (m of modules(); track m.id) {\n <hm-sidebar-node [node]=\"m\" [depth]=\"0\" />\n }\n } @else {\n @for (menu of menus(); track menu.id) {\n <!-- Infinity, n\u00E3o um n\u00FAmero fixo: o backend do spine j\u00E1 usa `route_type` pra limitar a\n \u00E1rvore a 6 n\u00EDveis (header\u2192navigation\u2192menu\u2192item\u2192submenu\u2192page \u2014 ver `route.model.ts`),\n ent\u00E3o n\u00E3o h\u00E1 necessidade de um segundo teto aqui. Um teto baixo (achado ao vivo,\n 2026-09-28: `depth=2`) for\u00E7ava o pen\u00FAltimo n\u00EDvel a virar link plano mesmo tendo\n filhos reais (ex.: \"submenu\" com p\u00E1ginas embaixo sumia da \u00E1rvore vis\u00EDvel). -->\n <hm-sidebar-node [node]=\"menu\" [depth]=\"maxDepth\" />\n }\n }\n</hm-sidebar>\n", styles: [".spine-sidebar-apps{display:none}@media(max-width:767px){.spine-sidebar-apps{display:flex;flex-direction:column;gap:var(--hm-space-micro);margin-bottom:var(--hm-space-sm);padding-bottom:var(--hm-space-sm);border-bottom:var(--hm-border-hairline) solid var(--hm-divider)}.spine-sidebar-apps__title{display:flex;align-items:center;gap:var(--hm-space-nano);margin:0 var(--hm-space-xxxs);color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.04em;text-transform:uppercase}.spine-sidebar-apps__link{display:flex;align-items:center;gap:var(--hm-space-nano);min-height:var(--hm-space-2xl);padding:var(--hm-space-nano) var(--hm-space-xxxs);border-radius:var(--hm-radius-control);color:inherit;font-size:var(--hm-font-size-sm);text-decoration:none}.spine-sidebar-apps__link:hover,.spine-sidebar-apps__link:focus-visible{background:var(--hm-hover)}}:host{display:flex;flex-direction:column;height:100%}:host ::ng-deep .hm-sidebar{height:100%;flex-grow:1}\n"] }]
2894
+ args: [{ selector: 'spine-sidebar', imports: [CommonModule, ContextMenuDirective, SidebarComponent$1, SidebarNodeComponent, IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span hidden [hmContextMenu]=\"pageMenu\" #pageContextMenu=\"hmContextMenu\" (chosen)=\"openPageInNewTab()\" (closed)=\"onPageMenuClosed()\"></span>\n<hm-sidebar\n *ngIf=\"modules().length != 0 || menus().length != 0\"\n [label]=\"!viewingModules() && currentModule() ? fmt(text.moduleNavigation, { module: currentModule()!.route_display_name ?? '' }) : text.modules\"\n [heading]=\"!viewingModules() ? (currentModule()?.route_display_name ?? '') : ''\"\n [headingIcon]=\"currentModule()?.route_icon ?? 'grid'\"\n [backLabel]=\"!viewingModules() && modules().length > 1 ? text.modules : ''\"\n [direction]=\"viewingModules() ? 'back' : 'forward'\"\n (back)=\"showModules()\"\n (contextmenu)=\"onContextMenu($event, pageContextMenu)\"\n>\n <!-- S\u00F3 no mobile: o seletor de aplica\u00E7\u00F5es do header some e vem para c\u00E1. -->\n @if (apps().length) {\n <section class=\"spine-sidebar-apps\" [attr.aria-label]=\"text.apps\">\n <p class=\"spine-sidebar-apps__title\"><hm-icon shape=\"grid\" /> {{ text.apps }}</p>\n @for (app of apps(); track app.app_url) {\n <a class=\"spine-sidebar-apps__link\" [href]=\"app.app_url\" target=\"_blank\" rel=\"noopener noreferrer\">\n <hm-icon shape=\"applications\" />\n <span>{{ app.name }}</span>\n </a>\n }\n </section>\n }\n @if (viewingModules()) {\n @for (m of modules(); track m.id) {\n <hm-sidebar-node [node]=\"m\" [depth]=\"0\" />\n }\n } @else {\n @for (menu of menus(); track menu.id) {\n <!-- Infinity, n\u00E3o um n\u00FAmero fixo: o backend do spine j\u00E1 usa `route_type` pra limitar a\n \u00E1rvore a 6 n\u00EDveis (header\u2192navigation\u2192menu\u2192item\u2192submenu\u2192page \u2014 ver `route.model.ts`),\n ent\u00E3o n\u00E3o h\u00E1 necessidade de um segundo teto aqui. Um teto baixo (achado ao vivo,\n 2026-09-28: `depth=2`) for\u00E7ava o pen\u00FAltimo n\u00EDvel a virar link plano mesmo tendo\n filhos reais (ex.: \"submenu\" com p\u00E1ginas embaixo sumia da \u00E1rvore vis\u00EDvel). -->\n <hm-sidebar-node [node]=\"menu\" [depth]=\"maxDepth\" />\n }\n }\n</hm-sidebar>\n", styles: [".spine-sidebar-apps{display:none}@media(max-width:767px){.spine-sidebar-apps{display:flex;flex-direction:column;gap:var(--hm-space-micro);margin-bottom:var(--hm-space-sm);padding-bottom:var(--hm-space-sm);border-bottom:var(--hm-border-hairline) solid var(--hm-divider)}.spine-sidebar-apps__title{display:flex;align-items:center;gap:var(--hm-space-nano);margin:0 var(--hm-space-xxxs);color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.04em;text-transform:uppercase}.spine-sidebar-apps__link{display:flex;align-items:center;gap:var(--hm-space-nano);min-height:var(--hm-space-2xl);padding:var(--hm-space-nano) var(--hm-space-xxxs);border-radius:var(--hm-radius-control);color:inherit;font-size:var(--hm-font-size-sm);text-decoration:none}.spine-sidebar-apps__link:hover,.spine-sidebar-apps__link:focus-visible{background:var(--hm-hover)}}:host{display:flex;flex-direction:column;height:100%}:host ::ng-deep .hm-sidebar{height:100%;flex-grow:1}\n"] }]
2809
2895
  }], ctorParameters: () => [] });
2810
2896
 
2811
2897
  /**
@@ -2902,11 +2988,11 @@ class SpineInitialSetupComponent {
2902
2988
  return { id, icon, title, before, after, keys: keys ? formatShortcut(keys, this.platform)[0] : null };
2903
2989
  }
2904
2990
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineInitialSetupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2905
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: SpineInitialSetupComponent, isStandalone: true, selector: "spine-initial-setup", viewQueries: [{ propertyName: "heading", first: true, predicate: ["heading"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"setup\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"t.label\">\n <div class=\"setup__bar\">\n @if (stepTotal() > 1) {\n <span class=\"setup__step\">{{ fmt(t.step, { current: stepNumber(), total: stepTotal() }) }}</span>\n <span class=\"setup__dots\" aria-hidden=\"true\">\n <span class=\"setup__dot\" [class.setup__dot--on]=\"stepNumber() >= 1\"></span>\n <span class=\"setup__dot\" [class.setup__dot--on]=\"stepNumber() >= 2\"></span>\n </span>\n }\n @if (setup.mode() === 'full') {\n <hm-button class=\"setup__skip\" emphasis=\"minimal\" (clicked)=\"setup.finish()\">{{ t.skip }}</hm-button>\n }\n </div>\n\n @if (setup.step() === 'appearance') {\n <div class=\"setup__body\">\n <section class=\"setup__form\">\n <h1 #heading class=\"setup__title\" tabindex=\"-1\">{{ greeting }}</h1>\n <p class=\"setup__intro\">{{ t.appearanceIntro }}</p>\n\n <div class=\"setup__fields\">\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.theme }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.theme\" [options]=\"themeOptions\" [value]=\"themeMode()\" (valueChange)=\"setThemeMode($event)\" />\n </div>\n\n <div class=\"setup__field setup__field--wide\">\n <span class=\"setup__label\">{{ s.accent }}</span>\n <hm-theme-picker\n [families]=\"families\"\n [family]=\"family()\"\n [theme]=\"appliedTheme()\"\n [showMode]=\"false\"\n (familyChosen)=\"setFamily($event)\"\n />\n </div>\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.density }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.density\" [options]=\"densityOptions\" [value]=\"densityValue()\" (valueChange)=\"setDensity($event)\" />\n </div>\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.textSize }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.textSize\" [options]=\"textSizeOptions()\" [value]=\"textSizeValue()\" (valueChange)=\"setTextSize($event)\" />\n </div>\n\n @if (fontOptions.length > 1) {\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.font }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.font\" [options]=\"fontOptions\" [value]=\"fontValue()\" (valueChange)=\"setFont($event)\" />\n </div>\n }\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.motion }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.motion\" [options]=\"motionOptions\" [value]=\"preferences.motion()\" (valueChange)=\"setMotion($event)\" />\n </div>\n </div>\n\n <div class=\"setup__actions\">\n <hm-button emphasis=\"high\" iconEnd=\"arrow-right\" (clicked)=\"setup.next()\">{{ t.next }}</hm-button>\n </div>\n </section>\n\n <!-- Pr\u00E9via: um shell em miniatura pintado com os tokens do tema, ent\u00E3o acompanha cada escolha. Decorativa. -->\n <figure class=\"setup__preview\">\n <figcaption class=\"setup__preview-label\">{{ t.preview }}</figcaption>\n <div class=\"pv\" aria-hidden=\"true\">\n <div class=\"pv__header\">\n <spine-brand class=\"pv__brand\" [label]=\"appName\" />\n <span class=\"pv__search\"></span>\n <span class=\"pv__avatar\"></span>\n </div>\n <div class=\"pv__body\">\n <div class=\"pv__side\">\n <span class=\"pv__item pv__item--on\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item pv__item--short\"></span>\n </div>\n <div class=\"pv__main\">\n <div class=\"pv__tabs\">\n <span class=\"pv__tab pv__tab--on\">{{ t.previewPage }}</span>\n <span class=\"pv__tab\"><span class=\"pv__line\"></span></span>\n <span class=\"pv__tab\"><span class=\"pv__line\"></span></span>\n </div>\n <div class=\"pv__page\">\n <span class=\"pv__title\"></span>\n <div class=\"pv__cards\">\n @for (n of [1, 2, 3]; track n) {\n <span class=\"pv__card\"><span class=\"pv__num\"></span><span class=\"pv__line\"></span></span>\n }\n </div>\n <div class=\"pv__table\">\n <span class=\"pv__row pv__row--head\"></span>\n @for (n of [1, 2, 3, 4]; track n) {\n <span class=\"pv__row\"><span class=\"pv__line\"></span><span class=\"pv__line pv__line--short\"></span></span>\n }\n </div>\n <div class=\"pv__buttons\">\n <span class=\"pv__btn pv__btn--ghost\"></span>\n <span class=\"pv__btn\"></span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </figure>\n </div>\n } @else {\n <div class=\"setup__body setup__body--essentials\">\n <section class=\"setup__essentials\">\n <h1 #heading class=\"setup__title\" tabindex=\"-1\">{{ t.essentialsTitle }}</h1>\n <p class=\"setup__intro\">{{ t.essentialsIntro }}</p>\n\n <ul class=\"setup__cards\">\n @for (card of essentials; track card.id) {\n <li class=\"setup__card\">\n <span class=\"setup__card-icon\"><hm-icon [shape]=\"card.icon\" /></span>\n <h2 class=\"setup__card-title\">{{ card.title }}</h2>\n <p class=\"setup__card-text\">\n {{ card.before }}@if (card.keys) {<span class=\"setup__keys\">@for (step of card.keys; track $index) {@for (key of step; track $index) {<kbd>{{ key.label }}</kbd>}}</span>}{{ card.after }}\n </p>\n </li>\n }\n </ul>\n\n <div class=\"setup__actions\">\n @if (setup.mode() === 'full') {\n <hm-button emphasis=\"medium\" icon=\"arrow-left\" (clicked)=\"setup.back()\">{{ t.back }}</hm-button>\n <hm-button emphasis=\"high\" (clicked)=\"setup.finish()\">{{ t.finish }}</hm-button>\n } @else {\n <hm-button emphasis=\"high\" (clicked)=\"setup.finish()\">{{ t.close }}</hm-button>\n }\n </div>\n </section>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{position:fixed;inset:0;z-index:var(--hm-z-overlay);display:block;overflow:auto;background:var(--hm-surface);color:var(--hm-text)}.setup{display:flex;flex-direction:column;min-height:100%;max-width:76rem;margin:0 auto;padding:var(--hm-space-lg) var(--hm-space-xl) var(--hm-space-2xl);box-sizing:border-box}.setup__bar{display:flex;align-items:center;gap:var(--hm-space-md);min-height:var(--hm-control-height, 40px)}.setup__step{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.setup__dots{display:inline-flex;gap:6px}.setup__dot{width:24px;height:4px;border-radius:var(--hm-radius-pill);background:var(--hm-border)}.setup__dot--on{background:var(--hm-accent)}.setup__skip{margin-left:auto}.setup__body{flex:1;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);gap:clamp(var(--hm-space-xl),5vw,5rem);align-items:center;padding-top:var(--hm-space-xl)}.setup__body--essentials{grid-template-columns:minmax(0,1fr);justify-items:center}.setup__title{margin:0;font-size:var(--hm-font-size-xxl);font-weight:650;line-height:1.2;letter-spacing:-.01em;outline:none}.setup__intro{margin:var(--hm-space-sm) 0 0;max-width:34rem;color:var(--hm-muted);line-height:1.5}.setup__fields{display:flex;flex-direction:column;gap:var(--hm-space-lg);margin-top:var(--hm-space-xl)}.setup__field{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-xs)}.setup__field--wide{align-items:stretch}.setup__label{font-size:var(--hm-font-size-sm);font-weight:600}.setup__actions{display:flex;gap:var(--hm-space-sm);margin-top:var(--hm-space-xl)}.setup__preview{margin:0;display:flex;flex-direction:column;gap:var(--hm-space-sm)}.setup__preview-label{color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase}.pv{display:flex;flex-direction:column;aspect-ratio:16/11;overflow:hidden;border:1px solid var(--hm-border);border-radius:14px;background:var(--hm-surface);box-shadow:var(--hm-shadow-lg)}.pv__header{display:flex;align-items:center;gap:10px;height:34px;padding:0 10px;background:var(--hm-navbar-surface);color:var(--hm-navbar-text);flex:none}.pv__brand{display:flex;align-items:center;height:18px;font-size:11px;pointer-events:none}.pv__brand ::ng-deep img{height:18px;width:auto}.pv__search{flex:0 1 36%;height:16px;margin-left:auto;border-radius:var(--hm-radius-pill);background:color-mix(in srgb,var(--hm-navbar-text) 16%,transparent)}.pv__avatar{width:18px;height:18px;border-radius:50%;background:color-mix(in srgb,var(--hm-navbar-text) 30%,transparent)}.pv__body{flex:1;display:flex;min-height:0}.pv__side{display:flex;flex-direction:column;gap:8px;width:22%;padding:12px 10px;background:var(--hm-sidebar-bg);border-right:1px solid var(--hm-border);box-sizing:border-box}.pv__item{height:9px;border-radius:4px;background:color-mix(in srgb,var(--hm-text) 14%,transparent)}.pv__item--on{background:var(--hm-accent)}.pv__item--short{width:60%}.pv__main{flex:1;display:flex;flex-direction:column;min-width:0}.pv__tabs{display:flex;align-items:flex-end;gap:4px;height:26px;padding:0 8px;background:var(--hm-shell-tab-strip-bg);flex:none}.pv__tab{display:flex;align-items:center;width:64px;height:20px;padding:0 8px;border-radius:7px 7px 0 0;box-sizing:border-box;font-size:9px;color:var(--hm-muted)}.pv__tab--on{background:var(--hm-surface-raised);color:var(--hm-text);font-weight:600}.pv__page{flex:1;display:flex;flex-direction:column;gap:10px;padding:12px 14px;background:var(--hm-surface-raised);min-height:0}.pv__title{width:38%;height:11px;border-radius:4px;background:color-mix(in srgb,var(--hm-text) 55%,transparent)}.pv__cards{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.pv__card{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid var(--hm-border);border-radius:8px;background:var(--hm-surface)}.pv__num{width:40%;height:10px;border-radius:3px;background:var(--hm-accent)}.pv__line{display:block;width:70%;height:6px;border-radius:3px;background:color-mix(in srgb,var(--hm-text) 18%,transparent)}.pv__line--short{width:30%}.pv__table{display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--hm-border);border-radius:8px}.pv__row{display:flex;align-items:center;justify-content:space-between;gap:8px;height:calc(16px * var(--hm-text-scale, 1));padding:0 8px;background:var(--hm-surface)}.pv__row:nth-child(odd):not(.pv__row--head){background:color-mix(in srgb,var(--hm-accent) 5%,var(--hm-surface))}:host-context([data-density=compact]) .pv__row{height:calc(12px * var(--hm-text-scale, 1))}.pv__row--head{background:color-mix(in srgb,var(--hm-accent) 20%,var(--hm-surface-raised))}.pv__buttons{display:flex;justify-content:flex-end;gap:6px;margin-top:auto}.pv__btn{width:52px;height:16px;border-radius:var(--hm-radius-control);background:var(--hm-action)}.pv__btn--ghost{background:transparent;border:1px solid var(--hm-border)}.setup__essentials{width:100%;max-width:56rem}.setup__cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:var(--hm-space-md);margin:var(--hm-space-xl) 0 0;padding:0;list-style:none}.setup__card{display:flex;flex-direction:column;gap:var(--hm-space-xs);padding:var(--hm-space-lg);border:1px solid var(--hm-border);border-radius:12px;background:var(--hm-surface-raised)}.setup__card-icon{display:inline-grid;place-items:center;width:36px;height:36px;margin-bottom:var(--hm-space-xs);border-radius:10px;background:var(--hm-accent-subtle);color:var(--hm-accent)}.setup__card-title{margin:0;font-size:var(--hm-font-size-md);font-weight:600}.setup__card-text{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-sm);line-height:1.5}.setup__keys{display:inline-flex;gap:3px;vertical-align:baseline}.setup__keys kbd{padding:0 5px;border:1px solid var(--hm-border);border-bottom-width:2px;border-radius:5px;background:var(--hm-surface);color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-xs);font-weight:600}@media(max-width:860px){.setup{padding:var(--hm-space-md)}.setup__body{grid-template-columns:minmax(0,1fr);align-items:start}.setup__preview{display:none}}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: SegmentedControlComponent, selector: "hm-segmented-control", inputs: ["options", "value", "label", "size", "emphasis"], outputs: ["valueChange"] }, { kind: "component", type: ThemePickerComponent, selector: "hm-theme-picker", inputs: ["families", "family", "theme", "showMode"], outputs: ["familyChosen", "themeChosen"] }, { kind: "component", type: SpineBrandComponent, selector: "spine-brand", inputs: ["homeRoute", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2991
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: SpineInitialSetupComponent, isStandalone: true, selector: "spine-initial-setup", viewQueries: [{ propertyName: "heading", first: true, predicate: ["heading"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"setup\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"t.label\">\n <div class=\"setup__bar\">\n @if (stepTotal() > 1) {\n <span class=\"setup__step\">{{ fmt(t.step, { current: stepNumber(), total: stepTotal() }) }}</span>\n <span class=\"setup__dots\" aria-hidden=\"true\">\n <span class=\"setup__dot\" [class.setup__dot--on]=\"stepNumber() >= 1\"></span>\n <span class=\"setup__dot\" [class.setup__dot--on]=\"stepNumber() >= 2\"></span>\n </span>\n }\n @if (setup.mode() === 'full') {\n <hm-button class=\"setup__skip\" emphasis=\"minimal\" (clicked)=\"setup.finish()\">{{ t.skip }}</hm-button>\n }\n </div>\n\n @if (setup.step() === 'appearance') {\n <div class=\"setup__body\">\n <section class=\"setup__form\">\n <h1 #heading class=\"setup__title\" tabindex=\"-1\">{{ greeting }}</h1>\n <p class=\"setup__intro\">{{ t.appearanceIntro }}</p>\n\n <div class=\"setup__fields\">\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.theme }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.theme\" [options]=\"themeOptions\" [value]=\"themeMode()\" (valueChange)=\"setThemeMode($event)\" />\n </div>\n\n <div class=\"setup__field setup__field--wide\">\n <span class=\"setup__label\">{{ s.accent }}</span>\n <hm-theme-picker\n [families]=\"families\"\n [family]=\"family()\"\n [theme]=\"appliedTheme()\"\n [showMode]=\"false\"\n (familyChosen)=\"setFamily($event)\"\n />\n </div>\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.density }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.density\" [options]=\"densityOptions\" [value]=\"densityValue()\" (valueChange)=\"setDensity($event)\" />\n </div>\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.textSize }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.textSize\" [options]=\"textSizeOptions()\" [value]=\"textSizeValue()\" (valueChange)=\"setTextSize($event)\" />\n </div>\n\n @if (fontOptions.length > 1) {\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.font }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.font\" [options]=\"fontOptions\" [value]=\"fontValue()\" (valueChange)=\"setFont($event)\" />\n </div>\n }\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.motion }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.motion\" [options]=\"motionOptions\" [value]=\"preferences.motion()\" (valueChange)=\"setMotion($event)\" />\n </div>\n </div>\n\n <div class=\"setup__actions\">\n <hm-button emphasis=\"high\" iconEnd=\"arrow-right\" (clicked)=\"setup.next()\">{{ t.next }}</hm-button>\n </div>\n </section>\n\n <!-- Pr\u00E9via: um shell em miniatura pintado com os tokens do tema, ent\u00E3o acompanha cada escolha. Decorativa. -->\n <figure class=\"setup__preview\">\n <figcaption class=\"setup__preview-label\">{{ t.preview }}</figcaption>\n <div class=\"pv\" aria-hidden=\"true\">\n <div class=\"pv__header\">\n <spine-brand class=\"pv__brand\" [label]=\"appName\" />\n <span class=\"pv__search\"></span>\n <span class=\"pv__avatar\"></span>\n </div>\n <div class=\"pv__body\">\n <div class=\"pv__side\">\n <span class=\"pv__item pv__item--on\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item pv__item--short\"></span>\n </div>\n <div class=\"pv__main\">\n <div class=\"pv__tabs\">\n <span class=\"pv__tab pv__tab--on\">{{ t.previewPage }}</span>\n <span class=\"pv__tab\"><span class=\"pv__line\"></span></span>\n <span class=\"pv__tab\"><span class=\"pv__line\"></span></span>\n </div>\n <div class=\"pv__page\">\n <span class=\"pv__title\"></span>\n <div class=\"pv__cards\">\n @for (n of [1, 2, 3]; track n) {\n <span class=\"pv__card\"><span class=\"pv__num\"></span><span class=\"pv__line\"></span></span>\n }\n </div>\n <div class=\"pv__table\">\n <span class=\"pv__row pv__row--head\"></span>\n @for (n of [1, 2, 3, 4]; track n) {\n <span class=\"pv__row\"><span class=\"pv__line\"></span><span class=\"pv__line pv__line--short\"></span></span>\n }\n </div>\n <div class=\"pv__buttons\">\n <span class=\"pv__btn pv__btn--ghost\"></span>\n <span class=\"pv__btn\"></span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </figure>\n </div>\n } @else {\n <div class=\"setup__body setup__body--essentials\">\n <section class=\"setup__essentials\">\n <h1 #heading class=\"setup__title\" tabindex=\"-1\">{{ t.essentialsTitle }}</h1>\n <p class=\"setup__intro\">{{ t.essentialsIntro }}</p>\n\n <ul class=\"setup__cards\">\n @for (card of essentials; track card.id) {\n <li class=\"setup__card\">\n <span class=\"setup__card-icon\"><hm-icon [shape]=\"card.icon\" /></span>\n <h2 class=\"setup__card-title\">{{ card.title }}</h2>\n <p class=\"setup__card-text\">\n {{ card.before }}@if (card.keys) {<span class=\"setup__keys\">@for (step of card.keys; track $index) {@for (key of step; track $index) {<kbd>{{ key.label }}</kbd>}}</span>}{{ card.after }}\n </p>\n </li>\n }\n </ul>\n\n <div class=\"setup__actions\">\n @if (setup.mode() === 'full') {\n <hm-button emphasis=\"medium\" icon=\"arrow-left\" (clicked)=\"setup.back()\">{{ t.back }}</hm-button>\n <hm-button emphasis=\"high\" (clicked)=\"setup.finish()\">{{ t.finish }}</hm-button>\n } @else {\n <hm-button emphasis=\"high\" (clicked)=\"setup.finish()\">{{ t.close }}</hm-button>\n }\n </div>\n </section>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{position:fixed;inset:0;z-index:var(--hm-z-overlay);display:block;overflow:auto;background:var(--hm-surface);color:var(--hm-text)}.setup{display:flex;flex-direction:column;min-height:100%;max-width:76rem;margin:0 auto;padding:var(--hm-space-lg) var(--hm-space-xl) var(--hm-space-2xl);box-sizing:border-box}.setup__bar{display:flex;align-items:center;gap:var(--hm-space-md);min-height:var(--hm-control-height, 40px)}.setup__step{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.setup__dots{display:inline-flex;gap:6px}.setup__dot{width:24px;height:4px;border-radius:var(--hm-radius-pill);background:var(--hm-border)}.setup__dot--on{background:var(--hm-accent)}.setup__skip{margin-left:auto}.setup__body{flex:1;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);gap:clamp(var(--hm-space-xl),5vw,5rem);align-items:center;padding-top:var(--hm-space-xl)}.setup__body--essentials{grid-template-columns:minmax(0,1fr);justify-items:center}.setup__title{margin:0;font-size:var(--hm-font-size-xxl);font-weight:var(--hm-font-weight-semibold);line-height:1.2;letter-spacing:-.01em;outline:none}.setup__intro{margin:var(--hm-space-sm) 0 0;max-width:34rem;color:var(--hm-muted);line-height:1.5}.setup__fields{display:flex;flex-direction:column;gap:var(--hm-space-lg);margin-top:var(--hm-space-xl)}.setup__field{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-xs)}.setup__field--wide{align-items:stretch}.setup__label{font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.setup__actions{display:flex;gap:var(--hm-space-sm);margin-top:var(--hm-space-xl)}.setup__preview{margin:0;display:flex;flex-direction:column;gap:var(--hm-space-sm)}.setup__preview-label{color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.06em;text-transform:uppercase}.pv{display:flex;flex-direction:column;aspect-ratio:16/11;overflow:hidden;border:1px solid var(--hm-border);border-radius:14px;background:var(--hm-surface);box-shadow:var(--hm-shadow-lg)}.pv__header{display:flex;align-items:center;gap:10px;height:34px;padding:0 10px;background:var(--hm-navbar-surface);color:var(--hm-navbar-text);flex:none}.pv__brand{display:flex;align-items:center;height:18px;font-size:var(--hm-font-size-xs);pointer-events:none}.pv__brand ::ng-deep img{height:18px;width:auto}.pv__search{flex:0 1 36%;height:16px;margin-left:auto;border-radius:var(--hm-radius-pill);background:color-mix(in srgb,var(--hm-navbar-text) 16%,transparent)}.pv__avatar{width:18px;height:18px;border-radius:50%;background:color-mix(in srgb,var(--hm-navbar-text) 30%,transparent)}.pv__body{flex:1;display:flex;min-height:0}.pv__side{display:flex;flex-direction:column;gap:8px;width:22%;padding:12px 10px;background:var(--hm-sidebar-bg);border-right:1px solid var(--hm-border);box-sizing:border-box}.pv__item{height:9px;border-radius:4px;background:color-mix(in srgb,var(--hm-text) 14%,transparent)}.pv__item--on{background:var(--hm-accent)}.pv__item--short{width:60%}.pv__main{flex:1;display:flex;flex-direction:column;min-width:0}.pv__tabs{display:flex;align-items:flex-end;gap:4px;height:26px;padding:0 8px;background:var(--hm-shell-tab-strip-bg);flex:none}.pv__tab{display:flex;align-items:center;width:64px;height:20px;padding:0 8px;border-radius:7px 7px 0 0;box-sizing:border-box;font-size:var(--hm-font-size-3xs);color:var(--hm-muted)}.pv__tab--on{background:var(--hm-surface-raised);color:var(--hm-text);font-weight:var(--hm-font-weight-semibold)}.pv__page{flex:1;display:flex;flex-direction:column;gap:10px;padding:12px 14px;background:var(--hm-surface-raised);min-height:0}.pv__title{width:38%;height:11px;border-radius:4px;background:color-mix(in srgb,var(--hm-text) 55%,transparent)}.pv__cards{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.pv__card{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid var(--hm-border);border-radius:8px;background:var(--hm-surface)}.pv__num{width:40%;height:10px;border-radius:3px;background:var(--hm-accent)}.pv__line{display:block;width:70%;height:6px;border-radius:3px;background:color-mix(in srgb,var(--hm-text) 18%,transparent)}.pv__line--short{width:30%}.pv__table{display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--hm-border);border-radius:8px}.pv__row{display:flex;align-items:center;justify-content:space-between;gap:8px;height:calc(16px * var(--hm-text-scale, 1));padding:0 8px;background:var(--hm-surface)}.pv__row:nth-child(odd):not(.pv__row--head){background:color-mix(in srgb,var(--hm-accent) 5%,var(--hm-surface))}:host-context([data-density=compact]) .pv__row{height:calc(12px * var(--hm-text-scale, 1))}.pv__row--head{background:color-mix(in srgb,var(--hm-accent) 20%,var(--hm-surface-raised))}.pv__buttons{display:flex;justify-content:flex-end;gap:6px;margin-top:auto}.pv__btn{width:52px;height:16px;border-radius:var(--hm-radius-control);background:var(--hm-action)}.pv__btn--ghost{background:transparent;border:1px solid var(--hm-border)}.setup__essentials{width:100%;max-width:56rem}.setup__cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:var(--hm-space-md);margin:var(--hm-space-xl) 0 0;padding:0;list-style:none}.setup__card{display:flex;flex-direction:column;gap:var(--hm-space-xs);padding:var(--hm-space-lg);border:1px solid var(--hm-border);border-radius:12px;background:var(--hm-surface-raised)}.setup__card-icon{display:inline-grid;place-items:center;width:36px;height:36px;margin-bottom:var(--hm-space-xs);border-radius:10px;background:var(--hm-accent-subtle);color:var(--hm-accent)}.setup__card-title{margin:0;font-size:var(--hm-font-size-md);font-weight:var(--hm-font-weight-semibold)}.setup__card-text{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-sm);line-height:1.5}.setup__keys{display:inline-flex;gap:3px;vertical-align:baseline}.setup__keys kbd{padding:0 5px;border:1px solid var(--hm-border);border-bottom-width:2px;border-radius:5px;background:var(--hm-surface);color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold)}@media(max-width:860px){.setup{padding:var(--hm-space-md)}.setup__body{grid-template-columns:minmax(0,1fr);align-items:start}.setup__preview{display:none}}\n"], dependencies: [{ kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: SegmentedControlComponent, selector: "hm-segmented-control", inputs: ["options", "value", "label", "size", "emphasis"], outputs: ["valueChange"] }, { kind: "component", type: ThemePickerComponent, selector: "hm-theme-picker", inputs: ["families", "family", "theme", "showMode"], outputs: ["familyChosen", "themeChosen"] }, { kind: "component", type: SpineBrandComponent, selector: "spine-brand", inputs: ["homeRoute", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2906
2992
  }
2907
2993
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineInitialSetupComponent, decorators: [{
2908
2994
  type: Component,
2909
- args: [{ selector: 'spine-initial-setup', imports: [ButtonComponent, IconComponent, SegmentedControlComponent, ThemePickerComponent, SpineBrandComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"setup\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"t.label\">\n <div class=\"setup__bar\">\n @if (stepTotal() > 1) {\n <span class=\"setup__step\">{{ fmt(t.step, { current: stepNumber(), total: stepTotal() }) }}</span>\n <span class=\"setup__dots\" aria-hidden=\"true\">\n <span class=\"setup__dot\" [class.setup__dot--on]=\"stepNumber() >= 1\"></span>\n <span class=\"setup__dot\" [class.setup__dot--on]=\"stepNumber() >= 2\"></span>\n </span>\n }\n @if (setup.mode() === 'full') {\n <hm-button class=\"setup__skip\" emphasis=\"minimal\" (clicked)=\"setup.finish()\">{{ t.skip }}</hm-button>\n }\n </div>\n\n @if (setup.step() === 'appearance') {\n <div class=\"setup__body\">\n <section class=\"setup__form\">\n <h1 #heading class=\"setup__title\" tabindex=\"-1\">{{ greeting }}</h1>\n <p class=\"setup__intro\">{{ t.appearanceIntro }}</p>\n\n <div class=\"setup__fields\">\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.theme }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.theme\" [options]=\"themeOptions\" [value]=\"themeMode()\" (valueChange)=\"setThemeMode($event)\" />\n </div>\n\n <div class=\"setup__field setup__field--wide\">\n <span class=\"setup__label\">{{ s.accent }}</span>\n <hm-theme-picker\n [families]=\"families\"\n [family]=\"family()\"\n [theme]=\"appliedTheme()\"\n [showMode]=\"false\"\n (familyChosen)=\"setFamily($event)\"\n />\n </div>\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.density }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.density\" [options]=\"densityOptions\" [value]=\"densityValue()\" (valueChange)=\"setDensity($event)\" />\n </div>\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.textSize }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.textSize\" [options]=\"textSizeOptions()\" [value]=\"textSizeValue()\" (valueChange)=\"setTextSize($event)\" />\n </div>\n\n @if (fontOptions.length > 1) {\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.font }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.font\" [options]=\"fontOptions\" [value]=\"fontValue()\" (valueChange)=\"setFont($event)\" />\n </div>\n }\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.motion }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.motion\" [options]=\"motionOptions\" [value]=\"preferences.motion()\" (valueChange)=\"setMotion($event)\" />\n </div>\n </div>\n\n <div class=\"setup__actions\">\n <hm-button emphasis=\"high\" iconEnd=\"arrow-right\" (clicked)=\"setup.next()\">{{ t.next }}</hm-button>\n </div>\n </section>\n\n <!-- Pr\u00E9via: um shell em miniatura pintado com os tokens do tema, ent\u00E3o acompanha cada escolha. Decorativa. -->\n <figure class=\"setup__preview\">\n <figcaption class=\"setup__preview-label\">{{ t.preview }}</figcaption>\n <div class=\"pv\" aria-hidden=\"true\">\n <div class=\"pv__header\">\n <spine-brand class=\"pv__brand\" [label]=\"appName\" />\n <span class=\"pv__search\"></span>\n <span class=\"pv__avatar\"></span>\n </div>\n <div class=\"pv__body\">\n <div class=\"pv__side\">\n <span class=\"pv__item pv__item--on\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item pv__item--short\"></span>\n </div>\n <div class=\"pv__main\">\n <div class=\"pv__tabs\">\n <span class=\"pv__tab pv__tab--on\">{{ t.previewPage }}</span>\n <span class=\"pv__tab\"><span class=\"pv__line\"></span></span>\n <span class=\"pv__tab\"><span class=\"pv__line\"></span></span>\n </div>\n <div class=\"pv__page\">\n <span class=\"pv__title\"></span>\n <div class=\"pv__cards\">\n @for (n of [1, 2, 3]; track n) {\n <span class=\"pv__card\"><span class=\"pv__num\"></span><span class=\"pv__line\"></span></span>\n }\n </div>\n <div class=\"pv__table\">\n <span class=\"pv__row pv__row--head\"></span>\n @for (n of [1, 2, 3, 4]; track n) {\n <span class=\"pv__row\"><span class=\"pv__line\"></span><span class=\"pv__line pv__line--short\"></span></span>\n }\n </div>\n <div class=\"pv__buttons\">\n <span class=\"pv__btn pv__btn--ghost\"></span>\n <span class=\"pv__btn\"></span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </figure>\n </div>\n } @else {\n <div class=\"setup__body setup__body--essentials\">\n <section class=\"setup__essentials\">\n <h1 #heading class=\"setup__title\" tabindex=\"-1\">{{ t.essentialsTitle }}</h1>\n <p class=\"setup__intro\">{{ t.essentialsIntro }}</p>\n\n <ul class=\"setup__cards\">\n @for (card of essentials; track card.id) {\n <li class=\"setup__card\">\n <span class=\"setup__card-icon\"><hm-icon [shape]=\"card.icon\" /></span>\n <h2 class=\"setup__card-title\">{{ card.title }}</h2>\n <p class=\"setup__card-text\">\n {{ card.before }}@if (card.keys) {<span class=\"setup__keys\">@for (step of card.keys; track $index) {@for (key of step; track $index) {<kbd>{{ key.label }}</kbd>}}</span>}{{ card.after }}\n </p>\n </li>\n }\n </ul>\n\n <div class=\"setup__actions\">\n @if (setup.mode() === 'full') {\n <hm-button emphasis=\"medium\" icon=\"arrow-left\" (clicked)=\"setup.back()\">{{ t.back }}</hm-button>\n <hm-button emphasis=\"high\" (clicked)=\"setup.finish()\">{{ t.finish }}</hm-button>\n } @else {\n <hm-button emphasis=\"high\" (clicked)=\"setup.finish()\">{{ t.close }}</hm-button>\n }\n </div>\n </section>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{position:fixed;inset:0;z-index:var(--hm-z-overlay);display:block;overflow:auto;background:var(--hm-surface);color:var(--hm-text)}.setup{display:flex;flex-direction:column;min-height:100%;max-width:76rem;margin:0 auto;padding:var(--hm-space-lg) var(--hm-space-xl) var(--hm-space-2xl);box-sizing:border-box}.setup__bar{display:flex;align-items:center;gap:var(--hm-space-md);min-height:var(--hm-control-height, 40px)}.setup__step{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.setup__dots{display:inline-flex;gap:6px}.setup__dot{width:24px;height:4px;border-radius:var(--hm-radius-pill);background:var(--hm-border)}.setup__dot--on{background:var(--hm-accent)}.setup__skip{margin-left:auto}.setup__body{flex:1;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);gap:clamp(var(--hm-space-xl),5vw,5rem);align-items:center;padding-top:var(--hm-space-xl)}.setup__body--essentials{grid-template-columns:minmax(0,1fr);justify-items:center}.setup__title{margin:0;font-size:var(--hm-font-size-xxl);font-weight:650;line-height:1.2;letter-spacing:-.01em;outline:none}.setup__intro{margin:var(--hm-space-sm) 0 0;max-width:34rem;color:var(--hm-muted);line-height:1.5}.setup__fields{display:flex;flex-direction:column;gap:var(--hm-space-lg);margin-top:var(--hm-space-xl)}.setup__field{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-xs)}.setup__field--wide{align-items:stretch}.setup__label{font-size:var(--hm-font-size-sm);font-weight:600}.setup__actions{display:flex;gap:var(--hm-space-sm);margin-top:var(--hm-space-xl)}.setup__preview{margin:0;display:flex;flex-direction:column;gap:var(--hm-space-sm)}.setup__preview-label{color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:600;letter-spacing:.06em;text-transform:uppercase}.pv{display:flex;flex-direction:column;aspect-ratio:16/11;overflow:hidden;border:1px solid var(--hm-border);border-radius:14px;background:var(--hm-surface);box-shadow:var(--hm-shadow-lg)}.pv__header{display:flex;align-items:center;gap:10px;height:34px;padding:0 10px;background:var(--hm-navbar-surface);color:var(--hm-navbar-text);flex:none}.pv__brand{display:flex;align-items:center;height:18px;font-size:11px;pointer-events:none}.pv__brand ::ng-deep img{height:18px;width:auto}.pv__search{flex:0 1 36%;height:16px;margin-left:auto;border-radius:var(--hm-radius-pill);background:color-mix(in srgb,var(--hm-navbar-text) 16%,transparent)}.pv__avatar{width:18px;height:18px;border-radius:50%;background:color-mix(in srgb,var(--hm-navbar-text) 30%,transparent)}.pv__body{flex:1;display:flex;min-height:0}.pv__side{display:flex;flex-direction:column;gap:8px;width:22%;padding:12px 10px;background:var(--hm-sidebar-bg);border-right:1px solid var(--hm-border);box-sizing:border-box}.pv__item{height:9px;border-radius:4px;background:color-mix(in srgb,var(--hm-text) 14%,transparent)}.pv__item--on{background:var(--hm-accent)}.pv__item--short{width:60%}.pv__main{flex:1;display:flex;flex-direction:column;min-width:0}.pv__tabs{display:flex;align-items:flex-end;gap:4px;height:26px;padding:0 8px;background:var(--hm-shell-tab-strip-bg);flex:none}.pv__tab{display:flex;align-items:center;width:64px;height:20px;padding:0 8px;border-radius:7px 7px 0 0;box-sizing:border-box;font-size:9px;color:var(--hm-muted)}.pv__tab--on{background:var(--hm-surface-raised);color:var(--hm-text);font-weight:600}.pv__page{flex:1;display:flex;flex-direction:column;gap:10px;padding:12px 14px;background:var(--hm-surface-raised);min-height:0}.pv__title{width:38%;height:11px;border-radius:4px;background:color-mix(in srgb,var(--hm-text) 55%,transparent)}.pv__cards{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.pv__card{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid var(--hm-border);border-radius:8px;background:var(--hm-surface)}.pv__num{width:40%;height:10px;border-radius:3px;background:var(--hm-accent)}.pv__line{display:block;width:70%;height:6px;border-radius:3px;background:color-mix(in srgb,var(--hm-text) 18%,transparent)}.pv__line--short{width:30%}.pv__table{display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--hm-border);border-radius:8px}.pv__row{display:flex;align-items:center;justify-content:space-between;gap:8px;height:calc(16px * var(--hm-text-scale, 1));padding:0 8px;background:var(--hm-surface)}.pv__row:nth-child(odd):not(.pv__row--head){background:color-mix(in srgb,var(--hm-accent) 5%,var(--hm-surface))}:host-context([data-density=compact]) .pv__row{height:calc(12px * var(--hm-text-scale, 1))}.pv__row--head{background:color-mix(in srgb,var(--hm-accent) 20%,var(--hm-surface-raised))}.pv__buttons{display:flex;justify-content:flex-end;gap:6px;margin-top:auto}.pv__btn{width:52px;height:16px;border-radius:var(--hm-radius-control);background:var(--hm-action)}.pv__btn--ghost{background:transparent;border:1px solid var(--hm-border)}.setup__essentials{width:100%;max-width:56rem}.setup__cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:var(--hm-space-md);margin:var(--hm-space-xl) 0 0;padding:0;list-style:none}.setup__card{display:flex;flex-direction:column;gap:var(--hm-space-xs);padding:var(--hm-space-lg);border:1px solid var(--hm-border);border-radius:12px;background:var(--hm-surface-raised)}.setup__card-icon{display:inline-grid;place-items:center;width:36px;height:36px;margin-bottom:var(--hm-space-xs);border-radius:10px;background:var(--hm-accent-subtle);color:var(--hm-accent)}.setup__card-title{margin:0;font-size:var(--hm-font-size-md);font-weight:600}.setup__card-text{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-sm);line-height:1.5}.setup__keys{display:inline-flex;gap:3px;vertical-align:baseline}.setup__keys kbd{padding:0 5px;border:1px solid var(--hm-border);border-bottom-width:2px;border-radius:5px;background:var(--hm-surface);color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-xs);font-weight:600}@media(max-width:860px){.setup{padding:var(--hm-space-md)}.setup__body{grid-template-columns:minmax(0,1fr);align-items:start}.setup__preview{display:none}}\n"] }]
2995
+ args: [{ selector: 'spine-initial-setup', imports: [ButtonComponent, IconComponent, SegmentedControlComponent, ThemePickerComponent, SpineBrandComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"setup\" role=\"dialog\" aria-modal=\"true\" [attr.aria-label]=\"t.label\">\n <div class=\"setup__bar\">\n @if (stepTotal() > 1) {\n <span class=\"setup__step\">{{ fmt(t.step, { current: stepNumber(), total: stepTotal() }) }}</span>\n <span class=\"setup__dots\" aria-hidden=\"true\">\n <span class=\"setup__dot\" [class.setup__dot--on]=\"stepNumber() >= 1\"></span>\n <span class=\"setup__dot\" [class.setup__dot--on]=\"stepNumber() >= 2\"></span>\n </span>\n }\n @if (setup.mode() === 'full') {\n <hm-button class=\"setup__skip\" emphasis=\"minimal\" (clicked)=\"setup.finish()\">{{ t.skip }}</hm-button>\n }\n </div>\n\n @if (setup.step() === 'appearance') {\n <div class=\"setup__body\">\n <section class=\"setup__form\">\n <h1 #heading class=\"setup__title\" tabindex=\"-1\">{{ greeting }}</h1>\n <p class=\"setup__intro\">{{ t.appearanceIntro }}</p>\n\n <div class=\"setup__fields\">\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.theme }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.theme\" [options]=\"themeOptions\" [value]=\"themeMode()\" (valueChange)=\"setThemeMode($event)\" />\n </div>\n\n <div class=\"setup__field setup__field--wide\">\n <span class=\"setup__label\">{{ s.accent }}</span>\n <hm-theme-picker\n [families]=\"families\"\n [family]=\"family()\"\n [theme]=\"appliedTheme()\"\n [showMode]=\"false\"\n (familyChosen)=\"setFamily($event)\"\n />\n </div>\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.density }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.density\" [options]=\"densityOptions\" [value]=\"densityValue()\" (valueChange)=\"setDensity($event)\" />\n </div>\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.textSize }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.textSize\" [options]=\"textSizeOptions()\" [value]=\"textSizeValue()\" (valueChange)=\"setTextSize($event)\" />\n </div>\n\n @if (fontOptions.length > 1) {\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.font }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.font\" [options]=\"fontOptions\" [value]=\"fontValue()\" (valueChange)=\"setFont($event)\" />\n </div>\n }\n\n <div class=\"setup__field\">\n <span class=\"setup__label\">{{ s.motion }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"s.motion\" [options]=\"motionOptions\" [value]=\"preferences.motion()\" (valueChange)=\"setMotion($event)\" />\n </div>\n </div>\n\n <div class=\"setup__actions\">\n <hm-button emphasis=\"high\" iconEnd=\"arrow-right\" (clicked)=\"setup.next()\">{{ t.next }}</hm-button>\n </div>\n </section>\n\n <!-- Pr\u00E9via: um shell em miniatura pintado com os tokens do tema, ent\u00E3o acompanha cada escolha. Decorativa. -->\n <figure class=\"setup__preview\">\n <figcaption class=\"setup__preview-label\">{{ t.preview }}</figcaption>\n <div class=\"pv\" aria-hidden=\"true\">\n <div class=\"pv__header\">\n <spine-brand class=\"pv__brand\" [label]=\"appName\" />\n <span class=\"pv__search\"></span>\n <span class=\"pv__avatar\"></span>\n </div>\n <div class=\"pv__body\">\n <div class=\"pv__side\">\n <span class=\"pv__item pv__item--on\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item\"></span>\n <span class=\"pv__item pv__item--short\"></span>\n </div>\n <div class=\"pv__main\">\n <div class=\"pv__tabs\">\n <span class=\"pv__tab pv__tab--on\">{{ t.previewPage }}</span>\n <span class=\"pv__tab\"><span class=\"pv__line\"></span></span>\n <span class=\"pv__tab\"><span class=\"pv__line\"></span></span>\n </div>\n <div class=\"pv__page\">\n <span class=\"pv__title\"></span>\n <div class=\"pv__cards\">\n @for (n of [1, 2, 3]; track n) {\n <span class=\"pv__card\"><span class=\"pv__num\"></span><span class=\"pv__line\"></span></span>\n }\n </div>\n <div class=\"pv__table\">\n <span class=\"pv__row pv__row--head\"></span>\n @for (n of [1, 2, 3, 4]; track n) {\n <span class=\"pv__row\"><span class=\"pv__line\"></span><span class=\"pv__line pv__line--short\"></span></span>\n }\n </div>\n <div class=\"pv__buttons\">\n <span class=\"pv__btn pv__btn--ghost\"></span>\n <span class=\"pv__btn\"></span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </figure>\n </div>\n } @else {\n <div class=\"setup__body setup__body--essentials\">\n <section class=\"setup__essentials\">\n <h1 #heading class=\"setup__title\" tabindex=\"-1\">{{ t.essentialsTitle }}</h1>\n <p class=\"setup__intro\">{{ t.essentialsIntro }}</p>\n\n <ul class=\"setup__cards\">\n @for (card of essentials; track card.id) {\n <li class=\"setup__card\">\n <span class=\"setup__card-icon\"><hm-icon [shape]=\"card.icon\" /></span>\n <h2 class=\"setup__card-title\">{{ card.title }}</h2>\n <p class=\"setup__card-text\">\n {{ card.before }}@if (card.keys) {<span class=\"setup__keys\">@for (step of card.keys; track $index) {@for (key of step; track $index) {<kbd>{{ key.label }}</kbd>}}</span>}{{ card.after }}\n </p>\n </li>\n }\n </ul>\n\n <div class=\"setup__actions\">\n @if (setup.mode() === 'full') {\n <hm-button emphasis=\"medium\" icon=\"arrow-left\" (clicked)=\"setup.back()\">{{ t.back }}</hm-button>\n <hm-button emphasis=\"high\" (clicked)=\"setup.finish()\">{{ t.finish }}</hm-button>\n } @else {\n <hm-button emphasis=\"high\" (clicked)=\"setup.finish()\">{{ t.close }}</hm-button>\n }\n </div>\n </section>\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{position:fixed;inset:0;z-index:var(--hm-z-overlay);display:block;overflow:auto;background:var(--hm-surface);color:var(--hm-text)}.setup{display:flex;flex-direction:column;min-height:100%;max-width:76rem;margin:0 auto;padding:var(--hm-space-lg) var(--hm-space-xl) var(--hm-space-2xl);box-sizing:border-box}.setup__bar{display:flex;align-items:center;gap:var(--hm-space-md);min-height:var(--hm-control-height, 40px)}.setup__step{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.setup__dots{display:inline-flex;gap:6px}.setup__dot{width:24px;height:4px;border-radius:var(--hm-radius-pill);background:var(--hm-border)}.setup__dot--on{background:var(--hm-accent)}.setup__skip{margin-left:auto}.setup__body{flex:1;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);gap:clamp(var(--hm-space-xl),5vw,5rem);align-items:center;padding-top:var(--hm-space-xl)}.setup__body--essentials{grid-template-columns:minmax(0,1fr);justify-items:center}.setup__title{margin:0;font-size:var(--hm-font-size-xxl);font-weight:var(--hm-font-weight-semibold);line-height:1.2;letter-spacing:-.01em;outline:none}.setup__intro{margin:var(--hm-space-sm) 0 0;max-width:34rem;color:var(--hm-muted);line-height:1.5}.setup__fields{display:flex;flex-direction:column;gap:var(--hm-space-lg);margin-top:var(--hm-space-xl)}.setup__field{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-xs)}.setup__field--wide{align-items:stretch}.setup__label{font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.setup__actions{display:flex;gap:var(--hm-space-sm);margin-top:var(--hm-space-xl)}.setup__preview{margin:0;display:flex;flex-direction:column;gap:var(--hm-space-sm)}.setup__preview-label{color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.06em;text-transform:uppercase}.pv{display:flex;flex-direction:column;aspect-ratio:16/11;overflow:hidden;border:1px solid var(--hm-border);border-radius:14px;background:var(--hm-surface);box-shadow:var(--hm-shadow-lg)}.pv__header{display:flex;align-items:center;gap:10px;height:34px;padding:0 10px;background:var(--hm-navbar-surface);color:var(--hm-navbar-text);flex:none}.pv__brand{display:flex;align-items:center;height:18px;font-size:var(--hm-font-size-xs);pointer-events:none}.pv__brand ::ng-deep img{height:18px;width:auto}.pv__search{flex:0 1 36%;height:16px;margin-left:auto;border-radius:var(--hm-radius-pill);background:color-mix(in srgb,var(--hm-navbar-text) 16%,transparent)}.pv__avatar{width:18px;height:18px;border-radius:50%;background:color-mix(in srgb,var(--hm-navbar-text) 30%,transparent)}.pv__body{flex:1;display:flex;min-height:0}.pv__side{display:flex;flex-direction:column;gap:8px;width:22%;padding:12px 10px;background:var(--hm-sidebar-bg);border-right:1px solid var(--hm-border);box-sizing:border-box}.pv__item{height:9px;border-radius:4px;background:color-mix(in srgb,var(--hm-text) 14%,transparent)}.pv__item--on{background:var(--hm-accent)}.pv__item--short{width:60%}.pv__main{flex:1;display:flex;flex-direction:column;min-width:0}.pv__tabs{display:flex;align-items:flex-end;gap:4px;height:26px;padding:0 8px;background:var(--hm-shell-tab-strip-bg);flex:none}.pv__tab{display:flex;align-items:center;width:64px;height:20px;padding:0 8px;border-radius:7px 7px 0 0;box-sizing:border-box;font-size:var(--hm-font-size-3xs);color:var(--hm-muted)}.pv__tab--on{background:var(--hm-surface-raised);color:var(--hm-text);font-weight:var(--hm-font-weight-semibold)}.pv__page{flex:1;display:flex;flex-direction:column;gap:10px;padding:12px 14px;background:var(--hm-surface-raised);min-height:0}.pv__title{width:38%;height:11px;border-radius:4px;background:color-mix(in srgb,var(--hm-text) 55%,transparent)}.pv__cards{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.pv__card{display:flex;flex-direction:column;gap:6px;padding:8px;border:1px solid var(--hm-border);border-radius:8px;background:var(--hm-surface)}.pv__num{width:40%;height:10px;border-radius:3px;background:var(--hm-accent)}.pv__line{display:block;width:70%;height:6px;border-radius:3px;background:color-mix(in srgb,var(--hm-text) 18%,transparent)}.pv__line--short{width:30%}.pv__table{display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--hm-border);border-radius:8px}.pv__row{display:flex;align-items:center;justify-content:space-between;gap:8px;height:calc(16px * var(--hm-text-scale, 1));padding:0 8px;background:var(--hm-surface)}.pv__row:nth-child(odd):not(.pv__row--head){background:color-mix(in srgb,var(--hm-accent) 5%,var(--hm-surface))}:host-context([data-density=compact]) .pv__row{height:calc(12px * var(--hm-text-scale, 1))}.pv__row--head{background:color-mix(in srgb,var(--hm-accent) 20%,var(--hm-surface-raised))}.pv__buttons{display:flex;justify-content:flex-end;gap:6px;margin-top:auto}.pv__btn{width:52px;height:16px;border-radius:var(--hm-radius-control);background:var(--hm-action)}.pv__btn--ghost{background:transparent;border:1px solid var(--hm-border)}.setup__essentials{width:100%;max-width:56rem}.setup__cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:var(--hm-space-md);margin:var(--hm-space-xl) 0 0;padding:0;list-style:none}.setup__card{display:flex;flex-direction:column;gap:var(--hm-space-xs);padding:var(--hm-space-lg);border:1px solid var(--hm-border);border-radius:12px;background:var(--hm-surface-raised)}.setup__card-icon{display:inline-grid;place-items:center;width:36px;height:36px;margin-bottom:var(--hm-space-xs);border-radius:10px;background:var(--hm-accent-subtle);color:var(--hm-accent)}.setup__card-title{margin:0;font-size:var(--hm-font-size-md);font-weight:var(--hm-font-weight-semibold)}.setup__card-text{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-sm);line-height:1.5}.setup__keys{display:inline-flex;gap:3px;vertical-align:baseline}.setup__keys kbd{padding:0 5px;border:1px solid var(--hm-border);border-bottom-width:2px;border-radius:5px;background:var(--hm-surface);color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold)}@media(max-width:860px){.setup{padding:var(--hm-space-md)}.setup__body{grid-template-columns:minmax(0,1fr);align-items:start}.setup__preview{display:none}}\n"] }]
2910
2996
  }], ctorParameters: () => [], propDecorators: { heading: [{ type: i0.ViewChild, args: ['heading', { isSignal: true }] }] } });
2911
2997
 
2912
2998
  /** Novidades por chamada ao servidor: o painel lê as páginas em sequência até ter tudo (ou o teto). */
@@ -3328,7 +3414,7 @@ class SpineNewsComponent {
3328
3414
  </ng-container>
3329
3415
  </hm-modal>
3330
3416
  }
3331
- `, isInline: true, styles: [".news-state{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-sm);margin:0}.news-state--muted{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.news-tools{display:flex;flex-direction:column;gap:var(--hm-space-sm);margin-block-end:var(--hm-space-md)}.news-filters{display:flex;flex-wrap:wrap;gap:var(--hm-space-xs)}.news-filter{display:inline-flex;align-items:center;gap:var(--hm-space-xs);padding:var(--hm-space-micro) var(--hm-space-sm);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-pill);background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);cursor:pointer}.news-filter:hover{background:var(--hm-hover)}.news-filter:focus-visible,.news-item:focus-visible,.news-fav:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:2px}.news-filter--on{border-color:var(--hm-accent);background:var(--hm-accent-subtle);color:var(--hm-accent);font-weight:var(--hm-font-weight-semibold)}.news-filter__count{font-size:var(--hm-font-size-xs);opacity:.8}.news-groups{display:flex;flex-direction:column;gap:var(--hm-space-md)}.news-groups[aria-busy=true]{opacity:.8}.news-month{display:flex;flex-direction:column;gap:var(--hm-space-xs)}.news-month .news-list{border-block-start:var(--hm-border-hairline) solid var(--hm-border)}.news-pages{margin-inline-end:auto}.news-month__title{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.06em;text-transform:uppercase}.news-list{display:flex;flex-direction:column;margin:0;padding:0;list-style:none}.news-row{position:relative;display:flex;align-items:flex-start;gap:var(--hm-space-xs);border-block-end:var(--hm-border-hairline) solid var(--hm-border)}.news-row:last-child{border-block-end:0}.news-row--selected{background:var(--hm-accent-subtle)}.news-row--selected:before{content:\"\";position:absolute;inset-block:var(--hm-space-xs);inset-inline-start:0;inline-size:3px;border-radius:var(--hm-radius-pill);background:var(--hm-accent)}.news-item{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--hm-space-micro);min-inline-size:0;padding:var(--hm-space-sm) var(--hm-space-xs) var(--hm-space-sm) var(--hm-space-sm);border:0;border-radius:0;background:transparent;color:inherit;font:inherit;text-align:start;cursor:pointer}.news-item:hover{background:var(--hm-hover)}.news-item__meta{display:flex;align-items:center;gap:var(--hm-space-xs)}.news-item__date{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.news-item__badge{padding:var(--hm-space-nano) var(--hm-space-xs);border-radius:var(--hm-radius-pill);background:var(--hm-accent-subtle);color:var(--hm-accent);font-size:var(--hm-font-size-3xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.04em;text-transform:uppercase}.news-item__title{color:var(--hm-text);font-size:var(--hm-font-size-md);font-weight:var(--hm-font-weight-bold);line-height:var(--hm-line-height-tight)}.news-item__summary{display:-webkit-box;overflow:hidden;color:var(--hm-muted);font-size:var(--hm-font-size-sm);line-height:var(--hm-line-height-normal);-webkit-box-orient:vertical;-webkit-line-clamp:2}.news-item__author{display:inline-flex;align-items:center;gap:var(--hm-space-xs);color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.news-fav{display:grid;flex:none;place-items:center;inline-size:var(--hm-control-height);block-size:var(--hm-control-height);margin-block-start:var(--hm-space-sm);border:0;border-radius:var(--hm-radius-control);background:transparent;color:var(--hm-muted);cursor:pointer}.news-fav:hover{background:var(--hm-hover);color:var(--hm-text)}.news-fav--on{color:var(--hm-accent)}:host ::ng-deep .hm-modal__window:has(>.hm-modal__body>.news-read)>.hm-modal__header{position:absolute;z-index:1;inset-block-start:0;inset-inline-end:0;border:0;padding:var(--hm-space-sm)}:host ::ng-deep .hm-modal__window:has(>.hm-modal__body>.news-read) .hm-modal__heading{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.news-read{display:flex;flex-direction:column;gap:var(--hm-space-sm);padding-block-start:var(--hm-space-xs)}.news-read__head{display:flex;flex-direction:column;gap:var(--hm-space-xs);padding-inline-end:calc(var(--hm-control-height) + var(--hm-space-sm));padding-block-end:var(--hm-space-md);border-block-end:var(--hm-border-hairline) solid var(--hm-divider)}.news-read__title-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm)}.news-read__title{color:var(--hm-text);font-size:var(--hm-font-size-xxl);font-weight:var(--hm-font-weight-bold);line-height:var(--hm-line-height-tight)}.news-read__meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-md);color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.news-read__meta-part{display:inline-flex;align-items:center;gap:var(--hm-space-xs)}.news-read__tag{display:inline-flex;align-items:center;gap:var(--hm-space-micro);padding:var(--hm-space-nano) var(--hm-space-xs);border:var(--hm-border-hairline) solid var(--hm-divider);border-radius:var(--hm-radius-pill);background:var(--hm-surface-muted);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.news-read__lead{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-lg);line-height:var(--hm-line-height-snug)}.news-read__section{display:flex;flex-direction:column;gap:var(--hm-space-sm)}.news-read__section-title,.news-compare__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-lg);font-weight:var(--hm-font-weight-bold)}.news-changes{display:flex;flex-direction:column;gap:var(--hm-space-xs);margin:0;padding:0;list-style:none}.news-changes__item{display:flex;align-items:flex-start;gap:var(--hm-space-xs);color:var(--hm-text);font-size:var(--hm-font-size-sm);line-height:var(--hm-line-height-normal)}.news-changes__icon{flex:none;margin-block-start:2px;color:var(--hm-success)}.news-compare{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));gap:var(--hm-space-md)}.news-compare__side{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-inline-size:0;padding:var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-divider);border-inline-start-width:var(--hm-border-thin);border-radius:var(--hm-radius)}.news-compare__side--before{border-inline-start-color:var(--hm-muted);background:var(--hm-surface-muted)}.news-compare__side--after{border-inline-start-color:var(--hm-success);background:var(--hm-surface-raised)}.news-compare__side--after .news-compare__title{color:var(--hm-success)}.news-read__edited{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs);margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.news-read__unread{margin-inline-end:auto}.news-read__body ::ng-deep .hm-rich-text__frame{border:0;background:transparent}.news-read__body ::ng-deep .hm-rich-text__area{padding:0;min-block-size:0}.news-read__body ::ng-deep .hm-prose>:last-child{margin-block-end:0}.news-read__body ::ng-deep :is(h2,h3){color:var(--hm-text);font-weight:var(--hm-font-weight-bold)}\n"], dependencies: [{ kind: "component", type: AccountAvatarComponent, selector: "hm-account-avatar", inputs: ["name", "imageUrl", "size", "loading"] }, { kind: "component", type: AlertComponent, selector: "hm-alert", inputs: ["variant", "appearance", "heading", "closable", "size", "showIcon", "link", "linkLabel", "linkTarget", "dismissKey", "duration"], outputs: ["closed"] }, { kind: "component", type: BreadcrumbComponent, selector: "hm-breadcrumb", inputs: ["items", "maxItems", "itemsBeforeCollapse", "itemsAfterCollapse", "label", "loading"], outputs: ["itemClick"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: DrawerComponent, selector: "hm-drawer", inputs: ["open", "heading", "size", "position", "pinned", "closeLabel", "closable", "beforeClose"], outputs: ["openChange", "closed"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: ModalComponent, selector: "hm-modal", inputs: ["open", "heading", "size", "closeLabel", "closable", "beforeClose"], outputs: ["openChange", "closed"] }, { kind: "component", type: PaginatorComponent, selector: "hm-paginator", inputs: ["state", "totalItems", "variant", "label", "showPage", "hasNext", "hasPrevious", "sizes", "maxNumbers"], outputs: ["stateChanged", "nextPage", "previousPage"] }, { kind: "component", type: RichTextComponent, selector: "hm-rich-text", inputs: ["value", "label", "labelHidden", "hint", "placeholder", "readonly", "loading", "tools", "headingLevels", "hideToolbar", "rows", "maxRows", "maxLength", "showCounter", "pasteMode", "linkProtocols", "allowRelativeLinks", "spellcheck"], outputs: ["valueChange", "focusChange"] }, { kind: "component", type: SearchFieldComponent, selector: "hm-search-field", inputs: ["value", "label", "labelHidden", "placeholder", "hint", "size", "debounce", "landmark", "loading"], outputs: ["valueChange", "search", "cleared"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3417
+ `, isInline: true, styles: [".news-state{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-sm);margin:0}.news-state--muted{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.news-tools{display:flex;flex-direction:column;gap:var(--hm-space-sm);margin-block-end:var(--hm-space-md)}.news-filters{display:flex;flex-wrap:wrap;gap:var(--hm-space-xs)}.news-filter{display:inline-flex;align-items:center;gap:var(--hm-space-xs);padding:var(--hm-space-micro) var(--hm-space-sm);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-pill);background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);cursor:pointer}.news-filter:hover{background:var(--hm-hover)}.news-filter:focus-visible,.news-item:focus-visible,.news-fav:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:2px}.news-filter--on{border-color:var(--hm-accent);background:var(--hm-accent-subtle);color:var(--hm-accent);font-weight:var(--hm-font-weight-semibold)}.news-filter__count{font-size:var(--hm-font-size-xs);opacity:.8}.news-groups{display:flex;flex-direction:column;gap:var(--hm-space-md)}.news-groups[aria-busy=true]{opacity:.8}.news-month{display:flex;flex-direction:column;gap:var(--hm-space-xs)}.news-month .news-list{border-block-start:var(--hm-border-hairline) solid var(--hm-border)}.news-pages{margin-inline-end:auto}.news-month__title{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.06em;text-transform:uppercase}.news-list{display:flex;flex-direction:column;margin:0;padding:0;list-style:none}.news-row{position:relative;display:flex;align-items:flex-start;gap:var(--hm-space-xs);border-block-end:var(--hm-border-hairline) solid var(--hm-border)}.news-row:last-child{border-block-end:0}.news-row--selected{background:var(--hm-accent-subtle)}.news-row--selected:before{content:\"\";position:absolute;inset-block:var(--hm-space-xs);inset-inline-start:0;inline-size:3px;border-radius:var(--hm-radius-pill);background:var(--hm-accent)}.news-item{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--hm-space-micro);min-inline-size:0;padding:var(--hm-space-sm) var(--hm-space-xs) var(--hm-space-sm) var(--hm-space-sm);border:0;border-radius:0;background:transparent;color:inherit;font:inherit;text-align:start;cursor:pointer}.news-item:hover{background:var(--hm-hover)}.news-item__meta{display:flex;align-items:center;gap:var(--hm-space-xs)}.news-item__date{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.news-item__badge{padding:var(--hm-space-nano) var(--hm-space-xs);border-radius:var(--hm-radius-pill);background:var(--hm-accent-subtle);color:var(--hm-accent);font-size:var(--hm-font-size-3xs);font-weight:var(--hm-font-weight-semibold);letter-spacing:.04em;text-transform:uppercase}.news-item__title{color:var(--hm-text);font-size:var(--hm-font-size-md);font-weight:var(--hm-font-weight-bold);line-height:var(--hm-line-height-tight)}.news-item__summary{display:-webkit-box;overflow:hidden;color:var(--hm-muted);font-size:var(--hm-font-size-sm);line-height:var(--hm-line-height-normal);-webkit-box-orient:vertical;-webkit-line-clamp:2}.news-item__author{display:inline-flex;align-items:center;gap:var(--hm-space-xs);color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.news-fav{display:grid;flex:none;place-items:center;inline-size:var(--hm-control-height);block-size:var(--hm-control-height);margin-block-start:var(--hm-space-sm);border:0;border-radius:var(--hm-radius-control);background:transparent;color:var(--hm-muted);cursor:pointer}.news-fav:hover{background:var(--hm-hover);color:var(--hm-text)}.news-fav--on{color:var(--hm-accent)}:host ::ng-deep .hm-modal__window:has(>.hm-modal__body>.news-read)>.hm-modal__header{position:absolute;z-index:1;inset-block-start:0;inset-inline-end:0;border:0;padding:var(--hm-space-sm)}:host ::ng-deep .hm-modal__window:has(>.hm-modal__body>.news-read) .hm-modal__heading{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.news-read{display:flex;flex-direction:column;gap:var(--hm-space-sm);padding-block-start:var(--hm-space-xs)}.news-read__head{display:flex;flex-direction:column;gap:var(--hm-space-xs);padding-inline-end:calc(var(--hm-control-height) + var(--hm-space-sm));padding-block-end:var(--hm-space-md);border-block-end:var(--hm-border-hairline) solid var(--hm-divider)}.news-read__title-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm)}.news-read__title{color:var(--hm-text);font-size:var(--hm-font-size-xxl);font-weight:var(--hm-font-weight-bold);line-height:var(--hm-line-height-tight)}.news-read__meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-md);color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.news-read__meta-part{display:inline-flex;align-items:center;gap:var(--hm-space-xs)}.news-read__tag{display:inline-flex;align-items:center;gap:var(--hm-space-micro);padding:var(--hm-space-nano) var(--hm-space-xs);border:var(--hm-border-hairline) solid var(--hm-divider);border-radius:var(--hm-radius-pill);background:var(--hm-surface-muted);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.news-read__lead{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-lg);line-height:var(--hm-line-height-snug)}.news-read__section{display:flex;flex-direction:column;gap:var(--hm-space-sm)}.news-read__section-title,.news-compare__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-lg);font-weight:var(--hm-font-weight-bold)}.news-changes{display:flex;flex-direction:column;gap:var(--hm-space-xs);margin:0;padding:0;list-style:none}.news-changes__item{display:flex;align-items:flex-start;gap:var(--hm-space-xs);color:var(--hm-text);font-size:var(--hm-font-size-sm);line-height:var(--hm-line-height-normal)}.news-changes__icon{flex:none;margin-block-start:2px;color:var(--hm-success)}.news-compare{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,16rem),1fr));gap:var(--hm-space-md)}.news-compare__side{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-inline-size:0;padding:var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-divider);border-inline-start-width:var(--hm-border-thin);border-radius:var(--hm-radius)}.news-compare__side--before{border-inline-start-color:var(--hm-muted);background:var(--hm-surface-muted)}.news-compare__side--after{border-inline-start-color:var(--hm-success);background:var(--hm-surface-raised)}.news-compare__side--after .news-compare__title{color:var(--hm-success)}.news-read__edited{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs);margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.news-read__unread{margin-inline-end:auto}.news-read__body ::ng-deep .hm-rich-text__frame{border:0;background:transparent}.news-read__body ::ng-deep .hm-rich-text__area{padding:0;min-block-size:0}.news-read__body ::ng-deep .hm-prose>:last-child{margin-block-end:0}.news-read__body ::ng-deep :is(h2,h3){color:var(--hm-text);font-weight:var(--hm-font-weight-bold)}\n"], dependencies: [{ kind: "component", type: AccountAvatarComponent, selector: "hm-account-avatar", inputs: ["name", "imageUrl", "size", "loading", "presence", "texts"] }, { kind: "component", type: AlertComponent, selector: "hm-alert", inputs: ["variant", "appearance", "heading", "closable", "size", "showIcon", "link", "linkLabel", "linkTarget", "dismissKey", "duration"], outputs: ["closed"] }, { kind: "component", type: BreadcrumbComponent, selector: "hm-breadcrumb", inputs: ["items", "maxItems", "itemsBeforeCollapse", "itemsAfterCollapse", "label", "loading"], outputs: ["itemClick"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: DrawerComponent, selector: "hm-drawer", inputs: ["open", "heading", "size", "position", "pinned", "closeLabel", "closable", "closeOnBackdrop", "beforeClose"], outputs: ["openChange", "closed"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: ModalComponent, selector: "hm-modal", inputs: ["open", "heading", "size", "closeLabel", "closable", "beforeClose"], outputs: ["openChange", "closed"] }, { kind: "component", type: PaginatorComponent, selector: "hm-paginator", inputs: ["state", "totalItems", "variant", "label", "showPage", "hasNext", "hasPrevious", "sizes", "maxNumbers"], outputs: ["stateChanged", "nextPage", "previousPage"] }, { kind: "component", type: RichTextComponent, selector: "hm-rich-text", inputs: ["value", "label", "labelHidden", "hint", "placeholder", "readonly", "loading", "tools", "headingLevels", "hideToolbar", "rows", "maxRows", "maxLength", "showCounter", "pasteMode", "linkProtocols", "allowRelativeLinks", "spellcheck"], outputs: ["valueChange", "focusChange"] }, { kind: "component", type: SearchFieldComponent, selector: "hm-search-field", inputs: ["value", "label", "labelHidden", "placeholder", "hint", "size", "debounce", "landmark", "loading"], outputs: ["valueChange", "search", "cleared"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3332
3418
  }
3333
3419
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineNewsComponent, decorators: [{
3334
3420
  type: Component,
@@ -5390,6 +5476,114 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
5390
5476
  args: [{ selector: 'spine-app-tokens', imports: [ReactiveFormsModule, ButtonComponent, InputComponent, IconComponent, SegmentedControlComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (!available) {\n <p class=\"sp-account__note\">{{ text.unavailable }}</p>\n} @else {\n <!-- Token rec\u00E9m-criado: \u00FAnica vez em que o valor aparece. -->\n @if (created(); as c) {\n <section class=\"sp-account__panel sp-account__panel--success\" role=\"status\" [attr.aria-labelledby]=\"ids + '-created'\">\n <div class=\"sp-account__panel-head\">\n <hm-icon shape=\"check-circle\" />\n <div class=\"sp-account__panel-text\">\n <h4 class=\"sp-account__title\" [id]=\"ids + '-created'\">{{ fmt(text.created, { name: c.name }) }}</h4>\n <p class=\"sp-account__note\" [innerHTML]=\"fmtHtml(text.sendHeader, { date: formatDate(c.expiresAt) })\"></p>\n </div>\n </div>\n <code class=\"sp-account__secret sp-account__secret--block\">{{ c.token }}</code>\n <div class=\"sp-account__actions\">\n <hm-button emphasis=\"high\" icon=\"copy\" (clicked)=\"copyCreated()\">{{ text.copyToken }}</hm-button>\n <hm-button emphasis=\"medium\" (clicked)=\"dismissCreated()\">{{ text.saved }}</hm-button>\n </div>\n </section>\n }\n\n @if (loading()) {\n <p class=\"sp-account__note\" role=\"status\">{{ text.loading }}</p>\n } @else if (loadError()) {\n <p class=\"sp-account__note sp-account__note--error\">{{ loadError() }}</p>\n } @else {\n <div class=\"sp-account__list\">\n <ul class=\"sp-account__items\">\n @for (t of tokens(); track t.id) {\n <li class=\"sp-account__item\" [class.sp-account__item--muted]=\"t.expired\">\n <span class=\"sp-account__icon\" [class.sp-account__icon--accent]=\"!t.expired\"><hm-icon shape=\"key\" /></span>\n <span class=\"sp-account__item-main\">\n <span class=\"sp-account__item-title\"><strong>{{ t.name }}</strong></span>\n <span class=\"sp-account__meta\">{{ t.badge_name }} \u00B7 <code>{{ t.prefix }}</code></span>\n <span class=\"sp-account__meta\">\n @if (t.expired) { <span class=\"sp-account__meta--danger\">{{ fmt(text.expiredOn, { date: formatDate(t.expires_at) }) }}</span> }\n @else { {{ fmt(text.expiresOn, { date: formatDate(t.expires_at) }) }} }\n \u00B7 {{ fmt(text.lastUsed, { date: formatDate(t.last_used_at) }) }}\n </span>\n </span>\n <span class=\"sp-account__item-actions\">\n @if (confirmingRevoke() === t.id) {\n <hm-button size=\"small\" emphasis=\"medium\" (clicked)=\"cancelRevoke()\">{{ text.cancel }}</hm-button>\n }\n <hm-button size=\"small\" [emphasis]=\"confirmingRevoke() === t.id ? 'high' : 'medium'\" intent=\"danger\" (clicked)=\"revoke(t)\">\n {{ confirmingRevoke() === t.id ? text.confirmRevoke : text.revoke }}\n </hm-button>\n </span>\n </li>\n } @empty {\n <li class=\"sp-account__item sp-account__item--empty\">{{ text.empty }}</li>\n }\n </ul>\n\n @if (!creating()) {\n <div class=\"sp-account__list-footer\">\n <hm-button size=\"small\" emphasis=\"medium\" icon=\"plus\" [disabled]=\"atLimit()\" (clicked)=\"startCreate()\">{{ text.newToken }}</hm-button>\n <span class=\"sp-account__note\">{{ atLimit() ? text.limit : fmt(text.validCount, { valid: validCount(), max: maxActive() }) }}</span>\n </div>\n }\n </div>\n\n @if (creating()) {\n <section class=\"sp-account__panel\" [attr.aria-labelledby]=\"ids + '-new'\">\n <div class=\"sp-account__panel-head\">\n <hm-icon shape=\"key\" />\n <div class=\"sp-account__panel-text\">\n <h4 class=\"sp-account__title\" [id]=\"ids + '-new'\">{{ text.newToken }}</h4>\n @if (clientName()) {\n <p class=\"sp-account__note\" [innerHTML]=\"fmtHtml(text.appOnly, { name: clientName() })\"></p>\n }\n </div>\n </div>\n <form class=\"sp-account__form\" (submit)=\"$event.preventDefault(); submit()\">\n @if (!badges().length) {\n <p class=\"sp-account__note\">{{ text.noBadges }}</p>\n } @else {\n @if (badgeOptions().length > 1) {\n <div class=\"sp-account__field\">\n <span class=\"sp-account__label\">{{ text.accessLevel }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"text.accessLevel\" [options]=\"badgeOptions()\" [value]=\"badgeId()\" (valueChange)=\"badgeId.set($event)\" />\n </div>\n }\n <hm-input [label]=\"text.name\" [placeholder]=\"text.namePlaceholder\" [formControl]=\"nameControl\" [messages]=\"{ required: text.nameRequired, maxlength: text.nameMax }\" />\n <div class=\"sp-account__field\">\n <span class=\"sp-account__label\">{{ text.validity }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"text.validity\" [options]=\"validityOptions()\" [value]=\"validity()\" (valueChange)=\"validity.set($event)\" />\n </div>\n }\n <div class=\"sp-account__actions\">\n @if (badges().length) {\n <hm-button type=\"submit\" emphasis=\"high\" icon=\"key\" [disabled]=\"!canSubmit()\" [loading]=\"submitting()\">{{ text.create }}</hm-button>\n }\n <hm-button emphasis=\"medium\" (clicked)=\"cancelCreate()\">{{ text.cancel }}</hm-button>\n </div>\n </form>\n </section>\n }\n }\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--hm-space-xs);width:100%;min-width:0}.sp-account__note{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);line-height:1.45}.sp-account__note--error{color:var(--hm-danger)}.sp-account__note code{font-family:var(--hm-font-mono)}.sp-account__list{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-account__items{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}.sp-account__item{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-sm)}.sp-account__item+.sp-account__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-account__item--empty{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-account__item--muted .sp-account__item-title strong{color:var(--hm-muted)}.sp-account__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--hm-control-height-large);height:var(--hm-control-height-large);border-radius:var(--hm-radius-control);background:var(--hm-hover);color:var(--hm-muted)}.sp-account__icon--accent{background:var(--hm-accent-subtle);color:var(--hm-accent-strong)}.sp-account__item-main{display:flex;min-width:0;flex:1 1 14rem;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere}.sp-account__item-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-nano)}.sp-account__item-title strong{font-weight:var(--hm-font-weight-semibold)}.sp-account__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-account__meta code{font-family:var(--hm-font-mono)}.sp-account__meta--danger{color:var(--hm-danger)}.sp-account__item-actions,.sp-account__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__item-actions{margin-inline-start:auto}.sp-account__list-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);background:var(--hm-surface-raised)}.sp-account__list-footer:first-child{border-top:0}.sp-account__panel{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-width:0;padding:var(--hm-space-sm) var(--hm-space-md) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-inline-start:var(--hm-border-thin) solid var(--hm-accent);border-radius:var(--hm-radius);background:var(--hm-surface-raised)}.sp-account__panel--success{border-color:var(--hm-success);background:var(--hm-success-bg)}.sp-account__panel--success .sp-account__panel-head hm-icon{color:var(--hm-success)}.sp-account__panel--danger{border-inline-start-color:var(--hm-danger)}.sp-account__panel--danger .sp-account__panel-head hm-icon{color:var(--hm-danger)}.sp-account__panel-head{display:flex;align-items:flex-start;gap:var(--hm-space-xs)}.sp-account__panel-head hm-icon{flex:none;margin-block-start:var(--hm-space-micro);color:var(--hm-accent-strong)}.sp-account__panel-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro)}.sp-account__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__form,.sp-account__code,.sp-account__field{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0}.sp-account__form{gap:var(--hm-space-sm)}.sp-account__label{color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__qr{align-self:flex-start;padding:var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-surface)}.sp-account__qr img{display:block;width:10rem;max-width:100%;height:auto}.sp-account__secret,.sp-account__codes code{font-family:var(--hm-font-mono);overflow-wrap:anywhere}.sp-account__secret{-webkit-user-select:all;user-select:all}.sp-account__secret--block{display:block;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.sp-account__codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0;padding:var(--hm-space-xxxs) var(--hm-space-xs) var(--hm-space-xxxs) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-sm)}@media(forced-colors:active){.sp-account__panel{border-inline-start-color:Highlight}.sp-account__icon{border:var(--hm-border-hairline) solid CanvasText}}.sp-account__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__choices{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0;margin:0;padding:0;border:0}\n"] }]
5391
5477
  }], ctorParameters: () => [] });
5392
5478
 
5479
+ // Mesma peça de argos admin/ui src/shared/members-loader.ts (o painel central usa a dele). Interna deste ponto de
5480
+ // entrada: não vai no public-api.
5481
+ /** Membros por página pedidos ao servidor: o teto das rotas `GET …/members` do argos-admin. */
5482
+ const MEMBERS_PAGE_LIMIT = 200;
5483
+ /**
5484
+ * Lista inteira de membros carregada aos poucos: o servidor pagina (nunca monta o grupo inteiro em memória) e a tela
5485
+ * pede as páginas uma depois da outra, acrescentando conforme chegam, até ter o `total` (ou vir uma página vazia). A
5486
+ * lista já aparece e funciona com a primeira página; `loading` diz que ainda há páginas a caminho.
5487
+ *
5488
+ * `cancel()` desfaz a inscrição da requisição pendente (o HttpClient a cancela); chame ao fechar a tela ou trocar de
5489
+ * grupo. Falha numa página para em `error`, com o que já chegou intacto; `retry()` continua da página que falhou.
5490
+ */
5491
+ class MembersLoader {
5492
+ constructor(fetchPage, idOf) {
5493
+ this.fetchPage = fetchPage;
5494
+ this.idOf = idOf;
5495
+ this.items = signal([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
5496
+ /** Total do servidor (o grupo inteiro). */
5497
+ this.total = signal(0, ...(ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []));
5498
+ /** Ainda pedindo páginas. */
5499
+ this.loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
5500
+ /** A primeira página já chegou (antes dela a tela mostra o estado de carregando). */
5501
+ this.ready = signal(false, ...(ngDevMode ? [{ debugName: "ready" }] : /* istanbul ignore next */ []));
5502
+ this.error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
5503
+ this.loaded = computed(() => this.items().length, ...(ngDevMode ? [{ debugName: "loaded" }] : /* istanbul ignore next */ []));
5504
+ this.current = null;
5505
+ this.nextPage = 1;
5506
+ this.ids = new Set();
5507
+ }
5508
+ /** Recomeça do zero (cancela o que estava a caminho). */
5509
+ start() {
5510
+ this.cancel();
5511
+ this.items.set([]);
5512
+ this.ids.clear();
5513
+ this.total.set(0);
5514
+ this.ready.set(false);
5515
+ this.error.set(null);
5516
+ this.nextPage = 1;
5517
+ this.fetchNext();
5518
+ }
5519
+ /** Depois de uma falha: continua da página que falhou, sem perder o que já chegou. */
5520
+ retry() {
5521
+ if (this.loading())
5522
+ return;
5523
+ this.error.set(null);
5524
+ this.fetchNext();
5525
+ }
5526
+ cancel() {
5527
+ this.current?.unsubscribe();
5528
+ this.current = null;
5529
+ this.loading.set(false);
5530
+ }
5531
+ /**
5532
+ * Mudança feita pela própria tela depois de gravar (incluir, remover, trocar função), sem reler: `totalDelta` mantém o
5533
+ * total coerente com a lista.
5534
+ */
5535
+ patch(change, totalDelta = 0) {
5536
+ const next = change(this.items());
5537
+ this.ids.clear();
5538
+ for (const item of next)
5539
+ this.ids.add(this.idOf(item));
5540
+ this.items.set(next);
5541
+ this.total.update((n) => Math.max(next.length, n + totalDelta));
5542
+ }
5543
+ fetchNext() {
5544
+ this.loading.set(true);
5545
+ // A continuação (próxima página) roda no `complete`; com uma resposta síncrona ela começa antes do `subscribe`
5546
+ // devolver, por isso a inscrição vai num contêiner criado antes.
5547
+ const holder = new Subscription();
5548
+ this.current = holder;
5549
+ let more = false;
5550
+ holder.add(this.fetchPage(this.nextPage).subscribe({
5551
+ next: (res) => {
5552
+ const page = res.members ?? [];
5553
+ const fresh = page.filter((item) => !this.ids.has(this.idOf(item)));
5554
+ for (const item of fresh)
5555
+ this.ids.add(this.idOf(item));
5556
+ if (fresh.length)
5557
+ this.items.update((list) => [...list, ...fresh]);
5558
+ this.total.set(Math.max(res.total ?? 0, this.items().length));
5559
+ this.ready.set(true);
5560
+ this.nextPage += 1;
5561
+ // Página vazia, ou só com quem já chegou (o servidor mudou no meio), encerra: sem isso a carga não teria fim.
5562
+ more = fresh.length > 0 && this.items().length < this.total();
5563
+ },
5564
+ error: (err) => {
5565
+ if (this.current !== holder)
5566
+ return;
5567
+ this.current = null;
5568
+ this.error.set(err);
5569
+ this.ready.set(true);
5570
+ this.loading.set(false);
5571
+ },
5572
+ complete: () => {
5573
+ if (this.current !== holder)
5574
+ return;
5575
+ if (more) {
5576
+ this.fetchNext();
5577
+ }
5578
+ else {
5579
+ this.current = null;
5580
+ this.loading.set(false);
5581
+ }
5582
+ },
5583
+ }));
5584
+ }
5585
+ }
5586
+
5393
5587
  /**
5394
5588
  * Grupos de que a pessoa é membro e o que a função dela permite — `argos-admin /me/groups`. Sempre da aplicação LOGADA (o
5395
5589
  * servidor tira o client do access-token da sessão) e sempre com o token da sessão (o servidor recusa x-app-token).
@@ -5425,8 +5619,19 @@ class SpineMyGroupsService {
5425
5619
  this.groups.set([]);
5426
5620
  }
5427
5621
  }
5428
- members(groupId) {
5429
- return firstValueFrom(this.http.get(`${this.baseUrl}${urlPath `/${groupId}/members`}`, { headers: this.headers }));
5622
+ /**
5623
+ * Uma página dos membros, pela ordem do login. Observable (não Promise): a tela pede as páginas até o fim e cancela a
5624
+ * pendente ao sair.
5625
+ */
5626
+ members(groupId, query = {}) {
5627
+ let params = new HttpParams();
5628
+ if (query.page)
5629
+ params = params.set('page', query.page);
5630
+ if (query.limit)
5631
+ params = params.set('limit', query.limit);
5632
+ if (query.search?.trim())
5633
+ params = params.set('search', query.search.trim());
5634
+ return this.http.get(`${this.baseUrl}${urlPath `/${groupId}/members`}`, { headers: this.headers, params });
5430
5635
  }
5431
5636
  searchUsers(search, limit = 10) {
5432
5637
  return firstValueFrom(this.http.get(`${this.baseUrl}/users`, {
@@ -5470,6 +5675,9 @@ function groupRoleLabel(text, role) {
5470
5675
  * Membros; Membro só vê. "Incluir membro" fica acima da lista. Cada ação vale na hora (como os tokens e as sessões do
5471
5676
  * painel); remover pede confirmação. A regra é do servidor: os botões só aparecem onde ele aceitaria, e um 403/409 que
5472
5677
  * venha mesmo assim vira aviso.
5678
+ *
5679
+ * A lista inteira vem do servidor em páginas, uma depois da outra (`MembersLoader`), e já funciona com a primeira; a
5680
+ * busca é local e instantânea sobre o que já chegou.
5473
5681
  */
5474
5682
  class MyGroupComponent {
5475
5683
  constructor() {
@@ -5481,16 +5689,39 @@ class MyGroupComponent {
5481
5689
  this.i18n = inject(HermesI18nService);
5482
5690
  this.auth = inject(AuthorizationService);
5483
5691
  this.ids = `sp-group-${++sequence$1}`;
5484
- this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
5485
- this.loadError = signal('', ...(ngDevMode ? [{ debugName: "loadError" }] : /* istanbul ignore next */ []));
5486
- this.members = signal([], ...(ngDevMode ? [{ debugName: "members" }] : /* istanbul ignore next */ []));
5692
+ /** Todos os membros, carregados aos poucos (o servidor pagina; aqui se pede até o fim). */
5693
+ this.loader = new MembersLoader((page) => this.service.members(this.group().id, { page, limit: MEMBERS_PAGE_LIMIT }), (m) => m.user_id.toLowerCase());
5694
+ /** Busca na lista: local e instantânea (nome, login, e-mail). */
5695
+ this.filterControl = new FormControl('', { nonNullable: true });
5696
+ this.filter = toSignal(this.filterControl.valueChanges, { initialValue: '' });
5487
5697
  /** Membro com a remoção aguardando confirmação (segundo clique). */
5488
5698
  this.confirmingRemove = signal(null, ...(ngDevMode ? [{ debugName: "confirmingRemove" }] : /* istanbul ignore next */ []));
5489
5699
  /** Membro com uma troca de função ou remoção em andamento. */
5490
5700
  this.busy = signal(null, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
5491
5701
  /** `sub` da sessão, para marcar "você" e não oferecer ações sobre si mesmo. */
5492
5702
  this.me = (tokenSub(this.auth.getToken()) ?? '').toLowerCase();
5493
- this.countLabel = computed(() => pluralMessage(this.text.membersCount, this.members().length, this.i18n.formatLocale()), ...(ngDevMode ? [{ debugName: "countLabel" }] : /* istanbul ignore next */ []));
5703
+ this.members = computed(() => {
5704
+ const term = this.filter().trim().toLowerCase();
5705
+ const all = this.loader.items();
5706
+ if (!term)
5707
+ return all;
5708
+ return all.filter((m) => [m.name, m.username, m.email].some((v) => (v ?? '').toLowerCase().includes(term)));
5709
+ }, ...(ngDevMode ? [{ debugName: "members" }] : /* istanbul ignore next */ []));
5710
+ this.countLabel = computed(() => {
5711
+ const locale = this.i18n.formatLocale();
5712
+ return this.filter().trim()
5713
+ ? pluralMessage(this.text.matchesCount, this.members().length, locale)
5714
+ : pluralMessage(this.text.membersCount, this.loader.total(), locale);
5715
+ }, ...(ngDevMode ? [{ debugName: "countLabel" }] : /* istanbul ignore next */ []));
5716
+ /** "Carregando membros… 400 de 1.250". */
5717
+ this.progressLabel = computed(() => {
5718
+ const number = new Intl.NumberFormat(this.i18n.formatLocale());
5719
+ return formatMessage(this.text.loadingProgress, { loaded: number.format(this.loader.loaded()), total: number.format(this.loader.total()) });
5720
+ }, ...(ngDevMode ? [{ debugName: "progressLabel" }] : /* istanbul ignore next */ []));
5721
+ this.incompleteLabel = computed(() => {
5722
+ const number = new Intl.NumberFormat(this.i18n.formatLocale());
5723
+ return formatMessage(this.text.loadIncomplete, { loaded: number.format(this.loader.loaded()), total: number.format(this.loader.total()) });
5724
+ }, ...(ngDevMode ? [{ debugName: "incompleteLabel" }] : /* istanbul ignore next */ []));
5494
5725
  /** Funções que o Administrador escolhe (seletor de cada membro e inclusão): Membro, Editor, Administrador. */
5495
5726
  this.roleOptions = computed(() => [
5496
5727
  { value: MEMBER, label: this.text.roleMember },
@@ -5505,13 +5736,16 @@ class MyGroupComponent {
5505
5736
  this.selected = signal(null, ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
5506
5737
  this.newRole = signal(MEMBER, ...(ngDevMode ? [{ debugName: "newRole" }] : /* istanbul ignore next */ []));
5507
5738
  this.submitting = signal(false, ...(ngDevMode ? [{ debugName: "submitting" }] : /* istanbul ignore next */ []));
5508
- this.memberIds = computed(() => new Set(this.members().map((m) => m.user_id.toLowerCase())), ...(ngDevMode ? [{ debugName: "memberIds" }] : /* istanbul ignore next */ []));
5739
+ this.memberIds = computed(() => new Set(this.loader.items().map((m) => m.user_id.toLowerCase())), ...(ngDevMode ? [{ debugName: "memberIds" }] : /* istanbul ignore next */ []));
5740
+ const destroyRef = inject(DestroyRef);
5509
5741
  this.searchControl.valueChanges
5510
- .pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed(inject(DestroyRef)))
5742
+ .pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed(destroyRef))
5511
5743
  .subscribe((value) => void this.search(value));
5744
+ // Sair da tela (voltar aos grupos, fechar o painel) cancela a página que estiver a caminho.
5745
+ destroyRef.onDestroy(() => this.loader.cancel());
5512
5746
  }
5513
5747
  ngOnInit() {
5514
- void this.load();
5748
+ this.loader.start();
5515
5749
  }
5516
5750
  roleLabel(role) {
5517
5751
  return groupRoleLabel(this.text, role);
@@ -5519,6 +5753,7 @@ class MyGroupComponent {
5519
5753
  isMe(member) {
5520
5754
  return member.user_id.toLowerCase() === this.me;
5521
5755
  }
5756
+ /** Já é membro? Com a lista ainda carregando, quem não chegou passa (e o servidor responde "já é membro"). */
5522
5757
  isMember(user) {
5523
5758
  return this.memberIds().has(user.id.toLowerCase());
5524
5759
  }
@@ -5532,21 +5767,15 @@ class MyGroupComponent {
5532
5767
  canChangeRole(member) {
5533
5768
  return this.group().can_change_roles && !this.isMe(member);
5534
5769
  }
5535
- /** `silent`: relê sem trocar a lista pelo "carregando" (depois de incluir; a primeira carga mostra o estado). */
5536
- async load(silent = false) {
5537
- if (!silent)
5538
- this.loading.set(true);
5539
- this.loadError.set('');
5540
- try {
5541
- const res = await this.service.members(this.group().id);
5542
- this.members.set(res.members ?? []);
5543
- }
5544
- catch (err) {
5545
- this.loadError.set(errorText(err, this.text.loadError));
5546
- }
5547
- finally {
5548
- this.loading.set(false);
5549
- }
5770
+ /**
5771
+ * Depois de gravar: muda a lista na tela, sem reler. Inclusão e remoção deslocam as páginas do servidor, então com a
5772
+ * carga ainda em andamento ela recomeça do zero (senão alguém ficaria de fora ou repetido).
5773
+ */
5774
+ afterWrite(change, totalDelta) {
5775
+ if (totalDelta !== 0 && this.loader.loading())
5776
+ this.loader.start();
5777
+ else
5778
+ this.loader.patch(change, totalDelta);
5550
5779
  }
5551
5780
  async changeRole(member, value) {
5552
5781
  const role = (value || null);
@@ -5555,12 +5784,12 @@ class MyGroupComponent {
5555
5784
  this.busy.set(member.user_id);
5556
5785
  try {
5557
5786
  await this.service.updateRole(this.group().id, member.user_id, role);
5558
- this.members.update((list) => list.map((m) => (m.user_id === member.user_id ? { ...m, role } : m)));
5787
+ this.afterWrite((list) => list.map((m) => (m.user_id === member.user_id ? { ...m, role } : m)), 0);
5559
5788
  this.toast.success(formatMessage(this.text.roleChanged, { name: displayName(member), role: this.roleLabel(role) }));
5560
5789
  }
5561
5790
  catch (err) {
5562
5791
  this.toast.error(errorText(err, this.text.roleError));
5563
- await this.load();
5792
+ this.loader.start();
5564
5793
  }
5565
5794
  finally {
5566
5795
  this.busy.set(null);
@@ -5575,12 +5804,12 @@ class MyGroupComponent {
5575
5804
  this.busy.set(member.user_id);
5576
5805
  try {
5577
5806
  await this.service.removeMember(this.group().id, member.user_id);
5578
- this.members.update((list) => list.filter((m) => m.user_id !== member.user_id));
5807
+ this.afterWrite((list) => list.filter((m) => m.user_id !== member.user_id), -1);
5579
5808
  this.toast.success(formatMessage(this.text.removed, { name: displayName(member) }));
5580
5809
  }
5581
5810
  catch (err) {
5582
5811
  this.toast.error(errorText(err, this.text.removeError));
5583
- await this.load();
5812
+ this.loader.start();
5584
5813
  }
5585
5814
  finally {
5586
5815
  this.busy.set(null);
@@ -5638,7 +5867,12 @@ class MyGroupComponent {
5638
5867
  await this.service.addMember(this.group().id, user.id, role);
5639
5868
  this.toast.success(formatMessage(this.text.added, { name: user.name || user.username }));
5640
5869
  this.adding.set(false);
5641
- await this.load(true);
5870
+ const added = {
5871
+ user_id: user.id, username: user.username, email: user.email, name: user.name, enabled: user.enabled, role,
5872
+ picture: user.picture ?? null, joined_at: new Date().toISOString(), added_by_username: null,
5873
+ };
5874
+ // Entra na posição do login, como o servidor ordena.
5875
+ this.afterWrite((list) => [...list, added].sort((a, b) => a.username.localeCompare(b.username)), 1);
5642
5876
  }
5643
5877
  catch (err) {
5644
5878
  this.toast.error(errorText(err, this.text.addError));
@@ -5648,14 +5882,14 @@ class MyGroupComponent {
5648
5882
  }
5649
5883
  }
5650
5884
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: MyGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5651
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: MyGroupComponent, isStandalone: true, selector: "spine-my-group", inputs: { group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<!-- Incluir membro fica acima da lista: o bot\u00E3o, ou o painel de inclus\u00E3o aberto no lugar dele. -->\n@if (group().can_add) {\n @if (adding()) {\n <section class=\"sp-account__panel\" [attr.aria-labelledby]=\"ids + '-add'\">\n <div class=\"sp-account__panel-head\">\n <hm-icon shape=\"assign-user\" />\n <div class=\"sp-account__panel-text\">\n <h4 class=\"sp-account__title\" [id]=\"ids + '-add'\">{{ fmt(text.addHeading, { group: group().name }) }}</h4>\n @if (!group().can_change_roles) {\n <p class=\"sp-account__note\">{{ text.editorAddsMembers }}</p>\n }\n </div>\n </div>\n <form class=\"sp-account__form\" (submit)=\"$event.preventDefault(); submitAdd()\">\n <hm-input\n icon=\"search\"\n autocomplete=\"off\"\n [label]=\"text.search\"\n [placeholder]=\"text.searchPlaceholder\"\n [hint]=\"text.searchHint\"\n [formControl]=\"searchControl\"\n />\n\n @if (searching()) {\n <p class=\"sp-account__note\" role=\"status\">{{ text.searching }}</p>\n } @else if (searchError()) {\n <p class=\"sp-account__note sp-account__note--error\">{{ searchError() }}</p>\n } @else if (results(); as list) {\n @if (list.length) {\n <ul class=\"sp-account__list sp-group__results\" [attr.aria-label]=\"text.search\">\n @for (u of list; track u.id) {\n <li>\n <button\n type=\"button\"\n class=\"sp-group__result\"\n [class.sp-group__result--selected]=\"selected()?.id === u.id\"\n [attr.aria-pressed]=\"selected()?.id === u.id\"\n [disabled]=\"isMember(u)\"\n (click)=\"pick(u)\"\n >\n <strong>{{ u.name || u.username }}</strong>\n <span class=\"sp-account__meta\">{{ u.username }}@if (u.email) { \u00B7 {{ u.email }} }@if (isMember(u)) { \u00B7 {{ text.alreadyMember }} }</span>\n </button>\n </li>\n }\n </ul>\n } @else {\n <p class=\"sp-account__note\" role=\"status\">{{ text.noResults }}</p>\n }\n }\n\n @if (group().can_change_roles) {\n <div class=\"sp-account__field\">\n <span class=\"sp-account__label\">{{ text.role }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"text.role\" [options]=\"roleOptions()\" [value]=\"newRole()\" (valueChange)=\"newRole.set($event)\" />\n </div>\n }\n\n <div class=\"sp-account__actions\">\n <hm-button type=\"submit\" emphasis=\"high\" icon=\"plus\" [disabled]=\"!selected()\" [loading]=\"submitting()\">{{ text.add }}</hm-button>\n <hm-button emphasis=\"medium\" (clicked)=\"cancelAdd()\">{{ text.cancel }}</hm-button>\n </div>\n </form>\n </section>\n } @else {\n <div class=\"sp-account__actions\">\n <hm-button size=\"small\" emphasis=\"medium\" icon=\"plus\" (clicked)=\"startAdd()\">{{ text.addMember }}</hm-button>\n </div>\n }\n} @else {\n <p class=\"sp-account__note\">{{ text.readOnly }}</p>\n}\n\n@if (loading()) {\n <p class=\"sp-account__note\" role=\"status\">{{ text.loading }}</p>\n} @else if (loadError()) {\n <p class=\"sp-account__note sp-account__note--error\">{{ loadError() }}</p>\n} @else {\n <div class=\"sp-account__list\">\n <ul class=\"sp-account__items\" [attr.aria-label]=\"group().name\">\n @for (m of members(); track m.user_id) {\n <li class=\"sp-account__item\" [class.sp-account__item--muted]=\"!m.enabled\">\n <hm-account-avatar class=\"sp-group__avatar\" [name]=\"m.name || m.username\" [imageUrl]=\"m.picture\" />\n <span class=\"sp-account__item-main\">\n <span class=\"sp-account__item-title\">\n <strong>{{ m.name || m.username }}</strong>\n @if (isMe(m)) { <hm-tag color=\"info\" [name]=\"text.you\" /> }\n </span>\n <span class=\"sp-account__meta\">{{ m.username }}@if (m.email) { \u00B7 {{ m.email }} }</span>\n @if (!canChangeRole(m)) {\n <span class=\"sp-account__meta\">{{ roleLabel(m.role) }}</span>\n }\n </span>\n @if (canChangeRole(m) || canRemove(m)) {\n <span class=\"sp-account__item-actions\">\n <!-- Confirmando a remo\u00E7\u00E3o, a linha mostra s\u00F3 Cancelar e Confirmar (a fun\u00E7\u00E3o n\u00E3o importa mais). -->\n @if (canChangeRole(m) && confirmingRemove() !== m.user_id) {\n <hm-select\n class=\"sp-group__role\"\n [label]=\"fmt(text.roleOf, { name: m.name || m.username })\"\n [labelHidden]=\"true\"\n [value]=\"m.role ?? ''\"\n [disabled]=\"busy() === m.user_id\"\n (valueChange)=\"changeRole(m, $event)\"\n >\n @for (option of roleOptions(); track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </hm-select>\n }\n @if (canRemove(m)) {\n @if (confirmingRemove() === m.user_id) {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelRemove()\">{{ text.cancel }}</hm-button>\n }\n <hm-button\n intent=\"danger\"\n [emphasis]=\"confirmingRemove() === m.user_id ? 'high' : 'medium'\"\n [loading]=\"busy() === m.user_id\"\n (clicked)=\"remove(m)\"\n >{{ confirmingRemove() === m.user_id ? text.confirmRemove : text.remove }}</hm-button>\n }\n </span>\n }\n </li>\n } @empty {\n <li class=\"sp-account__item sp-account__item--empty\">{{ text.empty }}</li>\n }\n </ul>\n <div class=\"sp-account__list-footer\">\n <span class=\"sp-account__note\">{{ countLabel() }}</span>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--hm-space-xs);width:100%;min-width:0}.sp-account__note{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);line-height:1.45}.sp-account__note--error{color:var(--hm-danger)}.sp-account__note code{font-family:var(--hm-font-mono)}.sp-account__list{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-account__items{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}.sp-account__item{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-sm)}.sp-account__item+.sp-account__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-account__item--empty{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-account__item--muted .sp-account__item-title strong{color:var(--hm-muted)}.sp-account__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--hm-control-height-large);height:var(--hm-control-height-large);border-radius:var(--hm-radius-control);background:var(--hm-hover);color:var(--hm-muted)}.sp-account__icon--accent{background:var(--hm-accent-subtle);color:var(--hm-accent-strong)}.sp-account__item-main{display:flex;min-width:0;flex:1 1 14rem;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere}.sp-account__item-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-nano)}.sp-account__item-title strong{font-weight:var(--hm-font-weight-semibold)}.sp-account__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-account__meta code{font-family:var(--hm-font-mono)}.sp-account__meta--danger{color:var(--hm-danger)}.sp-account__item-actions,.sp-account__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__item-actions{margin-inline-start:auto}.sp-account__list-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);background:var(--hm-surface-raised)}.sp-account__list-footer:first-child{border-top:0}.sp-account__panel{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-width:0;padding:var(--hm-space-sm) var(--hm-space-md) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-inline-start:var(--hm-border-thin) solid var(--hm-accent);border-radius:var(--hm-radius);background:var(--hm-surface-raised)}.sp-account__panel--success{border-color:var(--hm-success);background:var(--hm-success-bg)}.sp-account__panel--success .sp-account__panel-head hm-icon{color:var(--hm-success)}.sp-account__panel--danger{border-inline-start-color:var(--hm-danger)}.sp-account__panel--danger .sp-account__panel-head hm-icon{color:var(--hm-danger)}.sp-account__panel-head{display:flex;align-items:flex-start;gap:var(--hm-space-xs)}.sp-account__panel-head hm-icon{flex:none;margin-block-start:var(--hm-space-micro);color:var(--hm-accent-strong)}.sp-account__panel-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro)}.sp-account__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__form,.sp-account__code,.sp-account__field{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0}.sp-account__form{gap:var(--hm-space-sm)}.sp-account__label{color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__qr{align-self:flex-start;padding:var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-surface)}.sp-account__qr img{display:block;width:10rem;max-width:100%;height:auto}.sp-account__secret,.sp-account__codes code{font-family:var(--hm-font-mono);overflow-wrap:anywhere}.sp-account__secret{-webkit-user-select:all;user-select:all}.sp-account__secret--block{display:block;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.sp-account__codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0;padding:var(--hm-space-xxxs) var(--hm-space-xs) var(--hm-space-xxxs) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-sm)}@media(forced-colors:active){.sp-account__panel{border-inline-start-color:Highlight}.sp-account__icon{border:var(--hm-border-hairline) solid CanvasText}}.sp-account__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__choices{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0;margin:0;padding:0;border:0}\n", "@charset \"UTF-8\";.sp-group__avatar{flex:none}.sp-group__role{min-width:10rem}.sp-group__results{max-height:16rem;overflow-y:auto}.sp-group__results li+li{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-group__result{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-micro);width:100%;min-width:0;padding:var(--hm-space-xs) var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;overflow-wrap:anywhere;cursor:pointer}.sp-group__result:hover:not(:disabled){background:var(--hm-hover)}.sp-group__result:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-group__result--selected{background:var(--hm-accent-subtle)}.sp-group__result:disabled{color:var(--hm-muted);cursor:default}.sp-groups__item{display:flex;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);width:100%;min-width:0;padding:var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}li+li .sp-groups__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-groups__item:hover{background:var(--hm-hover)}.sp-groups__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-groups__faces{flex:none;margin-inline-start:auto}.sp-groups__chevron{flex:none;color:var(--hm-muted)}.sp-groups__back{display:flex}.sp-groups__heading{margin-block-end:var(--hm-space-xxxs)}@media(forced-colors:active){.sp-group__result--selected{outline:var(--hm-border-thin) solid Highlight;outline-offset:calc(var(--hm-border-thin) * -1)}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { 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", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: InputComponent, selector: "hm-input", inputs: ["value", "label", "type", "placeholder", "icon", "labelHidden", "hint", "prefix", "suffix", "prefixIcon", "mask", "maskValue", "min", "max", "step", "stepButtons", "maxLength", "autocomplete", "inputMode"], outputs: ["valueChange"] }, { kind: "component", type: SegmentedControlComponent, selector: "hm-segmented-control", inputs: ["options", "value", "label", "size", "emphasis"], outputs: ["valueChange"] }, { kind: "component", type: SelectComponent, selector: "hm-select", inputs: ["value", "label", "labelHidden", "hint"], outputs: ["valueChange"] }, { kind: "component", type: TagComponent, selector: "hm-tag", inputs: ["name", "color", "compact"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5885
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: MyGroupComponent, isStandalone: true, selector: "spine-my-group", inputs: { group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<!-- Incluir membro fica acima da lista: o bot\u00E3o, ou o painel de inclus\u00E3o aberto no lugar dele. -->\n@if (group().can_add) {\n @if (adding()) {\n <section class=\"sp-account__panel\" [attr.aria-labelledby]=\"ids + '-add'\">\n <div class=\"sp-account__panel-head\">\n <hm-icon shape=\"assign-user\" />\n <div class=\"sp-account__panel-text\">\n <h4 class=\"sp-account__title\" [id]=\"ids + '-add'\">{{ fmt(text.addHeading, { group: group().name }) }}</h4>\n @if (!group().can_change_roles) {\n <p class=\"sp-account__note\">{{ text.editorAddsMembers }}</p>\n }\n </div>\n </div>\n <form class=\"sp-account__form\" (submit)=\"$event.preventDefault(); submitAdd()\">\n <hm-input\n icon=\"search\"\n autocomplete=\"off\"\n [label]=\"text.search\"\n [placeholder]=\"text.searchPlaceholder\"\n [hint]=\"text.searchHint\"\n [formControl]=\"searchControl\"\n />\n\n @if (searching()) {\n <p class=\"sp-account__note\" role=\"status\">{{ text.searching }}</p>\n } @else if (searchError()) {\n <p class=\"sp-account__note sp-account__note--error\">{{ searchError() }}</p>\n } @else if (results(); as list) {\n @if (list.length) {\n <ul class=\"sp-account__list sp-group__results\" [attr.aria-label]=\"text.search\">\n @for (u of list; track u.id) {\n <li>\n <button\n type=\"button\"\n class=\"sp-group__result\"\n [class.sp-group__result--selected]=\"selected()?.id === u.id\"\n [attr.aria-pressed]=\"selected()?.id === u.id\"\n [disabled]=\"isMember(u)\"\n (click)=\"pick(u)\"\n >\n <strong>{{ u.name || u.username }}</strong>\n <span class=\"sp-account__meta\">{{ u.username }}@if (u.email) { \u00B7 {{ u.email }} }@if (isMember(u)) { \u00B7 {{ text.alreadyMember }} }</span>\n </button>\n </li>\n }\n </ul>\n } @else {\n <p class=\"sp-account__note\" role=\"status\">{{ text.noResults }}</p>\n }\n }\n\n @if (group().can_change_roles) {\n <div class=\"sp-account__field\">\n <span class=\"sp-account__label\">{{ text.role }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"text.role\" [options]=\"roleOptions()\" [value]=\"newRole()\" (valueChange)=\"newRole.set($event)\" />\n </div>\n }\n\n <div class=\"sp-account__actions\">\n <hm-button type=\"submit\" emphasis=\"high\" icon=\"plus\" [disabled]=\"!selected()\" [loading]=\"submitting()\">{{ text.add }}</hm-button>\n <hm-button emphasis=\"medium\" (clicked)=\"cancelAdd()\">{{ text.cancel }}</hm-button>\n </div>\n </form>\n </section>\n } @else {\n <div class=\"sp-account__actions\">\n <hm-button size=\"small\" emphasis=\"medium\" icon=\"plus\" (clicked)=\"startAdd()\">{{ text.addMember }}</hm-button>\n </div>\n }\n} @else {\n <p class=\"sp-account__note\">{{ text.readOnly }}</p>\n}\n\n<!-- Busca na lista: local e instant\u00E2nea sobre o que j\u00E1 chegou. Fica fora do \"carregando\" para n\u00E3o perder o foco. -->\n<hm-input\n class=\"sp-group__filter\"\n icon=\"search\"\n type=\"search\"\n autocomplete=\"off\"\n [label]=\"text.filterMembers\"\n [placeholder]=\"text.searchPlaceholder\"\n [formControl]=\"filterControl\"\n/>\n\n@if (loader.error()) {\n <!-- Falha numa p\u00E1gina: o que j\u00E1 chegou continua na lista; \"Tentar de novo\" segue de onde parou. -->\n <div class=\"sp-group__status\">\n <p class=\"sp-account__note sp-account__note--error\">{{ loader.loaded() ? incompleteLabel() : text.loadError }}</p>\n <hm-button size=\"small\" emphasis=\"medium\" icon=\"refresh\" (clicked)=\"loader.retry()\">{{ text.retry }}</hm-button>\n </div>\n} @else if (loader.loading() && loader.ready()) {\n <p class=\"sp-account__note sp-group__status\" role=\"status\">\n {{ progressLabel() }}@if (filter().trim()) { \u00B7 {{ text.partialResults }} }\n </p>\n}\n\n@if (!loader.ready()) {\n <p class=\"sp-account__note\" role=\"status\">{{ text.loading }}</p>\n} @else if (loader.loaded() || !loader.error()) {\n <div class=\"sp-account__list\">\n <ul class=\"sp-account__items\" [attr.aria-label]=\"group().name\">\n @for (m of members(); track m.user_id) {\n <li class=\"sp-account__item\" [class.sp-account__item--muted]=\"!m.enabled\">\n <hm-account-avatar class=\"sp-group__avatar\" [name]=\"m.name || m.username\" [imageUrl]=\"m.picture\" />\n <span class=\"sp-account__item-main\">\n <span class=\"sp-account__item-title\">\n <strong>{{ m.name || m.username }}</strong>\n @if (isMe(m)) { <hm-tag color=\"info\" [name]=\"text.you\" /> }\n </span>\n <span class=\"sp-account__meta\">{{ m.username }}@if (m.email) { \u00B7 {{ m.email }} }</span>\n @if (!canChangeRole(m)) {\n <span class=\"sp-account__meta\">{{ roleLabel(m.role) }}</span>\n }\n </span>\n @if (canChangeRole(m) || canRemove(m)) {\n <span class=\"sp-account__item-actions\">\n <!-- Confirmando a remo\u00E7\u00E3o, a linha mostra s\u00F3 Cancelar e Confirmar (a fun\u00E7\u00E3o n\u00E3o importa mais). -->\n @if (canChangeRole(m) && confirmingRemove() !== m.user_id) {\n <hm-select\n class=\"sp-group__role\"\n [label]=\"fmt(text.roleOf, { name: m.name || m.username })\"\n [labelHidden]=\"true\"\n [value]=\"m.role ?? ''\"\n [disabled]=\"busy() === m.user_id\"\n (valueChange)=\"changeRole(m, $event)\"\n >\n @for (option of roleOptions(); track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </hm-select>\n }\n @if (canRemove(m)) {\n @if (confirmingRemove() === m.user_id) {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelRemove()\">{{ text.cancel }}</hm-button>\n }\n <hm-button\n intent=\"danger\"\n [emphasis]=\"confirmingRemove() === m.user_id ? 'high' : 'medium'\"\n [loading]=\"busy() === m.user_id\"\n (clicked)=\"remove(m)\"\n >{{ confirmingRemove() === m.user_id ? text.confirmRemove : text.remove }}</hm-button>\n }\n </span>\n }\n </li>\n } @empty {\n <li class=\"sp-account__item sp-account__item--empty\">{{ filter().trim() ? text.noMatches : text.empty }}</li>\n }\n </ul>\n <div class=\"sp-account__list-footer\">\n <span class=\"sp-account__note\">{{ countLabel() }}</span>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--hm-space-xs);width:100%;min-width:0}.sp-account__note{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);line-height:1.45}.sp-account__note--error{color:var(--hm-danger)}.sp-account__note code{font-family:var(--hm-font-mono)}.sp-account__list{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-account__items{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}.sp-account__item{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-sm)}.sp-account__item+.sp-account__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-account__item--empty{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-account__item--muted .sp-account__item-title strong{color:var(--hm-muted)}.sp-account__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--hm-control-height-large);height:var(--hm-control-height-large);border-radius:var(--hm-radius-control);background:var(--hm-hover);color:var(--hm-muted)}.sp-account__icon--accent{background:var(--hm-accent-subtle);color:var(--hm-accent-strong)}.sp-account__item-main{display:flex;min-width:0;flex:1 1 14rem;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere}.sp-account__item-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-nano)}.sp-account__item-title strong{font-weight:var(--hm-font-weight-semibold)}.sp-account__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-account__meta code{font-family:var(--hm-font-mono)}.sp-account__meta--danger{color:var(--hm-danger)}.sp-account__item-actions,.sp-account__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__item-actions{margin-inline-start:auto}.sp-account__list-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);background:var(--hm-surface-raised)}.sp-account__list-footer:first-child{border-top:0}.sp-account__panel{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-width:0;padding:var(--hm-space-sm) var(--hm-space-md) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-inline-start:var(--hm-border-thin) solid var(--hm-accent);border-radius:var(--hm-radius);background:var(--hm-surface-raised)}.sp-account__panel--success{border-color:var(--hm-success);background:var(--hm-success-bg)}.sp-account__panel--success .sp-account__panel-head hm-icon{color:var(--hm-success)}.sp-account__panel--danger{border-inline-start-color:var(--hm-danger)}.sp-account__panel--danger .sp-account__panel-head hm-icon{color:var(--hm-danger)}.sp-account__panel-head{display:flex;align-items:flex-start;gap:var(--hm-space-xs)}.sp-account__panel-head hm-icon{flex:none;margin-block-start:var(--hm-space-micro);color:var(--hm-accent-strong)}.sp-account__panel-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro)}.sp-account__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__form,.sp-account__code,.sp-account__field{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0}.sp-account__form{gap:var(--hm-space-sm)}.sp-account__label{color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__qr{align-self:flex-start;padding:var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-surface)}.sp-account__qr img{display:block;width:10rem;max-width:100%;height:auto}.sp-account__secret,.sp-account__codes code{font-family:var(--hm-font-mono);overflow-wrap:anywhere}.sp-account__secret{-webkit-user-select:all;user-select:all}.sp-account__secret--block{display:block;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.sp-account__codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0;padding:var(--hm-space-xxxs) var(--hm-space-xs) var(--hm-space-xxxs) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-sm)}@media(forced-colors:active){.sp-account__panel{border-inline-start-color:Highlight}.sp-account__icon{border:var(--hm-border-hairline) solid CanvasText}}.sp-account__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__choices{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0;margin:0;padding:0;border:0}\n", "@charset \"UTF-8\";.sp-group__avatar{flex:none}.sp-group__role{min-width:10rem}.sp-group__filter{display:block;margin-block:var(--hm-space-sm)}.sp-group__status{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs);margin-block-end:var(--hm-space-sm)}.sp-group__results{max-height:16rem;overflow-y:auto}.sp-group__results li+li{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-group__result{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-micro);width:100%;min-width:0;padding:var(--hm-space-xs) var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;overflow-wrap:anywhere;cursor:pointer}.sp-group__result:hover:not(:disabled){background:var(--hm-hover)}.sp-group__result:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-group__result--selected{background:var(--hm-accent-subtle)}.sp-group__result:disabled{color:var(--hm-muted);cursor:default}.sp-groups__item{display:flex;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);width:100%;min-width:0;padding:var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;cursor:pointer}li+li .sp-groups__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-groups__item:hover{background:var(--hm-hover)}.sp-groups__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-groups__faces{flex:none;margin-inline-start:auto}.sp-groups__chevron{flex:none;color:var(--hm-muted)}.sp-groups__back{display:flex}.sp-groups__heading{margin-block-end:var(--hm-space-xxxs)}@media(forced-colors:active){.sp-group__result--selected{outline:var(--hm-border-thin) solid Highlight;outline-offset:calc(var(--hm-border-thin) * -1)}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: AccountAvatarComponent, selector: "hm-account-avatar", inputs: ["name", "imageUrl", "size", "loading", "presence", "texts"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: InputComponent, selector: "hm-input", inputs: ["value", "label", "type", "placeholder", "icon", "labelHidden", "hint", "prefix", "suffix", "prefixIcon", "mask", "maskValue", "min", "max", "step", "stepButtons", "maxLength", "autocomplete", "inputMode"], outputs: ["valueChange"] }, { kind: "component", type: SegmentedControlComponent, selector: "hm-segmented-control", inputs: ["options", "value", "label", "size", "emphasis"], outputs: ["valueChange"] }, { kind: "component", type: SelectComponent, selector: "hm-select", inputs: ["value", "label", "labelHidden", "hint"], outputs: ["valueChange"] }, { kind: "component", type: TagComponent, selector: "hm-tag", inputs: ["name", "color", "compact"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5652
5886
  }
5653
5887
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: MyGroupComponent, decorators: [{
5654
5888
  type: Component,
5655
5889
  args: [{ selector: 'spine-my-group', imports: [
5656
5890
  ReactiveFormsModule, AccountAvatarComponent, ButtonComponent, IconComponent, InputComponent, SegmentedControlComponent,
5657
5891
  SelectComponent, TagComponent,
5658
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Incluir membro fica acima da lista: o bot\u00E3o, ou o painel de inclus\u00E3o aberto no lugar dele. -->\n@if (group().can_add) {\n @if (adding()) {\n <section class=\"sp-account__panel\" [attr.aria-labelledby]=\"ids + '-add'\">\n <div class=\"sp-account__panel-head\">\n <hm-icon shape=\"assign-user\" />\n <div class=\"sp-account__panel-text\">\n <h4 class=\"sp-account__title\" [id]=\"ids + '-add'\">{{ fmt(text.addHeading, { group: group().name }) }}</h4>\n @if (!group().can_change_roles) {\n <p class=\"sp-account__note\">{{ text.editorAddsMembers }}</p>\n }\n </div>\n </div>\n <form class=\"sp-account__form\" (submit)=\"$event.preventDefault(); submitAdd()\">\n <hm-input\n icon=\"search\"\n autocomplete=\"off\"\n [label]=\"text.search\"\n [placeholder]=\"text.searchPlaceholder\"\n [hint]=\"text.searchHint\"\n [formControl]=\"searchControl\"\n />\n\n @if (searching()) {\n <p class=\"sp-account__note\" role=\"status\">{{ text.searching }}</p>\n } @else if (searchError()) {\n <p class=\"sp-account__note sp-account__note--error\">{{ searchError() }}</p>\n } @else if (results(); as list) {\n @if (list.length) {\n <ul class=\"sp-account__list sp-group__results\" [attr.aria-label]=\"text.search\">\n @for (u of list; track u.id) {\n <li>\n <button\n type=\"button\"\n class=\"sp-group__result\"\n [class.sp-group__result--selected]=\"selected()?.id === u.id\"\n [attr.aria-pressed]=\"selected()?.id === u.id\"\n [disabled]=\"isMember(u)\"\n (click)=\"pick(u)\"\n >\n <strong>{{ u.name || u.username }}</strong>\n <span class=\"sp-account__meta\">{{ u.username }}@if (u.email) { \u00B7 {{ u.email }} }@if (isMember(u)) { \u00B7 {{ text.alreadyMember }} }</span>\n </button>\n </li>\n }\n </ul>\n } @else {\n <p class=\"sp-account__note\" role=\"status\">{{ text.noResults }}</p>\n }\n }\n\n @if (group().can_change_roles) {\n <div class=\"sp-account__field\">\n <span class=\"sp-account__label\">{{ text.role }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"text.role\" [options]=\"roleOptions()\" [value]=\"newRole()\" (valueChange)=\"newRole.set($event)\" />\n </div>\n }\n\n <div class=\"sp-account__actions\">\n <hm-button type=\"submit\" emphasis=\"high\" icon=\"plus\" [disabled]=\"!selected()\" [loading]=\"submitting()\">{{ text.add }}</hm-button>\n <hm-button emphasis=\"medium\" (clicked)=\"cancelAdd()\">{{ text.cancel }}</hm-button>\n </div>\n </form>\n </section>\n } @else {\n <div class=\"sp-account__actions\">\n <hm-button size=\"small\" emphasis=\"medium\" icon=\"plus\" (clicked)=\"startAdd()\">{{ text.addMember }}</hm-button>\n </div>\n }\n} @else {\n <p class=\"sp-account__note\">{{ text.readOnly }}</p>\n}\n\n@if (loading()) {\n <p class=\"sp-account__note\" role=\"status\">{{ text.loading }}</p>\n} @else if (loadError()) {\n <p class=\"sp-account__note sp-account__note--error\">{{ loadError() }}</p>\n} @else {\n <div class=\"sp-account__list\">\n <ul class=\"sp-account__items\" [attr.aria-label]=\"group().name\">\n @for (m of members(); track m.user_id) {\n <li class=\"sp-account__item\" [class.sp-account__item--muted]=\"!m.enabled\">\n <hm-account-avatar class=\"sp-group__avatar\" [name]=\"m.name || m.username\" [imageUrl]=\"m.picture\" />\n <span class=\"sp-account__item-main\">\n <span class=\"sp-account__item-title\">\n <strong>{{ m.name || m.username }}</strong>\n @if (isMe(m)) { <hm-tag color=\"info\" [name]=\"text.you\" /> }\n </span>\n <span class=\"sp-account__meta\">{{ m.username }}@if (m.email) { \u00B7 {{ m.email }} }</span>\n @if (!canChangeRole(m)) {\n <span class=\"sp-account__meta\">{{ roleLabel(m.role) }}</span>\n }\n </span>\n @if (canChangeRole(m) || canRemove(m)) {\n <span class=\"sp-account__item-actions\">\n <!-- Confirmando a remo\u00E7\u00E3o, a linha mostra s\u00F3 Cancelar e Confirmar (a fun\u00E7\u00E3o n\u00E3o importa mais). -->\n @if (canChangeRole(m) && confirmingRemove() !== m.user_id) {\n <hm-select\n class=\"sp-group__role\"\n [label]=\"fmt(text.roleOf, { name: m.name || m.username })\"\n [labelHidden]=\"true\"\n [value]=\"m.role ?? ''\"\n [disabled]=\"busy() === m.user_id\"\n (valueChange)=\"changeRole(m, $event)\"\n >\n @for (option of roleOptions(); track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </hm-select>\n }\n @if (canRemove(m)) {\n @if (confirmingRemove() === m.user_id) {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelRemove()\">{{ text.cancel }}</hm-button>\n }\n <hm-button\n intent=\"danger\"\n [emphasis]=\"confirmingRemove() === m.user_id ? 'high' : 'medium'\"\n [loading]=\"busy() === m.user_id\"\n (clicked)=\"remove(m)\"\n >{{ confirmingRemove() === m.user_id ? text.confirmRemove : text.remove }}</hm-button>\n }\n </span>\n }\n </li>\n } @empty {\n <li class=\"sp-account__item sp-account__item--empty\">{{ text.empty }}</li>\n }\n </ul>\n <div class=\"sp-account__list-footer\">\n <span class=\"sp-account__note\">{{ countLabel() }}</span>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--hm-space-xs);width:100%;min-width:0}.sp-account__note{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);line-height:1.45}.sp-account__note--error{color:var(--hm-danger)}.sp-account__note code{font-family:var(--hm-font-mono)}.sp-account__list{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-account__items{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}.sp-account__item{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-sm)}.sp-account__item+.sp-account__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-account__item--empty{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-account__item--muted .sp-account__item-title strong{color:var(--hm-muted)}.sp-account__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--hm-control-height-large);height:var(--hm-control-height-large);border-radius:var(--hm-radius-control);background:var(--hm-hover);color:var(--hm-muted)}.sp-account__icon--accent{background:var(--hm-accent-subtle);color:var(--hm-accent-strong)}.sp-account__item-main{display:flex;min-width:0;flex:1 1 14rem;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere}.sp-account__item-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-nano)}.sp-account__item-title strong{font-weight:var(--hm-font-weight-semibold)}.sp-account__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-account__meta code{font-family:var(--hm-font-mono)}.sp-account__meta--danger{color:var(--hm-danger)}.sp-account__item-actions,.sp-account__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__item-actions{margin-inline-start:auto}.sp-account__list-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);background:var(--hm-surface-raised)}.sp-account__list-footer:first-child{border-top:0}.sp-account__panel{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-width:0;padding:var(--hm-space-sm) var(--hm-space-md) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-inline-start:var(--hm-border-thin) solid var(--hm-accent);border-radius:var(--hm-radius);background:var(--hm-surface-raised)}.sp-account__panel--success{border-color:var(--hm-success);background:var(--hm-success-bg)}.sp-account__panel--success .sp-account__panel-head hm-icon{color:var(--hm-success)}.sp-account__panel--danger{border-inline-start-color:var(--hm-danger)}.sp-account__panel--danger .sp-account__panel-head hm-icon{color:var(--hm-danger)}.sp-account__panel-head{display:flex;align-items:flex-start;gap:var(--hm-space-xs)}.sp-account__panel-head hm-icon{flex:none;margin-block-start:var(--hm-space-micro);color:var(--hm-accent-strong)}.sp-account__panel-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro)}.sp-account__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__form,.sp-account__code,.sp-account__field{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0}.sp-account__form{gap:var(--hm-space-sm)}.sp-account__label{color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__qr{align-self:flex-start;padding:var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-surface)}.sp-account__qr img{display:block;width:10rem;max-width:100%;height:auto}.sp-account__secret,.sp-account__codes code{font-family:var(--hm-font-mono);overflow-wrap:anywhere}.sp-account__secret{-webkit-user-select:all;user-select:all}.sp-account__secret--block{display:block;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.sp-account__codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0;padding:var(--hm-space-xxxs) var(--hm-space-xs) var(--hm-space-xxxs) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-sm)}@media(forced-colors:active){.sp-account__panel{border-inline-start-color:Highlight}.sp-account__icon{border:var(--hm-border-hairline) solid CanvasText}}.sp-account__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__choices{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0;margin:0;padding:0;border:0}\n", "@charset \"UTF-8\";.sp-group__avatar{flex:none}.sp-group__role{min-width:10rem}.sp-group__results{max-height:16rem;overflow-y:auto}.sp-group__results li+li{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-group__result{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-micro);width:100%;min-width:0;padding:var(--hm-space-xs) var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;overflow-wrap:anywhere;cursor:pointer}.sp-group__result:hover:not(:disabled){background:var(--hm-hover)}.sp-group__result:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-group__result--selected{background:var(--hm-accent-subtle)}.sp-group__result:disabled{color:var(--hm-muted);cursor:default}.sp-groups__item{display:flex;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);width:100%;min-width:0;padding:var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}li+li .sp-groups__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-groups__item:hover{background:var(--hm-hover)}.sp-groups__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-groups__faces{flex:none;margin-inline-start:auto}.sp-groups__chevron{flex:none;color:var(--hm-muted)}.sp-groups__back{display:flex}.sp-groups__heading{margin-block-end:var(--hm-space-xxxs)}@media(forced-colors:active){.sp-group__result--selected{outline:var(--hm-border-thin) solid Highlight;outline-offset:calc(var(--hm-border-thin) * -1)}}\n"] }]
5892
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Incluir membro fica acima da lista: o bot\u00E3o, ou o painel de inclus\u00E3o aberto no lugar dele. -->\n@if (group().can_add) {\n @if (adding()) {\n <section class=\"sp-account__panel\" [attr.aria-labelledby]=\"ids + '-add'\">\n <div class=\"sp-account__panel-head\">\n <hm-icon shape=\"assign-user\" />\n <div class=\"sp-account__panel-text\">\n <h4 class=\"sp-account__title\" [id]=\"ids + '-add'\">{{ fmt(text.addHeading, { group: group().name }) }}</h4>\n @if (!group().can_change_roles) {\n <p class=\"sp-account__note\">{{ text.editorAddsMembers }}</p>\n }\n </div>\n </div>\n <form class=\"sp-account__form\" (submit)=\"$event.preventDefault(); submitAdd()\">\n <hm-input\n icon=\"search\"\n autocomplete=\"off\"\n [label]=\"text.search\"\n [placeholder]=\"text.searchPlaceholder\"\n [hint]=\"text.searchHint\"\n [formControl]=\"searchControl\"\n />\n\n @if (searching()) {\n <p class=\"sp-account__note\" role=\"status\">{{ text.searching }}</p>\n } @else if (searchError()) {\n <p class=\"sp-account__note sp-account__note--error\">{{ searchError() }}</p>\n } @else if (results(); as list) {\n @if (list.length) {\n <ul class=\"sp-account__list sp-group__results\" [attr.aria-label]=\"text.search\">\n @for (u of list; track u.id) {\n <li>\n <button\n type=\"button\"\n class=\"sp-group__result\"\n [class.sp-group__result--selected]=\"selected()?.id === u.id\"\n [attr.aria-pressed]=\"selected()?.id === u.id\"\n [disabled]=\"isMember(u)\"\n (click)=\"pick(u)\"\n >\n <strong>{{ u.name || u.username }}</strong>\n <span class=\"sp-account__meta\">{{ u.username }}@if (u.email) { \u00B7 {{ u.email }} }@if (isMember(u)) { \u00B7 {{ text.alreadyMember }} }</span>\n </button>\n </li>\n }\n </ul>\n } @else {\n <p class=\"sp-account__note\" role=\"status\">{{ text.noResults }}</p>\n }\n }\n\n @if (group().can_change_roles) {\n <div class=\"sp-account__field\">\n <span class=\"sp-account__label\">{{ text.role }}</span>\n <hm-segmented-control emphasis=\"action\" [label]=\"text.role\" [options]=\"roleOptions()\" [value]=\"newRole()\" (valueChange)=\"newRole.set($event)\" />\n </div>\n }\n\n <div class=\"sp-account__actions\">\n <hm-button type=\"submit\" emphasis=\"high\" icon=\"plus\" [disabled]=\"!selected()\" [loading]=\"submitting()\">{{ text.add }}</hm-button>\n <hm-button emphasis=\"medium\" (clicked)=\"cancelAdd()\">{{ text.cancel }}</hm-button>\n </div>\n </form>\n </section>\n } @else {\n <div class=\"sp-account__actions\">\n <hm-button size=\"small\" emphasis=\"medium\" icon=\"plus\" (clicked)=\"startAdd()\">{{ text.addMember }}</hm-button>\n </div>\n }\n} @else {\n <p class=\"sp-account__note\">{{ text.readOnly }}</p>\n}\n\n<!-- Busca na lista: local e instant\u00E2nea sobre o que j\u00E1 chegou. Fica fora do \"carregando\" para n\u00E3o perder o foco. -->\n<hm-input\n class=\"sp-group__filter\"\n icon=\"search\"\n type=\"search\"\n autocomplete=\"off\"\n [label]=\"text.filterMembers\"\n [placeholder]=\"text.searchPlaceholder\"\n [formControl]=\"filterControl\"\n/>\n\n@if (loader.error()) {\n <!-- Falha numa p\u00E1gina: o que j\u00E1 chegou continua na lista; \"Tentar de novo\" segue de onde parou. -->\n <div class=\"sp-group__status\">\n <p class=\"sp-account__note sp-account__note--error\">{{ loader.loaded() ? incompleteLabel() : text.loadError }}</p>\n <hm-button size=\"small\" emphasis=\"medium\" icon=\"refresh\" (clicked)=\"loader.retry()\">{{ text.retry }}</hm-button>\n </div>\n} @else if (loader.loading() && loader.ready()) {\n <p class=\"sp-account__note sp-group__status\" role=\"status\">\n {{ progressLabel() }}@if (filter().trim()) { \u00B7 {{ text.partialResults }} }\n </p>\n}\n\n@if (!loader.ready()) {\n <p class=\"sp-account__note\" role=\"status\">{{ text.loading }}</p>\n} @else if (loader.loaded() || !loader.error()) {\n <div class=\"sp-account__list\">\n <ul class=\"sp-account__items\" [attr.aria-label]=\"group().name\">\n @for (m of members(); track m.user_id) {\n <li class=\"sp-account__item\" [class.sp-account__item--muted]=\"!m.enabled\">\n <hm-account-avatar class=\"sp-group__avatar\" [name]=\"m.name || m.username\" [imageUrl]=\"m.picture\" />\n <span class=\"sp-account__item-main\">\n <span class=\"sp-account__item-title\">\n <strong>{{ m.name || m.username }}</strong>\n @if (isMe(m)) { <hm-tag color=\"info\" [name]=\"text.you\" /> }\n </span>\n <span class=\"sp-account__meta\">{{ m.username }}@if (m.email) { \u00B7 {{ m.email }} }</span>\n @if (!canChangeRole(m)) {\n <span class=\"sp-account__meta\">{{ roleLabel(m.role) }}</span>\n }\n </span>\n @if (canChangeRole(m) || canRemove(m)) {\n <span class=\"sp-account__item-actions\">\n <!-- Confirmando a remo\u00E7\u00E3o, a linha mostra s\u00F3 Cancelar e Confirmar (a fun\u00E7\u00E3o n\u00E3o importa mais). -->\n @if (canChangeRole(m) && confirmingRemove() !== m.user_id) {\n <hm-select\n class=\"sp-group__role\"\n [label]=\"fmt(text.roleOf, { name: m.name || m.username })\"\n [labelHidden]=\"true\"\n [value]=\"m.role ?? ''\"\n [disabled]=\"busy() === m.user_id\"\n (valueChange)=\"changeRole(m, $event)\"\n >\n @for (option of roleOptions(); track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </hm-select>\n }\n @if (canRemove(m)) {\n @if (confirmingRemove() === m.user_id) {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelRemove()\">{{ text.cancel }}</hm-button>\n }\n <hm-button\n intent=\"danger\"\n [emphasis]=\"confirmingRemove() === m.user_id ? 'high' : 'medium'\"\n [loading]=\"busy() === m.user_id\"\n (clicked)=\"remove(m)\"\n >{{ confirmingRemove() === m.user_id ? text.confirmRemove : text.remove }}</hm-button>\n }\n </span>\n }\n </li>\n } @empty {\n <li class=\"sp-account__item sp-account__item--empty\">{{ filter().trim() ? text.noMatches : text.empty }}</li>\n }\n </ul>\n <div class=\"sp-account__list-footer\">\n <span class=\"sp-account__note\">{{ countLabel() }}</span>\n </div>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--hm-space-xs);width:100%;min-width:0}.sp-account__note{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);line-height:1.45}.sp-account__note--error{color:var(--hm-danger)}.sp-account__note code{font-family:var(--hm-font-mono)}.sp-account__list{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-account__items{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}.sp-account__item{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-sm)}.sp-account__item+.sp-account__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-account__item--empty{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-account__item--muted .sp-account__item-title strong{color:var(--hm-muted)}.sp-account__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--hm-control-height-large);height:var(--hm-control-height-large);border-radius:var(--hm-radius-control);background:var(--hm-hover);color:var(--hm-muted)}.sp-account__icon--accent{background:var(--hm-accent-subtle);color:var(--hm-accent-strong)}.sp-account__item-main{display:flex;min-width:0;flex:1 1 14rem;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere}.sp-account__item-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-nano)}.sp-account__item-title strong{font-weight:var(--hm-font-weight-semibold)}.sp-account__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-account__meta code{font-family:var(--hm-font-mono)}.sp-account__meta--danger{color:var(--hm-danger)}.sp-account__item-actions,.sp-account__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__item-actions{margin-inline-start:auto}.sp-account__list-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);background:var(--hm-surface-raised)}.sp-account__list-footer:first-child{border-top:0}.sp-account__panel{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-width:0;padding:var(--hm-space-sm) var(--hm-space-md) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-inline-start:var(--hm-border-thin) solid var(--hm-accent);border-radius:var(--hm-radius);background:var(--hm-surface-raised)}.sp-account__panel--success{border-color:var(--hm-success);background:var(--hm-success-bg)}.sp-account__panel--success .sp-account__panel-head hm-icon{color:var(--hm-success)}.sp-account__panel--danger{border-inline-start-color:var(--hm-danger)}.sp-account__panel--danger .sp-account__panel-head hm-icon{color:var(--hm-danger)}.sp-account__panel-head{display:flex;align-items:flex-start;gap:var(--hm-space-xs)}.sp-account__panel-head hm-icon{flex:none;margin-block-start:var(--hm-space-micro);color:var(--hm-accent-strong)}.sp-account__panel-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro)}.sp-account__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__form,.sp-account__code,.sp-account__field{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0}.sp-account__form{gap:var(--hm-space-sm)}.sp-account__label{color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__qr{align-self:flex-start;padding:var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-surface)}.sp-account__qr img{display:block;width:10rem;max-width:100%;height:auto}.sp-account__secret,.sp-account__codes code{font-family:var(--hm-font-mono);overflow-wrap:anywhere}.sp-account__secret{-webkit-user-select:all;user-select:all}.sp-account__secret--block{display:block;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.sp-account__codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0;padding:var(--hm-space-xxxs) var(--hm-space-xs) var(--hm-space-xxxs) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-sm)}@media(forced-colors:active){.sp-account__panel{border-inline-start-color:Highlight}.sp-account__icon{border:var(--hm-border-hairline) solid CanvasText}}.sp-account__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__choices{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0;margin:0;padding:0;border:0}\n", "@charset \"UTF-8\";.sp-group__avatar{flex:none}.sp-group__role{min-width:10rem}.sp-group__filter{display:block;margin-block:var(--hm-space-sm)}.sp-group__status{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs);margin-block-end:var(--hm-space-sm)}.sp-group__results{max-height:16rem;overflow-y:auto}.sp-group__results li+li{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-group__result{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-micro);width:100%;min-width:0;padding:var(--hm-space-xs) var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;overflow-wrap:anywhere;cursor:pointer}.sp-group__result:hover:not(:disabled){background:var(--hm-hover)}.sp-group__result:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-group__result--selected{background:var(--hm-accent-subtle)}.sp-group__result:disabled{color:var(--hm-muted);cursor:default}.sp-groups__item{display:flex;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);width:100%;min-width:0;padding:var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;cursor:pointer}li+li .sp-groups__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-groups__item:hover{background:var(--hm-hover)}.sp-groups__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-groups__faces{flex:none;margin-inline-start:auto}.sp-groups__chevron{flex:none;color:var(--hm-muted)}.sp-groups__back{display:flex}.sp-groups__heading{margin-block-end:var(--hm-space-xxxs)}@media(forced-colors:active){.sp-group__result--selected{outline:var(--hm-border-thin) solid Highlight;outline-offset:calc(var(--hm-border-thin) * -1)}}\n"] }]
5659
5893
  }], ctorParameters: () => [], propDecorators: { group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: true }] }] } });
5660
5894
  function displayName(member) {
5661
5895
  return member.name || member.username;
@@ -5730,11 +5964,11 @@ class MyGroupsComponent {
5730
5964
  : formatMessage(settings.groupRoleDisabled, { role });
5731
5965
  }
5732
5966
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: MyGroupsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5733
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: MyGroupsComponent, isStandalone: true, selector: "spine-my-groups", inputs: { groups: { classPropertyName: "groups", publicName: "groups", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { openChange: "openChange" }, ngImport: i0, template: "@if (open(); as group) {\n <!-- Tela de membros de um grupo: voltar no topo, depois o grupo e os membros (com \"Incluir membro\" acima da lista). -->\n <div class=\"sp-groups__back\">\n <hm-button size=\"small\" emphasis=\"minimal\" icon=\"arrow-left\" (clicked)=\"back()\">{{ text.back }}</hm-button>\n </div>\n <div class=\"sp-account__panel-text sp-groups__heading\">\n <h4 class=\"sp-account__title\">{{ group.name }}</h4>\n <p class=\"sp-account__note\">{{ summary(group) }}</p>\n </div>\n <spine-my-group [group]=\"group\" />\n} @else {\n <!-- Lista dos grupos: cada um abre a tela de membros. -->\n <div class=\"sp-account__list\">\n <ul class=\"sp-account__items\" [attr.aria-label]=\"text.listLabel\">\n @for (g of groups(); track g.id) {\n <li>\n <button type=\"button\" class=\"sp-groups__item\" (click)=\"openGroup(g)\">\n <span class=\"sp-account__icon\" [class.sp-account__icon--accent]=\"g.enabled\"><hm-icon shape=\"employee-group\" /></span>\n <span class=\"sp-account__item-main\">\n <span class=\"sp-account__item-title\"><strong>{{ g.name }}</strong></span>\n <span class=\"sp-account__meta\">{{ summary(g) }}</span>\n </span>\n @if (g.members_count) {\n <hm-avatar-group class=\"sp-groups__faces\" size=\"small\" [max]=\"4\" [people]=\"peopleOf(g)\" [total]=\"g.members_count\" [label]=\"membersLabel(g)\" />\n }\n <hm-icon class=\"sp-groups__chevron\" shape=\"angle-right\" />\n </button>\n </li>\n }\n </ul>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--hm-space-xs);width:100%;min-width:0}.sp-account__note{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);line-height:1.45}.sp-account__note--error{color:var(--hm-danger)}.sp-account__note code{font-family:var(--hm-font-mono)}.sp-account__list{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-account__items{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}.sp-account__item{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-sm)}.sp-account__item+.sp-account__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-account__item--empty{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-account__item--muted .sp-account__item-title strong{color:var(--hm-muted)}.sp-account__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--hm-control-height-large);height:var(--hm-control-height-large);border-radius:var(--hm-radius-control);background:var(--hm-hover);color:var(--hm-muted)}.sp-account__icon--accent{background:var(--hm-accent-subtle);color:var(--hm-accent-strong)}.sp-account__item-main{display:flex;min-width:0;flex:1 1 14rem;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere}.sp-account__item-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-nano)}.sp-account__item-title strong{font-weight:var(--hm-font-weight-semibold)}.sp-account__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-account__meta code{font-family:var(--hm-font-mono)}.sp-account__meta--danger{color:var(--hm-danger)}.sp-account__item-actions,.sp-account__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__item-actions{margin-inline-start:auto}.sp-account__list-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);background:var(--hm-surface-raised)}.sp-account__list-footer:first-child{border-top:0}.sp-account__panel{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-width:0;padding:var(--hm-space-sm) var(--hm-space-md) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-inline-start:var(--hm-border-thin) solid var(--hm-accent);border-radius:var(--hm-radius);background:var(--hm-surface-raised)}.sp-account__panel--success{border-color:var(--hm-success);background:var(--hm-success-bg)}.sp-account__panel--success .sp-account__panel-head hm-icon{color:var(--hm-success)}.sp-account__panel--danger{border-inline-start-color:var(--hm-danger)}.sp-account__panel--danger .sp-account__panel-head hm-icon{color:var(--hm-danger)}.sp-account__panel-head{display:flex;align-items:flex-start;gap:var(--hm-space-xs)}.sp-account__panel-head hm-icon{flex:none;margin-block-start:var(--hm-space-micro);color:var(--hm-accent-strong)}.sp-account__panel-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro)}.sp-account__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__form,.sp-account__code,.sp-account__field{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0}.sp-account__form{gap:var(--hm-space-sm)}.sp-account__label{color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__qr{align-self:flex-start;padding:var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-surface)}.sp-account__qr img{display:block;width:10rem;max-width:100%;height:auto}.sp-account__secret,.sp-account__codes code{font-family:var(--hm-font-mono);overflow-wrap:anywhere}.sp-account__secret{-webkit-user-select:all;user-select:all}.sp-account__secret--block{display:block;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.sp-account__codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0;padding:var(--hm-space-xxxs) var(--hm-space-xs) var(--hm-space-xxxs) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-sm)}@media(forced-colors:active){.sp-account__panel{border-inline-start-color:Highlight}.sp-account__icon{border:var(--hm-border-hairline) solid CanvasText}}.sp-account__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__choices{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0;margin:0;padding:0;border:0}\n", "@charset \"UTF-8\";.sp-group__avatar{flex:none}.sp-group__role{min-width:10rem}.sp-group__results{max-height:16rem;overflow-y:auto}.sp-group__results li+li{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-group__result{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-micro);width:100%;min-width:0;padding:var(--hm-space-xs) var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;overflow-wrap:anywhere;cursor:pointer}.sp-group__result:hover:not(:disabled){background:var(--hm-hover)}.sp-group__result:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-group__result--selected{background:var(--hm-accent-subtle)}.sp-group__result:disabled{color:var(--hm-muted);cursor:default}.sp-groups__item{display:flex;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);width:100%;min-width:0;padding:var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}li+li .sp-groups__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-groups__item:hover{background:var(--hm-hover)}.sp-groups__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-groups__faces{flex:none;margin-inline-start:auto}.sp-groups__chevron{flex:none;color:var(--hm-muted)}.sp-groups__back{display:flex}.sp-groups__heading{margin-block-end:var(--hm-space-xxxs)}@media(forced-colors:active){.sp-group__result--selected{outline:var(--hm-border-thin) solid Highlight;outline-offset:calc(var(--hm-border-thin) * -1)}}\n"], dependencies: [{ kind: "component", type: AvatarGroupComponent, selector: "hm-avatar-group", inputs: ["people", "max", "size", "label", "total"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: MyGroupComponent, selector: "spine-my-group", inputs: ["group"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5967
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: MyGroupsComponent, isStandalone: true, selector: "spine-my-groups", inputs: { groups: { classPropertyName: "groups", publicName: "groups", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { openChange: "openChange" }, ngImport: i0, template: "@if (open(); as group) {\n <!-- Tela de membros de um grupo: voltar no topo, depois o grupo e os membros (com \"Incluir membro\" acima da lista). -->\n <div class=\"sp-groups__back\">\n <hm-button size=\"small\" emphasis=\"minimal\" icon=\"arrow-left\" (clicked)=\"back()\">{{ text.back }}</hm-button>\n </div>\n <div class=\"sp-account__panel-text sp-groups__heading\">\n <h4 class=\"sp-account__title\">{{ group.name }}</h4>\n <p class=\"sp-account__note\">{{ summary(group) }}</p>\n </div>\n <spine-my-group [group]=\"group\" />\n} @else {\n <!-- Lista dos grupos: cada um abre a tela de membros. -->\n <div class=\"sp-account__list\">\n <ul class=\"sp-account__items\" [attr.aria-label]=\"text.listLabel\">\n @for (g of groups(); track g.id) {\n <li>\n <button type=\"button\" class=\"sp-groups__item\" (click)=\"openGroup(g)\">\n <span class=\"sp-account__icon\" [class.sp-account__icon--accent]=\"g.enabled\"><hm-icon shape=\"employee-group\" /></span>\n <span class=\"sp-account__item-main\">\n <span class=\"sp-account__item-title\"><strong>{{ g.name }}</strong></span>\n <span class=\"sp-account__meta\">{{ summary(g) }}</span>\n </span>\n @if (g.members_count) {\n <hm-avatar-group class=\"sp-groups__faces\" size=\"small\" [max]=\"4\" [people]=\"peopleOf(g)\" [total]=\"g.members_count\" [label]=\"membersLabel(g)\" />\n }\n <hm-icon class=\"sp-groups__chevron\" shape=\"angle-right\" />\n </button>\n </li>\n }\n </ul>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--hm-space-xs);width:100%;min-width:0}.sp-account__note{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);line-height:1.45}.sp-account__note--error{color:var(--hm-danger)}.sp-account__note code{font-family:var(--hm-font-mono)}.sp-account__list{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-account__items{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}.sp-account__item{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-sm)}.sp-account__item+.sp-account__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-account__item--empty{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-account__item--muted .sp-account__item-title strong{color:var(--hm-muted)}.sp-account__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--hm-control-height-large);height:var(--hm-control-height-large);border-radius:var(--hm-radius-control);background:var(--hm-hover);color:var(--hm-muted)}.sp-account__icon--accent{background:var(--hm-accent-subtle);color:var(--hm-accent-strong)}.sp-account__item-main{display:flex;min-width:0;flex:1 1 14rem;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere}.sp-account__item-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-nano)}.sp-account__item-title strong{font-weight:var(--hm-font-weight-semibold)}.sp-account__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-account__meta code{font-family:var(--hm-font-mono)}.sp-account__meta--danger{color:var(--hm-danger)}.sp-account__item-actions,.sp-account__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__item-actions{margin-inline-start:auto}.sp-account__list-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);background:var(--hm-surface-raised)}.sp-account__list-footer:first-child{border-top:0}.sp-account__panel{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-width:0;padding:var(--hm-space-sm) var(--hm-space-md) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-inline-start:var(--hm-border-thin) solid var(--hm-accent);border-radius:var(--hm-radius);background:var(--hm-surface-raised)}.sp-account__panel--success{border-color:var(--hm-success);background:var(--hm-success-bg)}.sp-account__panel--success .sp-account__panel-head hm-icon{color:var(--hm-success)}.sp-account__panel--danger{border-inline-start-color:var(--hm-danger)}.sp-account__panel--danger .sp-account__panel-head hm-icon{color:var(--hm-danger)}.sp-account__panel-head{display:flex;align-items:flex-start;gap:var(--hm-space-xs)}.sp-account__panel-head hm-icon{flex:none;margin-block-start:var(--hm-space-micro);color:var(--hm-accent-strong)}.sp-account__panel-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro)}.sp-account__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__form,.sp-account__code,.sp-account__field{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0}.sp-account__form{gap:var(--hm-space-sm)}.sp-account__label{color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__qr{align-self:flex-start;padding:var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-surface)}.sp-account__qr img{display:block;width:10rem;max-width:100%;height:auto}.sp-account__secret,.sp-account__codes code{font-family:var(--hm-font-mono);overflow-wrap:anywhere}.sp-account__secret{-webkit-user-select:all;user-select:all}.sp-account__secret--block{display:block;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.sp-account__codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0;padding:var(--hm-space-xxxs) var(--hm-space-xs) var(--hm-space-xxxs) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-sm)}@media(forced-colors:active){.sp-account__panel{border-inline-start-color:Highlight}.sp-account__icon{border:var(--hm-border-hairline) solid CanvasText}}.sp-account__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__choices{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0;margin:0;padding:0;border:0}\n", "@charset \"UTF-8\";.sp-group__avatar{flex:none}.sp-group__role{min-width:10rem}.sp-group__filter{display:block;margin-block:var(--hm-space-sm)}.sp-group__status{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs);margin-block-end:var(--hm-space-sm)}.sp-group__results{max-height:16rem;overflow-y:auto}.sp-group__results li+li{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-group__result{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-micro);width:100%;min-width:0;padding:var(--hm-space-xs) var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;overflow-wrap:anywhere;cursor:pointer}.sp-group__result:hover:not(:disabled){background:var(--hm-hover)}.sp-group__result:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-group__result--selected{background:var(--hm-accent-subtle)}.sp-group__result:disabled{color:var(--hm-muted);cursor:default}.sp-groups__item{display:flex;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);width:100%;min-width:0;padding:var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;cursor:pointer}li+li .sp-groups__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-groups__item:hover{background:var(--hm-hover)}.sp-groups__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-groups__faces{flex:none;margin-inline-start:auto}.sp-groups__chevron{flex:none;color:var(--hm-muted)}.sp-groups__back{display:flex}.sp-groups__heading{margin-block-end:var(--hm-space-xxxs)}@media(forced-colors:active){.sp-group__result--selected{outline:var(--hm-border-thin) solid Highlight;outline-offset:calc(var(--hm-border-thin) * -1)}}\n"], dependencies: [{ kind: "component", type: AvatarGroupComponent, selector: "hm-avatar-group", inputs: ["people", "max", "size", "label", "total"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: MyGroupComponent, selector: "spine-my-group", inputs: ["group"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5734
5968
  }
5735
5969
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: MyGroupsComponent, decorators: [{
5736
5970
  type: Component,
5737
- args: [{ selector: 'spine-my-groups', imports: [AvatarGroupComponent, ButtonComponent, IconComponent, MyGroupComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open(); as group) {\n <!-- Tela de membros de um grupo: voltar no topo, depois o grupo e os membros (com \"Incluir membro\" acima da lista). -->\n <div class=\"sp-groups__back\">\n <hm-button size=\"small\" emphasis=\"minimal\" icon=\"arrow-left\" (clicked)=\"back()\">{{ text.back }}</hm-button>\n </div>\n <div class=\"sp-account__panel-text sp-groups__heading\">\n <h4 class=\"sp-account__title\">{{ group.name }}</h4>\n <p class=\"sp-account__note\">{{ summary(group) }}</p>\n </div>\n <spine-my-group [group]=\"group\" />\n} @else {\n <!-- Lista dos grupos: cada um abre a tela de membros. -->\n <div class=\"sp-account__list\">\n <ul class=\"sp-account__items\" [attr.aria-label]=\"text.listLabel\">\n @for (g of groups(); track g.id) {\n <li>\n <button type=\"button\" class=\"sp-groups__item\" (click)=\"openGroup(g)\">\n <span class=\"sp-account__icon\" [class.sp-account__icon--accent]=\"g.enabled\"><hm-icon shape=\"employee-group\" /></span>\n <span class=\"sp-account__item-main\">\n <span class=\"sp-account__item-title\"><strong>{{ g.name }}</strong></span>\n <span class=\"sp-account__meta\">{{ summary(g) }}</span>\n </span>\n @if (g.members_count) {\n <hm-avatar-group class=\"sp-groups__faces\" size=\"small\" [max]=\"4\" [people]=\"peopleOf(g)\" [total]=\"g.members_count\" [label]=\"membersLabel(g)\" />\n }\n <hm-icon class=\"sp-groups__chevron\" shape=\"angle-right\" />\n </button>\n </li>\n }\n </ul>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--hm-space-xs);width:100%;min-width:0}.sp-account__note{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);line-height:1.45}.sp-account__note--error{color:var(--hm-danger)}.sp-account__note code{font-family:var(--hm-font-mono)}.sp-account__list{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-account__items{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}.sp-account__item{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-sm)}.sp-account__item+.sp-account__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-account__item--empty{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-account__item--muted .sp-account__item-title strong{color:var(--hm-muted)}.sp-account__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--hm-control-height-large);height:var(--hm-control-height-large);border-radius:var(--hm-radius-control);background:var(--hm-hover);color:var(--hm-muted)}.sp-account__icon--accent{background:var(--hm-accent-subtle);color:var(--hm-accent-strong)}.sp-account__item-main{display:flex;min-width:0;flex:1 1 14rem;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere}.sp-account__item-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-nano)}.sp-account__item-title strong{font-weight:var(--hm-font-weight-semibold)}.sp-account__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-account__meta code{font-family:var(--hm-font-mono)}.sp-account__meta--danger{color:var(--hm-danger)}.sp-account__item-actions,.sp-account__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__item-actions{margin-inline-start:auto}.sp-account__list-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);background:var(--hm-surface-raised)}.sp-account__list-footer:first-child{border-top:0}.sp-account__panel{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-width:0;padding:var(--hm-space-sm) var(--hm-space-md) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-inline-start:var(--hm-border-thin) solid var(--hm-accent);border-radius:var(--hm-radius);background:var(--hm-surface-raised)}.sp-account__panel--success{border-color:var(--hm-success);background:var(--hm-success-bg)}.sp-account__panel--success .sp-account__panel-head hm-icon{color:var(--hm-success)}.sp-account__panel--danger{border-inline-start-color:var(--hm-danger)}.sp-account__panel--danger .sp-account__panel-head hm-icon{color:var(--hm-danger)}.sp-account__panel-head{display:flex;align-items:flex-start;gap:var(--hm-space-xs)}.sp-account__panel-head hm-icon{flex:none;margin-block-start:var(--hm-space-micro);color:var(--hm-accent-strong)}.sp-account__panel-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro)}.sp-account__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__form,.sp-account__code,.sp-account__field{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0}.sp-account__form{gap:var(--hm-space-sm)}.sp-account__label{color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__qr{align-self:flex-start;padding:var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-surface)}.sp-account__qr img{display:block;width:10rem;max-width:100%;height:auto}.sp-account__secret,.sp-account__codes code{font-family:var(--hm-font-mono);overflow-wrap:anywhere}.sp-account__secret{-webkit-user-select:all;user-select:all}.sp-account__secret--block{display:block;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.sp-account__codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0;padding:var(--hm-space-xxxs) var(--hm-space-xs) var(--hm-space-xxxs) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-sm)}@media(forced-colors:active){.sp-account__panel{border-inline-start-color:Highlight}.sp-account__icon{border:var(--hm-border-hairline) solid CanvasText}}.sp-account__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__choices{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0;margin:0;padding:0;border:0}\n", "@charset \"UTF-8\";.sp-group__avatar{flex:none}.sp-group__role{min-width:10rem}.sp-group__results{max-height:16rem;overflow-y:auto}.sp-group__results li+li{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-group__result{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-micro);width:100%;min-width:0;padding:var(--hm-space-xs) var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;overflow-wrap:anywhere;cursor:pointer}.sp-group__result:hover:not(:disabled){background:var(--hm-hover)}.sp-group__result:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-group__result--selected{background:var(--hm-accent-subtle)}.sp-group__result:disabled{color:var(--hm-muted);cursor:default}.sp-groups__item{display:flex;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);width:100%;min-width:0;padding:var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;text-align:start;cursor:pointer}li+li .sp-groups__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-groups__item:hover{background:var(--hm-hover)}.sp-groups__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-groups__faces{flex:none;margin-inline-start:auto}.sp-groups__chevron{flex:none;color:var(--hm-muted)}.sp-groups__back{display:flex}.sp-groups__heading{margin-block-end:var(--hm-space-xxxs)}@media(forced-colors:active){.sp-group__result--selected{outline:var(--hm-border-thin) solid Highlight;outline-offset:calc(var(--hm-border-thin) * -1)}}\n"] }]
5971
+ args: [{ selector: 'spine-my-groups', imports: [AvatarGroupComponent, ButtonComponent, IconComponent, MyGroupComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open(); as group) {\n <!-- Tela de membros de um grupo: voltar no topo, depois o grupo e os membros (com \"Incluir membro\" acima da lista). -->\n <div class=\"sp-groups__back\">\n <hm-button size=\"small\" emphasis=\"minimal\" icon=\"arrow-left\" (clicked)=\"back()\">{{ text.back }}</hm-button>\n </div>\n <div class=\"sp-account__panel-text sp-groups__heading\">\n <h4 class=\"sp-account__title\">{{ group.name }}</h4>\n <p class=\"sp-account__note\">{{ summary(group) }}</p>\n </div>\n <spine-my-group [group]=\"group\" />\n} @else {\n <!-- Lista dos grupos: cada um abre a tela de membros. -->\n <div class=\"sp-account__list\">\n <ul class=\"sp-account__items\" [attr.aria-label]=\"text.listLabel\">\n @for (g of groups(); track g.id) {\n <li>\n <button type=\"button\" class=\"sp-groups__item\" (click)=\"openGroup(g)\">\n <span class=\"sp-account__icon\" [class.sp-account__icon--accent]=\"g.enabled\"><hm-icon shape=\"employee-group\" /></span>\n <span class=\"sp-account__item-main\">\n <span class=\"sp-account__item-title\"><strong>{{ g.name }}</strong></span>\n <span class=\"sp-account__meta\">{{ summary(g) }}</span>\n </span>\n @if (g.members_count) {\n <hm-avatar-group class=\"sp-groups__faces\" size=\"small\" [max]=\"4\" [people]=\"peopleOf(g)\" [total]=\"g.members_count\" [label]=\"membersLabel(g)\" />\n }\n <hm-icon class=\"sp-groups__chevron\" shape=\"angle-right\" />\n </button>\n </li>\n }\n </ul>\n </div>\n}\n", styles: ["@charset \"UTF-8\";:host{display:flex;flex-direction:column;gap:var(--hm-space-xs);width:100%;min-width:0}.sp-account__note{margin:0;color:var(--hm-muted);font-size:var(--hm-font-size-xs);line-height:1.45}.sp-account__note--error{color:var(--hm-danger)}.sp-account__note code{font-family:var(--hm-font-mono)}.sp-account__list{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-account__items{display:flex;flex-direction:column;min-width:0;margin:0;padding:0;list-style:none}.sp-account__item{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-sm)}.sp-account__item+.sp-account__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-account__item--empty{color:var(--hm-muted);font-size:var(--hm-font-size-sm)}.sp-account__item--muted .sp-account__item-title strong{color:var(--hm-muted)}.sp-account__icon{display:inline-flex;flex:none;align-items:center;justify-content:center;width:var(--hm-control-height-large);height:var(--hm-control-height-large);border-radius:var(--hm-radius-control);background:var(--hm-hover);color:var(--hm-muted)}.sp-account__icon--accent{background:var(--hm-accent-subtle);color:var(--hm-accent-strong)}.sp-account__item-main{display:flex;min-width:0;flex:1 1 14rem;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);font-size:var(--hm-font-size-sm);overflow-wrap:anywhere}.sp-account__item-title{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-nano)}.sp-account__item-title strong{font-weight:var(--hm-font-weight-semibold)}.sp-account__meta{color:var(--hm-muted);font-size:var(--hm-font-size-xs)}.sp-account__meta code{font-family:var(--hm-font-mono)}.sp-account__meta--danger{color:var(--hm-danger)}.sp-account__item-actions,.sp-account__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__item-actions{margin-inline-start:auto}.sp-account__list-footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--hm-space-xs) var(--hm-space-sm);padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);background:var(--hm-surface-raised)}.sp-account__list-footer:first-child{border-top:0}.sp-account__panel{display:flex;flex-direction:column;gap:var(--hm-space-sm);min-width:0;padding:var(--hm-space-sm) var(--hm-space-md) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-inline-start:var(--hm-border-thin) solid var(--hm-accent);border-radius:var(--hm-radius);background:var(--hm-surface-raised)}.sp-account__panel--success{border-color:var(--hm-success);background:var(--hm-success-bg)}.sp-account__panel--success .sp-account__panel-head hm-icon{color:var(--hm-success)}.sp-account__panel--danger{border-inline-start-color:var(--hm-danger)}.sp-account__panel--danger .sp-account__panel-head hm-icon{color:var(--hm-danger)}.sp-account__panel-head{display:flex;align-items:flex-start;gap:var(--hm-space-xs)}.sp-account__panel-head hm-icon{flex:none;margin-block-start:var(--hm-space-micro);color:var(--hm-accent-strong)}.sp-account__panel-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro)}.sp-account__title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__form,.sp-account__code,.sp-account__field{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0}.sp-account__form{gap:var(--hm-space-sm)}.sp-account__label{color:var(--hm-text);font-size:var(--hm-font-size-sm);font-weight:var(--hm-font-weight-semibold)}.sp-account__qr{align-self:flex-start;padding:var(--hm-space-xxxs);border-radius:var(--hm-radius-control);background:var(--hm-surface)}.sp-account__qr img{display:block;width:10rem;max-width:100%;height:auto}.sp-account__secret,.sp-account__codes code{font-family:var(--hm-font-mono);overflow-wrap:anywhere}.sp-account__secret{-webkit-user-select:all;user-select:all}.sp-account__secret--block{display:block;padding:var(--hm-space-xs) var(--hm-space-xxxs);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-xs)}.sp-account__codes{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(9rem,100%),1fr));gap:var(--hm-space-nano) var(--hm-space-sm);margin:0;padding:var(--hm-space-xxxs) var(--hm-space-xs) var(--hm-space-xxxs) var(--hm-space-md);border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius-control);background:var(--hm-surface);color:var(--hm-text);font-size:var(--hm-font-size-sm)}@media(forced-colors:active){.sp-account__panel{border-inline-start-color:Highlight}.sp-account__icon{border:var(--hm-border-hairline) solid CanvasText}}.sp-account__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs)}.sp-account__choices{display:flex;flex-direction:column;gap:var(--hm-space-xs);min-width:0;margin:0;padding:0;border:0}\n", "@charset \"UTF-8\";.sp-group__avatar{flex:none}.sp-group__role{min-width:10rem}.sp-group__filter{display:block;margin-block:var(--hm-space-sm)}.sp-group__status{display:flex;flex-wrap:wrap;align-items:center;gap:var(--hm-space-xs);margin-block-end:var(--hm-space-sm)}.sp-group__results{max-height:16rem;overflow-y:auto}.sp-group__results li+li{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-group__result{display:flex;flex-direction:column;align-items:flex-start;gap:var(--hm-space-micro);width:100%;min-width:0;padding:var(--hm-space-xs) var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;overflow-wrap:anywhere;cursor:pointer}.sp-group__result:hover:not(:disabled){background:var(--hm-hover)}.sp-group__result:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-group__result--selected{background:var(--hm-accent-subtle)}.sp-group__result:disabled{color:var(--hm-muted);cursor:default}.sp-groups__item{display:flex;align-items:center;gap:var(--hm-space-xs) var(--hm-space-sm);width:100%;min-width:0;padding:var(--hm-space-sm);border:0;background:transparent;color:var(--hm-text);font:inherit;font-size:var(--hm-font-size-sm);text-align:start;cursor:pointer}li+li .sp-groups__item{border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-groups__item:hover{background:var(--hm-hover)}.sp-groups__item:focus-visible{outline:var(--hm-border-thin) solid var(--hm-accent);outline-offset:calc(var(--hm-border-thin) * -1)}.sp-groups__faces{flex:none;margin-inline-start:auto}.sp-groups__chevron{flex:none;color:var(--hm-muted)}.sp-groups__back{display:flex}.sp-groups__heading{margin-block-end:var(--hm-space-xxxs)}@media(forced-colors:active){.sp-group__result--selected{outline:var(--hm-border-thin) solid Highlight;outline-offset:calc(var(--hm-border-thin) * -1)}}\n"] }]
5738
5972
  }], ctorParameters: () => [], propDecorators: { groups: [{ type: i0.Input, args: [{ isSignal: true, alias: "groups", required: true }] }], openChange: [{ type: i0.Output, args: ["openChange"] }] } });
5739
5973
 
5740
5974
  /**
@@ -6134,8 +6368,8 @@ class SettingsPanelComponent {
6134
6368
  }
6135
6369
  /**
6136
6370
  * Restaurar preferências (com confirmação): tema (sistema), cor principal (a padrão do app), densidade
6137
- * (confortável), fonte (a padrão do app), menu lateral (o modo padrão do app), tamanho do texto (100%),
6138
- * animações (sistema), formato de hora e primeiro dia da semana (automáticos). Idioma, fuso horário e
6371
+ * (confortável), fonte (a padrão do app), menu lateral (o modo padrão do app), dividir abas (desligado), tamanho do
6372
+ * texto (100%), animações (sistema), formato de hora e primeiro dia da semana (automáticos). Idioma, fuso horário e
6139
6373
  * atalhos de tecla única ficam: mudam como a pessoa lê e digita, e não são "aparência".
6140
6374
  */
6141
6375
  restorePreferences() {
@@ -6158,6 +6392,7 @@ class SettingsPanelComponent {
6158
6392
  if (font)
6159
6393
  this.hermesTheme.setFont(font);
6160
6394
  this.sidebar.setMode(this.sidebarConfig.defaultMode ?? 'auto');
6395
+ this.preferences.setSplitTabs(false);
6161
6396
  this.preferences.setTextScale(1);
6162
6397
  this.preferences.setMotion('system');
6163
6398
  this.preferences.setHourCycle('');
@@ -6273,7 +6508,7 @@ class SettingsPanelComponent {
6273
6508
  this.settings.close();
6274
6509
  }
6275
6510
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SettingsPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
6276
- 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]=\"activeSection()\"\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 <!-- Recursos opcionais (SpineConfig.features): crach\u00E1 padr\u00E3o (Argos), inatividade e tela inicial. -->\n @if (showDefaultBadge) {\n <hm-settings-row [label]=\"featureText.defaultBadge\" [description]=\"featureText.defaultBadgeDescription\" [keywords]=\"featureText.defaultBadgeKeywords\">\n <spine-default-badge />\n </hm-settings-row>\n }\n\n @if (idleOptions().length) {\n <hm-settings-row [label]=\"featureText.idle\" [description]=\"idleHint || featureText.idleDescription\" [keywords]=\"featureText.idleKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"featureText.idle\" [options]=\"idleOptions()\" [value]=\"idleValue()\" (valueChange)=\"setIdle($event)\" />\n </hm-settings-row>\n }\n\n @if (features.startPage) {\n <hm-settings-row [label]=\"featureText.startPage\" [description]=\"quickAccessHasFavorites() ? featureText.startPageDescription : featureText.startPageHint\" [keywords]=\"featureText.startPageKeywords\">\n <hm-select class=\"sp-settings__language\" [label]=\"featureText.startPage\" [labelHidden]=\"true\" [value]=\"startPageValue()\" (valueChange)=\"setStartPage($event ?? 'home')\">\n @for (option of startPageOptions(); track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </hm-select>\n </hm-settings-row>\n }\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 MFA \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 e c\u00F3digos de recupera\u00E7\u00E3o aqui mesmo, sem\n abrir o Argos. O spineAccountSecurity d\u00E1 \u00E0s linhas o mesmo estado (reautentica\u00E7\u00E3o, cadastro em\n andamento), que morre quando o painel fecha. `?settings=seguranca` (chave antiga) abre esta se\u00E7\u00E3o. -->\n @if (accountSecurity) {\n <hm-settings-section spineAccountSecurity [key]=\"keys.mfa\" [label]=\"text.mfa\" icon=\"shield-check\" [description]=\"text.mfaDescription\" [keywords]=\"text.mfaKeywords\">\n <!-- Realm s\u00F3 pelo nome: o id (UUID) n\u00E3o diz nada a quem l\u00EA, ent\u00E3o some. -->\n @if (accountLogin || accountRealm) {\n <hm-settings-row [label]=\"text.signedInAccount\" [description]=\"accountRealm ? fmt(text.signedInRealm, { realm: accountRealm }) : ''\">\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 <!-- Entradas e tentativas desta conta (SpineFeatures.accountActivity), \u00FAltimos 90 dias. -->\n @if (features.accountActivity) {\n <hm-settings-row [label]=\"featureText.activity\" [description]=\"featureText.activityDescription\" [keywords]=\"featureText.activityKeywords\" [stacked]=\"true\">\n <spine-account-activity />\n </hm-settings-row>\n }\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Sess\u00F5es \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Os tokens e onde a conta est\u00E1 conectada (a validade desta sess\u00E3o fica em Conta). Cada linha tem a\n pr\u00F3pria condi\u00E7\u00E3o; a se\u00E7\u00E3o some se nenhuma sobrar (hasSessionsSection). -->\n @if (hasSessionsSection) {\n <hm-settings-section [key]=\"keys.sessions\" [label]=\"text.sessionsSection\" icon=\"devices\" [description]=\"text.sessionsSectionDescription\" [keywords]=\"text.sessionsSectionKeywords\">\n <!-- S\u00F3 com SpineConfig.technicalDocs (padr\u00E3o: isDevMode()): token da sess\u00E3o no clipboard \u00E9 ferramenta de dev. -->\n @if (sessionsItems.accessToken) {\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 @if (sessionsItems.appTokens) {\n <hm-settings-row [label]=\"text.appTokens\" [stacked]=\"true\"\n [description]=\"text.appTokensDescription\"\n [keywords]=\"text.appTokensKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.appTokens })\" [text]=\"text.appTokensHelp\" />\n <spine-app-tokens />\n </hm-settings-row>\n }\n\n <!-- Token + .mcp.json prontos para um agente de IA (SpineFeatures.aiAgent). -->\n @if (sessionsItems.aiAgent) {\n <hm-settings-row [label]=\"featureText.aiAgent\" [description]=\"featureText.aiAgentDescription\" [keywords]=\"featureText.aiAgentKeywords\" [stacked]=\"true\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: featureText.aiAgent })\" [text]=\"featureText.aiAgentHelp\" />\n <spine-ai-agent />\n </hm-settings-row>\n }\n\n <!-- Sess\u00F5es e dispositivos: API da conta do Argos, mesma condi\u00E7\u00E3o da se\u00E7\u00E3o MFA. -->\n @if (sessionsItems.devices) {\n <hm-settings-row [label]=\"text.sessions\" [description]=\"text.sessionsDescription\" [keywords]=\"text.sessionsKeywords\" [stacked]=\"true\">\n <spine-account-sessions />\n </hm-settings-row>\n }\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Grupos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Os grupos desta aplica\u00E7\u00E3o de que a pessoa \u00E9 membro (argos-admin /me/groups, relidos a cada abertura): a lista,\n e escolhendo um, a tela de membros dele. Sem grupo nenhum, a se\u00E7\u00E3o n\u00E3o aparece. -->\n @if (myGroups().length) {\n <hm-settings-section [key]=\"keys.groups\" [label]=\"text.groups\" icon=\"employee-group\" [keywords]=\"text.groupsKeywords\"\n [class.sp-settings__section--group-open]=\"groupOpen()\" [description]=\"groupOpen() ? '' : text.groupsDescription\">\n <spine-my-groups [groups]=\"myGroups()\" (openChange)=\"groupOpen.set($event)\" />\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 class=\"sp-settings__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-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.sidebar })\" [text]=\"text.sidebarHelp\" />\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 Acessibilidade \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Tamanho do texto (--hm-text-scale no <html>, HermesZoomService) e movimento (data-motion no <html>,\n HermesMotionService), guardados pelo SpinePreferencesService. -->\n <hm-settings-section [key]=\"keys.accessibility\" [label]=\"text.accessibility\" icon=\"accessibility-1\" [keywords]=\"text.accessibilityKeywords\">\n <hm-settings-row [label]=\"text.textSize\" [description]=\"text.textSizeDescription\" [keywords]=\"text.textSizeKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.textSize\" [options]=\"textSizeOptions()\" [value]=\"textSizeValue()\" (valueChange)=\"setTextSize($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.motion\" [description]=\"text.motionDescription\" [keywords]=\"text.motionKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.motion\" [options]=\"motionOptions()\" [value]=\"preferences.motion()\" (valueChange)=\"setMotion($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\" [description]=\"fmt(text.timeZoneHint, { now: timeZonePreview() })\" [keywords]=\"text.timeZoneKeywords\">\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\n <hm-settings-row [label]=\"text.hourCycle\" [description]=\"fmt(text.hourCycleHint, { now: hourCyclePreview() })\" [keywords]=\"text.hourCycleKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.hourCycle\" [options]=\"hourCycleOptions()\" [value]=\"preferences.hourCycle()\" (valueChange)=\"setHourCycle($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.weekStart\" [description]=\"text.weekStartDescription\" [keywords]=\"text.weekStartKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.weekStart\" [options]=\"weekStartOptions()\" [value]=\"weekStartValue()\" (valueChange)=\"setWeekStart($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Notifica\u00E7\u00F5es (SpineFeatures.notices) \u2500\u2500\u2500\u2500\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 <!-- Os tipos de aviso que o app declarou (SpineNoticesService) e o aviso do navegador com a aba em segundo plano. -->\n @if (notices.enabled) {\n <hm-settings-section [key]=\"keys.notices\" [label]=\"featureText.notices\" icon=\"bell\" [description]=\"featureText.noticesDescription\" [keywords]=\"featureText.noticesKeywords\">\n @for (type of notices.types; track type.id) {\n <hm-settings-row [label]=\"type.label\" [description]=\"type.description ?? ''\">\n <hm-checkbox [label]=\"type.label\" [labelHidden]=\"true\" [checked]=\"notices.isEnabled(type.id)\" (checkedChange)=\"notices.setEnabled(type.id, $event)\" />\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"featureText.browserNotices\" [description]=\"notices.permission() === 'denied' ? featureText.browserNoticesDenied : notices.permission() === 'unsupported' ? featureText.browserNoticesUnsupported : featureText.browserNoticesDescription\">\n <hm-checkbox [label]=\"featureText.browserNotices\" [labelHidden]=\"true\" [checked]=\"notices.browserActive()\" (checkedChange)=\"setBrowserNotices($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n }\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 class=\"sp-settings__shortcuts\" characterKeysToggle [registered]=\"false\" [items]=\"shortcutItems()\" [headingLevel]=\"4\" />\n </hm-settings-section>\n\n <!-- \u2500\u2500 Dados do navegador \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Listas por usu\u00E1rio do SpineUserStorage e as prefer\u00EAncias deste painel. Destrutivo: o 1\u00BA clique pede\n confirma\u00E7\u00E3o (bot\u00E3o vira \"Confirmar\" + Cancelar), como nas listas de tokens e sess\u00F5es. -->\n <hm-settings-section [key]=\"keys.data\" [label]=\"text.browserData\" icon=\"browser-setting\" [description]=\"text.browserDataDescription\" [keywords]=\"text.browserDataKeywords\">\n <!-- SpineFeatures.accountPreferences: as prefer\u00EAncias tamb\u00E9m na conta do Argos (as telas acima n\u00E3o mudam). -->\n @if (features.accountPreferences) {\n <hm-settings-row [label]=\"featureText.sync\" [description]=\"syncStatus() || featureText.syncDescription\" [keywords]=\"featureText.syncKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: featureText.sync })\" [text]=\"featureText.syncDescription\" />\n <hm-button emphasis=\"medium\" icon=\"refresh\" [disabled]=\"accountPreferences.status() === 'local'\" [loading]=\"accountPreferences.status() === 'saving' || accountPreferences.status() === 'loading'\" (clicked)=\"syncNow()\">{{ featureText.syncNow }}</hm-button>\n </hm-settings-row>\n }\n @for (list of browserLists(); track list.id) {\n <hm-settings-row [label]=\"list.label\" [description]=\"list.count\" [keywords]=\"list.keywords\">\n <span class=\"sp-settings__actions\">\n @if (confirming() === list.id) {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelConfirm()\">{{ text.cancel }}</hm-button>\n }\n <hm-button\n [emphasis]=\"confirming() === list.id ? 'high' : 'medium'\"\n intent=\"danger\"\n [disabled]=\"list.empty && confirming() !== list.id\"\n (clicked)=\"clearList(list.id)\"\n >{{ confirming() === list.id ? text.confirmClear : text.clear }}</hm-button>\n </span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.restorePreferences\" [description]=\"text.restorePreferencesDescription\" [keywords]=\"text.restorePreferencesKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.restorePreferences })\" [text]=\"text.restorePreferencesHelp\" />\n <span class=\"sp-settings__actions\">\n @if (confirming() === 'preferences') {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelConfirm()\">{{ text.cancel }}</hm-button>\n }\n <hm-button [emphasis]=\"confirming() === 'preferences' ? 'high' : 'medium'\" intent=\"danger\" (clicked)=\"restorePreferences()\">\n {{ confirming() === 'preferences' ? text.confirmRestore : text.restore }}\n </hm-button>\n </span>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Ajuda \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Novidades (s\u00F3 com alguma vigente), configura\u00E7\u00E3o inicial e o essencial: fecham o painel antes de abrir. -->\n <hm-settings-section [key]=\"keys.help\" [label]=\"text.help\" icon=\"help\" [keywords]=\"text.helpKeywords\">\n @if (hasNews()) {\n <hm-settings-row [label]=\"text.news\" [description]=\"text.newsDescription\" [keywords]=\"text.newsKeywords\">\n <hm-button emphasis=\"medium\" icon=\"star\" (clicked)=\"showNews()\">{{ text.showNews }}</hm-button>\n </hm-settings-row>\n }\n @if (setupEnabled) {\n <hm-settings-row [label]=\"text.setup\" [description]=\"text.setupDescription\" [keywords]=\"text.setupKeywords\">\n <hm-button emphasis=\"medium\" icon=\"color-palette\" (clicked)=\"redoSetup()\">{{ text.redoSetup }}</hm-button>\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"text.essentials\" [description]=\"text.essentialsDescription\" [keywords]=\"text.essentialsKeywords\">\n <hm-button emphasis=\"medium\" icon=\"help\" (clicked)=\"showEssentials()\">{{ text.showEssentials }}</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 <!-- M\u00E9dia da ferramenta (SpineFeatures.ratings) e \"Avaliar\", que abre o di\u00E1logo da estrela do cabe\u00E7alho. -->\n @if (ratingsEnabled) {\n <hm-settings-row [label]=\"ratingText.aboutRating\" [keywords]=\"ratingText.aboutRatingKeywords\">\n <spine-app-rating-summary />\n </hm-settings-row>\n }\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=\"medium\" icon=\"book\" [link]=\"packageInfo.docs\" [newTab]=\"true\">{{ text.docs }}</hm-button> }\n @if (packageInfo.issues) { <hm-button emphasis=\"medium\" icon=\"bug\" [link]=\"packageInfo.issues\" [newTab]=\"true\">{{ text.reportIssue }}</hm-button> }\n @if (packageInfo.repository?.url) { <hm-button emphasis=\"medium\" 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__timezone{display:block;width:min(320px,100%)}.sp-settings__profile{display:flex;align-items:center;gap:var(--hm-space-sm);padding:var(--hm-space-xs) 0 var(--hm-space-sm)}.sp-settings__profile-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);overflow-wrap:anywhere}.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;overflow-wrap:anywhere}.sp-settings__muted{color:var(--hm-muted)}.sp-settings__mono{font-family:var(--hm-font-mono)}.sp-settings__shortcuts{display:block;padding-block:var(--hm-space-xxxs);border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-settings__links,.sp-settings__actions{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--hm-space-xs)}.sp-settings__section--group-open ::ng-deep .hm-settings-section__heading{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.sp-settings__picker ::ng-deep .hm-theme-picker__title{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.sp-settings__deps{display:grid;flex:1;min-width:0;grid-template-columns:repeat(auto-fill,minmax(min(12rem,100%),1fr));margin:0;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-settings__deps div{display:flex;justify-content:space-between;gap:var(--hm-space-xs);margin-block-start:calc(-1 * var(--hm-border-hairline));margin-inline-start:calc(-1 * var(--hm-border-hairline));padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);border-inline-start:var(--hm-border-hairline) solid var(--hm-divider)}.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: MyGroupsComponent, selector: "spine-my-groups", inputs: ["groups"], outputs: ["openChange"] }, { kind: "component", type: AiAgentComponent, selector: "spine-ai-agent" }, { kind: "component", type: AccountActivityComponent, selector: "spine-account-activity" }, { kind: "component", type: DefaultBadgeComponent, selector: "spine-default-badge" }, { kind: "component", type: CheckboxComponent, selector: "hm-checkbox", inputs: ["label", "labelHidden", "checked", "indeterminate"], outputs: ["checkedChange"] }, { 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", "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", "size", "labelHidden", "clearable", "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"] }, { kind: "component", type: ToggletipComponent, selector: "hm-toggletip", inputs: ["label", "text", "side", "intent", "icon"] }, { kind: "component", type: SpineAppRatingSummaryComponent, selector: "spine-app-rating-summary" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6511
+ 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]=\"activeSection()\"\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 <!-- Recursos opcionais (SpineConfig.features): crach\u00E1 padr\u00E3o (Argos), inatividade e tela inicial. -->\n @if (showDefaultBadge) {\n <hm-settings-row [label]=\"featureText.defaultBadge\" [description]=\"featureText.defaultBadgeDescription\" [keywords]=\"featureText.defaultBadgeKeywords\">\n <spine-default-badge />\n </hm-settings-row>\n }\n\n @if (idleOptions().length) {\n <hm-settings-row [label]=\"featureText.idle\" [description]=\"idleHint || featureText.idleDescription\" [keywords]=\"featureText.idleKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"featureText.idle\" [options]=\"idleOptions()\" [value]=\"idleValue()\" (valueChange)=\"setIdle($event)\" />\n </hm-settings-row>\n }\n\n @if (features.startPage) {\n <hm-settings-row [label]=\"featureText.startPage\" [description]=\"quickAccessHasFavorites() ? featureText.startPageDescription : featureText.startPageHint\" [keywords]=\"featureText.startPageKeywords\">\n <hm-select class=\"sp-settings__language\" [label]=\"featureText.startPage\" [labelHidden]=\"true\" [value]=\"startPageValue()\" (valueChange)=\"setStartPage($event ?? 'home')\">\n @for (option of startPageOptions(); track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </hm-select>\n </hm-settings-row>\n }\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 MFA \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 e c\u00F3digos de recupera\u00E7\u00E3o aqui mesmo, sem\n abrir o Argos. O spineAccountSecurity d\u00E1 \u00E0s linhas o mesmo estado (reautentica\u00E7\u00E3o, cadastro em\n andamento), que morre quando o painel fecha. `?settings=seguranca` (chave antiga) abre esta se\u00E7\u00E3o. -->\n @if (accountSecurity) {\n <hm-settings-section spineAccountSecurity [key]=\"keys.mfa\" [label]=\"text.mfa\" icon=\"shield-check\" [description]=\"text.mfaDescription\" [keywords]=\"text.mfaKeywords\">\n <!-- Realm s\u00F3 pelo nome: o id (UUID) n\u00E3o diz nada a quem l\u00EA, ent\u00E3o some. -->\n @if (accountLogin || accountRealm) {\n <hm-settings-row [label]=\"text.signedInAccount\" [description]=\"accountRealm ? fmt(text.signedInRealm, { realm: accountRealm }) : ''\">\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 <!-- Entradas e tentativas desta conta (SpineFeatures.accountActivity), \u00FAltimos 90 dias. -->\n @if (features.accountActivity) {\n <hm-settings-row [label]=\"featureText.activity\" [description]=\"featureText.activityDescription\" [keywords]=\"featureText.activityKeywords\" [stacked]=\"true\">\n <spine-account-activity />\n </hm-settings-row>\n }\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Sess\u00F5es \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Os tokens e onde a conta est\u00E1 conectada (a validade desta sess\u00E3o fica em Conta). Cada linha tem a\n pr\u00F3pria condi\u00E7\u00E3o; a se\u00E7\u00E3o some se nenhuma sobrar (hasSessionsSection). -->\n @if (hasSessionsSection) {\n <hm-settings-section [key]=\"keys.sessions\" [label]=\"text.sessionsSection\" icon=\"devices\" [description]=\"text.sessionsSectionDescription\" [keywords]=\"text.sessionsSectionKeywords\">\n <!-- S\u00F3 com SpineConfig.technicalDocs (padr\u00E3o: isDevMode()): token da sess\u00E3o no clipboard \u00E9 ferramenta de dev. -->\n @if (sessionsItems.accessToken) {\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 @if (sessionsItems.appTokens) {\n <hm-settings-row [label]=\"text.appTokens\" [stacked]=\"true\"\n [description]=\"text.appTokensDescription\"\n [keywords]=\"text.appTokensKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.appTokens })\" [text]=\"text.appTokensHelp\" />\n <spine-app-tokens />\n </hm-settings-row>\n }\n\n <!-- Token + .mcp.json prontos para um agente de IA (SpineFeatures.aiAgent). -->\n @if (sessionsItems.aiAgent) {\n <hm-settings-row [label]=\"featureText.aiAgent\" [description]=\"featureText.aiAgentDescription\" [keywords]=\"featureText.aiAgentKeywords\" [stacked]=\"true\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: featureText.aiAgent })\" [text]=\"featureText.aiAgentHelp\" />\n <spine-ai-agent />\n </hm-settings-row>\n }\n\n <!-- Sess\u00F5es e dispositivos: API da conta do Argos, mesma condi\u00E7\u00E3o da se\u00E7\u00E3o MFA. -->\n @if (sessionsItems.devices) {\n <hm-settings-row [label]=\"text.sessions\" [description]=\"text.sessionsDescription\" [keywords]=\"text.sessionsKeywords\" [stacked]=\"true\">\n <spine-account-sessions />\n </hm-settings-row>\n }\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Grupos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Os grupos desta aplica\u00E7\u00E3o de que a pessoa \u00E9 membro (argos-admin /me/groups, relidos a cada abertura): a lista,\n e escolhendo um, a tela de membros dele. Sem grupo nenhum, a se\u00E7\u00E3o n\u00E3o aparece. -->\n @if (myGroups().length) {\n <hm-settings-section [key]=\"keys.groups\" [label]=\"text.groups\" icon=\"employee-group\" [keywords]=\"text.groupsKeywords\"\n [class.sp-settings__section--group-open]=\"groupOpen()\" [description]=\"groupOpen() ? '' : text.groupsDescription\">\n <spine-my-groups [groups]=\"myGroups()\" (openChange)=\"groupOpen.set($event)\" />\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 class=\"sp-settings__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-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.sidebar })\" [text]=\"text.sidebarHelp\" />\n <hm-segmented-control emphasis=\"action\" [label]=\"text.sidebar\" [options]=\"sidebarOptions()\" [value]=\"sidebar.mode()\" (valueChange)=\"setSidebarMode($event)\" />\n </hm-settings-row>\n\n <!-- Dividir abas: com ela desligada (o padr\u00E3o) a \u00E1rea de abas fica num painel s\u00F3 (SpineWorkspaceNavService). -->\n <hm-settings-row [label]=\"text.splitTabs\" [description]=\"text.splitTabsDescription\" [keywords]=\"text.splitTabsKeywords\">\n <hm-checkbox [label]=\"text.splitTabs\" [labelHidden]=\"true\" [checked]=\"preferences.splitTabs()\" (checkedChange)=\"preferences.setSplitTabs($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Acessibilidade \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Tamanho do texto (--hm-text-scale no <html>, HermesZoomService) e movimento (data-motion no <html>,\n HermesMotionService), guardados pelo SpinePreferencesService. -->\n <hm-settings-section [key]=\"keys.accessibility\" [label]=\"text.accessibility\" icon=\"accessibility-1\" [keywords]=\"text.accessibilityKeywords\">\n <hm-settings-row [label]=\"text.textSize\" [description]=\"text.textSizeDescription\" [keywords]=\"text.textSizeKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.textSize\" [options]=\"textSizeOptions()\" [value]=\"textSizeValue()\" (valueChange)=\"setTextSize($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.motion\" [description]=\"text.motionDescription\" [keywords]=\"text.motionKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.motion\" [options]=\"motionOptions()\" [value]=\"preferences.motion()\" (valueChange)=\"setMotion($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\" [description]=\"fmt(text.timeZoneHint, { now: timeZonePreview() })\" [keywords]=\"text.timeZoneKeywords\">\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\n <hm-settings-row [label]=\"text.hourCycle\" [description]=\"fmt(text.hourCycleHint, { now: hourCyclePreview() })\" [keywords]=\"text.hourCycleKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.hourCycle\" [options]=\"hourCycleOptions()\" [value]=\"preferences.hourCycle()\" (valueChange)=\"setHourCycle($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.weekStart\" [description]=\"text.weekStartDescription\" [keywords]=\"text.weekStartKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.weekStart\" [options]=\"weekStartOptions()\" [value]=\"weekStartValue()\" (valueChange)=\"setWeekStart($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Notifica\u00E7\u00F5es (SpineFeatures.notices) \u2500\u2500\u2500\u2500\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 <!-- Os tipos de aviso que o app declarou (SpineNoticesService) e o aviso do navegador com a aba em segundo plano. -->\n @if (notices.enabled) {\n <hm-settings-section [key]=\"keys.notices\" [label]=\"featureText.notices\" icon=\"bell\" [description]=\"featureText.noticesDescription\" [keywords]=\"featureText.noticesKeywords\">\n @for (type of notices.types; track type.id) {\n <hm-settings-row [label]=\"type.label\" [description]=\"type.description ?? ''\">\n <hm-checkbox [label]=\"type.label\" [labelHidden]=\"true\" [checked]=\"notices.isEnabled(type.id)\" (checkedChange)=\"notices.setEnabled(type.id, $event)\" />\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"featureText.browserNotices\" [description]=\"notices.permission() === 'denied' ? featureText.browserNoticesDenied : notices.permission() === 'unsupported' ? featureText.browserNoticesUnsupported : featureText.browserNoticesDescription\">\n <hm-checkbox [label]=\"featureText.browserNotices\" [labelHidden]=\"true\" [checked]=\"notices.browserActive()\" (checkedChange)=\"setBrowserNotices($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n }\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 class=\"sp-settings__shortcuts\" characterKeysToggle [registered]=\"false\" [items]=\"shortcutItems()\" [headingLevel]=\"4\" />\n </hm-settings-section>\n\n <!-- \u2500\u2500 Dados do navegador \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Listas por usu\u00E1rio do SpineUserStorage e as prefer\u00EAncias deste painel. Destrutivo: o 1\u00BA clique pede\n confirma\u00E7\u00E3o (bot\u00E3o vira \"Confirmar\" + Cancelar), como nas listas de tokens e sess\u00F5es. -->\n <hm-settings-section [key]=\"keys.data\" [label]=\"text.browserData\" icon=\"browser-setting\" [description]=\"text.browserDataDescription\" [keywords]=\"text.browserDataKeywords\">\n <!-- SpineFeatures.accountPreferences: as prefer\u00EAncias tamb\u00E9m na conta do Argos (as telas acima n\u00E3o mudam). -->\n @if (features.accountPreferences) {\n <hm-settings-row [label]=\"featureText.sync\" [description]=\"syncStatus() || featureText.syncDescription\" [keywords]=\"featureText.syncKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: featureText.sync })\" [text]=\"featureText.syncDescription\" />\n <hm-button emphasis=\"medium\" icon=\"refresh\" [disabled]=\"accountPreferences.status() === 'local'\" [loading]=\"accountPreferences.status() === 'saving' || accountPreferences.status() === 'loading'\" (clicked)=\"syncNow()\">{{ featureText.syncNow }}</hm-button>\n </hm-settings-row>\n }\n @for (list of browserLists(); track list.id) {\n <hm-settings-row [label]=\"list.label\" [description]=\"list.count\" [keywords]=\"list.keywords\">\n <span class=\"sp-settings__actions\">\n @if (confirming() === list.id) {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelConfirm()\">{{ text.cancel }}</hm-button>\n }\n <hm-button\n [emphasis]=\"confirming() === list.id ? 'high' : 'medium'\"\n intent=\"danger\"\n [disabled]=\"list.empty && confirming() !== list.id\"\n (clicked)=\"clearList(list.id)\"\n >{{ confirming() === list.id ? text.confirmClear : text.clear }}</hm-button>\n </span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.restorePreferences\" [description]=\"text.restorePreferencesDescription\" [keywords]=\"text.restorePreferencesKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.restorePreferences })\" [text]=\"text.restorePreferencesHelp\" />\n <span class=\"sp-settings__actions\">\n @if (confirming() === 'preferences') {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelConfirm()\">{{ text.cancel }}</hm-button>\n }\n <hm-button [emphasis]=\"confirming() === 'preferences' ? 'high' : 'medium'\" intent=\"danger\" (clicked)=\"restorePreferences()\">\n {{ confirming() === 'preferences' ? text.confirmRestore : text.restore }}\n </hm-button>\n </span>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Ajuda \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Novidades (s\u00F3 com alguma vigente), configura\u00E7\u00E3o inicial e o essencial: fecham o painel antes de abrir. -->\n <hm-settings-section [key]=\"keys.help\" [label]=\"text.help\" icon=\"help\" [keywords]=\"text.helpKeywords\">\n @if (hasNews()) {\n <hm-settings-row [label]=\"text.news\" [description]=\"text.newsDescription\" [keywords]=\"text.newsKeywords\">\n <hm-button emphasis=\"medium\" icon=\"star\" (clicked)=\"showNews()\">{{ text.showNews }}</hm-button>\n </hm-settings-row>\n }\n @if (setupEnabled) {\n <hm-settings-row [label]=\"text.setup\" [description]=\"text.setupDescription\" [keywords]=\"text.setupKeywords\">\n <hm-button emphasis=\"medium\" icon=\"color-palette\" (clicked)=\"redoSetup()\">{{ text.redoSetup }}</hm-button>\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"text.essentials\" [description]=\"text.essentialsDescription\" [keywords]=\"text.essentialsKeywords\">\n <hm-button emphasis=\"medium\" icon=\"help\" (clicked)=\"showEssentials()\">{{ text.showEssentials }}</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 <!-- M\u00E9dia da ferramenta (SpineFeatures.ratings) e \"Avaliar\", que abre o di\u00E1logo da estrela do cabe\u00E7alho. -->\n @if (ratingsEnabled) {\n <hm-settings-row [label]=\"ratingText.aboutRating\" [keywords]=\"ratingText.aboutRatingKeywords\">\n <spine-app-rating-summary />\n </hm-settings-row>\n }\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=\"medium\" icon=\"book\" [link]=\"packageInfo.docs\" [newTab]=\"true\">{{ text.docs }}</hm-button> }\n @if (packageInfo.issues) { <hm-button emphasis=\"medium\" icon=\"bug\" [link]=\"packageInfo.issues\" [newTab]=\"true\">{{ text.reportIssue }}</hm-button> }\n @if (packageInfo.repository?.url) { <hm-button emphasis=\"medium\" 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__timezone{display:block;width:min(320px,100%)}.sp-settings__profile{display:flex;align-items:center;gap:var(--hm-space-sm);padding:var(--hm-space-xs) 0 var(--hm-space-sm)}.sp-settings__profile-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);overflow-wrap:anywhere}.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;overflow-wrap:anywhere}.sp-settings__muted{color:var(--hm-muted)}.sp-settings__mono{font-family:var(--hm-font-mono)}.sp-settings__shortcuts{display:block;padding-block:var(--hm-space-xxxs);border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-settings__links,.sp-settings__actions{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--hm-space-xs)}.sp-settings__section--group-open ::ng-deep .hm-settings-section__heading{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.sp-settings__picker ::ng-deep .hm-theme-picker__title{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.sp-settings__deps{display:grid;flex:1;min-width:0;grid-template-columns:repeat(auto-fill,minmax(min(12rem,100%),1fr));margin:0;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-settings__deps div{display:flex;justify-content:space-between;gap:var(--hm-space-xs);margin-block-start:calc(-1 * var(--hm-border-hairline));margin-inline-start:calc(-1 * var(--hm-border-hairline));padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);border-inline-start:var(--hm-border-hairline) solid var(--hm-divider)}.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: MyGroupsComponent, selector: "spine-my-groups", inputs: ["groups"], outputs: ["openChange"] }, { kind: "component", type: AiAgentComponent, selector: "spine-ai-agent" }, { kind: "component", type: AccountActivityComponent, selector: "spine-account-activity" }, { kind: "component", type: DefaultBadgeComponent, selector: "spine-default-badge" }, { kind: "component", type: CheckboxComponent, selector: "hm-checkbox", inputs: ["label", "labelHidden", "checked", "indeterminate"], outputs: ["checkedChange"] }, { 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", "presence", "texts"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "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", "size", "labelHidden", "clearable", "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"] }, { kind: "component", type: ToggletipComponent, selector: "hm-toggletip", inputs: ["label", "text", "side", "intent", "icon"] }, { kind: "component", type: SpineAppRatingSummaryComponent, selector: "spine-app-rating-summary" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6277
6512
  }
6278
6513
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SettingsPanelComponent, decorators: [{
6279
6514
  type: Component,
@@ -6283,7 +6518,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
6283
6518
  AccountAvatarComponent, ButtonComponent, ComboboxComponent,
6284
6519
  SegmentedControlComponent, SettingsDialogComponent, SettingsRowComponent, SettingsSectionComponent,
6285
6520
  SelectComponent, ShortcutListComponent, ThemePickerComponent, ToggletipComponent, SpineAppRatingSummaryComponent,
6286
- ], 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]=\"activeSection()\"\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 <!-- Recursos opcionais (SpineConfig.features): crach\u00E1 padr\u00E3o (Argos), inatividade e tela inicial. -->\n @if (showDefaultBadge) {\n <hm-settings-row [label]=\"featureText.defaultBadge\" [description]=\"featureText.defaultBadgeDescription\" [keywords]=\"featureText.defaultBadgeKeywords\">\n <spine-default-badge />\n </hm-settings-row>\n }\n\n @if (idleOptions().length) {\n <hm-settings-row [label]=\"featureText.idle\" [description]=\"idleHint || featureText.idleDescription\" [keywords]=\"featureText.idleKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"featureText.idle\" [options]=\"idleOptions()\" [value]=\"idleValue()\" (valueChange)=\"setIdle($event)\" />\n </hm-settings-row>\n }\n\n @if (features.startPage) {\n <hm-settings-row [label]=\"featureText.startPage\" [description]=\"quickAccessHasFavorites() ? featureText.startPageDescription : featureText.startPageHint\" [keywords]=\"featureText.startPageKeywords\">\n <hm-select class=\"sp-settings__language\" [label]=\"featureText.startPage\" [labelHidden]=\"true\" [value]=\"startPageValue()\" (valueChange)=\"setStartPage($event ?? 'home')\">\n @for (option of startPageOptions(); track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </hm-select>\n </hm-settings-row>\n }\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 MFA \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 e c\u00F3digos de recupera\u00E7\u00E3o aqui mesmo, sem\n abrir o Argos. O spineAccountSecurity d\u00E1 \u00E0s linhas o mesmo estado (reautentica\u00E7\u00E3o, cadastro em\n andamento), que morre quando o painel fecha. `?settings=seguranca` (chave antiga) abre esta se\u00E7\u00E3o. -->\n @if (accountSecurity) {\n <hm-settings-section spineAccountSecurity [key]=\"keys.mfa\" [label]=\"text.mfa\" icon=\"shield-check\" [description]=\"text.mfaDescription\" [keywords]=\"text.mfaKeywords\">\n <!-- Realm s\u00F3 pelo nome: o id (UUID) n\u00E3o diz nada a quem l\u00EA, ent\u00E3o some. -->\n @if (accountLogin || accountRealm) {\n <hm-settings-row [label]=\"text.signedInAccount\" [description]=\"accountRealm ? fmt(text.signedInRealm, { realm: accountRealm }) : ''\">\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 <!-- Entradas e tentativas desta conta (SpineFeatures.accountActivity), \u00FAltimos 90 dias. -->\n @if (features.accountActivity) {\n <hm-settings-row [label]=\"featureText.activity\" [description]=\"featureText.activityDescription\" [keywords]=\"featureText.activityKeywords\" [stacked]=\"true\">\n <spine-account-activity />\n </hm-settings-row>\n }\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Sess\u00F5es \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Os tokens e onde a conta est\u00E1 conectada (a validade desta sess\u00E3o fica em Conta). Cada linha tem a\n pr\u00F3pria condi\u00E7\u00E3o; a se\u00E7\u00E3o some se nenhuma sobrar (hasSessionsSection). -->\n @if (hasSessionsSection) {\n <hm-settings-section [key]=\"keys.sessions\" [label]=\"text.sessionsSection\" icon=\"devices\" [description]=\"text.sessionsSectionDescription\" [keywords]=\"text.sessionsSectionKeywords\">\n <!-- S\u00F3 com SpineConfig.technicalDocs (padr\u00E3o: isDevMode()): token da sess\u00E3o no clipboard \u00E9 ferramenta de dev. -->\n @if (sessionsItems.accessToken) {\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 @if (sessionsItems.appTokens) {\n <hm-settings-row [label]=\"text.appTokens\" [stacked]=\"true\"\n [description]=\"text.appTokensDescription\"\n [keywords]=\"text.appTokensKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.appTokens })\" [text]=\"text.appTokensHelp\" />\n <spine-app-tokens />\n </hm-settings-row>\n }\n\n <!-- Token + .mcp.json prontos para um agente de IA (SpineFeatures.aiAgent). -->\n @if (sessionsItems.aiAgent) {\n <hm-settings-row [label]=\"featureText.aiAgent\" [description]=\"featureText.aiAgentDescription\" [keywords]=\"featureText.aiAgentKeywords\" [stacked]=\"true\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: featureText.aiAgent })\" [text]=\"featureText.aiAgentHelp\" />\n <spine-ai-agent />\n </hm-settings-row>\n }\n\n <!-- Sess\u00F5es e dispositivos: API da conta do Argos, mesma condi\u00E7\u00E3o da se\u00E7\u00E3o MFA. -->\n @if (sessionsItems.devices) {\n <hm-settings-row [label]=\"text.sessions\" [description]=\"text.sessionsDescription\" [keywords]=\"text.sessionsKeywords\" [stacked]=\"true\">\n <spine-account-sessions />\n </hm-settings-row>\n }\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Grupos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Os grupos desta aplica\u00E7\u00E3o de que a pessoa \u00E9 membro (argos-admin /me/groups, relidos a cada abertura): a lista,\n e escolhendo um, a tela de membros dele. Sem grupo nenhum, a se\u00E7\u00E3o n\u00E3o aparece. -->\n @if (myGroups().length) {\n <hm-settings-section [key]=\"keys.groups\" [label]=\"text.groups\" icon=\"employee-group\" [keywords]=\"text.groupsKeywords\"\n [class.sp-settings__section--group-open]=\"groupOpen()\" [description]=\"groupOpen() ? '' : text.groupsDescription\">\n <spine-my-groups [groups]=\"myGroups()\" (openChange)=\"groupOpen.set($event)\" />\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 class=\"sp-settings__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-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.sidebar })\" [text]=\"text.sidebarHelp\" />\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 Acessibilidade \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Tamanho do texto (--hm-text-scale no <html>, HermesZoomService) e movimento (data-motion no <html>,\n HermesMotionService), guardados pelo SpinePreferencesService. -->\n <hm-settings-section [key]=\"keys.accessibility\" [label]=\"text.accessibility\" icon=\"accessibility-1\" [keywords]=\"text.accessibilityKeywords\">\n <hm-settings-row [label]=\"text.textSize\" [description]=\"text.textSizeDescription\" [keywords]=\"text.textSizeKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.textSize\" [options]=\"textSizeOptions()\" [value]=\"textSizeValue()\" (valueChange)=\"setTextSize($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.motion\" [description]=\"text.motionDescription\" [keywords]=\"text.motionKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.motion\" [options]=\"motionOptions()\" [value]=\"preferences.motion()\" (valueChange)=\"setMotion($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\" [description]=\"fmt(text.timeZoneHint, { now: timeZonePreview() })\" [keywords]=\"text.timeZoneKeywords\">\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\n <hm-settings-row [label]=\"text.hourCycle\" [description]=\"fmt(text.hourCycleHint, { now: hourCyclePreview() })\" [keywords]=\"text.hourCycleKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.hourCycle\" [options]=\"hourCycleOptions()\" [value]=\"preferences.hourCycle()\" (valueChange)=\"setHourCycle($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.weekStart\" [description]=\"text.weekStartDescription\" [keywords]=\"text.weekStartKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.weekStart\" [options]=\"weekStartOptions()\" [value]=\"weekStartValue()\" (valueChange)=\"setWeekStart($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Notifica\u00E7\u00F5es (SpineFeatures.notices) \u2500\u2500\u2500\u2500\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 <!-- Os tipos de aviso que o app declarou (SpineNoticesService) e o aviso do navegador com a aba em segundo plano. -->\n @if (notices.enabled) {\n <hm-settings-section [key]=\"keys.notices\" [label]=\"featureText.notices\" icon=\"bell\" [description]=\"featureText.noticesDescription\" [keywords]=\"featureText.noticesKeywords\">\n @for (type of notices.types; track type.id) {\n <hm-settings-row [label]=\"type.label\" [description]=\"type.description ?? ''\">\n <hm-checkbox [label]=\"type.label\" [labelHidden]=\"true\" [checked]=\"notices.isEnabled(type.id)\" (checkedChange)=\"notices.setEnabled(type.id, $event)\" />\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"featureText.browserNotices\" [description]=\"notices.permission() === 'denied' ? featureText.browserNoticesDenied : notices.permission() === 'unsupported' ? featureText.browserNoticesUnsupported : featureText.browserNoticesDescription\">\n <hm-checkbox [label]=\"featureText.browserNotices\" [labelHidden]=\"true\" [checked]=\"notices.browserActive()\" (checkedChange)=\"setBrowserNotices($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n }\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 class=\"sp-settings__shortcuts\" characterKeysToggle [registered]=\"false\" [items]=\"shortcutItems()\" [headingLevel]=\"4\" />\n </hm-settings-section>\n\n <!-- \u2500\u2500 Dados do navegador \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Listas por usu\u00E1rio do SpineUserStorage e as prefer\u00EAncias deste painel. Destrutivo: o 1\u00BA clique pede\n confirma\u00E7\u00E3o (bot\u00E3o vira \"Confirmar\" + Cancelar), como nas listas de tokens e sess\u00F5es. -->\n <hm-settings-section [key]=\"keys.data\" [label]=\"text.browserData\" icon=\"browser-setting\" [description]=\"text.browserDataDescription\" [keywords]=\"text.browserDataKeywords\">\n <!-- SpineFeatures.accountPreferences: as prefer\u00EAncias tamb\u00E9m na conta do Argos (as telas acima n\u00E3o mudam). -->\n @if (features.accountPreferences) {\n <hm-settings-row [label]=\"featureText.sync\" [description]=\"syncStatus() || featureText.syncDescription\" [keywords]=\"featureText.syncKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: featureText.sync })\" [text]=\"featureText.syncDescription\" />\n <hm-button emphasis=\"medium\" icon=\"refresh\" [disabled]=\"accountPreferences.status() === 'local'\" [loading]=\"accountPreferences.status() === 'saving' || accountPreferences.status() === 'loading'\" (clicked)=\"syncNow()\">{{ featureText.syncNow }}</hm-button>\n </hm-settings-row>\n }\n @for (list of browserLists(); track list.id) {\n <hm-settings-row [label]=\"list.label\" [description]=\"list.count\" [keywords]=\"list.keywords\">\n <span class=\"sp-settings__actions\">\n @if (confirming() === list.id) {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelConfirm()\">{{ text.cancel }}</hm-button>\n }\n <hm-button\n [emphasis]=\"confirming() === list.id ? 'high' : 'medium'\"\n intent=\"danger\"\n [disabled]=\"list.empty && confirming() !== list.id\"\n (clicked)=\"clearList(list.id)\"\n >{{ confirming() === list.id ? text.confirmClear : text.clear }}</hm-button>\n </span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.restorePreferences\" [description]=\"text.restorePreferencesDescription\" [keywords]=\"text.restorePreferencesKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.restorePreferences })\" [text]=\"text.restorePreferencesHelp\" />\n <span class=\"sp-settings__actions\">\n @if (confirming() === 'preferences') {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelConfirm()\">{{ text.cancel }}</hm-button>\n }\n <hm-button [emphasis]=\"confirming() === 'preferences' ? 'high' : 'medium'\" intent=\"danger\" (clicked)=\"restorePreferences()\">\n {{ confirming() === 'preferences' ? text.confirmRestore : text.restore }}\n </hm-button>\n </span>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Ajuda \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Novidades (s\u00F3 com alguma vigente), configura\u00E7\u00E3o inicial e o essencial: fecham o painel antes de abrir. -->\n <hm-settings-section [key]=\"keys.help\" [label]=\"text.help\" icon=\"help\" [keywords]=\"text.helpKeywords\">\n @if (hasNews()) {\n <hm-settings-row [label]=\"text.news\" [description]=\"text.newsDescription\" [keywords]=\"text.newsKeywords\">\n <hm-button emphasis=\"medium\" icon=\"star\" (clicked)=\"showNews()\">{{ text.showNews }}</hm-button>\n </hm-settings-row>\n }\n @if (setupEnabled) {\n <hm-settings-row [label]=\"text.setup\" [description]=\"text.setupDescription\" [keywords]=\"text.setupKeywords\">\n <hm-button emphasis=\"medium\" icon=\"color-palette\" (clicked)=\"redoSetup()\">{{ text.redoSetup }}</hm-button>\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"text.essentials\" [description]=\"text.essentialsDescription\" [keywords]=\"text.essentialsKeywords\">\n <hm-button emphasis=\"medium\" icon=\"help\" (clicked)=\"showEssentials()\">{{ text.showEssentials }}</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 <!-- M\u00E9dia da ferramenta (SpineFeatures.ratings) e \"Avaliar\", que abre o di\u00E1logo da estrela do cabe\u00E7alho. -->\n @if (ratingsEnabled) {\n <hm-settings-row [label]=\"ratingText.aboutRating\" [keywords]=\"ratingText.aboutRatingKeywords\">\n <spine-app-rating-summary />\n </hm-settings-row>\n }\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=\"medium\" icon=\"book\" [link]=\"packageInfo.docs\" [newTab]=\"true\">{{ text.docs }}</hm-button> }\n @if (packageInfo.issues) { <hm-button emphasis=\"medium\" icon=\"bug\" [link]=\"packageInfo.issues\" [newTab]=\"true\">{{ text.reportIssue }}</hm-button> }\n @if (packageInfo.repository?.url) { <hm-button emphasis=\"medium\" 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__timezone{display:block;width:min(320px,100%)}.sp-settings__profile{display:flex;align-items:center;gap:var(--hm-space-sm);padding:var(--hm-space-xs) 0 var(--hm-space-sm)}.sp-settings__profile-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);overflow-wrap:anywhere}.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;overflow-wrap:anywhere}.sp-settings__muted{color:var(--hm-muted)}.sp-settings__mono{font-family:var(--hm-font-mono)}.sp-settings__shortcuts{display:block;padding-block:var(--hm-space-xxxs);border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-settings__links,.sp-settings__actions{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--hm-space-xs)}.sp-settings__section--group-open ::ng-deep .hm-settings-section__heading{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.sp-settings__picker ::ng-deep .hm-theme-picker__title{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.sp-settings__deps{display:grid;flex:1;min-width:0;grid-template-columns:repeat(auto-fill,minmax(min(12rem,100%),1fr));margin:0;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-settings__deps div{display:flex;justify-content:space-between;gap:var(--hm-space-xs);margin-block-start:calc(-1 * var(--hm-border-hairline));margin-inline-start:calc(-1 * var(--hm-border-hairline));padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);border-inline-start:var(--hm-border-hairline) solid var(--hm-divider)}.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"] }]
6521
+ ], 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]=\"activeSection()\"\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 <!-- Recursos opcionais (SpineConfig.features): crach\u00E1 padr\u00E3o (Argos), inatividade e tela inicial. -->\n @if (showDefaultBadge) {\n <hm-settings-row [label]=\"featureText.defaultBadge\" [description]=\"featureText.defaultBadgeDescription\" [keywords]=\"featureText.defaultBadgeKeywords\">\n <spine-default-badge />\n </hm-settings-row>\n }\n\n @if (idleOptions().length) {\n <hm-settings-row [label]=\"featureText.idle\" [description]=\"idleHint || featureText.idleDescription\" [keywords]=\"featureText.idleKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"featureText.idle\" [options]=\"idleOptions()\" [value]=\"idleValue()\" (valueChange)=\"setIdle($event)\" />\n </hm-settings-row>\n }\n\n @if (features.startPage) {\n <hm-settings-row [label]=\"featureText.startPage\" [description]=\"quickAccessHasFavorites() ? featureText.startPageDescription : featureText.startPageHint\" [keywords]=\"featureText.startPageKeywords\">\n <hm-select class=\"sp-settings__language\" [label]=\"featureText.startPage\" [labelHidden]=\"true\" [value]=\"startPageValue()\" (valueChange)=\"setStartPage($event ?? 'home')\">\n @for (option of startPageOptions(); track option.value) {\n <option [value]=\"option.value\">{{ option.label }}</option>\n }\n </hm-select>\n </hm-settings-row>\n }\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 MFA \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 e c\u00F3digos de recupera\u00E7\u00E3o aqui mesmo, sem\n abrir o Argos. O spineAccountSecurity d\u00E1 \u00E0s linhas o mesmo estado (reautentica\u00E7\u00E3o, cadastro em\n andamento), que morre quando o painel fecha. `?settings=seguranca` (chave antiga) abre esta se\u00E7\u00E3o. -->\n @if (accountSecurity) {\n <hm-settings-section spineAccountSecurity [key]=\"keys.mfa\" [label]=\"text.mfa\" icon=\"shield-check\" [description]=\"text.mfaDescription\" [keywords]=\"text.mfaKeywords\">\n <!-- Realm s\u00F3 pelo nome: o id (UUID) n\u00E3o diz nada a quem l\u00EA, ent\u00E3o some. -->\n @if (accountLogin || accountRealm) {\n <hm-settings-row [label]=\"text.signedInAccount\" [description]=\"accountRealm ? fmt(text.signedInRealm, { realm: accountRealm }) : ''\">\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 <!-- Entradas e tentativas desta conta (SpineFeatures.accountActivity), \u00FAltimos 90 dias. -->\n @if (features.accountActivity) {\n <hm-settings-row [label]=\"featureText.activity\" [description]=\"featureText.activityDescription\" [keywords]=\"featureText.activityKeywords\" [stacked]=\"true\">\n <spine-account-activity />\n </hm-settings-row>\n }\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Sess\u00F5es \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Os tokens e onde a conta est\u00E1 conectada (a validade desta sess\u00E3o fica em Conta). Cada linha tem a\n pr\u00F3pria condi\u00E7\u00E3o; a se\u00E7\u00E3o some se nenhuma sobrar (hasSessionsSection). -->\n @if (hasSessionsSection) {\n <hm-settings-section [key]=\"keys.sessions\" [label]=\"text.sessionsSection\" icon=\"devices\" [description]=\"text.sessionsSectionDescription\" [keywords]=\"text.sessionsSectionKeywords\">\n <!-- S\u00F3 com SpineConfig.technicalDocs (padr\u00E3o: isDevMode()): token da sess\u00E3o no clipboard \u00E9 ferramenta de dev. -->\n @if (sessionsItems.accessToken) {\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 @if (sessionsItems.appTokens) {\n <hm-settings-row [label]=\"text.appTokens\" [stacked]=\"true\"\n [description]=\"text.appTokensDescription\"\n [keywords]=\"text.appTokensKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.appTokens })\" [text]=\"text.appTokensHelp\" />\n <spine-app-tokens />\n </hm-settings-row>\n }\n\n <!-- Token + .mcp.json prontos para um agente de IA (SpineFeatures.aiAgent). -->\n @if (sessionsItems.aiAgent) {\n <hm-settings-row [label]=\"featureText.aiAgent\" [description]=\"featureText.aiAgentDescription\" [keywords]=\"featureText.aiAgentKeywords\" [stacked]=\"true\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: featureText.aiAgent })\" [text]=\"featureText.aiAgentHelp\" />\n <spine-ai-agent />\n </hm-settings-row>\n }\n\n <!-- Sess\u00F5es e dispositivos: API da conta do Argos, mesma condi\u00E7\u00E3o da se\u00E7\u00E3o MFA. -->\n @if (sessionsItems.devices) {\n <hm-settings-row [label]=\"text.sessions\" [description]=\"text.sessionsDescription\" [keywords]=\"text.sessionsKeywords\" [stacked]=\"true\">\n <spine-account-sessions />\n </hm-settings-row>\n }\n </hm-settings-section>\n }\n\n <!-- \u2500\u2500 Grupos \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Os grupos desta aplica\u00E7\u00E3o de que a pessoa \u00E9 membro (argos-admin /me/groups, relidos a cada abertura): a lista,\n e escolhendo um, a tela de membros dele. Sem grupo nenhum, a se\u00E7\u00E3o n\u00E3o aparece. -->\n @if (myGroups().length) {\n <hm-settings-section [key]=\"keys.groups\" [label]=\"text.groups\" icon=\"employee-group\" [keywords]=\"text.groupsKeywords\"\n [class.sp-settings__section--group-open]=\"groupOpen()\" [description]=\"groupOpen() ? '' : text.groupsDescription\">\n <spine-my-groups [groups]=\"myGroups()\" (openChange)=\"groupOpen.set($event)\" />\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 class=\"sp-settings__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-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.sidebar })\" [text]=\"text.sidebarHelp\" />\n <hm-segmented-control emphasis=\"action\" [label]=\"text.sidebar\" [options]=\"sidebarOptions()\" [value]=\"sidebar.mode()\" (valueChange)=\"setSidebarMode($event)\" />\n </hm-settings-row>\n\n <!-- Dividir abas: com ela desligada (o padr\u00E3o) a \u00E1rea de abas fica num painel s\u00F3 (SpineWorkspaceNavService). -->\n <hm-settings-row [label]=\"text.splitTabs\" [description]=\"text.splitTabsDescription\" [keywords]=\"text.splitTabsKeywords\">\n <hm-checkbox [label]=\"text.splitTabs\" [labelHidden]=\"true\" [checked]=\"preferences.splitTabs()\" (checkedChange)=\"preferences.setSplitTabs($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Acessibilidade \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Tamanho do texto (--hm-text-scale no <html>, HermesZoomService) e movimento (data-motion no <html>,\n HermesMotionService), guardados pelo SpinePreferencesService. -->\n <hm-settings-section [key]=\"keys.accessibility\" [label]=\"text.accessibility\" icon=\"accessibility-1\" [keywords]=\"text.accessibilityKeywords\">\n <hm-settings-row [label]=\"text.textSize\" [description]=\"text.textSizeDescription\" [keywords]=\"text.textSizeKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.textSize\" [options]=\"textSizeOptions()\" [value]=\"textSizeValue()\" (valueChange)=\"setTextSize($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.motion\" [description]=\"text.motionDescription\" [keywords]=\"text.motionKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.motion\" [options]=\"motionOptions()\" [value]=\"preferences.motion()\" (valueChange)=\"setMotion($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\" [description]=\"fmt(text.timeZoneHint, { now: timeZonePreview() })\" [keywords]=\"text.timeZoneKeywords\">\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\n <hm-settings-row [label]=\"text.hourCycle\" [description]=\"fmt(text.hourCycleHint, { now: hourCyclePreview() })\" [keywords]=\"text.hourCycleKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.hourCycle\" [options]=\"hourCycleOptions()\" [value]=\"preferences.hourCycle()\" (valueChange)=\"setHourCycle($event)\" />\n </hm-settings-row>\n\n <hm-settings-row [label]=\"text.weekStart\" [description]=\"text.weekStartDescription\" [keywords]=\"text.weekStartKeywords\">\n <hm-segmented-control emphasis=\"action\" [label]=\"text.weekStart\" [options]=\"weekStartOptions()\" [value]=\"weekStartValue()\" (valueChange)=\"setWeekStart($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Notifica\u00E7\u00F5es (SpineFeatures.notices) \u2500\u2500\u2500\u2500\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 <!-- Os tipos de aviso que o app declarou (SpineNoticesService) e o aviso do navegador com a aba em segundo plano. -->\n @if (notices.enabled) {\n <hm-settings-section [key]=\"keys.notices\" [label]=\"featureText.notices\" icon=\"bell\" [description]=\"featureText.noticesDescription\" [keywords]=\"featureText.noticesKeywords\">\n @for (type of notices.types; track type.id) {\n <hm-settings-row [label]=\"type.label\" [description]=\"type.description ?? ''\">\n <hm-checkbox [label]=\"type.label\" [labelHidden]=\"true\" [checked]=\"notices.isEnabled(type.id)\" (checkedChange)=\"notices.setEnabled(type.id, $event)\" />\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"featureText.browserNotices\" [description]=\"notices.permission() === 'denied' ? featureText.browserNoticesDenied : notices.permission() === 'unsupported' ? featureText.browserNoticesUnsupported : featureText.browserNoticesDescription\">\n <hm-checkbox [label]=\"featureText.browserNotices\" [labelHidden]=\"true\" [checked]=\"notices.browserActive()\" (checkedChange)=\"setBrowserNotices($event)\" />\n </hm-settings-row>\n </hm-settings-section>\n }\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 class=\"sp-settings__shortcuts\" characterKeysToggle [registered]=\"false\" [items]=\"shortcutItems()\" [headingLevel]=\"4\" />\n </hm-settings-section>\n\n <!-- \u2500\u2500 Dados do navegador \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Listas por usu\u00E1rio do SpineUserStorage e as prefer\u00EAncias deste painel. Destrutivo: o 1\u00BA clique pede\n confirma\u00E7\u00E3o (bot\u00E3o vira \"Confirmar\" + Cancelar), como nas listas de tokens e sess\u00F5es. -->\n <hm-settings-section [key]=\"keys.data\" [label]=\"text.browserData\" icon=\"browser-setting\" [description]=\"text.browserDataDescription\" [keywords]=\"text.browserDataKeywords\">\n <!-- SpineFeatures.accountPreferences: as prefer\u00EAncias tamb\u00E9m na conta do Argos (as telas acima n\u00E3o mudam). -->\n @if (features.accountPreferences) {\n <hm-settings-row [label]=\"featureText.sync\" [description]=\"syncStatus() || featureText.syncDescription\" [keywords]=\"featureText.syncKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: featureText.sync })\" [text]=\"featureText.syncDescription\" />\n <hm-button emphasis=\"medium\" icon=\"refresh\" [disabled]=\"accountPreferences.status() === 'local'\" [loading]=\"accountPreferences.status() === 'saving' || accountPreferences.status() === 'loading'\" (clicked)=\"syncNow()\">{{ featureText.syncNow }}</hm-button>\n </hm-settings-row>\n }\n @for (list of browserLists(); track list.id) {\n <hm-settings-row [label]=\"list.label\" [description]=\"list.count\" [keywords]=\"list.keywords\">\n <span class=\"sp-settings__actions\">\n @if (confirming() === list.id) {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelConfirm()\">{{ text.cancel }}</hm-button>\n }\n <hm-button\n [emphasis]=\"confirming() === list.id ? 'high' : 'medium'\"\n intent=\"danger\"\n [disabled]=\"list.empty && confirming() !== list.id\"\n (clicked)=\"clearList(list.id)\"\n >{{ confirming() === list.id ? text.confirmClear : text.clear }}</hm-button>\n </span>\n </hm-settings-row>\n }\n\n <hm-settings-row [label]=\"text.restorePreferences\" [description]=\"text.restorePreferencesDescription\" [keywords]=\"text.restorePreferencesKeywords\">\n <hm-toggletip rowHelp [label]=\"fmt(text.moreAbout, { label: text.restorePreferences })\" [text]=\"text.restorePreferencesHelp\" />\n <span class=\"sp-settings__actions\">\n @if (confirming() === 'preferences') {\n <hm-button emphasis=\"medium\" (clicked)=\"cancelConfirm()\">{{ text.cancel }}</hm-button>\n }\n <hm-button [emphasis]=\"confirming() === 'preferences' ? 'high' : 'medium'\" intent=\"danger\" (clicked)=\"restorePreferences()\">\n {{ confirming() === 'preferences' ? text.confirmRestore : text.restore }}\n </hm-button>\n </span>\n </hm-settings-row>\n </hm-settings-section>\n\n <!-- \u2500\u2500 Ajuda \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 <!-- Novidades (s\u00F3 com alguma vigente), configura\u00E7\u00E3o inicial e o essencial: fecham o painel antes de abrir. -->\n <hm-settings-section [key]=\"keys.help\" [label]=\"text.help\" icon=\"help\" [keywords]=\"text.helpKeywords\">\n @if (hasNews()) {\n <hm-settings-row [label]=\"text.news\" [description]=\"text.newsDescription\" [keywords]=\"text.newsKeywords\">\n <hm-button emphasis=\"medium\" icon=\"star\" (clicked)=\"showNews()\">{{ text.showNews }}</hm-button>\n </hm-settings-row>\n }\n @if (setupEnabled) {\n <hm-settings-row [label]=\"text.setup\" [description]=\"text.setupDescription\" [keywords]=\"text.setupKeywords\">\n <hm-button emphasis=\"medium\" icon=\"color-palette\" (clicked)=\"redoSetup()\">{{ text.redoSetup }}</hm-button>\n </hm-settings-row>\n }\n <hm-settings-row [label]=\"text.essentials\" [description]=\"text.essentialsDescription\" [keywords]=\"text.essentialsKeywords\">\n <hm-button emphasis=\"medium\" icon=\"help\" (clicked)=\"showEssentials()\">{{ text.showEssentials }}</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 <!-- M\u00E9dia da ferramenta (SpineFeatures.ratings) e \"Avaliar\", que abre o di\u00E1logo da estrela do cabe\u00E7alho. -->\n @if (ratingsEnabled) {\n <hm-settings-row [label]=\"ratingText.aboutRating\" [keywords]=\"ratingText.aboutRatingKeywords\">\n <spine-app-rating-summary />\n </hm-settings-row>\n }\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=\"medium\" icon=\"book\" [link]=\"packageInfo.docs\" [newTab]=\"true\">{{ text.docs }}</hm-button> }\n @if (packageInfo.issues) { <hm-button emphasis=\"medium\" icon=\"bug\" [link]=\"packageInfo.issues\" [newTab]=\"true\">{{ text.reportIssue }}</hm-button> }\n @if (packageInfo.repository?.url) { <hm-button emphasis=\"medium\" 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__timezone{display:block;width:min(320px,100%)}.sp-settings__profile{display:flex;align-items:center;gap:var(--hm-space-sm);padding:var(--hm-space-xs) 0 var(--hm-space-sm)}.sp-settings__profile-text{display:flex;min-width:0;flex-direction:column;gap:var(--hm-space-micro);color:var(--hm-text);overflow-wrap:anywhere}.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;overflow-wrap:anywhere}.sp-settings__muted{color:var(--hm-muted)}.sp-settings__mono{font-family:var(--hm-font-mono)}.sp-settings__shortcuts{display:block;padding-block:var(--hm-space-xxxs);border-top:var(--hm-border-hairline) solid var(--hm-divider)}.sp-settings__links,.sp-settings__actions{display:inline-flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--hm-space-xs)}.sp-settings__section--group-open ::ng-deep .hm-settings-section__heading{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.sp-settings__picker ::ng-deep .hm-theme-picker__title{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.sp-settings__deps{display:grid;flex:1;min-width:0;grid-template-columns:repeat(auto-fill,minmax(min(12rem,100%),1fr));margin:0;overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.sp-settings__deps div{display:flex;justify-content:space-between;gap:var(--hm-space-xs);margin-block-start:calc(-1 * var(--hm-border-hairline));margin-inline-start:calc(-1 * var(--hm-border-hairline));padding:var(--hm-space-xs) var(--hm-space-sm);border-top:var(--hm-border-hairline) solid var(--hm-divider);border-inline-start:var(--hm-border-hairline) solid var(--hm-divider)}.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"] }]
6287
6522
  }], ctorParameters: () => [] });
6288
6523
  const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6289
6524
  /** "UTC−03:00" para um fuso IANA. */
@@ -6761,7 +6996,7 @@ function registerPage() {
6761
6996
  route_redirect: SPINE_SUGGESTIONS_PAGE_PATH,
6762
6997
  description: text.description,
6763
6998
  },
6764
- load: () => import('./trismegisto-spine-layout-suggestions-page.component-xI2gDMok.mjs').then((m) => m.SpineSuggestionsPageComponent),
6999
+ load: () => import('./trismegisto-spine-layout-suggestions-page.component-CEKMqera.mjs').then((m) => m.SpineSuggestionsPageComponent),
6765
7000
  });
6766
7001
  });
6767
7002
  }
@@ -7459,7 +7694,7 @@ class SpinePagePanelComponent {
7459
7694
  </div>
7460
7695
  }
7461
7696
  </div>
7462
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;block-size:100%;min-block-size:0}.spine-page-panel__content{flex:1;min-block-size:0;display:flex;flex-direction:column;padding:var(--hm-space-lg, 1.5rem) var(--hm-space-md, 1rem)}.spine-page-panel__loading{display:flex;flex:1;align-items:center;justify-content:center;min-block-size:50vh;padding:var(--hm-space-xl)}.spine-page-panel__empty{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:var(--hm-space-sm);padding:var(--hm-space-xl);text-align:center;color:var(--hm-muted)}.spine-page-panel__empty hm-icon{color:var(--hm-muted)}.spine-page-panel__empty-title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-lg, 18px);font-weight:600}.spine-page-panel__empty-description{margin:0;max-inline-size:32ch;font-size:var(--hm-font-size-sm, 14px)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: LoadingSquareComponent, selector: "hm-loading-square" }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: SpinePageBarComponent, selector: "spine-page-bar", inputs: ["chain", "recordRecent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7697
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;block-size:100%;min-block-size:0}.spine-page-panel__content{flex:1;min-block-size:0;display:flex;flex-direction:column;padding:var(--hm-space-lg, 1.5rem) var(--hm-space-md, 1rem)}.spine-page-panel__loading{display:flex;flex:1;align-items:center;justify-content:center;min-block-size:50vh;padding:var(--hm-space-xl)}.spine-page-panel__empty{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:var(--hm-space-sm);padding:var(--hm-space-xl);text-align:center;color:var(--hm-muted)}.spine-page-panel__empty hm-icon{color:var(--hm-muted)}.spine-page-panel__empty-title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-lg, 18px);font-weight:var(--hm-font-weight-semibold)}.spine-page-panel__empty-description{margin:0;max-inline-size:32ch;font-size:var(--hm-font-size-sm, 14px)}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: LoadingSquareComponent, selector: "hm-loading-square" }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: SpinePageBarComponent, selector: "spine-page-bar", inputs: ["chain", "recordRecent"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7463
7698
  }
7464
7699
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpinePagePanelComponent, decorators: [{
7465
7700
  type: Component,
@@ -7478,7 +7713,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
7478
7713
  </div>
7479
7714
  }
7480
7715
  </div>
7481
- `, styles: [":host{display:flex;flex-direction:column;block-size:100%;min-block-size:0}.spine-page-panel__content{flex:1;min-block-size:0;display:flex;flex-direction:column;padding:var(--hm-space-lg, 1.5rem) var(--hm-space-md, 1rem)}.spine-page-panel__loading{display:flex;flex:1;align-items:center;justify-content:center;min-block-size:50vh;padding:var(--hm-space-xl)}.spine-page-panel__empty{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:var(--hm-space-sm);padding:var(--hm-space-xl);text-align:center;color:var(--hm-muted)}.spine-page-panel__empty hm-icon{color:var(--hm-muted)}.spine-page-panel__empty-title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-lg, 18px);font-weight:600}.spine-page-panel__empty-description{margin:0;max-inline-size:32ch;font-size:var(--hm-font-size-sm, 14px)}\n"] }]
7716
+ `, styles: [":host{display:flex;flex-direction:column;block-size:100%;min-block-size:0}.spine-page-panel__content{flex:1;min-block-size:0;display:flex;flex-direction:column;padding:var(--hm-space-lg, 1.5rem) var(--hm-space-md, 1rem)}.spine-page-panel__loading{display:flex;flex:1;align-items:center;justify-content:center;min-block-size:50vh;padding:var(--hm-space-xl)}.spine-page-panel__empty{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:var(--hm-space-sm);padding:var(--hm-space-xl);text-align:center;color:var(--hm-muted)}.spine-page-panel__empty hm-icon{color:var(--hm-muted)}.spine-page-panel__empty-title{margin:0;color:var(--hm-text);font-size:var(--hm-font-size-lg, 18px);font-weight:var(--hm-font-weight-semibold)}.spine-page-panel__empty-description{margin:0;max-inline-size:32ch;font-size:var(--hm-font-size-sm, 14px)}\n"] }]
7482
7717
  }], ctorParameters: () => [], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: true }] }], isFocusedPanel: [{ type: i0.Input, args: [{ isSignal: true, alias: "isFocusedPanel", required: false }] }], tabId: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabId", required: false }] }], panelId: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelId", required: false }] }] } });
7483
7718
 
7484
7719
  /**
@@ -7909,6 +8144,79 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
7909
8144
  `, styles: [".sp-idle__text{margin:0}.sp-idle__actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--hm-space-sm)}\n"] }]
7910
8145
  }] });
7911
8146
 
8147
+ /**
8148
+ * Registra a verificação de alterações da tela, presa à aba do workspace em que ela está (`SPINE_TAB`):
8149
+ * com `SpineFeatures.unsavedChanges` ligado, fechar ESSA aba (ou o painel dela, ou "fechar as outras") e
8150
+ * trocar de tela dentro dela pergunta antes. Chame num contexto de injeção (campo da tela); a verificação
8151
+ * sai sozinha quando a tela é destruída.
8152
+ *
8153
+ * ```ts
8154
+ * readonly #unsaved = registerSpineUnsavedChanges(() => this.form.dirty);
8155
+ * ```
8156
+ *
8157
+ * Sem o recurso ligado, continua registrando no `HermesUnsavedChangesService` (o `hermesUnsavedChangesGuard`
8158
+ * de uma rota do app enxerga), mas o shell não pergunta nada.
8159
+ */
8160
+ function registerSpineUnsavedChanges(check) {
8161
+ const tab = inject(SPINE_TAB, { optional: true });
8162
+ return registerUnsavedChanges(check, tab ? { scope: () => tab.id() } : {});
8163
+ }
8164
+ /**
8165
+ * O lado do shell do aviso de alterações não salvas (`SpineFeatures.unsavedChanges`, desligado por padrão).
8166
+ * Ligado: o `hm-workspace` recebe um store que pergunta antes de fechar aba com alteração; trocar de tela
8167
+ * dentro da aba pergunta também; e o navegador avisa ao recarregar/fechar a página. Desligado: `store` é o
8168
+ * mesmo do `SpineWorkspaceNavService` e nada muda.
8169
+ */
8170
+ class SpineUnsavedChangesService {
8171
+ constructor() {
8172
+ this.config = inject(SPINE_CONFIG, { optional: true });
8173
+ this.unsaved = inject(HermesUnsavedChangesService);
8174
+ this.nav = inject(SpineWorkspaceNavService);
8175
+ /** O recurso está ligado no app. */
8176
+ this.enabled = !!this.config?.features?.unsavedChanges;
8177
+ /** O store que o shell passa para o `hm-workspace`. */
8178
+ this.store = this.enabled ? this.guard(this.nav.store) : this.nav.store;
8179
+ }
8180
+ /** Liga o aviso do navegador (só com o recurso ligado). Devolve a função que desliga. */
8181
+ start() {
8182
+ return this.enabled ? this.unsaved.warnBeforeUnload() : () => { };
8183
+ }
8184
+ /** Pode trocar a tela da aba ativa do painel? Sem o recurso ou sem alteração nela, `true` na hora. */
8185
+ canReplaceActive(panelId) {
8186
+ if (!this.enabled)
8187
+ return true;
8188
+ const active = findPanelTabs$1(this.nav.store.getState().root, panelId)?.activeId;
8189
+ return active && this.unsaved.hasUnsavedChanges(active) ? this.unsaved.confirmDiscard() : true;
8190
+ }
8191
+ guard(store) {
8192
+ const tabsOf = (panelId) => findPanelTabs$1(store.getState().root, panelId)?.tabs.map((t) => t.id) ?? [];
8193
+ // Sem alteração em nenhuma das abas, fecha na hora (mesmo comportamento de antes); com alteração, pergunta.
8194
+ const closing = (ids, close) => {
8195
+ if (!ids.some((id) => this.unsaved.hasUnsavedChanges(id))) {
8196
+ close();
8197
+ return;
8198
+ }
8199
+ void this.unsaved.confirmDiscard().then((leave) => leave && close());
8200
+ };
8201
+ return {
8202
+ ...store,
8203
+ closeTab: (panelId, tabId) => closing([tabId], () => store.closeTab(panelId, tabId)),
8204
+ closePanel: (panelId) => closing(tabsOf(panelId), () => store.closePanel(panelId)),
8205
+ closeOtherTabs: (panelId, tabId) => closing(tabsOf(panelId).filter((id) => id !== tabId), () => store.closeOtherTabs(panelId, tabId)),
8206
+ closeTabsToTheRight: (panelId, tabId) => {
8207
+ const ids = tabsOf(panelId);
8208
+ closing(ids.slice(ids.indexOf(tabId) + 1), () => store.closeTabsToTheRight(panelId, tabId));
8209
+ },
8210
+ };
8211
+ }
8212
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineUnsavedChangesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
8213
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineUnsavedChangesService, providedIn: 'root' }); }
8214
+ }
8215
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineUnsavedChangesService, decorators: [{
8216
+ type: Injectable,
8217
+ args: [{ providedIn: 'root' }]
8218
+ }] });
8219
+
7912
8220
  /**
7913
8221
  * Shell do spine — MESMA estrutura do `docs/website`'s `app.component.html`/`.scss` (decisão de
7914
8222
  * 2026-09-28, ver `hefesto-migration-plan.md`): `.shell` com altura de viewport de verdade
@@ -7939,13 +8247,19 @@ class LayoutComponent {
7939
8247
  this.preferences = inject(SpinePreferencesService);
7940
8248
  this.workspace = inject(SpineWorkspaceNavService);
7941
8249
  this.settings = inject(SpineSettingsService);
8250
+ this.sidebar = inject(HermesSidebarService$1);
7942
8251
  /** Também liga, uma vez, o aviso de falha do logout (`logoutFailed$` do `@orfeu/angular`). */
7943
- this.logoutService = inject(SpineLogoutService);
8252
+ this.logoutNotice = inject(SpineLogoutService);
7944
8253
  /** Recursos opcionais (`SpineConfig.features`): cada serviço não faz nada com o seu recurso desligado. */
7945
8254
  this.idle = inject(SpineIdleService);
7946
8255
  this.startPage = inject(SpineStartPageService);
7947
8256
  this.accountPreferences = inject(SpineAccountPreferencesService);
7948
8257
  this.setup = inject(SpineInitialSetupService);
8258
+ /** Aviso de alterações não salvas (SpineFeatures.unsavedChanges): sem o recurso, `unsaved.store` é o próprio `workspace.store`. */
8259
+ this.unsaved = inject(SpineUnsavedChangesService);
8260
+ /** SpineFeatures.skipLink. */
8261
+ this.skipLink = !!inject(SPINE_CONFIG, { optional: true })?.features?.skipLink;
8262
+ this.destroyRef = inject(DestroyRef);
7949
8263
  this.isNoPermission = this.matchesNoPermission(this.router.url);
7950
8264
  this.router.events.pipe(filter((e) => e instanceof NavigationEnd), takeUntilDestroyed())
7951
8265
  .subscribe(e => { this.isNoPermission = this.matchesNoPermission(e.urlAfterRedirects); });
@@ -7958,13 +8272,13 @@ class LayoutComponent {
7958
8272
  void this.accountPreferences.start();
7959
8273
  this.idle.start();
7960
8274
  this.startPage.track();
8275
+ this.destroyRef.onDestroy(this.unsaved.start());
7961
8276
  // Liga os atalhos de teclado globais às ações reais do sistema.
7962
8277
  this.shortcuts.setActions({
7963
8278
  navigate: (url) => this.router.navigateByUrl(url),
7964
8279
  toggleTheme: () => this.theme.setTheme(this.theme.current === 'light' ? 'dark' : 'light'),
7965
- toggleFullscreen: () => this.toggleFullscreen(),
7966
- logout: () => void this.logoutService.logout(),
7967
8280
  openSettings: () => this.settings.toggle(),
8281
+ toggleSidebar: () => this.toggleSidebar(),
7968
8282
  });
7969
8283
  // Link direto pra uma seção do painel de configurações: `?settings=aparencia` (ou só
7970
8284
  // `?settings`). Tira o parâmetro da URL depois de abrir — senão ele volta a abrir o painel a
@@ -7979,19 +8293,33 @@ class LayoutComponent {
7979
8293
  // Novidades do app (argos-admin /me/news): um aviso discreto, uma vez por novidade, depois da configuração inicial.
7980
8294
  void this.news.announceOnEntry();
7981
8295
  }
7982
- onLocalNavigate(panelId, href) {
7983
- this.workspace.navigateLocally(panelId, href);
8296
+ /**
8297
+ * `Mod+B`. Tela estreita: a gaveta. Fixo e compacto: troca um pelo outro (é a preferência da pessoa, fica gravada).
8298
+ * Automático: a coluna abre por cima e fica até o próximo `Mod+B`, sem mudar a preferência.
8299
+ */
8300
+ toggleSidebar() {
8301
+ if (this.isNoPermission)
8302
+ return;
8303
+ if (window.matchMedia?.('(max-width: 767.98px)').matches)
8304
+ this.sidebar.toggleMobile();
8305
+ else if (this.sidebar.mode() === 'auto')
8306
+ this.sidebar.holdOpen(!this.sidebar.expanded());
8307
+ else
8308
+ this.sidebar.setMode(this.sidebar.mode() === 'fixed' ? 'compact' : 'fixed');
7984
8309
  }
7985
- toggleFullscreen() {
7986
- if (!document.fullscreenElement) {
7987
- document.documentElement.requestFullscreen?.().catch(() => { });
7988
- }
7989
- else {
7990
- document.exitFullscreen?.();
7991
- }
8310
+ onLocalNavigate(panelId, href) {
8311
+ const allowed = this.unsaved.canReplaceActive(panelId);
8312
+ if (allowed === true)
8313
+ this.workspace.navigateLocally(panelId, href);
8314
+ else
8315
+ void Promise.resolve(allowed).then((ok) => ok && this.workspace.navigateLocally(panelId, href));
7992
8316
  }
7993
8317
  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: SpineNewsService }], target: i0.ɵɵFactoryTarget.Component }); }
7994
8318
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: LayoutComponent, isStandalone: true, selector: "spine-layout", ngImport: i0, template: `
8319
+ <!-- "Pular para o conteúdo" (SpineFeatures.skipLink): primeira parada do Tab. -->
8320
+ @if (skipLink) {
8321
+ <hm-skip-link target="spine-main" />
8322
+ }
7995
8323
  <div class="shell" [attr.inert]="setup.isOpen() ? '' : null">
7996
8324
  <spine-header></spine-header>
7997
8325
  <!-- Aviso de conexão do @orfeu/angular (offline/reconectando/reautorizando): faixa entre o header e o corpo. -->
@@ -8000,9 +8328,10 @@ class LayoutComponent {
8000
8328
  @if (!isNoPermission) {
8001
8329
  <spine-sidebar class="sidebar"></spine-sidebar>
8002
8330
  }
8003
- <div class="main" role="main">
8331
+ <div class="main" role="main" [attr.id]="skipLink ? 'spine-main' : null">
8004
8332
  <div [hidden]="isNoPermission" class="workspace-area">
8005
- <hm-workspace [keepAlive]="true" [providedStore]="workspace.store" (newTabRequested)="workspace.openNewTab($event)">
8333
+ <!-- Dividir em painéis só com a preferência "Dividir abas" ligada (desligada por padrão), até 2 × 2. -->
8334
+ <hm-workspace [keepAlive]="true" [split]="workspace.split()" [maxColumns]="workspace.limits.maxColumns ?? null" [maxRows]="workspace.limits.maxRows ?? null" [providedStore]="unsaved.store" (newTabRequested)="workspace.openNewTab($event)">
8006
8335
  <ng-template #content let-tab let-panelId="panelId" let-active="active">
8007
8336
  <div
8008
8337
  class="panel-content"
@@ -8034,7 +8363,7 @@ class LayoutComponent {
8034
8363
  }
8035
8364
  <!-- Aviso de "Encerrar por inatividade" (SpineFeatures.idleTimeout): só desenha quando abre. -->
8036
8365
  <spine-idle-warning />
8037
- `, 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-shell-tab-strip-bg);--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", "tabId", "panelId"] }, { 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", inputs: ["position", "expand", "visibleToasts"] }, { 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: SidePanelComponent, selector: "spine-side-panel" }, { kind: "component", type: SpineInitialSetupComponent, selector: "spine-initial-setup" }, { kind: "component", type: ShortcutsCheatsheetComponent, selector: "spine-shortcuts-cheatsheet" }, { kind: "component", type: SettingsPanelComponent, selector: "spine-settings-panel" }, { kind: "component", type: SpineIdleWarningComponent, selector: "spine-idle-warning" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8366
+ `, 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-shell-tab-strip-bg);--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", "tabId", "panelId"] }, { kind: "directive", type: PanelLocalLinkDirective, selector: "[appPanelLocalLink]", inputs: ["appPanelLocalLink"], outputs: ["localNavigate"] }, { kind: "component", type: WorkspaceComponent, selector: "hm-workspace", inputs: ["initialTabs", "initialActiveId", "keepAlive", "split", "maxColumns", "maxRows", "label", "providedStore"], outputs: ["stateChange", "newTabRequested"] }, { kind: "component", type: ToastStackComponent, selector: "hm-toast-stack", inputs: ["position", "expand", "visibleToasts"] }, { 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: SidePanelComponent, selector: "spine-side-panel" }, { kind: "component", type: SpineInitialSetupComponent, selector: "spine-initial-setup" }, { kind: "component", type: ShortcutsCheatsheetComponent, selector: "spine-shortcuts-cheatsheet" }, { kind: "component", type: SettingsPanelComponent, selector: "spine-settings-panel" }, { kind: "component", type: SpineIdleWarningComponent, selector: "spine-idle-warning" }, { kind: "component", type: SkipLinkComponent, selector: "hm-skip-link", inputs: ["target", "label", "texts"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8038
8367
  }
8039
8368
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: LayoutComponent, decorators: [{
8040
8369
  type: Component,
@@ -8055,7 +8384,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
8055
8384
  ShortcutsCheatsheetComponent,
8056
8385
  SettingsPanelComponent,
8057
8386
  SpineIdleWarningComponent,
8387
+ SkipLinkComponent,
8058
8388
  ], changeDetection: ChangeDetectionStrategy.OnPush, template: `
8389
+ <!-- "Pular para o conteúdo" (SpineFeatures.skipLink): primeira parada do Tab. -->
8390
+ @if (skipLink) {
8391
+ <hm-skip-link target="spine-main" />
8392
+ }
8059
8393
  <div class="shell" [attr.inert]="setup.isOpen() ? '' : null">
8060
8394
  <spine-header></spine-header>
8061
8395
  <!-- Aviso de conexão do @orfeu/angular (offline/reconectando/reautorizando): faixa entre o header e o corpo. -->
@@ -8064,9 +8398,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
8064
8398
  @if (!isNoPermission) {
8065
8399
  <spine-sidebar class="sidebar"></spine-sidebar>
8066
8400
  }
8067
- <div class="main" role="main">
8401
+ <div class="main" role="main" [attr.id]="skipLink ? 'spine-main' : null">
8068
8402
  <div [hidden]="isNoPermission" class="workspace-area">
8069
- <hm-workspace [keepAlive]="true" [providedStore]="workspace.store" (newTabRequested)="workspace.openNewTab($event)">
8403
+ <!-- Dividir em painéis só com a preferência "Dividir abas" ligada (desligada por padrão), até 2 × 2. -->
8404
+ <hm-workspace [keepAlive]="true" [split]="workspace.split()" [maxColumns]="workspace.limits.maxColumns ?? null" [maxRows]="workspace.limits.maxRows ?? null" [providedStore]="unsaved.store" (newTabRequested)="workspace.openNewTab($event)">
8070
8405
  <ng-template #content let-tab let-panelId="panelId" let-active="active">
8071
8406
  <div
8072
8407
  class="panel-content"
@@ -8261,7 +8596,7 @@ class SpineSuggestionChatComponent {
8261
8596
  <ng-content select="[spineChatReadOnly]" />
8262
8597
  }
8263
8598
  </div>
8264
- `, isInline: true, styles: [":host{display:block;min-inline-size:0}.spine-chat{display:flex;flex-direction:column;block-size:var(--spine-chat-height, min(70dvh, 44rem));overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.spine-chat__scroller{flex:1 1 auto;min-block-size:0}.spine-chat__text{white-space:pre-line;overflow-wrap:anywhere}.spine-chat__tags{display:inline-flex;flex-wrap:wrap;gap:var(--hm-space-xxxs)}.spine-chat__internal{display:inline-flex;margin-inline-end:var(--hm-space-micro);color:var(--hm-warning);vertical-align:-.125em}.spine-chat__internal hm-icon{--hm-icon-size: .875rem}.spine-chat__sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}.spine-chat__empty{margin:auto;padding:var(--hm-space-md);color:var(--hm-muted);font-size:var(--hm-font-size-sm);text-align:center}.spine-chat__composer{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--hm-space-xs);padding:var(--hm-space-xs) var(--hm-space-sm);border-block-start:var(--hm-border-hairline) solid var(--hm-border)}.spine-chat__field{flex:1 1 16rem;min-inline-size:0}.spine-chat__actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--hm-space-sm)}\n"], dependencies: [{ kind: "component", type: BubbleComponent, selector: "hm-bubble", inputs: ["variant", "reactions", "typing"], outputs: ["reactionToggle"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "directive", type: TooltipDirective, selector: "[hmTooltip]", inputs: ["hmTooltip", "hmTooltipSide", "hmTooltipDisabled", "hmTooltipShowDelay", "hmTooltipHideDelay"] }, { kind: "component", type: MessageComponent, selector: "hm-message", inputs: ["author", "avatarUrl", "time", "timeFormat", "align", "grouped", "showAvatar", "status", "announce"], outputs: ["retry"] }, { kind: "component", type: MessageDividerComponent, selector: "hm-message-divider", inputs: ["label", "icon", "variant"] }, { kind: "component", type: MessageScrollerComponent, selector: "hm-message-scroller", inputs: ["label", "hasMoreHistory", "loadingHistory", "autoLoadHistory", "live", "itemSelector", "threshold"], outputs: ["loadHistory", "atEndChange"] }, { kind: "component", type: TagComponent, selector: "hm-tag", inputs: ["name", "color", "compact"] }, { kind: "component", type: TextareaComponent, selector: "hm-textarea", inputs: ["value", "label", "hint", "placeholder", "maxLength", "rows", "autoGrow", "labelHidden", "loading", "maxRows", "submitKey"], outputs: ["valueChange", "submitted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8599
+ `, isInline: true, styles: [":host{display:block;min-inline-size:0}.spine-chat{display:flex;flex-direction:column;block-size:var(--spine-chat-height, min(70dvh, 44rem));overflow:hidden;border:var(--hm-border-hairline) solid var(--hm-border);border-radius:var(--hm-radius);background:var(--hm-surface)}.spine-chat__scroller{flex:1 1 auto;min-block-size:0}.spine-chat__text{white-space:pre-line;overflow-wrap:anywhere}.spine-chat__tags{display:inline-flex;flex-wrap:wrap;gap:var(--hm-space-xxxs)}.spine-chat__internal{display:inline-flex;margin-inline-end:var(--hm-space-micro);color:var(--hm-warning);vertical-align:-.125em}.spine-chat__internal hm-icon{--hm-icon-size: .875rem}.spine-chat__sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}.spine-chat__empty{margin:auto;padding:var(--hm-space-md);color:var(--hm-muted);font-size:var(--hm-font-size-sm);text-align:center}.spine-chat__composer{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--hm-space-xs);padding:var(--hm-space-xs) var(--hm-space-sm);border-block-start:var(--hm-border-hairline) solid var(--hm-border)}.spine-chat__field{flex:1 1 16rem;min-inline-size:0}.spine-chat__actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--hm-space-sm)}\n"], dependencies: [{ kind: "component", type: BubbleComponent, selector: "hm-bubble", inputs: ["variant", "reactions", "typing"], outputs: ["reactionToggle"] }, { kind: "component", type: ButtonComponent, selector: "hm-button", inputs: ["emphasis", "intent", "size", "type", "icon", "iconDirection", "iconEnd", "disabled", "loading", "loadingState", "block", "link", "newTab", "caret", "inMenu", "toggle", "pressed"], outputs: ["loadingStateChange", "pressedChange", "clicked"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "directive", type: TooltipDirective, selector: "[hmTooltip]", inputs: ["hmTooltip", "hmTooltipSide", "hmTooltipDisabled", "hmTooltipShowDelay", "hmTooltipHideDelay"] }, { kind: "component", type: MessageComponent, selector: "hm-message", inputs: ["author", "avatarUrl", "time", "timeFormat", "align", "grouped", "showAvatar", "status", "announce", "reactions", "reactionChoices", "texts"], outputs: ["retry", "reactionToggle"] }, { kind: "component", type: MessageDividerComponent, selector: "hm-message-divider", inputs: ["label", "icon", "variant"] }, { kind: "component", type: MessageScrollerComponent, selector: "hm-message-scroller", inputs: ["label", "hasMoreHistory", "loadingHistory", "autoLoadHistory", "live", "itemSelector", "threshold"], outputs: ["loadHistory", "atEndChange"] }, { kind: "component", type: TagComponent, selector: "hm-tag", inputs: ["name", "color", "compact"] }, { kind: "component", type: TextareaComponent, selector: "hm-textarea", inputs: ["value", "label", "hint", "placeholder", "maxLength", "rows", "autoGrow", "labelHidden", "loading", "maxRows", "submitKey", "mentionCandidates", "mentionTrigger", "texts"], outputs: ["valueChange", "submitted", "mentioned"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8265
8600
  }
8266
8601
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: SpineSuggestionChatComponent, decorators: [{
8267
8602
  type: Component,
@@ -8425,5 +8760,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
8425
8760
  * Generated bundle index. Do not edit.
8426
8761
  */
8427
8762
 
8428
- export { SpineDocsComponent as $, AiAgentComponent as A, SPINE_INBOX_SOURCES as B, SPINE_MARKDOWN_PURIFY_CONFIG as C, SPINE_PREFERENCES_SAVE_DELAY_MS as D, SPINE_RATING_COMMENT_MAX as E, SPINE_RATING_SCORE_MAX as F, SPINE_SETTINGS_ALIASES as G, HeaderComponent as H, SPINE_SETTINGS_KEYS as I, SPINE_SETTINGS_SECTIONS as J, SPINE_SUGGESTIONS_PAGE_ID as K, LayoutComponent as L, SPINE_SUGGESTION_LIMITS as M, NEW_SCREEN_VALUE as N, SPINE_SUGGESTION_PUBLIC_SORTS as O, SPINE_SUGGESTION_PUBLIC_STATUSES as P, SPINE_SUGGESTION_STATUS_ICONS as Q, SettingsPanelComponent as R, SpineSuggestionsService as S, ShortcutService as T, ShortcutsCheatsheetComponent as U, SidebarComponent as V, SpineAccountPreferencesService as W, SpineAccountSecurityService as X, SpineAppTokensService as Y, SpineBrandComponent as Z, SpineConnectionBannerComponent as _, SpineInboxService as a, SpineDocsService as a0, SpineIdleService as a1, SpineIdleWarningComponent as a2, SpineInboxButtonComponent as a3, SpineInitialSetupComponent as a4, SpineInitialSetupService as a5, SpineLogoutService as a6, SpineMarkdownComponent as a7, SpineMyGroupsService as a8, SpineNewsComponent as a9, spineSuggestionsEnabled as aA, spineTagColor as aB, stable as aC, SpineNewsService as aa, SpineNoticesService as ab, SpinePageActionsDirective as ac, SpinePageActionsSlot as ad, SpineRatingStarsComponent as ae, SpineRatingsService as af, SpineSettingsService as ag, SpineStartPageService as ah, SpineWorkspaceNavService as ai, accountErrorCode as aj, argosAccountApi as ak, buildMcpConfig as al, effectiveIdleMinutes as am, idleLimits as an, isInternalPath as ao, provideSettingsSection as ap, provideSpineInboxSource as aq, provideSpineSuggestionsPage as ar, ratingTargetKey as as, resolveSettingsSection as at, sanitizeSpineMarkdownHtml as au, spineNormalizeUrl as av, spineParamsOf as aw, spinePathOf as ax, spineScoreBand as ay, spineStarFills as az, SpineSuggestionChatComponent as b, SPINE_SUGGESTION_STATUS_COLORS as c, SPINE_SUGGESTION_KIND_COLORS as d, SPINE_SUGGESTION_KIND_ICONS as e, SPINE_SUGGESTION_WAITING_COLORS as f, SPINE_SUGGESTION_VISIBLE_SORTS as g, SPINE_SUGGESTION_REASONS as h, SPINE_SUGGESTION_STATUSES as i, SPINE_SUGGESTION_KINDS as j, SPINE_SUGGESTION_WAITING as k, SPINE_TAB as l, SPINE_SUGGESTION_PAGE_PARAM as m, SPINE_SUGGESTION_TAB_PARAM as n, SPINE_SUGGESTION_TAB_CONVERSATION as o, screenName as p, screenModule as q, SPINE_SUGGESTIONS_PAGE_PATH as r, suggestionScreens as s, SpineSuggestionsDialogComponent as t, SpineTagRowComponent as u, AppLauncherComponent as v, SPINE_BRAND as w, SPINE_GLOBAL_PREFERENCE_KEYS as x, SPINE_IDLE_WARNING_SECONDS as y, SPINE_INBOX_POLL_MS as z };
8429
- //# sourceMappingURL=trismegisto-spine-layout-trismegisto-spine-layout-Clphrsgc.mjs.map
8763
+ export { SpineConnectionBannerComponent as $, AiAgentComponent as A, SPINE_INBOX_SOURCES as B, SPINE_MARKDOWN_PURIFY_CONFIG as C, SPINE_PREFERENCES_SAVE_DELAY_MS as D, SPINE_RATING_COMMENT_MAX as E, SPINE_RATING_SCORE_MAX as F, SPINE_SETTINGS_ALIASES as G, HeaderComponent as H, SPINE_SETTINGS_KEYS as I, SPINE_SETTINGS_SECTIONS as J, SPINE_SUGGESTIONS_PAGE_ID as K, LayoutComponent as L, SPINE_SUGGESTION_LIMITS as M, NEW_SCREEN_VALUE as N, SPINE_SUGGESTION_PUBLIC_SORTS as O, SPINE_SUGGESTION_PUBLIC_STATUSES as P, SPINE_SUGGESTION_STATUS_ICONS as Q, SPINE_WORKSPACE_LIMITS as R, SpineSuggestionsService as S, SettingsPanelComponent as T, ShortcutService as U, ShortcutsCheatsheetComponent as V, SidebarComponent as W, SpineAccountPreferencesService as X, SpineAccountSecurityService as Y, SpineAppTokensService as Z, SpineBrandComponent as _, SpineInboxService as a, SpineDocsComponent as a0, SpineDocsService as a1, SpineIdleService as a2, SpineIdleWarningComponent as a3, SpineInboxButtonComponent as a4, SpineInitialSetupComponent as a5, SpineInitialSetupService as a6, SpineLogoutService as a7, SpineMarkdownComponent as a8, SpineMyGroupsService as a9, spinePathOf as aA, spineScoreBand as aB, spineStarFills as aC, spineSuggestionsEnabled as aD, spineTagColor as aE, stable as aF, SpineNewsComponent as aa, SpineNewsService as ab, SpineNoticesService as ac, SpinePageActionsDirective as ad, SpinePageActionsSlot as ae, SpineRatingStarsComponent as af, SpineRatingsService as ag, SpineSettingsService as ah, SpineStartPageService as ai, SpineUnsavedChangesService as aj, SpineWorkspaceNavService as ak, accountErrorCode as al, argosAccountApi as am, buildMcpConfig as an, effectiveIdleMinutes as ao, idleLimits as ap, isInternalPath as aq, provideSettingsSection as ar, provideSpineInboxSource as as, provideSpineSuggestionsPage as at, ratingTargetKey as au, registerSpineUnsavedChanges as av, resolveSettingsSection as aw, sanitizeSpineMarkdownHtml as ax, spineNormalizeUrl as ay, spineParamsOf as az, SpineSuggestionChatComponent as b, SPINE_SUGGESTION_STATUS_COLORS as c, SPINE_SUGGESTION_KIND_COLORS as d, SPINE_SUGGESTION_KIND_ICONS as e, SPINE_SUGGESTION_WAITING_COLORS as f, SPINE_SUGGESTION_VISIBLE_SORTS as g, SPINE_SUGGESTION_REASONS as h, SPINE_SUGGESTION_STATUSES as i, SPINE_SUGGESTION_KINDS as j, SPINE_SUGGESTION_WAITING as k, SPINE_TAB as l, SPINE_SUGGESTION_PAGE_PARAM as m, SPINE_SUGGESTION_TAB_PARAM as n, SPINE_SUGGESTION_TAB_CONVERSATION as o, screenName as p, screenModule as q, SPINE_SUGGESTIONS_PAGE_PATH as r, suggestionScreens as s, SpineSuggestionsDialogComponent as t, SpineTagRowComponent as u, AppLauncherComponent as v, SPINE_BRAND as w, SPINE_GLOBAL_PREFERENCE_KEYS as x, SPINE_IDLE_WARNING_SECONDS as y, SPINE_INBOX_POLL_MS as z };
8764
+ //# sourceMappingURL=trismegisto-spine-layout-trismegisto-spine-layout-DiAfxIGP.mjs.map