@nucleus-suite-pe/core 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, makeEnvironmentProviders, inject, Injector, signal, computed, Injectable, effect, DestroyRef, ElementRef, input, Directive, model, untracked, ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, viewChild, output, afterNextRender, TemplateRef, contentChildren } from '@angular/core';
2
+ import { InjectionToken, makeEnvironmentProviders, inject, Injector, signal, computed, Injectable, effect, DestroyRef, ElementRef, input, Directive, model, untracked, ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, TemplateRef, viewChild, output, afterNextRender, contentChildren } from '@angular/core';
3
3
  import { HttpContextToken, HttpClient, HttpContext, HttpErrorResponse } from '@angular/common/http';
4
- import { firstValueFrom, catchError, throwError, from, switchMap } from 'rxjs';
4
+ import { firstValueFrom, from, switchMap, catchError, throwError } from 'rxjs';
5
5
  import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
6
6
  import * as i1 from '@angular/forms';
7
7
  import { FormsModule } from '@angular/forms';
@@ -90,11 +90,19 @@ class NucleusSessionAuth {
90
90
  injector = inject(Injector);
91
91
  _ticket = signal(null, ...(ngDevMode ? [{ debugName: "_ticket" }] : /* istanbul ignore next */ []));
92
92
  _failed = signal(false, ...(ngDevMode ? [{ debugName: "_failed" }] : /* istanbul ignore next */ []));
93
+ _usable = signal(true, ...(ngDevMode ? [{ debugName: "_usable" }] : /* istanbul ignore next */ []));
93
94
  pending = null;
94
95
  ticket = this._ticket.asReadonly();
95
96
  failed = this._failed.asReadonly();
96
- established = computed(() => this._ticket() !== null, ...(ngDevMode ? [{ debugName: "established" }] : /* istanbul ignore next */ []));
97
+ established = computed(() => this._usable() && this._ticket() !== null, ...(ngDevMode ? [{ debugName: "established" }] : /* istanbul ignore next */ []));
98
+ usable = this._usable.asReadonly();
99
+ disable() {
100
+ this._usable.set(false);
101
+ this._ticket.set(null);
102
+ }
97
103
  establish(force = false) {
104
+ if (!this._usable())
105
+ return Promise.resolve(false);
98
106
  if (!force && this._ticket() !== null)
99
107
  return Promise.resolve(true);
100
108
  this.pending ??= this.request().finally(() => {
@@ -105,6 +113,7 @@ class NucleusSessionAuth {
105
113
  async clear() {
106
114
  this._ticket.set(null);
107
115
  this._failed.set(false);
116
+ this._usable.set(true);
108
117
  if (!isNucleusConfigured(this.config))
109
118
  return;
110
119
  try {
@@ -154,18 +163,26 @@ const nucleusAuthInterceptor = (req, next) => {
154
163
  const session = inject(NucleusSessionAuth);
155
164
  const auth = inject(NucleusAuth, { optional: true });
156
165
  const request = req.clone({ withCredentials: true });
157
- if (!req.context.get(NUCLEUS_FORCE_BEARER) && session.established()) {
158
- return next(request).pipe(catchError((error) => {
159
- if (!(error instanceof HttpErrorResponse) || error.status !== 401) {
160
- return throwError(() => error);
166
+ const withBearer = () => {
167
+ if (!auth)
168
+ return next(request);
169
+ return from(auth.idToken()).pipe(switchMap((token) => next(token ? request.clone({ setHeaders: { Authorization: `Bearer ${token}` } }) : request)));
170
+ };
171
+ if (req.context.get(NUCLEUS_FORCE_BEARER) || !session.established()) {
172
+ return withBearer();
173
+ }
174
+ return next(request).pipe(catchError((error) => {
175
+ if (!(error instanceof HttpErrorResponse) || error.status !== 401) {
176
+ return throwError(() => error);
177
+ }
178
+ return from(session.establish(true)).pipe(switchMap((renewed) => (renewed ? next(request) : throwError(() => error))), catchError((retried) => {
179
+ if (!(retried instanceof HttpErrorResponse) || retried.status !== 401) {
180
+ return throwError(() => retried);
161
181
  }
162
- return from(session.establish(true)).pipe(switchMap((renewed) => (renewed ? next(request) : throwError(() => error))));
182
+ session.disable();
183
+ return withBearer();
163
184
  }));
164
- }
165
- if (!auth) {
166
- return next(request);
167
- }
168
- return from(auth.idToken()).pipe(switchMap((token) => next(token ? request.clone({ setHeaders: { Authorization: `Bearer ${token}` } }) : request)));
185
+ }));
169
186
  };
170
187
 
171
188
  const NUCLEUS_LOGOUT_PARAM = 'nucleus_logout';
@@ -2386,11 +2403,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
2386
2403
 
2387
2404
  class NucleusPage {
2388
2405
  heading = signal('', ...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
2389
- lead = signal('', ...(ngDevMode ? [{ debugName: "lead" }] : /* istanbul ignore next */ []));
2390
2406
  crumbs = signal([], ...(ngDevMode ? [{ debugName: "crumbs" }] : /* istanbul ignore next */ []));
2391
- set(heading, lead = '', crumbs = []) {
2407
+ actions = signal(null, ...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
2408
+ set(heading, crumbs = []) {
2392
2409
  this.heading.set(heading);
2393
- this.lead.set(lead);
2394
2410
  this.crumbs.set(crumbs);
2395
2411
  }
2396
2412
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusPage, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
@@ -2401,6 +2417,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
2401
2417
  args: [{ providedIn: 'root' }]
2402
2418
  }] });
2403
2419
 
2420
+ class NucleusPageActions {
2421
+ page = inject(NucleusPage);
2422
+ template = inject(TemplateRef);
2423
+ constructor() {
2424
+ this.page.actions.set(this.template);
2425
+ }
2426
+ ngOnDestroy() {
2427
+ if (this.page.actions() === this.template)
2428
+ this.page.actions.set(null);
2429
+ }
2430
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusPageActions, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2431
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.22", type: NucleusPageActions, isStandalone: true, selector: "[nucleusPageActions]", ngImport: i0 });
2432
+ }
2433
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusPageActions, decorators: [{
2434
+ type: Directive,
2435
+ args: [{
2436
+ selector: '[nucleusPageActions]',
2437
+ }]
2438
+ }], ctorParameters: () => [] });
2439
+
2404
2440
  function provideNucleusFeedback() {
2405
2441
  return makeEnvironmentProviders([ConfirmationService, MessageService]);
2406
2442
  }
@@ -2868,9 +2904,12 @@ class NucleusList {
2868
2904
  minWidth = input('640px', ...(ngDevMode ? [{ debugName: "minWidth" }] : /* istanbul ignore next */ []));
2869
2905
  skeletonRows = input(5, ...(ngDevMode ? [{ debugName: "skeletonRows" }] : /* istanbul ignore next */ []));
2870
2906
  rowClass = input(() => '', ...(ngDevMode ? [{ debugName: "rowClass" }] : /* istanbul ignore next */ []));
2907
+ pageSize = input(10, ...(ngDevMode ? [{ debugName: "pageSize" }] : /* istanbul ignore next */ []));
2908
+ pageSizes = input([5, 10, 20, 25], ...(ngDevMode ? [{ debugName: "pageSizes" }] : /* istanbul ignore next */ []));
2871
2909
  action = output();
2872
2910
  toggle = output();
2873
2911
  cells = contentChildren(NucleusCell, ...(ngDevMode ? [{ debugName: "cells" }] : /* istanbul ignore next */ []));
2912
+ paginate = computed(() => !this.loading(), ...(ngDevMode ? [{ debugName: "paginate" }] : /* istanbul ignore next */ []));
2874
2913
  tableStyle = computed(() => ({ 'min-width': this.minWidth() }), ...(ngDevMode ? [{ debugName: "tableStyle" }] : /* istanbul ignore next */ []));
2875
2914
  showEmpty = computed(() => !this.loading() && this.rows().length === 0, ...(ngDevMode ? [{ debugName: "showEmpty" }] : /* istanbul ignore next */ []));
2876
2915
  placeholders = computed(() => Array.from({ length: Math.max(this.skeletonRows(), 1) }, (_, index) => ({ index })), ...(ngDevMode ? [{ debugName: "placeholders" }] : /* istanbul ignore next */ []));
@@ -2920,12 +2959,12 @@ class NucleusList {
2920
2959
  this.toggle.emit({ key: column.key, row, value: !this.isChecked(column, row) });
2921
2960
  }
2922
2961
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusList, deps: [], target: i0.ɵɵFactoryTarget.Component });
2923
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusList, isStandalone: true, selector: "nucleus-list", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, empty: { classPropertyName: "empty", publicName: "empty", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, skeletonRows: { classPropertyName: "skeletonRows", publicName: "skeletonRows", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action", toggle: "toggle" }, queries: [{ propertyName: "cells", predicate: NucleusCell, isSignal: true }], ngImport: i0, template: "<div class=\"head\">\n <ng-content select=\"[nucleusListTitle]\" />\n <div class=\"head-actions\">\n <ng-content select=\"[nucleusListActions]\" />\n </div>\n</div>\n\n@if (showEmpty()) {\n <div class=\"placeholder\">{{ empty() }}</div>\n} @else {\n <p-table [value]=\"data()\" styleClass=\"ns-table\" [tableStyle]=\"tableStyle()\">\n <ng-template #header>\n <tr>\n @for (column of columns(); track column.key) {\n <th [class]=\"'align-' + (column.align ?? 'left')\" [style.width]=\"column.width\">\n {{ column.header }}\n </th>\n }\n @if (actions().length) {\n <th class=\"align-right\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-row>\n @if (loading()) {\n <tr class=\"is-skeleton\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\">\n <p-skeleton height=\"13px\" [width]=\"skeletonWidth(column)\" />\n </td>\n }\n @if (actions().length) {\n <td class=\"align-right actions\">\n <p-skeleton shape=\"circle\" size=\"26px\" />\n </td>\n }\n </tr>\n } @else {\n <tr [class]=\"rowClass()(row)\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\" [class.strong]=\"column.strong\">\n @if (cellTemplate(column.key); as custom) {\n <ng-container\n [ngTemplateOutlet]=\"custom\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n />\n } @else if (column.kind === 'switch') {\n <span class=\"switch-cell\">\n <button\n type=\"button\"\n class=\"ns-switch\"\n role=\"switch\"\n [class.is-on]=\"isChecked(column, row)\"\n [attr.aria-checked]=\"isChecked(column, row)\"\n [attr.aria-label]=\"text(column, row)\"\n [disabled]=\"isLocked(column, row)\"\n [pTooltip]=\"hintOf(column, row)\"\n tooltipPosition=\"top\"\n (click)=\"flip(column, row)\"\n >\n <span class=\"knob\"></span>\n </button>\n <span class=\"switch-label\">{{ text(column, row) }}</span>\n </span>\n } @else {\n {{ text(column, row) }}\n @if (tagOf(column, row); as tag) {\n <p-tag [value]=\"tag.label\" [severity]=\"tag.severity ?? 'secondary'\" />\n }\n }\n </td>\n }\n\n @if (actions().length) {\n <td class=\"align-right actions\">\n @for (entry of visibleActions(row); track entry.id) {\n <p-button\n [icon]=\"entry.icon\"\n [severity]=\"entry.tone === 'danger' ? 'danger' : 'secondary'\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [pTooltip]=\"entry.label\"\n tooltipPosition=\"left\"\n [ariaLabel]=\"entry.label\"\n [disabled]=\"isActionLocked(entry, row)\"\n (onClick)=\"run(entry, row)\"\n />\n }\n </td>\n }\n </tr>\n }\n </ng-template>\n </p-table>\n}\n", styles: [":host{display:block}.head{display:flex;min-height:34px;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}.head-actions{display:flex;gap:8px}.placeholder{padding:26px;border:1px dashed var(--ac-border);border-radius:10px;color:var(--ac-text-muted);font-size:14px}.align-left{text-align:left}.align-center{text-align:center}.align-right{text-align:right}.strong{font-weight:600}.actions{white-space:nowrap}.switch-cell{display:inline-flex;align-items:center;gap:10px}.switch-label{color:var(--ac-text-muted);font-size:12px}.ns-switch{position:relative;width:38px;height:22px;flex:none;padding:0;border:1px solid var(--ac-border);border-radius:var(--ac-radius-pill);background:color-mix(in srgb,var(--ac-text) 10%,transparent);cursor:pointer;transition:background .14s ease,border-color .14s ease}.ns-switch .knob{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--ac-text);transition:transform .14s ease}.ns-switch.is-on{border-color:var(--ac-accent);background:var(--ac-accent)}.ns-switch.is-on .knob{background:var(--ac-on-accent);transform:translate(16px)}.ns-switch:disabled{cursor:not-allowed;opacity:.45}.ns-switch:focus-visible{outline:2px solid var(--ac-accent);outline-offset:2px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i3$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2962
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusList, isStandalone: true, selector: "nucleus-list", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, empty: { classPropertyName: "empty", publicName: "empty", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, skeletonRows: { classPropertyName: "skeletonRows", publicName: "skeletonRows", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizes: { classPropertyName: "pageSizes", publicName: "pageSizes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action", toggle: "toggle" }, queries: [{ propertyName: "cells", predicate: NucleusCell, isSignal: true }], ngImport: i0, template: "<div class=\"head\">\n <ng-content select=\"[nucleusListTitle]\" />\n <div class=\"head-actions\">\n <ng-content select=\"[nucleusListActions]\" />\n </div>\n</div>\n\n@if (showEmpty()) {\n <div class=\"placeholder\">{{ empty() }}</div>\n} @else {\n <p-table\n [value]=\"data()\"\n styleClass=\"ns-table\"\n [tableStyle]=\"tableStyle()\"\n [paginator]=\"paginate()\"\n [rows]=\"pageSize()\"\n [rowsPerPageOptions]=\"pageSizes()\"\n [alwaysShowPaginator]=\"true\"\n >\n <ng-template #header>\n <tr>\n @for (column of columns(); track column.key) {\n <th [class]=\"'align-' + (column.align ?? 'left')\" [style.width]=\"column.width\">\n {{ column.header }}\n </th>\n }\n @if (actions().length) {\n <th class=\"align-right\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-row>\n @if (loading()) {\n <tr class=\"is-skeleton\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\">\n <p-skeleton height=\"13px\" [width]=\"skeletonWidth(column)\" />\n </td>\n }\n @if (actions().length) {\n <td class=\"align-right actions\">\n <p-skeleton shape=\"circle\" size=\"26px\" />\n </td>\n }\n </tr>\n } @else {\n <tr [class]=\"rowClass()(row)\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\" [class.strong]=\"column.strong\">\n @if (cellTemplate(column.key); as custom) {\n <ng-container\n [ngTemplateOutlet]=\"custom\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n />\n } @else if (column.kind === 'switch') {\n <span class=\"switch-cell\">\n <button\n type=\"button\"\n class=\"ns-switch\"\n role=\"switch\"\n [class.is-on]=\"isChecked(column, row)\"\n [attr.aria-checked]=\"isChecked(column, row)\"\n [attr.aria-label]=\"text(column, row)\"\n [disabled]=\"isLocked(column, row)\"\n [pTooltip]=\"hintOf(column, row)\"\n tooltipPosition=\"top\"\n (click)=\"flip(column, row)\"\n >\n <span class=\"knob\"></span>\n </button>\n <span class=\"switch-label\">{{ text(column, row) }}</span>\n </span>\n } @else {\n {{ text(column, row) }}\n @if (tagOf(column, row); as tag) {\n <p-tag [value]=\"tag.label\" [severity]=\"tag.severity ?? 'secondary'\" />\n }\n }\n </td>\n }\n\n @if (actions().length) {\n <td class=\"align-right actions\">\n @for (entry of visibleActions(row); track entry.id) {\n <p-button\n [icon]=\"entry.icon\"\n [severity]=\"entry.tone === 'danger' ? 'danger' : 'secondary'\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [pTooltip]=\"entry.label\"\n tooltipPosition=\"left\"\n [ariaLabel]=\"entry.label\"\n [disabled]=\"isActionLocked(entry, row)\"\n (onClick)=\"run(entry, row)\"\n />\n }\n </td>\n }\n </tr>\n }\n </ng-template>\n </p-table>\n}\n", styles: [":host{display:block}.head{display:flex;align-items:center;justify-content:space-between;gap:12px}.head:has(>*:not(.head-actions)),.head:has(.head-actions>*){min-height:34px;margin-bottom:12px}.head-actions{display:flex;gap:8px}.placeholder{padding:26px;border:1px dashed var(--ac-border);border-radius:10px;color:var(--ac-text-muted);font-size:14px}.align-left{text-align:left}.align-center{text-align:center}.align-right{text-align:right}.strong{font-weight:600}.actions{white-space:nowrap}.switch-cell{display:inline-flex;align-items:center;gap:10px}.switch-label{color:var(--ac-text-muted);font-size:12px}.ns-switch{position:relative;width:38px;height:22px;flex:none;padding:0;border:1px solid var(--ac-border);border-radius:var(--ac-radius-pill);background:color-mix(in srgb,var(--ac-text) 10%,transparent);cursor:pointer;transition:background .14s ease,border-color .14s ease}.ns-switch .knob{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--ac-text);transition:transform .14s ease}.ns-switch.is-on{border-color:var(--ac-accent);background:var(--ac-accent)}.ns-switch.is-on .knob{background:var(--ac-on-accent);transform:translate(16px)}.ns-switch:disabled{cursor:not-allowed;opacity:.45}.ns-switch:focus-visible{outline:2px solid var(--ac-accent);outline-offset:2px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i3$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2924
2963
  }
2925
2964
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusList, decorators: [{
2926
2965
  type: Component,
2927
- args: [{ selector: 'nucleus-list', imports: [NgTemplateOutlet, ButtonModule, SkeletonModule, TableModule, TagModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"head\">\n <ng-content select=\"[nucleusListTitle]\" />\n <div class=\"head-actions\">\n <ng-content select=\"[nucleusListActions]\" />\n </div>\n</div>\n\n@if (showEmpty()) {\n <div class=\"placeholder\">{{ empty() }}</div>\n} @else {\n <p-table [value]=\"data()\" styleClass=\"ns-table\" [tableStyle]=\"tableStyle()\">\n <ng-template #header>\n <tr>\n @for (column of columns(); track column.key) {\n <th [class]=\"'align-' + (column.align ?? 'left')\" [style.width]=\"column.width\">\n {{ column.header }}\n </th>\n }\n @if (actions().length) {\n <th class=\"align-right\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-row>\n @if (loading()) {\n <tr class=\"is-skeleton\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\">\n <p-skeleton height=\"13px\" [width]=\"skeletonWidth(column)\" />\n </td>\n }\n @if (actions().length) {\n <td class=\"align-right actions\">\n <p-skeleton shape=\"circle\" size=\"26px\" />\n </td>\n }\n </tr>\n } @else {\n <tr [class]=\"rowClass()(row)\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\" [class.strong]=\"column.strong\">\n @if (cellTemplate(column.key); as custom) {\n <ng-container\n [ngTemplateOutlet]=\"custom\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n />\n } @else if (column.kind === 'switch') {\n <span class=\"switch-cell\">\n <button\n type=\"button\"\n class=\"ns-switch\"\n role=\"switch\"\n [class.is-on]=\"isChecked(column, row)\"\n [attr.aria-checked]=\"isChecked(column, row)\"\n [attr.aria-label]=\"text(column, row)\"\n [disabled]=\"isLocked(column, row)\"\n [pTooltip]=\"hintOf(column, row)\"\n tooltipPosition=\"top\"\n (click)=\"flip(column, row)\"\n >\n <span class=\"knob\"></span>\n </button>\n <span class=\"switch-label\">{{ text(column, row) }}</span>\n </span>\n } @else {\n {{ text(column, row) }}\n @if (tagOf(column, row); as tag) {\n <p-tag [value]=\"tag.label\" [severity]=\"tag.severity ?? 'secondary'\" />\n }\n }\n </td>\n }\n\n @if (actions().length) {\n <td class=\"align-right actions\">\n @for (entry of visibleActions(row); track entry.id) {\n <p-button\n [icon]=\"entry.icon\"\n [severity]=\"entry.tone === 'danger' ? 'danger' : 'secondary'\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [pTooltip]=\"entry.label\"\n tooltipPosition=\"left\"\n [ariaLabel]=\"entry.label\"\n [disabled]=\"isActionLocked(entry, row)\"\n (onClick)=\"run(entry, row)\"\n />\n }\n </td>\n }\n </tr>\n }\n </ng-template>\n </p-table>\n}\n", styles: [":host{display:block}.head{display:flex;min-height:34px;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px}.head-actions{display:flex;gap:8px}.placeholder{padding:26px;border:1px dashed var(--ac-border);border-radius:10px;color:var(--ac-text-muted);font-size:14px}.align-left{text-align:left}.align-center{text-align:center}.align-right{text-align:right}.strong{font-weight:600}.actions{white-space:nowrap}.switch-cell{display:inline-flex;align-items:center;gap:10px}.switch-label{color:var(--ac-text-muted);font-size:12px}.ns-switch{position:relative;width:38px;height:22px;flex:none;padding:0;border:1px solid var(--ac-border);border-radius:var(--ac-radius-pill);background:color-mix(in srgb,var(--ac-text) 10%,transparent);cursor:pointer;transition:background .14s ease,border-color .14s ease}.ns-switch .knob{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--ac-text);transition:transform .14s ease}.ns-switch.is-on{border-color:var(--ac-accent);background:var(--ac-accent)}.ns-switch.is-on .knob{background:var(--ac-on-accent);transform:translate(16px)}.ns-switch:disabled{cursor:not-allowed;opacity:.45}.ns-switch:focus-visible{outline:2px solid var(--ac-accent);outline-offset:2px}\n"] }]
2928
- }], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], empty: [{ type: i0.Input, args: [{ isSignal: true, alias: "empty", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], skeletonRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "skeletonRows", required: false }] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], action: [{ type: i0.Output, args: ["action"] }], toggle: [{ type: i0.Output, args: ["toggle"] }], cells: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NucleusCell), { isSignal: true }] }] } });
2966
+ args: [{ selector: 'nucleus-list', imports: [NgTemplateOutlet, ButtonModule, SkeletonModule, TableModule, TagModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"head\">\n <ng-content select=\"[nucleusListTitle]\" />\n <div class=\"head-actions\">\n <ng-content select=\"[nucleusListActions]\" />\n </div>\n</div>\n\n@if (showEmpty()) {\n <div class=\"placeholder\">{{ empty() }}</div>\n} @else {\n <p-table\n [value]=\"data()\"\n styleClass=\"ns-table\"\n [tableStyle]=\"tableStyle()\"\n [paginator]=\"paginate()\"\n [rows]=\"pageSize()\"\n [rowsPerPageOptions]=\"pageSizes()\"\n [alwaysShowPaginator]=\"true\"\n >\n <ng-template #header>\n <tr>\n @for (column of columns(); track column.key) {\n <th [class]=\"'align-' + (column.align ?? 'left')\" [style.width]=\"column.width\">\n {{ column.header }}\n </th>\n }\n @if (actions().length) {\n <th class=\"align-right\"></th>\n }\n </tr>\n </ng-template>\n\n <ng-template #body let-row>\n @if (loading()) {\n <tr class=\"is-skeleton\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\">\n <p-skeleton height=\"13px\" [width]=\"skeletonWidth(column)\" />\n </td>\n }\n @if (actions().length) {\n <td class=\"align-right actions\">\n <p-skeleton shape=\"circle\" size=\"26px\" />\n </td>\n }\n </tr>\n } @else {\n <tr [class]=\"rowClass()(row)\">\n @for (column of columns(); track column.key) {\n <td [class]=\"'align-' + (column.align ?? 'left')\" [class.strong]=\"column.strong\">\n @if (cellTemplate(column.key); as custom) {\n <ng-container\n [ngTemplateOutlet]=\"custom\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column }\"\n />\n } @else if (column.kind === 'switch') {\n <span class=\"switch-cell\">\n <button\n type=\"button\"\n class=\"ns-switch\"\n role=\"switch\"\n [class.is-on]=\"isChecked(column, row)\"\n [attr.aria-checked]=\"isChecked(column, row)\"\n [attr.aria-label]=\"text(column, row)\"\n [disabled]=\"isLocked(column, row)\"\n [pTooltip]=\"hintOf(column, row)\"\n tooltipPosition=\"top\"\n (click)=\"flip(column, row)\"\n >\n <span class=\"knob\"></span>\n </button>\n <span class=\"switch-label\">{{ text(column, row) }}</span>\n </span>\n } @else {\n {{ text(column, row) }}\n @if (tagOf(column, row); as tag) {\n <p-tag [value]=\"tag.label\" [severity]=\"tag.severity ?? 'secondary'\" />\n }\n }\n </td>\n }\n\n @if (actions().length) {\n <td class=\"align-right actions\">\n @for (entry of visibleActions(row); track entry.id) {\n <p-button\n [icon]=\"entry.icon\"\n [severity]=\"entry.tone === 'danger' ? 'danger' : 'secondary'\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [pTooltip]=\"entry.label\"\n tooltipPosition=\"left\"\n [ariaLabel]=\"entry.label\"\n [disabled]=\"isActionLocked(entry, row)\"\n (onClick)=\"run(entry, row)\"\n />\n }\n </td>\n }\n </tr>\n }\n </ng-template>\n </p-table>\n}\n", styles: [":host{display:block}.head{display:flex;align-items:center;justify-content:space-between;gap:12px}.head:has(>*:not(.head-actions)),.head:has(.head-actions>*){min-height:34px;margin-bottom:12px}.head-actions{display:flex;gap:8px}.placeholder{padding:26px;border:1px dashed var(--ac-border);border-radius:10px;color:var(--ac-text-muted);font-size:14px}.align-left{text-align:left}.align-center{text-align:center}.align-right{text-align:right}.strong{font-weight:600}.actions{white-space:nowrap}.switch-cell{display:inline-flex;align-items:center;gap:10px}.switch-label{color:var(--ac-text-muted);font-size:12px}.ns-switch{position:relative;width:38px;height:22px;flex:none;padding:0;border:1px solid var(--ac-border);border-radius:var(--ac-radius-pill);background:color-mix(in srgb,var(--ac-text) 10%,transparent);cursor:pointer;transition:background .14s ease,border-color .14s ease}.ns-switch .knob{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:var(--ac-text);transition:transform .14s ease}.ns-switch.is-on{border-color:var(--ac-accent);background:var(--ac-accent)}.ns-switch.is-on .knob{background:var(--ac-on-accent);transform:translate(16px)}.ns-switch:disabled{cursor:not-allowed;opacity:.45}.ns-switch:focus-visible{outline:2px solid var(--ac-accent);outline-offset:2px}\n"] }]
2967
+ }], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], empty: [{ type: i0.Input, args: [{ isSignal: true, alias: "empty", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], skeletonRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "skeletonRows", required: false }] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], pageSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizes", required: false }] }], action: [{ type: i0.Output, args: ["action"] }], toggle: [{ type: i0.Output, args: ["toggle"] }], cells: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => NucleusCell), { isSignal: true }] }] } });
2929
2968
 
2930
2969
  function toggleCollapsed(collapsed, id) {
2931
2970
  const next = new Set(collapsed);
@@ -3416,7 +3455,6 @@ class NucleusShell {
3416
3455
  router = inject(Router);
3417
3456
  brand = input('SUITE', ...(ngDevMode ? [{ debugName: "brand" }] : /* istanbul ignore next */ []));
3418
3457
  heading = input('', ...(ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []));
3419
- lead = input('', ...(ngDevMode ? [{ debugName: "lead" }] : /* istanbul ignore next */ []));
3420
3458
  navItems = input([], ...(ngDevMode ? [{ debugName: "navItems" }] : /* istanbul ignore next */ []));
3421
3459
  activeNavId = input(null, ...(ngDevMode ? [{ debugName: "activeNavId" }] : /* istanbul ignore next */ []));
3422
3460
  selectedMenuId = input(null, ...(ngDevMode ? [{ debugName: "selectedMenuId" }] : /* istanbul ignore next */ []));
@@ -3426,7 +3464,7 @@ class NucleusShell {
3426
3464
  icons = NUCLEUS_ICONS;
3427
3465
  menuPlaceholders = ['64%', '80%', '52%', '72%', '58%'];
3428
3466
  title = computed(() => this.heading() || this.page.heading(), ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
3429
- subtitle = computed(() => this.lead() || this.page.lead(), ...(ngDevMode ? [{ debugName: "subtitle" }] : /* istanbul ignore next */ []));
3467
+ pageActions = this.page.actions;
3430
3468
  home = computed(() => ({ icon: 'pi pi-home', routerLink: '/' }), ...(ngDevMode ? [{ debugName: "home" }] : /* istanbul ignore next */ []));
3431
3469
  crumbs = computed(() => this.page.crumbs().map((crumb) => ({
3432
3470
  label: crumb.label,
@@ -3680,6 +3718,11 @@ class NucleusShell {
3680
3718
  if (!this.canOpen(app))
3681
3719
  return;
3682
3720
  this.recents.remember(app.id);
3721
+ const target = new URL(app.url, window.location.origin);
3722
+ if (target.origin === window.location.origin) {
3723
+ void this.router.navigateByUrl(target.pathname + target.search);
3724
+ return;
3725
+ }
3683
3726
  window.location.href = app.url;
3684
3727
  }
3685
3728
  appInitials(app) {
@@ -3724,11 +3767,12 @@ class NucleusShell {
3724
3767
  }
3725
3768
  }
3726
3769
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
3727
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusShell, isStandalone: true, selector: "nucleus-shell", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, lead: { classPropertyName: "lead", publicName: "lead", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, activeNavId: { classPropertyName: "activeNavId", publicName: "activeNavId", isSignal: true, isRequired: false, transformFunction: null }, selectedMenuId: { classPropertyName: "selectedMenuId", publicName: "selectedMenuId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { navSelect: "navSelect", menuSelect: "menuSelect" }, host: { properties: { "style.--ns-topbar-h": "topbarHeight()" } }, ngImport: i0, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar ns-frame\">\n <div class=\"side\">\n <p-button\n icon=\"pi pi-bars\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.navToggle\"\n [pTooltip]=\"t.navToggle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleSidebar()\"\n />\n\n <div class=\"brand\">\n <nucleus-mark [size]=\"20\" />\n <div class=\"brand-name\">\n <span class=\"brand-primary\">NUCLEUS</span>\n <span class=\"brand-secondary\">{{ brand() }}</span>\n </div>\n </div>\n\n </div>\n\n @if (hasSiteContext()) {\n <div class=\"context ns-context\">\n <p-select\n styleClass=\"context-select\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [disabled]=\"switching()\"\n [filter]=\"companyChoices().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.companyLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <ng-template #item let-option>\n <span class=\"company-option\">\n <span class=\"company-name\">{{ option.name }}</span>\n <p-tag [value]=\"option.tag\" severity=\"secondary\" />\n </span>\n </ng-template>\n </p-select>\n\n <p-select\n styleClass=\"context-select\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"onSiteChange($event)\"\n [placeholder]=\"sitePlaceholder()\"\n [disabled]=\"switching() || sitesLoading() || !siteOptions().length\"\n [filter]=\"siteOptions().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.siteLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </div>\n }\n\n <div class=\"side is-end\">\n <div class=\"lang-switch\" role=\"group\" aria-label=\"Idioma / Language\">\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'es'\"\n [attr.aria-pressed]=\"lang() === 'es'\"\n (click)=\"setLang('es')\"\n >\n ES\n </button>\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'en'\"\n [attr.aria-pressed]=\"lang() === 'en'\"\n (click)=\"setLang('en')\"\n >\n EN\n </button>\n </div>\n\n <p-button\n icon=\"pi pi-th-large\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.appsLauncher\"\n [pTooltip]=\"t.appsLauncher\"\n tooltipPosition=\"bottom\"\n (onClick)=\"launcher.toggle($event)\"\n />\n\n <p-popover #launcher styleClass=\"ns-flush-popover\">\n @if (!hasCompany()) {\n <div class=\"launcher-notice\">\n <i class=\"pi pi-building\" aria-hidden=\"true\"></i>\n <div>\n <p class=\"launcher-notice-title\">{{ t.noCompanyTitle }}</p>\n <p class=\"launcher-notice-body\">{{ t.noCompanyHint }}</p>\n </div>\n </div>\n }\n\n <nucleus-menu\n [heading]=\"t.appsSwitch\"\n [badge]=\"launcherCount()\"\n [sections]=\"launcherSections()\"\n [searchable]=\"launcherRich()\"\n [searchPlaceholder]=\"t.appsSearch\"\n [emptyText]=\"launcherRich() ? t.appsNoMatch : t.appsEmpty\"\n [keyboardHint]=\"t.keyboardHint\"\n [width]=\"336\"\n (select)=\"onLauncherSelect($event); launcher.hide()\"\n (dismiss)=\"launcher.hide()\"\n />\n </p-popover>\n\n <p-button\n icon=\"pi pi-cog\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.settingsTitle\"\n [pTooltip]=\"t.settingsTitle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"settingsOpen.set(true)\"\n />\n\n @if (me) {\n <p-button\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n styleClass=\"avatar-trigger\"\n [ariaLabel]=\"me.name\"\n [pTooltip]=\"me.name\"\n tooltipPosition=\"bottom\"\n (onClick)=\"account.toggle($event)\"\n >\n <p-avatar\n [image]=\"me.photoUrl ?? undefined\"\n [label]=\"me.photoUrl ? undefined : initials()\"\n shape=\"circle\"\n size=\"normal\"\n />\n </p-button>\n }\n </div>\n</header>\n\n<p-popover #account styleClass=\"ns-flush-popover\">\n @if (me) {\n <div class=\"who\">\n @if (me.photoUrl) {\n <img class=\"who-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"42\" height=\"42\" />\n } @else {\n <span class=\"who-photo who-initials\">{{ initials() }}</span>\n }\n <span class=\"who-text\">\n <span class=\"who-name\">{{ me.name }}</span>\n <span class=\"who-mail\">{{ me.email }}</span>\n </span>\n </div>\n\n <p class=\"who-role\">\n <span class=\"who-dot\" aria-hidden=\"true\"></span>\n @if (roleName(); as role) {\n <b>{{ role }}</b>\n <span class=\"who-sep\">\u00B7</span>\n }\n <span class=\"who-context\">{{ accountContext() }}</span>\n </p>\n\n <nucleus-menu\n [sections]=\"accountSections()\"\n [width]=\"304\"\n (select)=\"onAccountSelect($event); account.hide()\"\n (dismiss)=\"account.hide()\"\n />\n }\n</p-popover>\n\n<div class=\"body\">\n @if (narrow() && sidebarOpen()) {\n <button\n type=\"button\"\n class=\"sidebar-scrim\"\n [attr.aria-label]=\"t.navToggle\"\n (click)=\"toggleSidebar()\"\n ></button>\n }\n\n <aside class=\"sidebar ns-frame\" [class.is-collapsed]=\"!sidebarOpen()\">\n @if (roleName(); as role) {\n <div class=\"role-badge\" [pTooltip]=\"sidebarOpen() ? '' : role\" tooltipPosition=\"right\">\n <i class=\"pi pi-key\" aria-hidden=\"true\"></i>\n <span class=\"nav-label role-text\">\n <span class=\"role-name\">{{ role }}</span>\n <span class=\"role-caption\">{{ t.roleCaption }}</span>\n </span>\n </div>\n }\n\n @if (navItems().length) {\n <nav>\n @for (item of navItems(); track item.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"activeNavId() === item.id\"\n [attr.aria-current]=\"activeNavId() === item.id ? 'page' : null\"\n [pTooltip]=\"sidebarOpen() ? '' : item.label\"\n tooltipPosition=\"right\"\n (click)=\"selectNav(item.id)\"\n >\n <i [class]=\"item.icon\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ item.label }}</span>\n </button>\n }\n </nav>\n }\n\n <nav class=\"access-menu\">\n @if (menuLoading()) {\n <div class=\"nav-skeleton\" role=\"status\" [attr.aria-label]=\"t.accessLoading\">\n @for (width of menuPlaceholders; track $index) {\n <div class=\"nav-skeleton-item\">\n <p-skeleton shape=\"circle\" size=\"15px\" />\n <p-skeleton height=\"9px\" [width]=\"width\" />\n </div>\n }\n </div>\n } @else if (menuError()) {\n <p class=\"access-state is-error\">{{ menuError() }}</p>\n } @else if (!menu().length) {\n <p class=\"access-state\">{{ t.accessEmpty }}</p>\n } @else {\n @for (node of menu(); track node.id) {\n @let group = isGroup(node);\n @let open = isExpanded(node);\n\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"!group && isSelected(node)\"\n [class.is-inert]=\"!group && !node.route\"\n [attr.aria-expanded]=\"group ? open : null\"\n [disabled]=\"!group && !node.route\"\n [pTooltip]=\"sidebarOpen() ? '' : node.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(node)\"\n >\n <i [class]=\"node.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ node.label }}</span>\n @if (group && sidebarOpen()) {\n <i\n class=\"pi nav-caret\"\n [class.pi-chevron-down]=\"open\"\n [class.pi-chevron-right]=\"!open\"\n aria-hidden=\"true\"\n ></i>\n }\n </button>\n\n @if (open) {\n @for (child of node.children; track child.id) {\n <button\n type=\"button\"\n class=\"nav-item is-child\"\n [class.is-active]=\"isSelected(child)\"\n [pTooltip]=\"sidebarOpen() ? '' : child.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(child)\"\n >\n <i [class]=\"child.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ child.label }}</span>\n </button>\n }\n }\n }\n }\n </nav>\n </aside>\n\n <main class=\"content\">\n <div class=\"measure\">\n <ng-content select=\"[nucleusTop]\" />\n\n @if (crumbs().length) {\n <p-breadcrumb [model]=\"crumbs()\" [home]=\"home()\" styleClass=\"ns-crumbs\" />\n }\n\n @if (title()) {\n <h1>{{ title() }}</h1>\n }\n @if (subtitle()) {\n <p class=\"lead\">{{ subtitle() }}</p>\n }\n\n <ng-content />\n </div>\n </main>\n</div>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [modal]=\"false\"\n [blockScroll]=\"false\"\n styleClass=\"ns-settings-drawer\"\n [style]=\"{ width: '380px' }\"\n>\n <nucleus-appearance />\n</p-drawer>\n\n<p-toast position=\"bottom-right\" styleClass=\"ns-toast\">\n <ng-template #message let-message>\n <nucleus-lordicon\n [src]=\"message.severity === 'success' ? icons.check : icons.warning\"\n [tone]=\"message.severity === 'success' ? 'accent' : 'danger'\"\n [size]=\"34\"\n trigger=\"in\"\n />\n <div class=\"toast-text\">\n <p class=\"toast-summary\">{{ message.summary }}</p>\n @if (message.detail) {\n <p class=\"toast-detail\">{{ message.detail }}</p>\n }\n </div>\n </ng-template>\n</p-toast>\n\n<p-confirmdialog [style]=\"{ width: '420px' }\">\n <ng-template #headless let-message let-onAccept=\"onAccept\" let-onReject=\"onReject\">\n <div class=\"confirm\">\n <nucleus-lordicon\n [src]=\"message.icon\"\n [tone]=\"message.acceptButtonStyleClass ? 'danger' : 'accent'\"\n [size]=\"64\"\n trigger=\"loop\"\n />\n <h2>{{ message.header }}</h2>\n <p>{{ message.message }}</p>\n <div class=\"confirm-actions\">\n <p-button\n [label]=\"message.rejectLabel\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onReject()\"\n />\n <p-button\n [label]=\"message.acceptLabel\"\n [severity]=\"message.acceptButtonStyleClass ? 'danger' : 'primary'\"\n (onClick)=\"onAccept()\"\n />\n </div>\n </div>\n </ng-template>\n</p-confirmdialog>\n", styles: [":host{display:flex;flex-direction:column;min-height:100dvh;background:var(--ac-bg);color:var(--ac-text)}.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:flex-start;gap:16px;height:56px;padding:0 12px 0 8px;border-bottom:1px solid var(--ns-frame-line)}.side{display:flex;min-width:0;align-items:center;gap:4px}.side.is-end{margin-left:auto}.brand{display:flex;align-items:center;gap:9px;margin-left:4px}.brand-name{display:flex;align-items:baseline;gap:7px}.brand-primary{font-size:13px;font-weight:700;letter-spacing:.15em}.brand-secondary{font-size:9px;font-weight:500;letter-spacing:.22em;color:var(--ns-frame-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ns-frame-line);border-radius:999px;padding:2px}.lang-pill{font-size:10px;font-weight:600;letter-spacing:.08em;padding:4px 10px;border:none;border-radius:999px;background:transparent;color:var(--ns-frame-muted);cursor:pointer}.lang-pill:hover{color:var(--ns-frame-ink)}.lang-pill.is-active{background:var(--ns-frame-glow);color:var(--ns-frame-on-glow)}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:max-content;min-width:224px;max-width:340px;padding:12px 10px;border-right:1px solid var(--ns-frame-line);transition:width .18s ease}.sidebar.is-collapsed{width:60px;min-width:0}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:var(--ns-nav-y, 9px) 12px;border:none;border-radius:6px;background:transparent;color:var(--ns-frame-muted);font-size:13px;font-weight:500;text-align:left;white-space:nowrap;cursor:pointer}.nav-item i{flex:none;font-size:15px}.nav-item:hover{background:var(--ns-frame-raised);color:var(--ns-frame-ink)}.nav-item.is-active{background:var(--ns-frame-raised);box-shadow:inset 2px 0 0 var(--ns-frame-glow);color:var(--ns-frame-ink)}.nav-item.is-active i{color:var(--ns-frame-glow)}.nav-caret{margin-left:auto;font-size:11px!important;opacity:.7}.nav-item.is-inert{opacity:.45;cursor:default}.nav-item.is-inert:hover{background:transparent;color:var(--ns-frame-muted)}.nav-label{overflow:hidden;text-overflow:ellipsis}.sidebar.is-collapsed .nav-label{display:none}.sidebar.is-collapsed .nav-item{justify-content:center;padding-inline:0}.content{flex:1;min-width:0;padding:28px 28px 64px;display:flex;flex-direction:column;gap:14px}.measure{display:flex;width:100%;max-width:var(--ac-content-max, 1440px);flex-direction:column;gap:14px;margin-inline:0}h1{margin:0;font-size:26px;line-height:1.2;font-weight:500;letter-spacing:-.02em}.lead{margin:0;font-size:14px;line-height:1.6;font-weight:300;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--ns-frame-line)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;color:var(--ns-frame-muted);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;color:var(--ns-frame-muted);font-size:.78rem}.access-state.is-error{color:var(--p-red-400, #f87171)}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}@media(max-width:720px){.sidebar{max-width:none;position:fixed;top:var(--ns-topbar-h, 56px);bottom:0;left:0;z-index:30;width:268px;max-width:82vw;overflow-y:auto;box-shadow:0 18px 44px #00000073;transform:translate(-100%);transition:transform .18s ease}.sidebar.is-collapsed{width:268px;max-width:82vw;transform:translate(-100%)}.sidebar:not(.is-collapsed){transform:translate(0)}.sidebar.is-collapsed .nav-label{display:inline}.sidebar.is-collapsed .role-text{display:flex}.sidebar.is-collapsed .nav-item,.sidebar.is-collapsed .role-badge{justify-content:flex-start;padding-inline:12px}.brand-secondary{display:none}.topbar{height:auto;min-height:56px;flex-wrap:wrap;padding-bottom:8px;row-gap:8px}.context{order:3;width:100%;margin-left:0}.context-select{min-width:0;max-width:none;flex:1 1 0}.content{padding:20px 16px 48px}h1{font-size:22px}}.who{display:flex;align-items:center;gap:12px;padding:15px 15px 13px;border-bottom:1px solid var(--ac-border);background:var(--ns-header-bg)}.who-photo{width:42px;height:42px;flex:none;border-radius:999px;object-fit:cover}.who-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:15px;font-weight:600}.who-text{display:flex;min-width:0;flex-direction:column}.who-name{overflow:hidden;font-size:14px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.who-mail{overflow:hidden;color:var(--ac-text-muted);font-size:11.5px;text-overflow:ellipsis;white-space:nowrap}.who-role{display:flex;align-items:center;gap:7px;margin:0;overflow:hidden;padding:9px 15px;border-bottom:1px solid var(--ac-border);color:var(--ac-text-muted);font-size:11.5px;white-space:nowrap}.who-role b{flex:none;color:var(--ac-text);font-size:12px;font-weight:600}.who-sep{flex:none}.who-context{overflow:hidden;text-overflow:ellipsis}.who-dot{width:5px;height:5px;flex:none;border-radius:50%;background:var(--ac-accent)}.company-option{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}.company-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.launcher-notice{display:flex;gap:10px;margin:12px 12px 0;padding:12px;border:1px solid color-mix(in srgb,var(--ac-accent) 40%,transparent);border-radius:8px;background:color-mix(in srgb,var(--ac-accent) 10%,transparent)}.launcher-notice i{margin-top:2px;color:var(--ac-accent);font-size:15px}.launcher-notice-title{margin:0 0 4px;font-size:13px;font-weight:600}.launcher-notice-body{margin:0;color:var(--ac-text-muted);font-size:12px;line-height:1.45}.confirm{display:flex;flex-direction:column;align-items:center;gap:6px;padding:26px 24px 20px;border:1px solid var(--ac-border);border-radius:14px;background:var(--ac-surface);text-align:center}.confirm h2{margin:8px 0 0;font-size:17px;font-weight:600}.confirm p{margin:0;max-width:34ch;color:var(--ac-text-muted);font-size:13px;line-height:1.5}.confirm-actions{display:flex;justify-content:center;gap:8px;margin-top:18px}.toast-text{flex:1;margin-left:12px}.toast-summary{margin:0;font-size:13px;font-weight:600}.toast-detail{margin:2px 0 0;color:var(--ac-text-muted);font-size:12px}.context{display:flex;min-width:0;align-items:center;gap:8px;margin-left:14px}.context-select{min-width:150px;max-width:210px}.role-badge{display:flex;align-items:center;gap:12px;margin-bottom:10px;padding:9px 12px;border-left:2px solid var(--ns-frame-glow);border-radius:0 7px 7px 0;background:var(--ns-frame-raised);box-shadow:-2px 0 12px -3px color-mix(in srgb,var(--ns-frame-glow) 65%,transparent)}.role-badge>i{flex:none;color:var(--ns-frame-glow);font-size:14px}.role-text{display:flex;min-width:0;flex-direction:column;gap:1px}.role-name{overflow:hidden;color:var(--ns-frame-ink);font-size:13px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.role-caption{color:var(--ns-frame-muted);font-size:10px;letter-spacing:.06em;text-transform:uppercase}.sidebar.is-collapsed .role-badge{justify-content:center;padding-inline:0}.sidebar-scrim{position:fixed;top:var(--ns-topbar-h, 56px);right:0;bottom:0;left:0;z-index:29;padding:0;border:0;background:#00000073;cursor:pointer}.nav-skeleton{display:flex;flex-direction:column;gap:3px}.nav-skeleton-item{display:flex;align-items:center;gap:12px;padding:11px 12px}.sidebar.is-collapsed .nav-skeleton-item .p-skeleton:last-child{display:none}\n"], dependencies: [{ kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i1$3.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "ngmodule", type: BreadcrumbModule }, { kind: "component", type: i2$3.Breadcrumb, selector: "p-breadcrumb", inputs: ["model", "style", "styleClass", "home", "homeAriaLabel"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ConfirmDialogModule }, { kind: "component", type: i4$1.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i5$2.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i6$1.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NucleusAppearance, selector: "nucleus-appearance" }, { kind: "component", type: NucleusLordicon, selector: "nucleus-lordicon", inputs: ["src", "trigger", "state", "size", "tone"] }, { kind: "component", type: NucleusMenu, selector: "nucleus-menu", inputs: ["sections", "heading", "badge", "searchable", "searchPlaceholder", "emptyText", "keyboardHint", "width"], outputs: ["select", "dismiss"] }, { kind: "component", type: NucleusMark, selector: "nucleus-mark", inputs: ["accent", "size"] }, { kind: "component", type: NucleusStarfield, selector: "nucleus-starfield" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3770
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NucleusShell, isStandalone: true, selector: "nucleus-shell", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: false, transformFunction: null }, heading: { classPropertyName: "heading", publicName: "heading", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, activeNavId: { classPropertyName: "activeNavId", publicName: "activeNavId", isSignal: true, isRequired: false, transformFunction: null }, selectedMenuId: { classPropertyName: "selectedMenuId", publicName: "selectedMenuId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { navSelect: "navSelect", menuSelect: "menuSelect" }, host: { properties: { "style.--ns-topbar-h": "topbarHeight()" } }, ngImport: i0, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar ns-frame\">\n <div class=\"side\">\n <p-button\n icon=\"pi pi-bars\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.navToggle\"\n [pTooltip]=\"t.navToggle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleSidebar()\"\n />\n\n <div class=\"brand\">\n <nucleus-mark [size]=\"20\" />\n <div class=\"brand-name\">\n <span class=\"brand-primary\">NUCLEUS</span>\n <span class=\"brand-secondary\">{{ brand() }}</span>\n </div>\n </div>\n\n </div>\n\n @if (hasSiteContext()) {\n <div class=\"context ns-context\">\n <p-select\n styleClass=\"context-select\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [disabled]=\"switching()\"\n [filter]=\"companyChoices().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.companyLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <ng-template #item let-option>\n <span class=\"company-option\">\n <span class=\"company-name\">{{ option.name }}</span>\n <p-tag [value]=\"option.tag\" severity=\"secondary\" />\n </span>\n </ng-template>\n </p-select>\n\n <p-select\n styleClass=\"context-select\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"onSiteChange($event)\"\n [placeholder]=\"sitePlaceholder()\"\n [disabled]=\"switching() || sitesLoading() || !siteOptions().length\"\n [filter]=\"siteOptions().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.siteLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </div>\n }\n\n <div class=\"side is-end\">\n <div class=\"lang-switch\" role=\"group\" aria-label=\"Idioma / Language\">\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'es'\"\n [attr.aria-pressed]=\"lang() === 'es'\"\n (click)=\"setLang('es')\"\n >\n ES\n </button>\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'en'\"\n [attr.aria-pressed]=\"lang() === 'en'\"\n (click)=\"setLang('en')\"\n >\n EN\n </button>\n </div>\n\n <p-button\n icon=\"pi pi-th-large\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.appsLauncher\"\n [pTooltip]=\"t.appsLauncher\"\n tooltipPosition=\"bottom\"\n (onClick)=\"launcher.toggle($event)\"\n />\n\n <p-popover #launcher styleClass=\"ns-flush-popover\">\n @if (!hasCompany()) {\n <div class=\"launcher-notice\">\n <i class=\"pi pi-building\" aria-hidden=\"true\"></i>\n <div>\n <p class=\"launcher-notice-title\">{{ t.noCompanyTitle }}</p>\n <p class=\"launcher-notice-body\">{{ t.noCompanyHint }}</p>\n </div>\n </div>\n }\n\n <nucleus-menu\n [heading]=\"t.appsSwitch\"\n [badge]=\"launcherCount()\"\n [sections]=\"launcherSections()\"\n [searchable]=\"launcherRich()\"\n [searchPlaceholder]=\"t.appsSearch\"\n [emptyText]=\"launcherRich() ? t.appsNoMatch : t.appsEmpty\"\n [keyboardHint]=\"t.keyboardHint\"\n [width]=\"336\"\n (select)=\"onLauncherSelect($event); launcher.hide()\"\n (dismiss)=\"launcher.hide()\"\n />\n </p-popover>\n\n <p-button\n icon=\"pi pi-cog\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.settingsTitle\"\n [pTooltip]=\"t.settingsTitle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"settingsOpen.set(true)\"\n />\n\n @if (me) {\n <p-button\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n styleClass=\"avatar-trigger\"\n [ariaLabel]=\"me.name\"\n [pTooltip]=\"me.name\"\n tooltipPosition=\"bottom\"\n (onClick)=\"account.toggle($event)\"\n >\n <p-avatar\n [image]=\"me.photoUrl ?? undefined\"\n [label]=\"me.photoUrl ? undefined : initials()\"\n shape=\"circle\"\n size=\"normal\"\n />\n </p-button>\n }\n </div>\n</header>\n\n<p-popover #account styleClass=\"ns-flush-popover\">\n @if (me) {\n <div class=\"who\">\n @if (me.photoUrl) {\n <img class=\"who-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"42\" height=\"42\" />\n } @else {\n <span class=\"who-photo who-initials\">{{ initials() }}</span>\n }\n <span class=\"who-text\">\n <span class=\"who-name\">{{ me.name }}</span>\n <span class=\"who-mail\">{{ me.email }}</span>\n </span>\n </div>\n\n <p class=\"who-role\">\n <span class=\"who-dot\" aria-hidden=\"true\"></span>\n @if (roleName(); as role) {\n <b>{{ role }}</b>\n <span class=\"who-sep\">\u00B7</span>\n }\n <span class=\"who-context\">{{ accountContext() }}</span>\n </p>\n\n <nucleus-menu\n [sections]=\"accountSections()\"\n [width]=\"304\"\n (select)=\"onAccountSelect($event); account.hide()\"\n (dismiss)=\"account.hide()\"\n />\n }\n</p-popover>\n\n<div class=\"body\">\n @if (narrow() && sidebarOpen()) {\n <button\n type=\"button\"\n class=\"sidebar-scrim\"\n [attr.aria-label]=\"t.navToggle\"\n (click)=\"toggleSidebar()\"\n ></button>\n }\n\n <aside class=\"sidebar ns-frame\" [class.is-collapsed]=\"!sidebarOpen()\">\n @if (roleName(); as role) {\n <div class=\"role-badge\" [pTooltip]=\"sidebarOpen() ? '' : role\" tooltipPosition=\"right\">\n <i class=\"pi pi-key\" aria-hidden=\"true\"></i>\n <span class=\"nav-label role-text\">\n <span class=\"role-name\">{{ role }}</span>\n <span class=\"role-caption\">{{ t.roleCaption }}</span>\n </span>\n </div>\n }\n\n @if (navItems().length) {\n <nav>\n @for (item of navItems(); track item.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"activeNavId() === item.id\"\n [attr.aria-current]=\"activeNavId() === item.id ? 'page' : null\"\n [pTooltip]=\"sidebarOpen() ? '' : item.label\"\n tooltipPosition=\"right\"\n (click)=\"selectNav(item.id)\"\n >\n <i [class]=\"item.icon\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ item.label }}</span>\n </button>\n }\n </nav>\n }\n\n <nav class=\"access-menu\">\n @if (menuLoading()) {\n <div class=\"nav-skeleton\" role=\"status\" [attr.aria-label]=\"t.accessLoading\">\n @for (width of menuPlaceholders; track $index) {\n <div class=\"nav-skeleton-item\">\n <p-skeleton shape=\"circle\" size=\"15px\" />\n <p-skeleton height=\"9px\" [width]=\"width\" />\n </div>\n }\n </div>\n } @else if (menuError()) {\n <p class=\"access-state is-error\">{{ menuError() }}</p>\n } @else if (!menu().length) {\n <p class=\"access-state\">{{ t.accessEmpty }}</p>\n } @else {\n @for (node of menu(); track node.id) {\n @let group = isGroup(node);\n @let open = isExpanded(node);\n\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"!group && isSelected(node)\"\n [class.is-inert]=\"!group && !node.route\"\n [attr.aria-expanded]=\"group ? open : null\"\n [disabled]=\"!group && !node.route\"\n [pTooltip]=\"sidebarOpen() ? '' : node.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(node)\"\n >\n <i [class]=\"node.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ node.label }}</span>\n @if (group && sidebarOpen()) {\n <i\n class=\"pi nav-caret\"\n [class.pi-chevron-down]=\"open\"\n [class.pi-chevron-right]=\"!open\"\n aria-hidden=\"true\"\n ></i>\n }\n </button>\n\n @if (open) {\n @for (child of node.children; track child.id) {\n <button\n type=\"button\"\n class=\"nav-item is-child\"\n [class.is-active]=\"isSelected(child)\"\n [pTooltip]=\"sidebarOpen() ? '' : child.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(child)\"\n >\n <i [class]=\"child.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ child.label }}</span>\n </button>\n }\n }\n }\n }\n </nav>\n </aside>\n\n <main class=\"content\">\n <div class=\"measure\">\n <ng-content select=\"[nucleusTop]\" />\n\n @if (crumbs().length || title() || pageActions()) {\n <header class=\"page-head\">\n <div class=\"page-head-main\">\n @if (crumbs().length) {\n <p-breadcrumb [model]=\"crumbs()\" [home]=\"home()\" styleClass=\"ns-crumbs\" />\n }\n\n @if (title()) {\n <h1>{{ title() }}</h1>\n }\n </div>\n\n @if (pageActions(); as actions) {\n <div class=\"page-head-actions\">\n <ng-container [ngTemplateOutlet]=\"actions\" />\n </div>\n }\n </header>\n }\n\n <ng-content />\n </div>\n </main>\n</div>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [modal]=\"false\"\n [blockScroll]=\"false\"\n styleClass=\"ns-settings-drawer\"\n [style]=\"{ width: '380px' }\"\n>\n <nucleus-appearance />\n</p-drawer>\n\n<p-toast position=\"bottom-right\" styleClass=\"ns-toast\">\n <ng-template #message let-message>\n <nucleus-lordicon\n [src]=\"message.severity === 'success' ? icons.check : icons.warning\"\n [tone]=\"message.severity === 'success' ? 'accent' : 'danger'\"\n [size]=\"34\"\n trigger=\"in\"\n />\n <div class=\"toast-text\">\n <p class=\"toast-summary\">{{ message.summary }}</p>\n @if (message.detail) {\n <p class=\"toast-detail\">{{ message.detail }}</p>\n }\n </div>\n </ng-template>\n</p-toast>\n\n<p-confirmdialog [style]=\"{ width: '420px' }\">\n <ng-template #headless let-message let-onAccept=\"onAccept\" let-onReject=\"onReject\">\n <div class=\"confirm\">\n <nucleus-lordicon\n [src]=\"message.icon\"\n [tone]=\"message.acceptButtonStyleClass ? 'danger' : 'accent'\"\n [size]=\"64\"\n trigger=\"loop\"\n />\n <h2>{{ message.header }}</h2>\n <p>{{ message.message }}</p>\n <div class=\"confirm-actions\">\n <p-button\n [label]=\"message.rejectLabel\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onReject()\"\n />\n <p-button\n [label]=\"message.acceptLabel\"\n [severity]=\"message.acceptButtonStyleClass ? 'danger' : 'primary'\"\n (onClick)=\"onAccept()\"\n />\n </div>\n </div>\n </ng-template>\n</p-confirmdialog>\n", styles: [":host{display:flex;flex-direction:column;min-height:100dvh;background:var(--ac-bg);color:var(--ac-text)}.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:flex-start;gap:16px;height:56px;padding:0 12px 0 8px;border-bottom:1px solid var(--ns-frame-line)}.side{display:flex;min-width:0;align-items:center;gap:4px}.side.is-end{margin-left:auto}.brand{display:flex;align-items:center;gap:9px;margin-left:4px}.brand-name{display:flex;align-items:baseline;gap:7px}.brand-primary{font-size:13px;font-weight:700;letter-spacing:.15em}.brand-secondary{font-size:9px;font-weight:500;letter-spacing:.22em;color:var(--ns-frame-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ns-frame-line);border-radius:999px;padding:2px}.lang-pill{font-size:10px;font-weight:600;letter-spacing:.08em;padding:4px 10px;border:none;border-radius:999px;background:transparent;color:var(--ns-frame-muted);cursor:pointer}.lang-pill:hover{color:var(--ns-frame-ink)}.lang-pill.is-active{background:var(--ns-frame-glow);color:var(--ns-frame-on-glow)}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:max-content;min-width:224px;max-width:340px;padding:12px 10px;border-right:1px solid var(--ns-frame-line);transition:width .18s ease}.sidebar.is-collapsed{width:60px;min-width:0}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:var(--ns-nav-y, 9px) 12px;border:none;border-radius:6px;background:transparent;color:var(--ns-frame-muted);font-size:13px;font-weight:500;text-align:left;white-space:nowrap;cursor:pointer}.nav-item i{flex:none;font-size:15px}.nav-item:hover{background:var(--ns-frame-raised);color:var(--ns-frame-ink)}.nav-item.is-active{background:var(--ns-frame-raised);box-shadow:inset 2px 0 0 var(--ns-frame-glow);color:var(--ns-frame-ink)}.nav-item.is-active i{color:var(--ns-frame-glow)}.nav-caret{margin-left:auto;font-size:11px!important;opacity:.7}.nav-item.is-inert{opacity:.45;cursor:default}.nav-item.is-inert:hover{background:transparent;color:var(--ns-frame-muted)}.nav-label{overflow:hidden;text-overflow:ellipsis}.sidebar.is-collapsed .nav-label{display:none}.sidebar.is-collapsed .nav-item{justify-content:center;padding-inline:0}.content{flex:1;min-width:0;padding:28px 28px 64px;display:flex;flex-direction:column;gap:14px}.page-head{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:46px;padding:7px 16px;border:1px solid var(--ac-border);border-radius:12px;background:var(--ac-surface);box-shadow:var(--ns-shadow-1)}.page-head-main{display:flex;min-width:0;flex:1;align-items:center;gap:14px}.page-head-actions{display:flex;flex:none;align-items:center;gap:8px}.page-head h1{margin:0;font-size:19px;font-weight:600}.measure{display:flex;width:100%;max-width:var(--ac-content-max, 1440px);flex-direction:column;gap:14px;margin-inline:0}h1{margin:0;font-size:26px;line-height:1.2;font-weight:500;letter-spacing:-.02em}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--ns-frame-line)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;color:var(--ns-frame-muted);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;color:var(--ns-frame-muted);font-size:.78rem}.access-state.is-error{color:var(--p-red-400, #f87171)}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}@media(max-width:720px){.sidebar{max-width:none;position:fixed;top:var(--ns-topbar-h, 56px);bottom:0;left:0;z-index:30;width:268px;max-width:82vw;overflow-y:auto;box-shadow:0 18px 44px #00000073;transform:translate(-100%);transition:transform .18s ease}.sidebar.is-collapsed{width:268px;max-width:82vw;transform:translate(-100%)}.sidebar:not(.is-collapsed){transform:translate(0)}.sidebar.is-collapsed .nav-label{display:inline}.sidebar.is-collapsed .role-text{display:flex}.sidebar.is-collapsed .nav-item,.sidebar.is-collapsed .role-badge{justify-content:flex-start;padding-inline:12px}.brand-secondary{display:none}.topbar{height:auto;min-height:56px;flex-wrap:wrap;padding-bottom:8px;row-gap:8px}.context{order:3;width:100%;margin-left:0}.context-select{min-width:0;max-width:none;flex:1 1 0}.content{padding:20px 16px 48px}h1{font-size:22px}.page-head{align-items:stretch;flex-direction:column;gap:10px;padding:10px 14px}.page-head-actions{justify-content:flex-end}}.who{display:flex;align-items:center;gap:12px;padding:15px 15px 13px;border-bottom:1px solid var(--ac-border);background:var(--ns-header-bg)}.who-photo{width:42px;height:42px;flex:none;border-radius:999px;object-fit:cover}.who-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:15px;font-weight:600}.who-text{display:flex;min-width:0;flex-direction:column}.who-name{overflow:hidden;font-size:14px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.who-mail{overflow:hidden;color:var(--ac-text-muted);font-size:11.5px;text-overflow:ellipsis;white-space:nowrap}.who-role{display:flex;align-items:center;gap:7px;margin:0;overflow:hidden;padding:9px 15px;border-bottom:1px solid var(--ac-border);color:var(--ac-text-muted);font-size:11.5px;white-space:nowrap}.who-role b{flex:none;color:var(--ac-text);font-size:12px;font-weight:600}.who-sep{flex:none}.who-context{overflow:hidden;text-overflow:ellipsis}.who-dot{width:5px;height:5px;flex:none;border-radius:50%;background:var(--ac-accent)}.company-option{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}.company-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.launcher-notice{display:flex;gap:10px;margin:12px 12px 0;padding:12px;border:1px solid color-mix(in srgb,var(--ac-accent) 40%,transparent);border-radius:8px;background:color-mix(in srgb,var(--ac-accent) 10%,transparent)}.launcher-notice i{margin-top:2px;color:var(--ac-accent);font-size:15px}.launcher-notice-title{margin:0 0 4px;font-size:13px;font-weight:600}.launcher-notice-body{margin:0;color:var(--ac-text-muted);font-size:12px;line-height:1.45}.confirm{display:flex;flex-direction:column;align-items:center;gap:6px;padding:26px 24px 20px;border:1px solid var(--ac-border);border-radius:14px;background:var(--ac-surface);text-align:center}.confirm h2{margin:8px 0 0;font-size:17px;font-weight:600}.confirm p{margin:0;max-width:34ch;color:var(--ac-text-muted);font-size:13px;line-height:1.5}.confirm-actions{display:flex;justify-content:center;gap:8px;margin-top:18px}.toast-text{flex:1;margin-left:12px}.toast-summary{margin:0;font-size:13px;font-weight:600}.toast-detail{margin:2px 0 0;color:var(--ac-text-muted);font-size:12px}.context{display:flex;min-width:0;align-items:center;gap:8px;margin-left:14px}.context-select{min-width:150px;max-width:210px}.role-badge{display:flex;align-items:center;gap:12px;margin-bottom:10px;padding:9px 12px;border-left:2px solid var(--ns-frame-glow);border-radius:0 7px 7px 0;background:var(--ns-frame-raised);box-shadow:-2px 0 12px -3px color-mix(in srgb,var(--ns-frame-glow) 65%,transparent)}.role-badge>i{flex:none;color:var(--ns-frame-glow);font-size:14px}.role-text{display:flex;min-width:0;flex-direction:column;gap:1px}.role-name{overflow:hidden;color:var(--ns-frame-ink);font-size:13px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.role-caption{color:var(--ns-frame-muted);font-size:10px;letter-spacing:.06em;text-transform:uppercase}.sidebar.is-collapsed .role-badge{justify-content:center;padding-inline:0}.sidebar-scrim{position:fixed;top:var(--ns-topbar-h, 56px);right:0;bottom:0;left:0;z-index:29;padding:0;border:0;background:#00000073;cursor:pointer}.nav-skeleton{display:flex;flex-direction:column;gap:3px}.nav-skeleton-item{display:flex;align-items:center;gap:12px;padding:11px 12px}.sidebar.is-collapsed .nav-skeleton-item .p-skeleton:last-child{display:none}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i1$3.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "ngmodule", type: BreadcrumbModule }, { kind: "component", type: i2$3.Breadcrumb, selector: "p-breadcrumb", inputs: ["model", "style", "styleClass", "home", "homeAriaLabel"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i1$2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ConfirmDialogModule }, { kind: "component", type: i4$1.ConfirmDialog, selector: "p-confirmDialog, p-confirmdialog, p-confirm-dialog", inputs: ["header", "icon", "message", "style", "styleClass", "maskStyleClass", "acceptIcon", "acceptLabel", "closeAriaLabel", "acceptAriaLabel", "acceptVisible", "rejectIcon", "rejectLabel", "rejectAriaLabel", "rejectVisible", "acceptButtonStyleClass", "rejectButtonStyleClass", "closeOnEscape", "dismissableMask", "blockScroll", "rtl", "closable", "appendTo", "key", "autoZIndex", "baseZIndex", "transitionOptions", "focusTrap", "defaultFocus", "breakpoints", "modal", "visible", "position", "draggable"], outputs: ["onHide"] }, { kind: "ngmodule", type: ToastModule }, { kind: "component", type: i5$2.Toast, selector: "p-toast", inputs: ["key", "autoZIndex", "baseZIndex", "life", "styleClass", "position", "preventOpenDuplicates", "preventDuplicates", "showTransformOptions", "hideTransformOptions", "showTransitionOptions", "hideTransitionOptions", "motionOptions", "breakpoints"], outputs: ["onClose"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i6$1.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i3.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i2.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$1.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i4.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NucleusAppearance, selector: "nucleus-appearance" }, { kind: "component", type: NucleusLordicon, selector: "nucleus-lordicon", inputs: ["src", "trigger", "state", "size", "tone"] }, { kind: "component", type: NucleusMenu, selector: "nucleus-menu", inputs: ["sections", "heading", "badge", "searchable", "searchPlaceholder", "emptyText", "keyboardHint", "width"], outputs: ["select", "dismiss"] }, { kind: "component", type: NucleusMark, selector: "nucleus-mark", inputs: ["accent", "size"] }, { kind: "component", type: NucleusStarfield, selector: "nucleus-starfield" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3728
3771
  }
3729
3772
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShell, decorators: [{
3730
3773
  type: Component,
3731
3774
  args: [{ selector: 'nucleus-shell', imports: [
3775
+ NgTemplateOutlet,
3732
3776
  AvatarModule,
3733
3777
  BreadcrumbModule,
3734
3778
  ButtonModule,
@@ -3746,8 +3790,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
3746
3790
  NucleusMenu,
3747
3791
  NucleusMark,
3748
3792
  NucleusStarfield,
3749
- ], host: { '[style.--ns-topbar-h]': 'topbarHeight()' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar ns-frame\">\n <div class=\"side\">\n <p-button\n icon=\"pi pi-bars\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.navToggle\"\n [pTooltip]=\"t.navToggle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleSidebar()\"\n />\n\n <div class=\"brand\">\n <nucleus-mark [size]=\"20\" />\n <div class=\"brand-name\">\n <span class=\"brand-primary\">NUCLEUS</span>\n <span class=\"brand-secondary\">{{ brand() }}</span>\n </div>\n </div>\n\n </div>\n\n @if (hasSiteContext()) {\n <div class=\"context ns-context\">\n <p-select\n styleClass=\"context-select\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [disabled]=\"switching()\"\n [filter]=\"companyChoices().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.companyLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <ng-template #item let-option>\n <span class=\"company-option\">\n <span class=\"company-name\">{{ option.name }}</span>\n <p-tag [value]=\"option.tag\" severity=\"secondary\" />\n </span>\n </ng-template>\n </p-select>\n\n <p-select\n styleClass=\"context-select\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"onSiteChange($event)\"\n [placeholder]=\"sitePlaceholder()\"\n [disabled]=\"switching() || sitesLoading() || !siteOptions().length\"\n [filter]=\"siteOptions().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.siteLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </div>\n }\n\n <div class=\"side is-end\">\n <div class=\"lang-switch\" role=\"group\" aria-label=\"Idioma / Language\">\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'es'\"\n [attr.aria-pressed]=\"lang() === 'es'\"\n (click)=\"setLang('es')\"\n >\n ES\n </button>\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'en'\"\n [attr.aria-pressed]=\"lang() === 'en'\"\n (click)=\"setLang('en')\"\n >\n EN\n </button>\n </div>\n\n <p-button\n icon=\"pi pi-th-large\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.appsLauncher\"\n [pTooltip]=\"t.appsLauncher\"\n tooltipPosition=\"bottom\"\n (onClick)=\"launcher.toggle($event)\"\n />\n\n <p-popover #launcher styleClass=\"ns-flush-popover\">\n @if (!hasCompany()) {\n <div class=\"launcher-notice\">\n <i class=\"pi pi-building\" aria-hidden=\"true\"></i>\n <div>\n <p class=\"launcher-notice-title\">{{ t.noCompanyTitle }}</p>\n <p class=\"launcher-notice-body\">{{ t.noCompanyHint }}</p>\n </div>\n </div>\n }\n\n <nucleus-menu\n [heading]=\"t.appsSwitch\"\n [badge]=\"launcherCount()\"\n [sections]=\"launcherSections()\"\n [searchable]=\"launcherRich()\"\n [searchPlaceholder]=\"t.appsSearch\"\n [emptyText]=\"launcherRich() ? t.appsNoMatch : t.appsEmpty\"\n [keyboardHint]=\"t.keyboardHint\"\n [width]=\"336\"\n (select)=\"onLauncherSelect($event); launcher.hide()\"\n (dismiss)=\"launcher.hide()\"\n />\n </p-popover>\n\n <p-button\n icon=\"pi pi-cog\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.settingsTitle\"\n [pTooltip]=\"t.settingsTitle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"settingsOpen.set(true)\"\n />\n\n @if (me) {\n <p-button\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n styleClass=\"avatar-trigger\"\n [ariaLabel]=\"me.name\"\n [pTooltip]=\"me.name\"\n tooltipPosition=\"bottom\"\n (onClick)=\"account.toggle($event)\"\n >\n <p-avatar\n [image]=\"me.photoUrl ?? undefined\"\n [label]=\"me.photoUrl ? undefined : initials()\"\n shape=\"circle\"\n size=\"normal\"\n />\n </p-button>\n }\n </div>\n</header>\n\n<p-popover #account styleClass=\"ns-flush-popover\">\n @if (me) {\n <div class=\"who\">\n @if (me.photoUrl) {\n <img class=\"who-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"42\" height=\"42\" />\n } @else {\n <span class=\"who-photo who-initials\">{{ initials() }}</span>\n }\n <span class=\"who-text\">\n <span class=\"who-name\">{{ me.name }}</span>\n <span class=\"who-mail\">{{ me.email }}</span>\n </span>\n </div>\n\n <p class=\"who-role\">\n <span class=\"who-dot\" aria-hidden=\"true\"></span>\n @if (roleName(); as role) {\n <b>{{ role }}</b>\n <span class=\"who-sep\">\u00B7</span>\n }\n <span class=\"who-context\">{{ accountContext() }}</span>\n </p>\n\n <nucleus-menu\n [sections]=\"accountSections()\"\n [width]=\"304\"\n (select)=\"onAccountSelect($event); account.hide()\"\n (dismiss)=\"account.hide()\"\n />\n }\n</p-popover>\n\n<div class=\"body\">\n @if (narrow() && sidebarOpen()) {\n <button\n type=\"button\"\n class=\"sidebar-scrim\"\n [attr.aria-label]=\"t.navToggle\"\n (click)=\"toggleSidebar()\"\n ></button>\n }\n\n <aside class=\"sidebar ns-frame\" [class.is-collapsed]=\"!sidebarOpen()\">\n @if (roleName(); as role) {\n <div class=\"role-badge\" [pTooltip]=\"sidebarOpen() ? '' : role\" tooltipPosition=\"right\">\n <i class=\"pi pi-key\" aria-hidden=\"true\"></i>\n <span class=\"nav-label role-text\">\n <span class=\"role-name\">{{ role }}</span>\n <span class=\"role-caption\">{{ t.roleCaption }}</span>\n </span>\n </div>\n }\n\n @if (navItems().length) {\n <nav>\n @for (item of navItems(); track item.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"activeNavId() === item.id\"\n [attr.aria-current]=\"activeNavId() === item.id ? 'page' : null\"\n [pTooltip]=\"sidebarOpen() ? '' : item.label\"\n tooltipPosition=\"right\"\n (click)=\"selectNav(item.id)\"\n >\n <i [class]=\"item.icon\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ item.label }}</span>\n </button>\n }\n </nav>\n }\n\n <nav class=\"access-menu\">\n @if (menuLoading()) {\n <div class=\"nav-skeleton\" role=\"status\" [attr.aria-label]=\"t.accessLoading\">\n @for (width of menuPlaceholders; track $index) {\n <div class=\"nav-skeleton-item\">\n <p-skeleton shape=\"circle\" size=\"15px\" />\n <p-skeleton height=\"9px\" [width]=\"width\" />\n </div>\n }\n </div>\n } @else if (menuError()) {\n <p class=\"access-state is-error\">{{ menuError() }}</p>\n } @else if (!menu().length) {\n <p class=\"access-state\">{{ t.accessEmpty }}</p>\n } @else {\n @for (node of menu(); track node.id) {\n @let group = isGroup(node);\n @let open = isExpanded(node);\n\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"!group && isSelected(node)\"\n [class.is-inert]=\"!group && !node.route\"\n [attr.aria-expanded]=\"group ? open : null\"\n [disabled]=\"!group && !node.route\"\n [pTooltip]=\"sidebarOpen() ? '' : node.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(node)\"\n >\n <i [class]=\"node.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ node.label }}</span>\n @if (group && sidebarOpen()) {\n <i\n class=\"pi nav-caret\"\n [class.pi-chevron-down]=\"open\"\n [class.pi-chevron-right]=\"!open\"\n aria-hidden=\"true\"\n ></i>\n }\n </button>\n\n @if (open) {\n @for (child of node.children; track child.id) {\n <button\n type=\"button\"\n class=\"nav-item is-child\"\n [class.is-active]=\"isSelected(child)\"\n [pTooltip]=\"sidebarOpen() ? '' : child.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(child)\"\n >\n <i [class]=\"child.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ child.label }}</span>\n </button>\n }\n }\n }\n }\n </nav>\n </aside>\n\n <main class=\"content\">\n <div class=\"measure\">\n <ng-content select=\"[nucleusTop]\" />\n\n @if (crumbs().length) {\n <p-breadcrumb [model]=\"crumbs()\" [home]=\"home()\" styleClass=\"ns-crumbs\" />\n }\n\n @if (title()) {\n <h1>{{ title() }}</h1>\n }\n @if (subtitle()) {\n <p class=\"lead\">{{ subtitle() }}</p>\n }\n\n <ng-content />\n </div>\n </main>\n</div>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [modal]=\"false\"\n [blockScroll]=\"false\"\n styleClass=\"ns-settings-drawer\"\n [style]=\"{ width: '380px' }\"\n>\n <nucleus-appearance />\n</p-drawer>\n\n<p-toast position=\"bottom-right\" styleClass=\"ns-toast\">\n <ng-template #message let-message>\n <nucleus-lordicon\n [src]=\"message.severity === 'success' ? icons.check : icons.warning\"\n [tone]=\"message.severity === 'success' ? 'accent' : 'danger'\"\n [size]=\"34\"\n trigger=\"in\"\n />\n <div class=\"toast-text\">\n <p class=\"toast-summary\">{{ message.summary }}</p>\n @if (message.detail) {\n <p class=\"toast-detail\">{{ message.detail }}</p>\n }\n </div>\n </ng-template>\n</p-toast>\n\n<p-confirmdialog [style]=\"{ width: '420px' }\">\n <ng-template #headless let-message let-onAccept=\"onAccept\" let-onReject=\"onReject\">\n <div class=\"confirm\">\n <nucleus-lordicon\n [src]=\"message.icon\"\n [tone]=\"message.acceptButtonStyleClass ? 'danger' : 'accent'\"\n [size]=\"64\"\n trigger=\"loop\"\n />\n <h2>{{ message.header }}</h2>\n <p>{{ message.message }}</p>\n <div class=\"confirm-actions\">\n <p-button\n [label]=\"message.rejectLabel\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onReject()\"\n />\n <p-button\n [label]=\"message.acceptLabel\"\n [severity]=\"message.acceptButtonStyleClass ? 'danger' : 'primary'\"\n (onClick)=\"onAccept()\"\n />\n </div>\n </div>\n </ng-template>\n</p-confirmdialog>\n", styles: [":host{display:flex;flex-direction:column;min-height:100dvh;background:var(--ac-bg);color:var(--ac-text)}.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:flex-start;gap:16px;height:56px;padding:0 12px 0 8px;border-bottom:1px solid var(--ns-frame-line)}.side{display:flex;min-width:0;align-items:center;gap:4px}.side.is-end{margin-left:auto}.brand{display:flex;align-items:center;gap:9px;margin-left:4px}.brand-name{display:flex;align-items:baseline;gap:7px}.brand-primary{font-size:13px;font-weight:700;letter-spacing:.15em}.brand-secondary{font-size:9px;font-weight:500;letter-spacing:.22em;color:var(--ns-frame-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ns-frame-line);border-radius:999px;padding:2px}.lang-pill{font-size:10px;font-weight:600;letter-spacing:.08em;padding:4px 10px;border:none;border-radius:999px;background:transparent;color:var(--ns-frame-muted);cursor:pointer}.lang-pill:hover{color:var(--ns-frame-ink)}.lang-pill.is-active{background:var(--ns-frame-glow);color:var(--ns-frame-on-glow)}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:max-content;min-width:224px;max-width:340px;padding:12px 10px;border-right:1px solid var(--ns-frame-line);transition:width .18s ease}.sidebar.is-collapsed{width:60px;min-width:0}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:var(--ns-nav-y, 9px) 12px;border:none;border-radius:6px;background:transparent;color:var(--ns-frame-muted);font-size:13px;font-weight:500;text-align:left;white-space:nowrap;cursor:pointer}.nav-item i{flex:none;font-size:15px}.nav-item:hover{background:var(--ns-frame-raised);color:var(--ns-frame-ink)}.nav-item.is-active{background:var(--ns-frame-raised);box-shadow:inset 2px 0 0 var(--ns-frame-glow);color:var(--ns-frame-ink)}.nav-item.is-active i{color:var(--ns-frame-glow)}.nav-caret{margin-left:auto;font-size:11px!important;opacity:.7}.nav-item.is-inert{opacity:.45;cursor:default}.nav-item.is-inert:hover{background:transparent;color:var(--ns-frame-muted)}.nav-label{overflow:hidden;text-overflow:ellipsis}.sidebar.is-collapsed .nav-label{display:none}.sidebar.is-collapsed .nav-item{justify-content:center;padding-inline:0}.content{flex:1;min-width:0;padding:28px 28px 64px;display:flex;flex-direction:column;gap:14px}.measure{display:flex;width:100%;max-width:var(--ac-content-max, 1440px);flex-direction:column;gap:14px;margin-inline:0}h1{margin:0;font-size:26px;line-height:1.2;font-weight:500;letter-spacing:-.02em}.lead{margin:0;font-size:14px;line-height:1.6;font-weight:300;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--ns-frame-line)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;color:var(--ns-frame-muted);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;color:var(--ns-frame-muted);font-size:.78rem}.access-state.is-error{color:var(--p-red-400, #f87171)}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}@media(max-width:720px){.sidebar{max-width:none;position:fixed;top:var(--ns-topbar-h, 56px);bottom:0;left:0;z-index:30;width:268px;max-width:82vw;overflow-y:auto;box-shadow:0 18px 44px #00000073;transform:translate(-100%);transition:transform .18s ease}.sidebar.is-collapsed{width:268px;max-width:82vw;transform:translate(-100%)}.sidebar:not(.is-collapsed){transform:translate(0)}.sidebar.is-collapsed .nav-label{display:inline}.sidebar.is-collapsed .role-text{display:flex}.sidebar.is-collapsed .nav-item,.sidebar.is-collapsed .role-badge{justify-content:flex-start;padding-inline:12px}.brand-secondary{display:none}.topbar{height:auto;min-height:56px;flex-wrap:wrap;padding-bottom:8px;row-gap:8px}.context{order:3;width:100%;margin-left:0}.context-select{min-width:0;max-width:none;flex:1 1 0}.content{padding:20px 16px 48px}h1{font-size:22px}}.who{display:flex;align-items:center;gap:12px;padding:15px 15px 13px;border-bottom:1px solid var(--ac-border);background:var(--ns-header-bg)}.who-photo{width:42px;height:42px;flex:none;border-radius:999px;object-fit:cover}.who-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:15px;font-weight:600}.who-text{display:flex;min-width:0;flex-direction:column}.who-name{overflow:hidden;font-size:14px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.who-mail{overflow:hidden;color:var(--ac-text-muted);font-size:11.5px;text-overflow:ellipsis;white-space:nowrap}.who-role{display:flex;align-items:center;gap:7px;margin:0;overflow:hidden;padding:9px 15px;border-bottom:1px solid var(--ac-border);color:var(--ac-text-muted);font-size:11.5px;white-space:nowrap}.who-role b{flex:none;color:var(--ac-text);font-size:12px;font-weight:600}.who-sep{flex:none}.who-context{overflow:hidden;text-overflow:ellipsis}.who-dot{width:5px;height:5px;flex:none;border-radius:50%;background:var(--ac-accent)}.company-option{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}.company-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.launcher-notice{display:flex;gap:10px;margin:12px 12px 0;padding:12px;border:1px solid color-mix(in srgb,var(--ac-accent) 40%,transparent);border-radius:8px;background:color-mix(in srgb,var(--ac-accent) 10%,transparent)}.launcher-notice i{margin-top:2px;color:var(--ac-accent);font-size:15px}.launcher-notice-title{margin:0 0 4px;font-size:13px;font-weight:600}.launcher-notice-body{margin:0;color:var(--ac-text-muted);font-size:12px;line-height:1.45}.confirm{display:flex;flex-direction:column;align-items:center;gap:6px;padding:26px 24px 20px;border:1px solid var(--ac-border);border-radius:14px;background:var(--ac-surface);text-align:center}.confirm h2{margin:8px 0 0;font-size:17px;font-weight:600}.confirm p{margin:0;max-width:34ch;color:var(--ac-text-muted);font-size:13px;line-height:1.5}.confirm-actions{display:flex;justify-content:center;gap:8px;margin-top:18px}.toast-text{flex:1;margin-left:12px}.toast-summary{margin:0;font-size:13px;font-weight:600}.toast-detail{margin:2px 0 0;color:var(--ac-text-muted);font-size:12px}.context{display:flex;min-width:0;align-items:center;gap:8px;margin-left:14px}.context-select{min-width:150px;max-width:210px}.role-badge{display:flex;align-items:center;gap:12px;margin-bottom:10px;padding:9px 12px;border-left:2px solid var(--ns-frame-glow);border-radius:0 7px 7px 0;background:var(--ns-frame-raised);box-shadow:-2px 0 12px -3px color-mix(in srgb,var(--ns-frame-glow) 65%,transparent)}.role-badge>i{flex:none;color:var(--ns-frame-glow);font-size:14px}.role-text{display:flex;min-width:0;flex-direction:column;gap:1px}.role-name{overflow:hidden;color:var(--ns-frame-ink);font-size:13px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.role-caption{color:var(--ns-frame-muted);font-size:10px;letter-spacing:.06em;text-transform:uppercase}.sidebar.is-collapsed .role-badge{justify-content:center;padding-inline:0}.sidebar-scrim{position:fixed;top:var(--ns-topbar-h, 56px);right:0;bottom:0;left:0;z-index:29;padding:0;border:0;background:#00000073;cursor:pointer}.nav-skeleton{display:flex;flex-direction:column;gap:3px}.nav-skeleton-item{display:flex;align-items:center;gap:12px;padding:11px 12px}.sidebar.is-collapsed .nav-skeleton-item .p-skeleton:last-child{display:none}\n"] }]
3750
- }], ctorParameters: () => [], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], lead: [{ type: i0.Input, args: [{ isSignal: true, alias: "lead", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], activeNavId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeNavId", required: false }] }], selectedMenuId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedMenuId", required: false }] }], navSelect: [{ type: i0.Output, args: ["navSelect"] }], menuSelect: [{ type: i0.Output, args: ["menuSelect"] }] } });
3793
+ ], host: { '[style.--ns-topbar-h]': 'topbarHeight()' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar ns-frame\">\n <div class=\"side\">\n <p-button\n icon=\"pi pi-bars\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.navToggle\"\n [pTooltip]=\"t.navToggle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"toggleSidebar()\"\n />\n\n <div class=\"brand\">\n <nucleus-mark [size]=\"20\" />\n <div class=\"brand-name\">\n <span class=\"brand-primary\">NUCLEUS</span>\n <span class=\"brand-secondary\">{{ brand() }}</span>\n </div>\n </div>\n\n </div>\n\n @if (hasSiteContext()) {\n <div class=\"context ns-context\">\n <p-select\n styleClass=\"context-select\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [disabled]=\"switching()\"\n [filter]=\"companyChoices().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.companyLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n >\n <ng-template #item let-option>\n <span class=\"company-option\">\n <span class=\"company-name\">{{ option.name }}</span>\n <p-tag [value]=\"option.tag\" severity=\"secondary\" />\n </span>\n </ng-template>\n </p-select>\n\n <p-select\n styleClass=\"context-select\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"onSiteChange($event)\"\n [placeholder]=\"sitePlaceholder()\"\n [disabled]=\"switching() || sitesLoading() || !siteOptions().length\"\n [filter]=\"siteOptions().length > 8\"\n filterBy=\"name\"\n [filterPlaceholder]=\"words().search\"\n [autofocusFilter]=\"true\"\n [resetFilterOnHide]=\"true\"\n [ariaLabel]=\"t.siteLabel\"\n [ngModelOptions]=\"{ standalone: true }\"\n />\n </div>\n }\n\n <div class=\"side is-end\">\n <div class=\"lang-switch\" role=\"group\" aria-label=\"Idioma / Language\">\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'es'\"\n [attr.aria-pressed]=\"lang() === 'es'\"\n (click)=\"setLang('es')\"\n >\n ES\n </button>\n <button\n type=\"button\"\n class=\"lang-pill\"\n [class.is-active]=\"lang() === 'en'\"\n [attr.aria-pressed]=\"lang() === 'en'\"\n (click)=\"setLang('en')\"\n >\n EN\n </button>\n </div>\n\n <p-button\n icon=\"pi pi-th-large\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.appsLauncher\"\n [pTooltip]=\"t.appsLauncher\"\n tooltipPosition=\"bottom\"\n (onClick)=\"launcher.toggle($event)\"\n />\n\n <p-popover #launcher styleClass=\"ns-flush-popover\">\n @if (!hasCompany()) {\n <div class=\"launcher-notice\">\n <i class=\"pi pi-building\" aria-hidden=\"true\"></i>\n <div>\n <p class=\"launcher-notice-title\">{{ t.noCompanyTitle }}</p>\n <p class=\"launcher-notice-body\">{{ t.noCompanyHint }}</p>\n </div>\n </div>\n }\n\n <nucleus-menu\n [heading]=\"t.appsSwitch\"\n [badge]=\"launcherCount()\"\n [sections]=\"launcherSections()\"\n [searchable]=\"launcherRich()\"\n [searchPlaceholder]=\"t.appsSearch\"\n [emptyText]=\"launcherRich() ? t.appsNoMatch : t.appsEmpty\"\n [keyboardHint]=\"t.keyboardHint\"\n [width]=\"336\"\n (select)=\"onLauncherSelect($event); launcher.hide()\"\n (dismiss)=\"launcher.hide()\"\n />\n </p-popover>\n\n <p-button\n icon=\"pi pi-cog\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n [ariaLabel]=\"t.settingsTitle\"\n [pTooltip]=\"t.settingsTitle\"\n tooltipPosition=\"bottom\"\n (onClick)=\"settingsOpen.set(true)\"\n />\n\n @if (me) {\n <p-button\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [rounded]=\"true\"\n styleClass=\"avatar-trigger\"\n [ariaLabel]=\"me.name\"\n [pTooltip]=\"me.name\"\n tooltipPosition=\"bottom\"\n (onClick)=\"account.toggle($event)\"\n >\n <p-avatar\n [image]=\"me.photoUrl ?? undefined\"\n [label]=\"me.photoUrl ? undefined : initials()\"\n shape=\"circle\"\n size=\"normal\"\n />\n </p-button>\n }\n </div>\n</header>\n\n<p-popover #account styleClass=\"ns-flush-popover\">\n @if (me) {\n <div class=\"who\">\n @if (me.photoUrl) {\n <img class=\"who-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"42\" height=\"42\" />\n } @else {\n <span class=\"who-photo who-initials\">{{ initials() }}</span>\n }\n <span class=\"who-text\">\n <span class=\"who-name\">{{ me.name }}</span>\n <span class=\"who-mail\">{{ me.email }}</span>\n </span>\n </div>\n\n <p class=\"who-role\">\n <span class=\"who-dot\" aria-hidden=\"true\"></span>\n @if (roleName(); as role) {\n <b>{{ role }}</b>\n <span class=\"who-sep\">\u00B7</span>\n }\n <span class=\"who-context\">{{ accountContext() }}</span>\n </p>\n\n <nucleus-menu\n [sections]=\"accountSections()\"\n [width]=\"304\"\n (select)=\"onAccountSelect($event); account.hide()\"\n (dismiss)=\"account.hide()\"\n />\n }\n</p-popover>\n\n<div class=\"body\">\n @if (narrow() && sidebarOpen()) {\n <button\n type=\"button\"\n class=\"sidebar-scrim\"\n [attr.aria-label]=\"t.navToggle\"\n (click)=\"toggleSidebar()\"\n ></button>\n }\n\n <aside class=\"sidebar ns-frame\" [class.is-collapsed]=\"!sidebarOpen()\">\n @if (roleName(); as role) {\n <div class=\"role-badge\" [pTooltip]=\"sidebarOpen() ? '' : role\" tooltipPosition=\"right\">\n <i class=\"pi pi-key\" aria-hidden=\"true\"></i>\n <span class=\"nav-label role-text\">\n <span class=\"role-name\">{{ role }}</span>\n <span class=\"role-caption\">{{ t.roleCaption }}</span>\n </span>\n </div>\n }\n\n @if (navItems().length) {\n <nav>\n @for (item of navItems(); track item.id) {\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"activeNavId() === item.id\"\n [attr.aria-current]=\"activeNavId() === item.id ? 'page' : null\"\n [pTooltip]=\"sidebarOpen() ? '' : item.label\"\n tooltipPosition=\"right\"\n (click)=\"selectNav(item.id)\"\n >\n <i [class]=\"item.icon\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ item.label }}</span>\n </button>\n }\n </nav>\n }\n\n <nav class=\"access-menu\">\n @if (menuLoading()) {\n <div class=\"nav-skeleton\" role=\"status\" [attr.aria-label]=\"t.accessLoading\">\n @for (width of menuPlaceholders; track $index) {\n <div class=\"nav-skeleton-item\">\n <p-skeleton shape=\"circle\" size=\"15px\" />\n <p-skeleton height=\"9px\" [width]=\"width\" />\n </div>\n }\n </div>\n } @else if (menuError()) {\n <p class=\"access-state is-error\">{{ menuError() }}</p>\n } @else if (!menu().length) {\n <p class=\"access-state\">{{ t.accessEmpty }}</p>\n } @else {\n @for (node of menu(); track node.id) {\n @let group = isGroup(node);\n @let open = isExpanded(node);\n\n <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"!group && isSelected(node)\"\n [class.is-inert]=\"!group && !node.route\"\n [attr.aria-expanded]=\"group ? open : null\"\n [disabled]=\"!group && !node.route\"\n [pTooltip]=\"sidebarOpen() ? '' : node.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(node)\"\n >\n <i [class]=\"node.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ node.label }}</span>\n @if (group && sidebarOpen()) {\n <i\n class=\"pi nav-caret\"\n [class.pi-chevron-down]=\"open\"\n [class.pi-chevron-right]=\"!open\"\n aria-hidden=\"true\"\n ></i>\n }\n </button>\n\n @if (open) {\n @for (child of node.children; track child.id) {\n <button\n type=\"button\"\n class=\"nav-item is-child\"\n [class.is-active]=\"isSelected(child)\"\n [pTooltip]=\"sidebarOpen() ? '' : child.label\"\n tooltipPosition=\"right\"\n (click)=\"selectMenu(child)\"\n >\n <i [class]=\"child.icon ?? 'pi pi-circle'\" aria-hidden=\"true\"></i>\n <span class=\"nav-label\">{{ child.label }}</span>\n </button>\n }\n }\n }\n }\n </nav>\n </aside>\n\n <main class=\"content\">\n <div class=\"measure\">\n <ng-content select=\"[nucleusTop]\" />\n\n @if (crumbs().length || title() || pageActions()) {\n <header class=\"page-head\">\n <div class=\"page-head-main\">\n @if (crumbs().length) {\n <p-breadcrumb [model]=\"crumbs()\" [home]=\"home()\" styleClass=\"ns-crumbs\" />\n }\n\n @if (title()) {\n <h1>{{ title() }}</h1>\n }\n </div>\n\n @if (pageActions(); as actions) {\n <div class=\"page-head-actions\">\n <ng-container [ngTemplateOutlet]=\"actions\" />\n </div>\n }\n </header>\n }\n\n <ng-content />\n </div>\n </main>\n</div>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [modal]=\"false\"\n [blockScroll]=\"false\"\n styleClass=\"ns-settings-drawer\"\n [style]=\"{ width: '380px' }\"\n>\n <nucleus-appearance />\n</p-drawer>\n\n<p-toast position=\"bottom-right\" styleClass=\"ns-toast\">\n <ng-template #message let-message>\n <nucleus-lordicon\n [src]=\"message.severity === 'success' ? icons.check : icons.warning\"\n [tone]=\"message.severity === 'success' ? 'accent' : 'danger'\"\n [size]=\"34\"\n trigger=\"in\"\n />\n <div class=\"toast-text\">\n <p class=\"toast-summary\">{{ message.summary }}</p>\n @if (message.detail) {\n <p class=\"toast-detail\">{{ message.detail }}</p>\n }\n </div>\n </ng-template>\n</p-toast>\n\n<p-confirmdialog [style]=\"{ width: '420px' }\">\n <ng-template #headless let-message let-onAccept=\"onAccept\" let-onReject=\"onReject\">\n <div class=\"confirm\">\n <nucleus-lordicon\n [src]=\"message.icon\"\n [tone]=\"message.acceptButtonStyleClass ? 'danger' : 'accent'\"\n [size]=\"64\"\n trigger=\"loop\"\n />\n <h2>{{ message.header }}</h2>\n <p>{{ message.message }}</p>\n <div class=\"confirm-actions\">\n <p-button\n [label]=\"message.rejectLabel\"\n severity=\"secondary\"\n [text]=\"true\"\n (onClick)=\"onReject()\"\n />\n <p-button\n [label]=\"message.acceptLabel\"\n [severity]=\"message.acceptButtonStyleClass ? 'danger' : 'primary'\"\n (onClick)=\"onAccept()\"\n />\n </div>\n </div>\n </ng-template>\n</p-confirmdialog>\n", styles: [":host{display:flex;flex-direction:column;min-height:100dvh;background:var(--ac-bg);color:var(--ac-text)}.topbar{position:sticky;top:0;z-index:20;display:flex;align-items:center;justify-content:flex-start;gap:16px;height:56px;padding:0 12px 0 8px;border-bottom:1px solid var(--ns-frame-line)}.side{display:flex;min-width:0;align-items:center;gap:4px}.side.is-end{margin-left:auto}.brand{display:flex;align-items:center;gap:9px;margin-left:4px}.brand-name{display:flex;align-items:baseline;gap:7px}.brand-primary{font-size:13px;font-weight:700;letter-spacing:.15em}.brand-secondary{font-size:9px;font-weight:500;letter-spacing:.22em;color:var(--ns-frame-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ns-frame-line);border-radius:999px;padding:2px}.lang-pill{font-size:10px;font-weight:600;letter-spacing:.08em;padding:4px 10px;border:none;border-radius:999px;background:transparent;color:var(--ns-frame-muted);cursor:pointer}.lang-pill:hover{color:var(--ns-frame-ink)}.lang-pill.is-active{background:var(--ns-frame-glow);color:var(--ns-frame-on-glow)}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:max-content;min-width:224px;max-width:340px;padding:12px 10px;border-right:1px solid var(--ns-frame-line);transition:width .18s ease}.sidebar.is-collapsed{width:60px;min-width:0}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:var(--ns-nav-y, 9px) 12px;border:none;border-radius:6px;background:transparent;color:var(--ns-frame-muted);font-size:13px;font-weight:500;text-align:left;white-space:nowrap;cursor:pointer}.nav-item i{flex:none;font-size:15px}.nav-item:hover{background:var(--ns-frame-raised);color:var(--ns-frame-ink)}.nav-item.is-active{background:var(--ns-frame-raised);box-shadow:inset 2px 0 0 var(--ns-frame-glow);color:var(--ns-frame-ink)}.nav-item.is-active i{color:var(--ns-frame-glow)}.nav-caret{margin-left:auto;font-size:11px!important;opacity:.7}.nav-item.is-inert{opacity:.45;cursor:default}.nav-item.is-inert:hover{background:transparent;color:var(--ns-frame-muted)}.nav-label{overflow:hidden;text-overflow:ellipsis}.sidebar.is-collapsed .nav-label{display:none}.sidebar.is-collapsed .nav-item{justify-content:center;padding-inline:0}.content{flex:1;min-width:0;padding:28px 28px 64px;display:flex;flex-direction:column;gap:14px}.page-head{display:flex;align-items:center;justify-content:space-between;gap:16px;min-height:46px;padding:7px 16px;border:1px solid var(--ac-border);border-radius:12px;background:var(--ac-surface);box-shadow:var(--ns-shadow-1)}.page-head-main{display:flex;min-width:0;flex:1;align-items:center;gap:14px}.page-head-actions{display:flex;flex:none;align-items:center;gap:8px}.page-head h1{margin:0;font-size:19px;font-weight:600}.measure{display:flex;width:100%;max-width:var(--ac-content-max, 1440px);flex-direction:column;gap:14px;margin-inline:0}h1{margin:0;font-size:26px;line-height:1.2;font-weight:500;letter-spacing:-.02em}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid var(--ns-frame-line)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;color:var(--ns-frame-muted);font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;color:var(--ns-frame-muted);font-size:.78rem}.access-state.is-error{color:var(--p-red-400, #f87171)}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}@media(max-width:720px){.sidebar{max-width:none;position:fixed;top:var(--ns-topbar-h, 56px);bottom:0;left:0;z-index:30;width:268px;max-width:82vw;overflow-y:auto;box-shadow:0 18px 44px #00000073;transform:translate(-100%);transition:transform .18s ease}.sidebar.is-collapsed{width:268px;max-width:82vw;transform:translate(-100%)}.sidebar:not(.is-collapsed){transform:translate(0)}.sidebar.is-collapsed .nav-label{display:inline}.sidebar.is-collapsed .role-text{display:flex}.sidebar.is-collapsed .nav-item,.sidebar.is-collapsed .role-badge{justify-content:flex-start;padding-inline:12px}.brand-secondary{display:none}.topbar{height:auto;min-height:56px;flex-wrap:wrap;padding-bottom:8px;row-gap:8px}.context{order:3;width:100%;margin-left:0}.context-select{min-width:0;max-width:none;flex:1 1 0}.content{padding:20px 16px 48px}h1{font-size:22px}.page-head{align-items:stretch;flex-direction:column;gap:10px;padding:10px 14px}.page-head-actions{justify-content:flex-end}}.who{display:flex;align-items:center;gap:12px;padding:15px 15px 13px;border-bottom:1px solid var(--ac-border);background:var(--ns-header-bg)}.who-photo{width:42px;height:42px;flex:none;border-radius:999px;object-fit:cover}.who-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:15px;font-weight:600}.who-text{display:flex;min-width:0;flex-direction:column}.who-name{overflow:hidden;font-size:14px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.who-mail{overflow:hidden;color:var(--ac-text-muted);font-size:11.5px;text-overflow:ellipsis;white-space:nowrap}.who-role{display:flex;align-items:center;gap:7px;margin:0;overflow:hidden;padding:9px 15px;border-bottom:1px solid var(--ac-border);color:var(--ac-text-muted);font-size:11.5px;white-space:nowrap}.who-role b{flex:none;color:var(--ac-text);font-size:12px;font-weight:600}.who-sep{flex:none}.who-context{overflow:hidden;text-overflow:ellipsis}.who-dot{width:5px;height:5px;flex:none;border-radius:50%;background:var(--ac-accent)}.company-option{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%}.company-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.launcher-notice{display:flex;gap:10px;margin:12px 12px 0;padding:12px;border:1px solid color-mix(in srgb,var(--ac-accent) 40%,transparent);border-radius:8px;background:color-mix(in srgb,var(--ac-accent) 10%,transparent)}.launcher-notice i{margin-top:2px;color:var(--ac-accent);font-size:15px}.launcher-notice-title{margin:0 0 4px;font-size:13px;font-weight:600}.launcher-notice-body{margin:0;color:var(--ac-text-muted);font-size:12px;line-height:1.45}.confirm{display:flex;flex-direction:column;align-items:center;gap:6px;padding:26px 24px 20px;border:1px solid var(--ac-border);border-radius:14px;background:var(--ac-surface);text-align:center}.confirm h2{margin:8px 0 0;font-size:17px;font-weight:600}.confirm p{margin:0;max-width:34ch;color:var(--ac-text-muted);font-size:13px;line-height:1.5}.confirm-actions{display:flex;justify-content:center;gap:8px;margin-top:18px}.toast-text{flex:1;margin-left:12px}.toast-summary{margin:0;font-size:13px;font-weight:600}.toast-detail{margin:2px 0 0;color:var(--ac-text-muted);font-size:12px}.context{display:flex;min-width:0;align-items:center;gap:8px;margin-left:14px}.context-select{min-width:150px;max-width:210px}.role-badge{display:flex;align-items:center;gap:12px;margin-bottom:10px;padding:9px 12px;border-left:2px solid var(--ns-frame-glow);border-radius:0 7px 7px 0;background:var(--ns-frame-raised);box-shadow:-2px 0 12px -3px color-mix(in srgb,var(--ns-frame-glow) 65%,transparent)}.role-badge>i{flex:none;color:var(--ns-frame-glow);font-size:14px}.role-text{display:flex;min-width:0;flex-direction:column;gap:1px}.role-name{overflow:hidden;color:var(--ns-frame-ink);font-size:13px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.role-caption{color:var(--ns-frame-muted);font-size:10px;letter-spacing:.06em;text-transform:uppercase}.sidebar.is-collapsed .role-badge{justify-content:center;padding-inline:0}.sidebar-scrim{position:fixed;top:var(--ns-topbar-h, 56px);right:0;bottom:0;left:0;z-index:29;padding:0;border:0;background:#00000073;cursor:pointer}.nav-skeleton{display:flex;flex-direction:column;gap:3px}.nav-skeleton-item{display:flex;align-items:center;gap:12px;padding:11px 12px}.sidebar.is-collapsed .nav-skeleton-item .p-skeleton:last-child{display:none}\n"] }]
3794
+ }], ctorParameters: () => [], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], activeNavId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeNavId", required: false }] }], selectedMenuId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedMenuId", required: false }] }], navSelect: [{ type: i0.Output, args: ["navSelect"] }], menuSelect: [{ type: i0.Output, args: ["menuSelect"] }] } });
3751
3795
  function initialsOf(source) {
3752
3796
  const parts = source.trim().split(/\s+/).filter(Boolean);
3753
3797
  if (!parts.length)
@@ -3761,5 +3805,5 @@ function initialsOf(source) {
3761
3805
  * Generated bundle index. Do not edit.
3762
3806
  */
3763
3807
 
3764
- export { ACCENTS, CANVASES, COMMON_COPY, CONTROL_CONTRAST, DEFAULT_APPEARANCE, DENSITIES, EMPTY_GEO_VALUE, FIELD_TARGETS, FRAMES, GEO_COPY, LEGACY_THEMES, NO_PERMISSIONS, NUCLEUS_ACCENTS, NUCLEUS_CONFIG, NUCLEUS_DARK_SELECTOR, NUCLEUS_FORCE_BEARER, NUCLEUS_ICONS, NUCLEUS_ICON_CATALOG, NUCLEUS_LANGS, NUCLEUS_LOGOUT_PARAM, NUCLEUS_SKIP_AUTH, NucleusAppearance, NucleusAuth, NucleusCache, NucleusCard, NucleusCell, NucleusColorPicker, NucleusEnterAdvance, NucleusFeedback, NucleusForm, NucleusGeo, NucleusGeoSelect, NucleusIconPicker, NucleusLanguage, NucleusList, NucleusLordicon, NucleusMark, NucleusMenu, NucleusMetric, NucleusMetrics, NucleusPage, NucleusPreset, NucleusRecentApps, NucleusSessionAuth, NucleusShell, NucleusShortcut, NucleusShortcuts, NucleusStarfield, NucleusTabs, SHELL_COPY, SessionService, TEXT_CONTRAST, THEME_COPY, ThemeService, UserSyncService, accentById, accentLabelOf, accentOn, ariaKeysOf, auditAppearance, canvasById, clock, comboOf, commonCopyOf, consumeNucleusLogout, contrastIssue, contrastRatio, day, densityById, endOfDayUtc, fieldsIn, findMenuByRoute, flagOf, foldText, frameById, geoCopyOf, hasChildren, hasModifier, iconClassOf, iconNameOf, integer, isAccentPreference, isApplePlatform, isBeforeToday, isCanvasPreference, isDarkCanvas, isDensityId, isFieldSearchable, isFrameId, isNucleusConfigured, isOnOrBeforeToday, isReachable, isToday, isTypingTarget, isoDay, levelLabel, loadLordicon, loadNucleusConfig, matchesRow, meetsContrast, menuGlyph, menuTile, minutesAsClock, mixHex, money, moveTo, normalizeKeys, nucleusAuthInterceptor, nucleusErrorMessage, nucleusLang, nucleusLogoutUrl, nucleusParseDate, ownsUrl, percent, permissionsOf, provideNucleus, provideNucleusFeedback, provideNucleusTheme, qty, readableOn, relativeLuminance, resolveAccent, resolveFieldSize, searchIcons, selectableRows, shiftDay, shortcutHint, signedMoney, stamp, startOfDayUtc, themeCopyOf, toNumber, toggleCollapsed, visibleTreeRows };
3808
+ export { ACCENTS, CANVASES, COMMON_COPY, CONTROL_CONTRAST, DEFAULT_APPEARANCE, DENSITIES, EMPTY_GEO_VALUE, FIELD_TARGETS, FRAMES, GEO_COPY, LEGACY_THEMES, NO_PERMISSIONS, NUCLEUS_ACCENTS, NUCLEUS_CONFIG, NUCLEUS_DARK_SELECTOR, NUCLEUS_FORCE_BEARER, NUCLEUS_ICONS, NUCLEUS_ICON_CATALOG, NUCLEUS_LANGS, NUCLEUS_LOGOUT_PARAM, NUCLEUS_SKIP_AUTH, NucleusAppearance, NucleusAuth, NucleusCache, NucleusCard, NucleusCell, NucleusColorPicker, NucleusEnterAdvance, NucleusFeedback, NucleusForm, NucleusGeo, NucleusGeoSelect, NucleusIconPicker, NucleusLanguage, NucleusList, NucleusLordicon, NucleusMark, NucleusMenu, NucleusMetric, NucleusMetrics, NucleusPage, NucleusPageActions, NucleusPreset, NucleusRecentApps, NucleusSessionAuth, NucleusShell, NucleusShortcut, NucleusShortcuts, NucleusStarfield, NucleusTabs, SHELL_COPY, SessionService, TEXT_CONTRAST, THEME_COPY, ThemeService, UserSyncService, accentById, accentLabelOf, accentOn, ariaKeysOf, auditAppearance, canvasById, clock, comboOf, commonCopyOf, consumeNucleusLogout, contrastIssue, contrastRatio, day, densityById, endOfDayUtc, fieldsIn, findMenuByRoute, flagOf, foldText, frameById, geoCopyOf, hasChildren, hasModifier, iconClassOf, iconNameOf, integer, isAccentPreference, isApplePlatform, isBeforeToday, isCanvasPreference, isDarkCanvas, isDensityId, isFieldSearchable, isFrameId, isNucleusConfigured, isOnOrBeforeToday, isReachable, isToday, isTypingTarget, isoDay, levelLabel, loadLordicon, loadNucleusConfig, matchesRow, meetsContrast, menuGlyph, menuTile, minutesAsClock, mixHex, money, moveTo, normalizeKeys, nucleusAuthInterceptor, nucleusErrorMessage, nucleusLang, nucleusLogoutUrl, nucleusParseDate, ownsUrl, percent, permissionsOf, provideNucleus, provideNucleusFeedback, provideNucleusTheme, qty, readableOn, relativeLuminance, resolveAccent, resolveFieldSize, searchIcons, selectableRows, shiftDay, shortcutHint, signedMoney, stamp, startOfDayUtc, themeCopyOf, toNumber, toggleCollapsed, visibleTreeRows };
3765
3809
  //# sourceMappingURL=nucleus-suite-pe-core.mjs.map