@nucleus-suite-pe/core 0.1.0 → 0.2.1

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.
@@ -5,7 +5,7 @@ import { firstValueFrom, catchError, throwError, from, switchMap } from 'rxjs';
5
5
  import { definePreset } from '@primeuix/themes';
6
6
  import Lara from '@primeuix/themes/lara';
7
7
  import { providePrimeNG } from 'primeng/config';
8
- import * as i7 from '@angular/forms';
8
+ import * as i9 from '@angular/forms';
9
9
  import { FormsModule } from '@angular/forms';
10
10
  import { Router } from '@angular/router';
11
11
  import * as i1 from 'primeng/avatar';
@@ -18,7 +18,11 @@ import * as i4 from 'primeng/listbox';
18
18
  import { ListboxModule } from 'primeng/listbox';
19
19
  import * as i5 from 'primeng/popover';
20
20
  import { PopoverModule } from 'primeng/popover';
21
- import * as i6 from 'primeng/tooltip';
21
+ import * as i6 from 'primeng/select';
22
+ import { SelectModule } from 'primeng/select';
23
+ import * as i7 from 'primeng/tag';
24
+ import { TagModule } from 'primeng/tag';
25
+ import * as i8 from 'primeng/tooltip';
22
26
  import { TooltipModule } from 'primeng/tooltip';
23
27
 
24
28
  const NUCLEUS_CONFIG = new InjectionToken('NUCLEUS_CONFIG');
@@ -146,6 +150,21 @@ const nucleusAuthInterceptor = (req, next) => {
146
150
  return from(auth.idToken()).pipe(switchMap((token) => next(token ? request.clone({ setHeaders: { Authorization: `Bearer ${token}` } }) : request)));
147
151
  };
148
152
 
153
+ const NUCLEUS_LOGOUT_PARAM = 'nucleus_logout';
154
+ function nucleusLogoutUrl(landingUrl) {
155
+ const url = new URL(landingUrl, window.location.origin);
156
+ url.searchParams.set(NUCLEUS_LOGOUT_PARAM, '1');
157
+ return url.toString();
158
+ }
159
+ function consumeNucleusLogout() {
160
+ const url = new URL(window.location.href);
161
+ if (url.searchParams.get(NUCLEUS_LOGOUT_PARAM) !== '1')
162
+ return false;
163
+ url.searchParams.delete(NUCLEUS_LOGOUT_PARAM);
164
+ window.history.replaceState({}, '', url.toString());
165
+ return true;
166
+ }
167
+
149
168
  const AUTH_SYNC_PATH = '/auth/sync';
150
169
  class UserSyncService {
151
170
  http = inject(HttpClient);
@@ -199,16 +218,21 @@ class SessionService {
199
218
  _access = signal(null, ...(ngDevMode ? [{ debugName: "_access" }] : /* istanbul ignore next */ []));
200
219
  _scope = signal(null, ...(ngDevMode ? [{ debugName: "_scope" }] : /* istanbul ignore next */ []));
201
220
  _apps = signal([], ...(ngDevMode ? [{ debugName: "_apps" }] : /* istanbul ignore next */ []));
221
+ _siteId = signal(null, ...(ngDevMode ? [{ debugName: "_siteId" }] : /* istanbul ignore next */ []));
202
222
  _loading = signal(false, ...(ngDevMode ? [{ debugName: "_loading" }] : /* istanbul ignore next */ []));
223
+ _switching = signal(false, ...(ngDevMode ? [{ debugName: "_switching" }] : /* istanbul ignore next */ []));
203
224
  _error = signal(null, ...(ngDevMode ? [{ debugName: "_error" }] : /* istanbul ignore next */ []));
204
225
  inFlight = false;
205
226
  loadedKey = null;
227
+ language = 'es';
206
228
  me = this._me.asReadonly();
207
229
  menu = this._menu.asReadonly();
208
230
  access = this._access.asReadonly();
209
231
  scope = this._scope.asReadonly();
210
232
  apps = this._apps.asReadonly();
233
+ siteId = this._siteId.asReadonly();
211
234
  loading = this._loading.asReadonly();
235
+ switching = this._switching.asReadonly();
212
236
  error = this._error.asReadonly();
213
237
  roleName = computed(() => this._access()?.roleName ?? null, ...(ngDevMode ? [{ debugName: "roleName" }] : /* istanbul ignore next */ []));
214
238
  siteName = computed(() => this._access()?.siteName ?? null, ...(ngDevMode ? [{ debugName: "siteName" }] : /* istanbul ignore next */ []));
@@ -218,6 +242,28 @@ class SessionService {
218
242
  return [];
219
243
  return [...me.ownedCompanies, ...me.sharedCompanies];
220
244
  }, ...(ngDevMode ? [{ debugName: "companies" }] : /* istanbul ignore next */ []));
245
+ companyOptions = computed(() => {
246
+ const me = this._me();
247
+ if (!me)
248
+ return [];
249
+ return [
250
+ ...me.ownedCompanies.map((company) => toOption(company, 'owner')),
251
+ ...me.sharedCompanies.map((company) => toOption(company, 'collaborator')),
252
+ ];
253
+ }, ...(ngDevMode ? [{ debugName: "companyOptions" }] : /* istanbul ignore next */ []));
254
+ companyId = computed(() => {
255
+ const siteId = this._siteId();
256
+ if (!siteId)
257
+ return this._me()?.profile.lastCompanyId ?? null;
258
+ const owner = this.companies().find((company) => company.myAccess.some((entry) => entry.siteId === siteId));
259
+ return owner?.id ?? this._me()?.profile.lastCompanyId ?? null;
260
+ }, ...(ngDevMode ? [{ debugName: "companyId" }] : /* istanbul ignore next */ []));
261
+ companyName = computed(() => {
262
+ const id = this.companyId();
263
+ if (!id)
264
+ return null;
265
+ return this.companyOptions().find((company) => company.id === id)?.name ?? null;
266
+ }, ...(ngDevMode ? [{ debugName: "companyName" }] : /* istanbul ignore next */ []));
221
267
  async load(language, force = false) {
222
268
  if (!isNucleusConfigured(this.config))
223
269
  return;
@@ -231,6 +277,7 @@ class SessionService {
231
277
  return;
232
278
  this.inFlight = true;
233
279
  this.loadedKey = key;
280
+ this.language = language;
234
281
  this._loading.set(true);
235
282
  this._error.set(null);
236
283
  try {
@@ -243,19 +290,8 @@ class SessionService {
243
290
  firstValueFrom(this.http.get(`${this.config.securityApiUrl}/apps`)),
244
291
  ]);
245
292
  this._me.set(me);
246
- this._apps.set(this.buildLauncher(me, catalog));
247
- const access = [...me.ownedCompanies, ...me.sharedCompanies]
248
- .flatMap((company) => company.myAccess)
249
- .find((entry) => entry.appId.toLowerCase() === this.config.appId);
250
- if (access) {
251
- this._access.set(access);
252
- this._scope.set('site');
253
- this._menu.set(await this.siteMenu(access.siteId, language));
254
- return;
255
- }
256
- this._access.set(null);
257
- this._scope.set('account');
258
- this._menu.set(await this.accountMenu(language));
293
+ this._apps.set(this.buildLauncher(catalog));
294
+ await this.resolveMenu(me, language);
259
295
  }
260
296
  catch (error) {
261
297
  this._menu.set([]);
@@ -270,13 +306,65 @@ class SessionService {
270
306
  reload(language) {
271
307
  return this.load(language, true);
272
308
  }
273
- buildLauncher(me, catalog) {
274
- const granted = new Set([...me.ownedCompanies, ...me.sharedCompanies]
309
+ sites(companyId) {
310
+ return firstValueFrom(this.http.get(`${this.config.securityApiUrl}/companies/${companyId}/sites`));
311
+ }
312
+ async accessibleSites(companyId) {
313
+ const company = this.companies().find((entry) => entry.id === companyId);
314
+ const all = await this.sites(companyId);
315
+ if (!company || company.isOwner)
316
+ return all;
317
+ const granted = new Set(company.myAccess.map((entry) => entry.siteId));
318
+ return all.filter((site) => granted.has(site.id));
319
+ }
320
+ async switchContext(companyId, siteId) {
321
+ if (this._switching())
322
+ return;
323
+ this._switching.set(true);
324
+ this._error.set(null);
325
+ try {
326
+ const profile = await firstValueFrom(this.http.put(`${this.config.securityApiUrl}/me/context`, {
327
+ companyId,
328
+ siteId,
329
+ }));
330
+ const me = this._me();
331
+ if (me) {
332
+ const updated = { ...me, profile };
333
+ this._me.set(updated);
334
+ this._siteId.set(siteId);
335
+ await this.resolveMenu(updated, this.language, siteId);
336
+ }
337
+ }
338
+ catch (error) {
339
+ this._error.set(describe(error));
340
+ console.error('[session] no se pudo cambiar de sede', error);
341
+ }
342
+ finally {
343
+ this._switching.set(false);
344
+ }
345
+ }
346
+ async resolveMenu(me, language, preferred) {
347
+ const entries = [...me.ownedCompanies, ...me.sharedCompanies]
275
348
  .flatMap((company) => company.myAccess)
276
- .map((entry) => entry.appId.toLowerCase()));
349
+ .filter((entry) => entry.appId.toLowerCase() === this.config.appId);
350
+ const wanted = preferred ?? me.profile.lastSiteId ?? null;
351
+ const access = entries.find((entry) => entry.siteId === wanted) ?? entries[0] ?? null;
352
+ if (access) {
353
+ this._access.set(access);
354
+ this._siteId.set(access.siteId);
355
+ this._scope.set('site');
356
+ this._menu.set(await this.siteMenu(access.siteId, language));
357
+ return;
358
+ }
359
+ this._access.set(null);
360
+ this._siteId.set(wanted);
361
+ this._scope.set('account');
362
+ this._menu.set(await this.accountMenu(language));
363
+ }
364
+ buildLauncher(catalog) {
277
365
  return catalog
278
366
  .filter((app) => app.id.toLowerCase() !== this.config.appId)
279
- .map((app) => ({ ...app, hasAccess: granted.has(app.id.toLowerCase()) }))
367
+ .slice()
280
368
  .sort((a, b) => a.sortOrder - b.sortOrder);
281
369
  }
282
370
  siteMenu(siteId, language) {
@@ -294,6 +382,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
294
382
  type: Injectable,
295
383
  args: [{ providedIn: 'root' }]
296
384
  }] });
385
+ function toOption(company, relation) {
386
+ return {
387
+ id: company.id,
388
+ name: company.tradeName ?? company.legalName,
389
+ relation,
390
+ };
391
+ }
297
392
  function describe(error) {
298
393
  if (error instanceof HttpErrorResponse) {
299
394
  const problem = error.error;
@@ -930,9 +1025,24 @@ const SHELL_COPY = {
930
1025
  accessLoading: 'Cargando accesos...',
931
1026
  accessEmpty: 'Sin accesos asignados',
932
1027
  accountScope: 'Mi cuenta',
933
- hintNoAccess: 'Sin acceso',
934
1028
  hintComingSoon: 'Próximamente',
935
1029
  hintNotDeployed: 'Sin publicar',
1030
+ switchSite: 'Cambiar sede',
1031
+ contextTitle: 'Compañía y sede',
1032
+ contextHint: 'Elige dónde quieres trabajar. El menú se carga según tu rol en la sede.',
1033
+ companyLabel: 'Compañía',
1034
+ companyPlaceholder: 'Selecciona una compañía',
1035
+ siteLabel: 'Sede',
1036
+ sitePlaceholder: 'Selecciona una sede',
1037
+ sitesLoading: 'Cargando sedes...',
1038
+ sitesEmpty: 'Esta compañía no tiene sedes a tu alcance',
1039
+ confirmSwitch: 'Cambiar',
1040
+ relationOwner: 'Propietario',
1041
+ relationCollaborator: 'Colaborador',
1042
+ noContext: 'Sin sede seleccionada',
1043
+ noCompanyTitle: 'Te falta una compañía',
1044
+ noCompanyHint: 'Para trabajar en las aplicaciones necesitas al menos una compañía con una sede, ' +
1045
+ 'propia o donde te hayan invitado. Puedes entrar igual, pero el menú saldrá vacío.',
936
1046
  },
937
1047
  en: {
938
1048
  navToggle: 'Show or hide the menu',
@@ -948,9 +1058,24 @@ const SHELL_COPY = {
948
1058
  accessLoading: 'Loading access...',
949
1059
  accessEmpty: 'No access granted',
950
1060
  accountScope: 'My account',
951
- hintNoAccess: 'No access',
952
1061
  hintComingSoon: 'Coming soon',
953
1062
  hintNotDeployed: 'Not deployed',
1063
+ switchSite: 'Switch site',
1064
+ contextTitle: 'Company and site',
1065
+ contextHint: 'Choose where you want to work. The menu loads from your role at that site.',
1066
+ companyLabel: 'Company',
1067
+ companyPlaceholder: 'Select a company',
1068
+ siteLabel: 'Site',
1069
+ sitePlaceholder: 'Select a site',
1070
+ sitesLoading: 'Loading sites...',
1071
+ sitesEmpty: 'This company has no sites available to you',
1072
+ confirmSwitch: 'Switch',
1073
+ relationOwner: 'Owner',
1074
+ relationCollaborator: 'Collaborator',
1075
+ noContext: 'No site selected',
1076
+ noCompanyTitle: 'You need a company',
1077
+ noCompanyHint: 'To work in the applications you need at least one company with a site, your own or ' +
1078
+ 'one you have been invited to. You can still open them, but the menu will be empty.',
954
1079
  },
955
1080
  };
956
1081
 
@@ -978,8 +1103,14 @@ class NucleusShell {
978
1103
  menuLoading = this.session.loading;
979
1104
  menuError = this.session.error;
980
1105
  launcherApps = this.session.apps;
1106
+ switching = this.session.switching;
981
1107
  sidebarOpen = signal(true, ...(ngDevMode ? [{ debugName: "sidebarOpen" }] : /* istanbul ignore next */ []));
982
1108
  settingsOpen = signal(false, ...(ngDevMode ? [{ debugName: "settingsOpen" }] : /* istanbul ignore next */ []));
1109
+ contextOpen = signal(false, ...(ngDevMode ? [{ debugName: "contextOpen" }] : /* istanbul ignore next */ []));
1110
+ companyChoice = signal(null, ...(ngDevMode ? [{ debugName: "companyChoice" }] : /* istanbul ignore next */ []));
1111
+ siteChoice = signal(null, ...(ngDevMode ? [{ debugName: "siteChoice" }] : /* istanbul ignore next */ []));
1112
+ siteOptions = signal([], ...(ngDevMode ? [{ debugName: "siteOptions" }] : /* istanbul ignore next */ []));
1113
+ sitesLoading = signal(false, ...(ngDevMode ? [{ debugName: "sitesLoading" }] : /* istanbul ignore next */ []));
983
1114
  auth = this.injector.get(NucleusAuth, null);
984
1115
  identity = computed(() => {
985
1116
  const me = this.session.me();
@@ -1001,15 +1132,26 @@ class NucleusShell {
1001
1132
  }, ...(ngDevMode ? [{ debugName: "identity" }] : /* istanbul ignore next */ []));
1002
1133
  initials = computed(() => initialsOf(this.identity()?.name ?? ''), ...(ngDevMode ? [{ debugName: "initials" }] : /* istanbul ignore next */ []));
1003
1134
  canSwitchAccount = computed(() => this.auth?.switchAccount !== undefined, ...(ngDevMode ? [{ debugName: "canSwitchAccount" }] : /* istanbul ignore next */ []));
1004
- accessCaption = computed(() => {
1135
+ hasCompany = computed(() => this.session.companies().length > 0, ...(ngDevMode ? [{ debugName: "hasCompany" }] : /* istanbul ignore next */ []));
1136
+ companyChoices = computed(() => {
1137
+ const t = this.copy();
1138
+ return this.session.companyOptions().map((company) => ({
1139
+ ...company,
1140
+ tag: company.relation === 'owner' ? t.relationOwner : t.relationCollaborator,
1141
+ }));
1142
+ }, ...(ngDevMode ? [{ debugName: "companyChoices" }] : /* istanbul ignore next */ []));
1143
+ contextCaption = computed(() => {
1005
1144
  const role = this.session.roleName();
1006
1145
  const site = this.session.siteName();
1146
+ const company = this.session.companyName();
1007
1147
  if (role && site)
1008
- return `${role} · ${site}`;
1148
+ return company ? `${company} · ${site}` : site;
1009
1149
  if (this.session.scope() === 'account')
1010
1150
  return this.copy().accountScope;
1011
- return '';
1012
- }, ...(ngDevMode ? [{ debugName: "accessCaption" }] : /* istanbul ignore next */ []));
1151
+ return this.copy().noContext;
1152
+ }, ...(ngDevMode ? [{ debugName: "contextCaption" }] : /* istanbul ignore next */ []));
1153
+ roleName = this.session.roleName;
1154
+ canConfirm = computed(() => this.companyChoice() !== null && this.siteChoice() !== null && !this.switching(), ...(ngDevMode ? [{ debugName: "canConfirm" }] : /* istanbul ignore next */ []));
1013
1155
  themeOptions = computed(() => [
1014
1156
  ...THEMES.map((theme) => ({
1015
1157
  value: theme.id,
@@ -1046,8 +1188,31 @@ class NucleusShell {
1046
1188
  isSelected(node) {
1047
1189
  return this.selectedMenuId() === node.id;
1048
1190
  }
1191
+ async openContext() {
1192
+ const companyId = this.session.companyId() ?? this.companyChoices()[0]?.id ?? null;
1193
+ this.companyChoice.set(companyId);
1194
+ this.siteChoice.set(this.session.siteId());
1195
+ this.contextOpen.set(true);
1196
+ if (companyId)
1197
+ await this.loadSites(companyId);
1198
+ }
1199
+ async onCompanyChange(companyId) {
1200
+ this.companyChoice.set(companyId);
1201
+ this.siteChoice.set(null);
1202
+ this.siteOptions.set([]);
1203
+ if (companyId)
1204
+ await this.loadSites(companyId);
1205
+ }
1206
+ async confirmSwitch() {
1207
+ const companyId = this.companyChoice();
1208
+ const siteId = this.siteChoice();
1209
+ if (!companyId || !siteId)
1210
+ return;
1211
+ await this.session.switchContext(companyId, siteId);
1212
+ this.contextOpen.set(false);
1213
+ }
1049
1214
  canOpen(app) {
1050
- return app.hasAccess && app.status === 1 && !!app.url;
1215
+ return app.status === 1 && !!app.url;
1051
1216
  }
1052
1217
  launch(app) {
1053
1218
  if (!this.canOpen(app))
@@ -1059,8 +1224,6 @@ class NucleusShell {
1059
1224
  }
1060
1225
  launchHint(app) {
1061
1226
  const t = this.copy();
1062
- if (!app.hasAccess)
1063
- return t.hintNoAccess;
1064
1227
  if (app.status !== 1)
1065
1228
  return t.hintComingSoon;
1066
1229
  if (!app.url)
@@ -1075,10 +1238,27 @@ class NucleusShell {
1075
1238
  await this.auth?.signOut?.();
1076
1239
  const landing = this.config.landingUrl;
1077
1240
  if (landing)
1078
- window.location.href = landing;
1241
+ window.location.href = nucleusLogoutUrl(landing);
1242
+ }
1243
+ async loadSites(companyId) {
1244
+ this.sitesLoading.set(true);
1245
+ try {
1246
+ const sites = await this.session.accessibleSites(companyId);
1247
+ this.siteOptions.set(sites);
1248
+ if (!sites.some((site) => site.id === this.siteChoice())) {
1249
+ this.siteChoice.set(sites[0]?.id ?? null);
1250
+ }
1251
+ }
1252
+ catch (error) {
1253
+ this.siteOptions.set([]);
1254
+ console.error('[shell] no se pudieron cargar las sedes', error);
1255
+ }
1256
+ finally {
1257
+ this.sitesLoading.set(false);
1258
+ }
1079
1259
  }
1080
1260
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShell, deps: [], target: i0.ɵɵFactoryTarget.Component });
1081
- 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" }, ngImport: i0, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar\">\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 </div>\n\n <div class=\"side\">\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>\n <div class=\"launcher\">\n <p class=\"launcher-title\">{{ t.appsLauncher }}</p>\n\n @if (!launcherApps().length) {\n <p class=\"launcher-empty\">{{ t.appsEmpty }}</p>\n }\n\n <div class=\"launcher-grid\">\n @for (app of launcherApps(); track app.id) {\n <button\n type=\"button\"\n class=\"launcher-item\"\n [class.is-disabled]=\"!canOpen(app)\"\n [disabled]=\"!canOpen(app)\"\n [pTooltip]=\"launchHint(app)\"\n tooltipPosition=\"bottom\"\n (click)=\"launch(app); launcher.hide()\"\n >\n <span class=\"launcher-icon\" [style.background]=\"app.color ?? '#64748b'\">\n @if (app.icon) {\n <i [class]=\"app.icon\" aria-hidden=\"true\"></i>\n } @else {\n <span class=\"launcher-initials\">{{ appInitials(app) }}</span>\n }\n </span>\n <span class=\"launcher-name\">{{ app.name }}</span>\n @if (launchHint(app)) {\n <span class=\"launcher-badge\">{{ launchHint(app) }}</span>\n }\n </button>\n }\n </div>\n </div>\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>\n @if (me) {\n <div class=\"account\">\n <p class=\"account-email\">{{ me.email }}</p>\n\n <div class=\"account-head\">\n @if (me.photoUrl) {\n <img class=\"account-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"72\" height=\"72\" />\n } @else {\n <span class=\"account-photo account-initials\">{{ initials() }}</span>\n }\n <p class=\"account-hi\">{{ me.name }}</p>\n </div>\n\n <div class=\"account-actions\">\n @if (canSwitchAccount()) {\n <p-button\n [label]=\"t.accountSwitch\"\n icon=\"pi pi-users\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"switchAccount(); account.hide()\"\n />\n }\n <p-button\n [label]=\"t.signOut\"\n icon=\"pi pi-sign-out\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"signOut(); account.hide()\"\n />\n </div>\n </div>\n }\n</p-popover>\n\n<div class=\"body\">\n <aside class=\"sidebar\" [class.is-collapsed]=\"!sidebarOpen()\">\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 (accessCaption()) {\n <p class=\"access-caption\">{{ accessCaption() }}</p>\n }\n\n @if (menuLoading()) {\n <p class=\"access-state\">{{ t.accessLoading }}</p>\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 <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"isSelected(node)\"\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 </button>\n\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 </nav>\n </aside>\n\n <main class=\"content\">\n @if (heading()) {\n <h1>{{ heading() }}</h1>\n }\n @if (lead()) {\n <p class=\"lead\">{{ lead() }}</p>\n }\n\n <ng-content />\n </main>\n</div>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [style]=\"{ width: '340px' }\"\n>\n <h3 class=\"settings-heading\">{{ t.settingsTheme }}</h3>\n <p class=\"settings-hint\">{{ t.settingsThemeHint }}</p>\n\n <p-listbox\n [options]=\"themeOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n [ngModel]=\"themePreference()\"\n (ngModelChange)=\"selectTheme($event)\"\n [ariaLabel]=\"t.settingsTheme\"\n >\n <ng-template #item let-option>\n <span class=\"theme-option\">\n @if (option.swatch) {\n <span class=\"theme-swatch\" [style.background]=\"option.swatch\" aria-hidden=\"true\"></span>\n } @else {\n <i class=\"pi pi-desktop theme-swatch-icon\" aria-hidden=\"true\"></i>\n }\n <span>{{ option.label }}</span>\n </span>\n </ng-template>\n </p-listbox>\n</p-drawer>\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:space-between;gap:16px;height:56px;padding:0 12px 0 8px;background:var(--ac-surface);border-bottom:1px solid var(--ac-border)}.side{display:flex;align-items:center;gap:4px}.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(--ac-text-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ac-border);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(--ac-text-muted);cursor:pointer}.lang-pill.is-active{background:var(--ac-accent);color:var(--ac-on-accent)}.launcher{width:320px;padding:4px}.launcher-title{margin:0 0 10px;font-size:12px;font-weight:600;color:var(--ac-text-muted)}.launcher-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px}.launcher-item{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px;padding:12px 6px;border:none;border-radius:8px;background:transparent;color:var(--ac-text);cursor:pointer}.launcher-item:hover:not(:disabled){background:var(--ac-surface-alt)}.launcher-item.is-disabled{opacity:.5;cursor:default}.launcher-icon{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;color:#fff}.launcher-icon i{font-size:18px}.launcher-name{font-size:11px;font-weight:500;line-height:1.3;text-align:center}.launcher-badge{font-size:9px;font-weight:500;color:var(--ac-text-muted)}.account{width:300px;padding:4px;text-align:center}.account-email{margin:0 0 12px;font-size:12px;color:var(--ac-text-muted)}.account-head{display:flex;flex-direction:column;align-items:center;gap:8px;padding-bottom:14px;border-bottom:1px solid var(--ac-border)}.account-photo{width:72px;height:72px;border-radius:999px;object-fit:cover}.account-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:26px;font-weight:600}.account-hi{margin:0;font-size:16px;font-weight:500}.account-actions{display:flex;flex-direction:column;gap:2px;padding-top:8px}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:224px;padding:12px 10px;background:var(--ac-surface);border-right:1px solid var(--ac-border);transition:width .18s ease}.sidebar.is-collapsed{width:60px}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:9px 12px;border:none;border-radius:6px;background:transparent;color:var(--ac-text-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(--ac-surface-alt);color:var(--ac-text)}.nav-item.is-active{background:color-mix(in srgb,var(--ac-accent) 18%,transparent);color:var(--ac-text)}.nav-item.is-active i{color:var(--ac-accent)}.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}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)}.settings-heading{margin:0 0 4px;font-size:14px;font-weight:600}.settings-hint{margin:0 0 14px;font-size:12px;font-weight:300;color:var(--ac-text-muted)}.theme-option{display:flex;align-items:center;gap:10px}.theme-swatch{flex:none;width:16px;height:16px;border-radius:3px;border:1px solid var(--ac-border)}.theme-swatch-icon{flex:none;width:16px;font-size:14px;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid color-mix(in srgb,currentColor 12%,transparent)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;font-size:.78rem;opacity:.7}.access-state.is-error{color:var(--p-red-400, #f87171);opacity:1}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}.launcher-initials{font-size:.85rem;font-weight:600;letter-spacing:.02em;line-height:1;color:#fff}.launcher-empty{margin:.25rem 0 .5rem;font-size:.82rem;opacity:.7}@media(max-width:720px){.sidebar{width:60px}.sidebar .nav-label{display:none}.sidebar .nav-item{justify-content:center;padding-inline:0}.brand-secondary{display:none}.content{padding:20px 16px 48px}h1{font-size:22px}.launcher{width:260px}}\n"], dependencies: [{ kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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: DrawerModule }, { kind: "component", type: i3.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: ListboxModule }, { kind: "component", type: i4.Listbox, selector: "p-listbox, p-listBox, p-list-box", inputs: ["hostName", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "ariaLabel", "selectOnFocus", "searchLocale", "focusOnHover", "filterMessage", "filterFields", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "scrollHeight", "tabindex", "multiple", "styleClass", "listStyle", "listStyleClass", "readonly", "checkbox", "filter", "filterBy", "filterMatchMode", "filterLocale", "metaKeySelection", "dataKey", "showToggleAll", "optionLabel", "optionValue", "optionGroupChildren", "optionGroupLabel", "optionDisabled", "ariaFilterLabel", "filterPlaceHolder", "emptyFilterMessage", "emptyMessage", "group", "options", "filterValue", "selectAll", "striped", "highlightOnSelect", "checkmark", "dragdrop", "dropListData", "fluid"], outputs: ["onChange", "onClick", "onDblClick", "onFilter", "onFocus", "onBlur", "onSelectAllChange", "onLazyLoad", "onDrop"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i5.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.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: i7.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i7.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NucleusMark, selector: "nucleus-mark", inputs: ["accent", "size"] }, { kind: "component", type: NucleusStarfield, selector: "nucleus-starfield" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1261
+ 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" }, ngImport: i0, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar\">\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 </div>\n\n <div class=\"side\">\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>\n <div class=\"launcher\">\n <p class=\"launcher-title\">{{ t.appsLauncher }}</p>\n\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 @if (!launcherApps().length) {\n <p class=\"launcher-empty\">{{ t.appsEmpty }}</p>\n }\n\n <div class=\"launcher-grid\">\n @for (app of launcherApps(); track app.id) {\n <button\n type=\"button\"\n class=\"launcher-item\"\n [class.is-disabled]=\"!canOpen(app)\"\n [disabled]=\"!canOpen(app)\"\n [pTooltip]=\"launchHint(app)\"\n tooltipPosition=\"bottom\"\n (click)=\"launch(app); launcher.hide()\"\n >\n <span class=\"launcher-icon\" [style.background]=\"app.color ?? '#64748b'\">\n @if (app.icon) {\n <i [class]=\"app.icon\" aria-hidden=\"true\"></i>\n } @else {\n <span class=\"launcher-initials\">{{ appInitials(app) }}</span>\n }\n </span>\n <span class=\"launcher-name\">{{ app.name }}</span>\n @if (launchHint(app)) {\n <span class=\"launcher-badge\">{{ launchHint(app) }}</span>\n }\n </button>\n }\n </div>\n </div>\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>\n @if (me) {\n <div class=\"account\">\n <p class=\"account-email\">{{ me.email }}</p>\n\n <div class=\"account-head\">\n @if (me.photoUrl) {\n <img class=\"account-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"72\" height=\"72\" />\n } @else {\n <span class=\"account-photo account-initials\">{{ initials() }}</span>\n }\n <p class=\"account-hi\">{{ me.name }}</p>\n </div>\n\n <div class=\"account-context\">\n @if (roleName()) {\n <p-tag [value]=\"roleName()!\" severity=\"secondary\" />\n }\n <p class=\"account-place\">{{ contextCaption() }}</p>\n <p-button\n [label]=\"t.switchSite\"\n icon=\"pi pi-building\"\n severity=\"secondary\"\n size=\"small\"\n [outlined]=\"true\"\n [rounded]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"openContext(); account.hide()\"\n />\n </div>\n\n <div class=\"account-actions\">\n @if (canSwitchAccount()) {\n <p-button\n [label]=\"t.accountSwitch\"\n icon=\"pi pi-users\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"switchAccount(); account.hide()\"\n />\n }\n <p-button\n [label]=\"t.signOut\"\n icon=\"pi pi-sign-out\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"signOut(); account.hide()\"\n />\n </div>\n </div>\n }\n</p-popover>\n\n<div class=\"body\">\n <aside class=\"sidebar\" [class.is-collapsed]=\"!sidebarOpen()\">\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 <p class=\"access-state\">{{ t.accessLoading }}</p>\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 <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"isSelected(node)\"\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 </button>\n\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 </nav>\n </aside>\n\n <main class=\"content\">\n @if (heading()) {\n <h1>{{ heading() }}</h1>\n }\n @if (lead()) {\n <p class=\"lead\">{{ lead() }}</p>\n }\n\n <ng-content />\n </main>\n</div>\n\n<p-drawer\n [visible]=\"contextOpen()\"\n (visibleChange)=\"contextOpen.set($event)\"\n position=\"right\"\n [header]=\"t.contextTitle\"\n [style]=\"{ width: '380px' }\"\n>\n <p class=\"settings-hint\">{{ t.contextHint }}</p>\n\n <label class=\"field-label\" for=\"nucleus-company\">{{ t.companyLabel }}</label>\n <p-select\n inputId=\"nucleus-company\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [fluid]=\"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 <label class=\"field-label\" for=\"nucleus-site\">{{ t.siteLabel }}</label>\n\n @if (sitesLoading()) {\n <p class=\"access-state\">{{ t.sitesLoading }}</p>\n } @else if (!siteOptions().length) {\n <p class=\"access-state\">{{ t.sitesEmpty }}</p>\n } @else {\n <p-select\n inputId=\"nucleus-site\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"siteChoice.set($event)\"\n [placeholder]=\"t.sitePlaceholder\"\n [fluid]=\"true\"\n />\n }\n\n <div class=\"context-actions\">\n <p-button\n [label]=\"t.confirmSwitch\"\n icon=\"pi pi-arrow-right\"\n [disabled]=\"!canConfirm()\"\n [loading]=\"switching()\"\n [fluid]=\"true\"\n (onClick)=\"confirmSwitch()\"\n />\n </div>\n</p-drawer>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [style]=\"{ width: '340px' }\"\n>\n <h3 class=\"settings-heading\">{{ t.settingsTheme }}</h3>\n <p class=\"settings-hint\">{{ t.settingsThemeHint }}</p>\n\n <p-listbox\n [options]=\"themeOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n [ngModel]=\"themePreference()\"\n (ngModelChange)=\"selectTheme($event)\"\n [ariaLabel]=\"t.settingsTheme\"\n >\n <ng-template #item let-option>\n <span class=\"theme-option\">\n @if (option.swatch) {\n <span class=\"theme-swatch\" [style.background]=\"option.swatch\" aria-hidden=\"true\"></span>\n } @else {\n <i class=\"pi pi-desktop theme-swatch-icon\" aria-hidden=\"true\"></i>\n }\n <span>{{ option.label }}</span>\n </span>\n </ng-template>\n </p-listbox>\n</p-drawer>\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:space-between;gap:16px;height:56px;padding:0 12px 0 8px;background:var(--ac-surface);border-bottom:1px solid var(--ac-border)}.side{display:flex;align-items:center;gap:4px}.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(--ac-text-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ac-border);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(--ac-text-muted);cursor:pointer}.lang-pill.is-active{background:var(--ac-accent);color:var(--ac-on-accent)}.launcher{width:320px;padding:4px}.launcher-title{margin:0 0 10px;font-size:12px;font-weight:600;color:var(--ac-text-muted)}.launcher-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px}.launcher-item{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px;padding:12px 6px;border:none;border-radius:8px;background:transparent;color:var(--ac-text);cursor:pointer}.launcher-item:hover:not(:disabled){background:var(--ac-surface-alt)}.launcher-item.is-disabled{opacity:.5;cursor:default}.launcher-icon{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;color:#fff}.launcher-icon i{font-size:18px}.launcher-name{font-size:11px;font-weight:500;line-height:1.3;text-align:center}.launcher-badge{font-size:9px;font-weight:500;color:var(--ac-text-muted)}.account{width:300px;padding:4px;text-align:center}.account-email{margin:0 0 12px;font-size:12px;color:var(--ac-text-muted)}.account-head{display:flex;flex-direction:column;align-items:center;gap:8px;padding-bottom:14px;border-bottom:1px solid var(--ac-border)}.account-photo{width:72px;height:72px;border-radius:999px;object-fit:cover}.account-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:26px;font-weight:600}.account-hi{margin:0;font-size:16px;font-weight:500}.account-actions{display:flex;flex-direction:column;gap:2px;padding-top:8px}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:224px;padding:12px 10px;background:var(--ac-surface);border-right:1px solid var(--ac-border);transition:width .18s ease}.sidebar.is-collapsed{width:60px}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:9px 12px;border:none;border-radius:6px;background:transparent;color:var(--ac-text-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(--ac-surface-alt);color:var(--ac-text)}.nav-item.is-active{background:color-mix(in srgb,var(--ac-accent) 18%,transparent);color:var(--ac-text)}.nav-item.is-active i{color:var(--ac-accent)}.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}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)}.settings-heading{margin:0 0 4px;font-size:14px;font-weight:600}.settings-hint{margin:0 0 14px;font-size:12px;font-weight:300;color:var(--ac-text-muted)}.theme-option{display:flex;align-items:center;gap:10px}.theme-swatch{flex:none;width:16px;height:16px;border-radius:3px;border:1px solid var(--ac-border)}.theme-swatch-icon{flex:none;width:16px;font-size:14px;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid color-mix(in srgb,currentColor 12%,transparent)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;font-size:.78rem;opacity:.7}.access-state.is-error{color:var(--p-red-400, #f87171);opacity:1}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}.launcher-initials{font-size:.85rem;font-weight:600;letter-spacing:.02em;line-height:1;color:#fff}.launcher-empty{margin:.25rem 0 .5rem;font-size:.82rem;opacity:.7}@media(max-width:720px){.sidebar{width:60px}.sidebar .nav-label{display:none}.sidebar .nav-item{justify-content:center;padding-inline:0}.brand-secondary{display:none}.content{padding:20px 16px 48px}h1{font-size:22px}.launcher{width:260px}}.account-context{display:flex;flex-direction:column;align-items:center;gap:8px;padding:14px 0;border-top:1px solid var(--ac-border);border-bottom:1px solid var(--ac-border)}.account-place{margin:0;color:var(--ac-text-muted);font-size:12px;text-align:center}.field-label{display:block;margin:18px 0 6px;color:var(--ac-text-muted);font-size:12px;letter-spacing:.04em;text-transform:uppercase}.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}.context-actions{margin-top:24px}.launcher-notice{display:flex;gap:10px;margin-bottom:14px;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}\n"], dependencies: [{ kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2.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: DrawerModule }, { kind: "component", type: i3.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: ListboxModule }, { kind: "component", type: i4.Listbox, selector: "p-listbox, p-listBox, p-list-box", inputs: ["hostName", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "ariaLabel", "selectOnFocus", "searchLocale", "focusOnHover", "filterMessage", "filterFields", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "scrollHeight", "tabindex", "multiple", "styleClass", "listStyle", "listStyleClass", "readonly", "checkbox", "filter", "filterBy", "filterMatchMode", "filterLocale", "metaKeySelection", "dataKey", "showToggleAll", "optionLabel", "optionValue", "optionGroupChildren", "optionGroupLabel", "optionDisabled", "ariaFilterLabel", "filterPlaceHolder", "emptyFilterMessage", "emptyMessage", "group", "options", "filterValue", "selectAll", "striped", "highlightOnSelect", "checkmark", "dragdrop", "dropListData", "fluid"], outputs: ["onChange", "onClick", "onDblClick", "onFilter", "onFocus", "onBlur", "onSelectAllChange", "onLazyLoad", "onDrop"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i5.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: i6.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: TagModule }, { kind: "component", type: i7.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i8.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: i9.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i9.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NucleusMark, selector: "nucleus-mark", inputs: ["accent", "size"] }, { kind: "component", type: NucleusStarfield, selector: "nucleus-starfield" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1082
1262
  }
1083
1263
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NucleusShell, decorators: [{
1084
1264
  type: Component,
@@ -1088,11 +1268,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
1088
1268
  DrawerModule,
1089
1269
  ListboxModule,
1090
1270
  PopoverModule,
1271
+ SelectModule,
1272
+ TagModule,
1091
1273
  TooltipModule,
1092
1274
  FormsModule,
1093
1275
  NucleusMark,
1094
1276
  NucleusStarfield,
1095
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar\">\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 </div>\n\n <div class=\"side\">\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>\n <div class=\"launcher\">\n <p class=\"launcher-title\">{{ t.appsLauncher }}</p>\n\n @if (!launcherApps().length) {\n <p class=\"launcher-empty\">{{ t.appsEmpty }}</p>\n }\n\n <div class=\"launcher-grid\">\n @for (app of launcherApps(); track app.id) {\n <button\n type=\"button\"\n class=\"launcher-item\"\n [class.is-disabled]=\"!canOpen(app)\"\n [disabled]=\"!canOpen(app)\"\n [pTooltip]=\"launchHint(app)\"\n tooltipPosition=\"bottom\"\n (click)=\"launch(app); launcher.hide()\"\n >\n <span class=\"launcher-icon\" [style.background]=\"app.color ?? '#64748b'\">\n @if (app.icon) {\n <i [class]=\"app.icon\" aria-hidden=\"true\"></i>\n } @else {\n <span class=\"launcher-initials\">{{ appInitials(app) }}</span>\n }\n </span>\n <span class=\"launcher-name\">{{ app.name }}</span>\n @if (launchHint(app)) {\n <span class=\"launcher-badge\">{{ launchHint(app) }}</span>\n }\n </button>\n }\n </div>\n </div>\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>\n @if (me) {\n <div class=\"account\">\n <p class=\"account-email\">{{ me.email }}</p>\n\n <div class=\"account-head\">\n @if (me.photoUrl) {\n <img class=\"account-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"72\" height=\"72\" />\n } @else {\n <span class=\"account-photo account-initials\">{{ initials() }}</span>\n }\n <p class=\"account-hi\">{{ me.name }}</p>\n </div>\n\n <div class=\"account-actions\">\n @if (canSwitchAccount()) {\n <p-button\n [label]=\"t.accountSwitch\"\n icon=\"pi pi-users\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"switchAccount(); account.hide()\"\n />\n }\n <p-button\n [label]=\"t.signOut\"\n icon=\"pi pi-sign-out\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"signOut(); account.hide()\"\n />\n </div>\n </div>\n }\n</p-popover>\n\n<div class=\"body\">\n <aside class=\"sidebar\" [class.is-collapsed]=\"!sidebarOpen()\">\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 (accessCaption()) {\n <p class=\"access-caption\">{{ accessCaption() }}</p>\n }\n\n @if (menuLoading()) {\n <p class=\"access-state\">{{ t.accessLoading }}</p>\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 <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"isSelected(node)\"\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 </button>\n\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 </nav>\n </aside>\n\n <main class=\"content\">\n @if (heading()) {\n <h1>{{ heading() }}</h1>\n }\n @if (lead()) {\n <p class=\"lead\">{{ lead() }}</p>\n }\n\n <ng-content />\n </main>\n</div>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [style]=\"{ width: '340px' }\"\n>\n <h3 class=\"settings-heading\">{{ t.settingsTheme }}</h3>\n <p class=\"settings-hint\">{{ t.settingsThemeHint }}</p>\n\n <p-listbox\n [options]=\"themeOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n [ngModel]=\"themePreference()\"\n (ngModelChange)=\"selectTheme($event)\"\n [ariaLabel]=\"t.settingsTheme\"\n >\n <ng-template #item let-option>\n <span class=\"theme-option\">\n @if (option.swatch) {\n <span class=\"theme-swatch\" [style.background]=\"option.swatch\" aria-hidden=\"true\"></span>\n } @else {\n <i class=\"pi pi-desktop theme-swatch-icon\" aria-hidden=\"true\"></i>\n }\n <span>{{ option.label }}</span>\n </span>\n </ng-template>\n </p-listbox>\n</p-drawer>\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:space-between;gap:16px;height:56px;padding:0 12px 0 8px;background:var(--ac-surface);border-bottom:1px solid var(--ac-border)}.side{display:flex;align-items:center;gap:4px}.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(--ac-text-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ac-border);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(--ac-text-muted);cursor:pointer}.lang-pill.is-active{background:var(--ac-accent);color:var(--ac-on-accent)}.launcher{width:320px;padding:4px}.launcher-title{margin:0 0 10px;font-size:12px;font-weight:600;color:var(--ac-text-muted)}.launcher-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px}.launcher-item{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px;padding:12px 6px;border:none;border-radius:8px;background:transparent;color:var(--ac-text);cursor:pointer}.launcher-item:hover:not(:disabled){background:var(--ac-surface-alt)}.launcher-item.is-disabled{opacity:.5;cursor:default}.launcher-icon{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;color:#fff}.launcher-icon i{font-size:18px}.launcher-name{font-size:11px;font-weight:500;line-height:1.3;text-align:center}.launcher-badge{font-size:9px;font-weight:500;color:var(--ac-text-muted)}.account{width:300px;padding:4px;text-align:center}.account-email{margin:0 0 12px;font-size:12px;color:var(--ac-text-muted)}.account-head{display:flex;flex-direction:column;align-items:center;gap:8px;padding-bottom:14px;border-bottom:1px solid var(--ac-border)}.account-photo{width:72px;height:72px;border-radius:999px;object-fit:cover}.account-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:26px;font-weight:600}.account-hi{margin:0;font-size:16px;font-weight:500}.account-actions{display:flex;flex-direction:column;gap:2px;padding-top:8px}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:224px;padding:12px 10px;background:var(--ac-surface);border-right:1px solid var(--ac-border);transition:width .18s ease}.sidebar.is-collapsed{width:60px}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:9px 12px;border:none;border-radius:6px;background:transparent;color:var(--ac-text-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(--ac-surface-alt);color:var(--ac-text)}.nav-item.is-active{background:color-mix(in srgb,var(--ac-accent) 18%,transparent);color:var(--ac-text)}.nav-item.is-active i{color:var(--ac-accent)}.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}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)}.settings-heading{margin:0 0 4px;font-size:14px;font-weight:600}.settings-hint{margin:0 0 14px;font-size:12px;font-weight:300;color:var(--ac-text-muted)}.theme-option{display:flex;align-items:center;gap:10px}.theme-swatch{flex:none;width:16px;height:16px;border-radius:3px;border:1px solid var(--ac-border)}.theme-swatch-icon{flex:none;width:16px;font-size:14px;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid color-mix(in srgb,currentColor 12%,transparent)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;font-size:.78rem;opacity:.7}.access-state.is-error{color:var(--p-red-400, #f87171);opacity:1}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}.launcher-initials{font-size:.85rem;font-weight:600;letter-spacing:.02em;line-height:1;color:#fff}.launcher-empty{margin:.25rem 0 .5rem;font-size:.82rem;opacity:.7}@media(max-width:720px){.sidebar{width:60px}.sidebar .nav-label{display:none}.sidebar .nav-item{justify-content:center;padding-inline:0}.brand-secondary{display:none}.content{padding:20px 16px 48px}h1{font-size:22px}.launcher{width:260px}}\n"] }]
1277
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "@let t = copy();\n@let me = identity();\n\n@if (starfield()) {\n <nucleus-starfield />\n}\n\n<header class=\"topbar\">\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 </div>\n\n <div class=\"side\">\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>\n <div class=\"launcher\">\n <p class=\"launcher-title\">{{ t.appsLauncher }}</p>\n\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 @if (!launcherApps().length) {\n <p class=\"launcher-empty\">{{ t.appsEmpty }}</p>\n }\n\n <div class=\"launcher-grid\">\n @for (app of launcherApps(); track app.id) {\n <button\n type=\"button\"\n class=\"launcher-item\"\n [class.is-disabled]=\"!canOpen(app)\"\n [disabled]=\"!canOpen(app)\"\n [pTooltip]=\"launchHint(app)\"\n tooltipPosition=\"bottom\"\n (click)=\"launch(app); launcher.hide()\"\n >\n <span class=\"launcher-icon\" [style.background]=\"app.color ?? '#64748b'\">\n @if (app.icon) {\n <i [class]=\"app.icon\" aria-hidden=\"true\"></i>\n } @else {\n <span class=\"launcher-initials\">{{ appInitials(app) }}</span>\n }\n </span>\n <span class=\"launcher-name\">{{ app.name }}</span>\n @if (launchHint(app)) {\n <span class=\"launcher-badge\">{{ launchHint(app) }}</span>\n }\n </button>\n }\n </div>\n </div>\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>\n @if (me) {\n <div class=\"account\">\n <p class=\"account-email\">{{ me.email }}</p>\n\n <div class=\"account-head\">\n @if (me.photoUrl) {\n <img class=\"account-photo\" [src]=\"me.photoUrl\" alt=\"\" width=\"72\" height=\"72\" />\n } @else {\n <span class=\"account-photo account-initials\">{{ initials() }}</span>\n }\n <p class=\"account-hi\">{{ me.name }}</p>\n </div>\n\n <div class=\"account-context\">\n @if (roleName()) {\n <p-tag [value]=\"roleName()!\" severity=\"secondary\" />\n }\n <p class=\"account-place\">{{ contextCaption() }}</p>\n <p-button\n [label]=\"t.switchSite\"\n icon=\"pi pi-building\"\n severity=\"secondary\"\n size=\"small\"\n [outlined]=\"true\"\n [rounded]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"openContext(); account.hide()\"\n />\n </div>\n\n <div class=\"account-actions\">\n @if (canSwitchAccount()) {\n <p-button\n [label]=\"t.accountSwitch\"\n icon=\"pi pi-users\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"switchAccount(); account.hide()\"\n />\n }\n <p-button\n [label]=\"t.signOut\"\n icon=\"pi pi-sign-out\"\n severity=\"secondary\"\n size=\"small\"\n [text]=\"true\"\n [fluid]=\"true\"\n (onClick)=\"signOut(); account.hide()\"\n />\n </div>\n </div>\n }\n</p-popover>\n\n<div class=\"body\">\n <aside class=\"sidebar\" [class.is-collapsed]=\"!sidebarOpen()\">\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 <p class=\"access-state\">{{ t.accessLoading }}</p>\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 <button\n type=\"button\"\n class=\"nav-item\"\n [class.is-active]=\"isSelected(node)\"\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 </button>\n\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 </nav>\n </aside>\n\n <main class=\"content\">\n @if (heading()) {\n <h1>{{ heading() }}</h1>\n }\n @if (lead()) {\n <p class=\"lead\">{{ lead() }}</p>\n }\n\n <ng-content />\n </main>\n</div>\n\n<p-drawer\n [visible]=\"contextOpen()\"\n (visibleChange)=\"contextOpen.set($event)\"\n position=\"right\"\n [header]=\"t.contextTitle\"\n [style]=\"{ width: '380px' }\"\n>\n <p class=\"settings-hint\">{{ t.contextHint }}</p>\n\n <label class=\"field-label\" for=\"nucleus-company\">{{ t.companyLabel }}</label>\n <p-select\n inputId=\"nucleus-company\"\n [options]=\"companyChoices()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"companyChoice()\"\n (ngModelChange)=\"onCompanyChange($event)\"\n [placeholder]=\"t.companyPlaceholder\"\n [fluid]=\"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 <label class=\"field-label\" for=\"nucleus-site\">{{ t.siteLabel }}</label>\n\n @if (sitesLoading()) {\n <p class=\"access-state\">{{ t.sitesLoading }}</p>\n } @else if (!siteOptions().length) {\n <p class=\"access-state\">{{ t.sitesEmpty }}</p>\n } @else {\n <p-select\n inputId=\"nucleus-site\"\n [options]=\"siteOptions()\"\n optionValue=\"id\"\n optionLabel=\"name\"\n [ngModel]=\"siteChoice()\"\n (ngModelChange)=\"siteChoice.set($event)\"\n [placeholder]=\"t.sitePlaceholder\"\n [fluid]=\"true\"\n />\n }\n\n <div class=\"context-actions\">\n <p-button\n [label]=\"t.confirmSwitch\"\n icon=\"pi pi-arrow-right\"\n [disabled]=\"!canConfirm()\"\n [loading]=\"switching()\"\n [fluid]=\"true\"\n (onClick)=\"confirmSwitch()\"\n />\n </div>\n</p-drawer>\n\n<p-drawer\n [visible]=\"settingsOpen()\"\n (visibleChange)=\"settingsOpen.set($event)\"\n position=\"right\"\n [header]=\"t.settingsTitle\"\n [style]=\"{ width: '340px' }\"\n>\n <h3 class=\"settings-heading\">{{ t.settingsTheme }}</h3>\n <p class=\"settings-hint\">{{ t.settingsThemeHint }}</p>\n\n <p-listbox\n [options]=\"themeOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n [ngModel]=\"themePreference()\"\n (ngModelChange)=\"selectTheme($event)\"\n [ariaLabel]=\"t.settingsTheme\"\n >\n <ng-template #item let-option>\n <span class=\"theme-option\">\n @if (option.swatch) {\n <span class=\"theme-swatch\" [style.background]=\"option.swatch\" aria-hidden=\"true\"></span>\n } @else {\n <i class=\"pi pi-desktop theme-swatch-icon\" aria-hidden=\"true\"></i>\n }\n <span>{{ option.label }}</span>\n </span>\n </ng-template>\n </p-listbox>\n</p-drawer>\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:space-between;gap:16px;height:56px;padding:0 12px 0 8px;background:var(--ac-surface);border-bottom:1px solid var(--ac-border)}.side{display:flex;align-items:center;gap:4px}.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(--ac-text-muted)}.lang-switch{display:flex;align-items:center;gap:2px;margin-right:6px;border:1px solid var(--ac-border);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(--ac-text-muted);cursor:pointer}.lang-pill.is-active{background:var(--ac-accent);color:var(--ac-on-accent)}.launcher{width:320px;padding:4px}.launcher-title{margin:0 0 10px;font-size:12px;font-weight:600;color:var(--ac-text-muted)}.launcher-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px}.launcher-item{position:relative;display:flex;flex-direction:column;align-items:center;gap:7px;padding:12px 6px;border:none;border-radius:8px;background:transparent;color:var(--ac-text);cursor:pointer}.launcher-item:hover:not(:disabled){background:var(--ac-surface-alt)}.launcher-item.is-disabled{opacity:.5;cursor:default}.launcher-icon{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;color:#fff}.launcher-icon i{font-size:18px}.launcher-name{font-size:11px;font-weight:500;line-height:1.3;text-align:center}.launcher-badge{font-size:9px;font-weight:500;color:var(--ac-text-muted)}.account{width:300px;padding:4px;text-align:center}.account-email{margin:0 0 12px;font-size:12px;color:var(--ac-text-muted)}.account-head{display:flex;flex-direction:column;align-items:center;gap:8px;padding-bottom:14px;border-bottom:1px solid var(--ac-border)}.account-photo{width:72px;height:72px;border-radius:999px;object-fit:cover}.account-initials{display:inline-flex;align-items:center;justify-content:center;background:var(--ac-accent);color:var(--ac-on-accent);font-size:26px;font-weight:600}.account-hi{margin:0;font-size:16px;font-weight:500}.account-actions{display:flex;flex-direction:column;gap:2px;padding-top:8px}.body{display:flex;flex:1;min-height:0}.sidebar{flex:none;width:224px;padding:12px 10px;background:var(--ac-surface);border-right:1px solid var(--ac-border);transition:width .18s ease}.sidebar.is-collapsed{width:60px}.sidebar nav{display:flex;flex-direction:column;gap:3px}.nav-item{display:flex;align-items:center;gap:12px;width:100%;padding:9px 12px;border:none;border-radius:6px;background:transparent;color:var(--ac-text-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(--ac-surface-alt);color:var(--ac-text)}.nav-item.is-active{background:color-mix(in srgb,var(--ac-accent) 18%,transparent);color:var(--ac-text)}.nav-item.is-active i{color:var(--ac-accent)}.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}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)}.settings-heading{margin:0 0 4px;font-size:14px;font-weight:600}.settings-hint{margin:0 0 14px;font-size:12px;font-weight:300;color:var(--ac-text-muted)}.theme-option{display:flex;align-items:center;gap:10px}.theme-swatch{flex:none;width:16px;height:16px;border-radius:3px;border:1px solid var(--ac-border)}.theme-swatch-icon{flex:none;width:16px;font-size:14px;color:var(--ac-text-muted)}.access-menu{display:flex;flex-direction:column;gap:.15rem;margin-top:.75rem;padding-top:.75rem;border-top:1px solid color-mix(in srgb,currentColor 12%,transparent)}.access-caption{margin:0 0 .4rem;padding:0 .75rem;font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.access-state{margin:0;padding:.35rem .75rem;font-size:.78rem;opacity:.7}.access-state.is-error{color:var(--p-red-400, #f87171);opacity:1}.nav-item.is-child{padding-left:2.1rem;font-size:.86rem}.launcher-initials{font-size:.85rem;font-weight:600;letter-spacing:.02em;line-height:1;color:#fff}.launcher-empty{margin:.25rem 0 .5rem;font-size:.82rem;opacity:.7}@media(max-width:720px){.sidebar{width:60px}.sidebar .nav-label{display:none}.sidebar .nav-item{justify-content:center;padding-inline:0}.brand-secondary{display:none}.content{padding:20px 16px 48px}h1{font-size:22px}.launcher{width:260px}}.account-context{display:flex;flex-direction:column;align-items:center;gap:8px;padding:14px 0;border-top:1px solid var(--ac-border);border-bottom:1px solid var(--ac-border)}.account-place{margin:0;color:var(--ac-text-muted);font-size:12px;text-align:center}.field-label{display:block;margin:18px 0 6px;color:var(--ac-text-muted);font-size:12px;letter-spacing:.04em;text-transform:uppercase}.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}.context-actions{margin-top:24px}.launcher-notice{display:flex;gap:10px;margin-bottom:14px;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}\n"] }]
1096
1278
  }], 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"] }] } });
1097
1279
  function initialsOf(source) {
1098
1280
  const parts = source.trim().split(/\s+/).filter(Boolean);
@@ -1107,5 +1289,5 @@ function initialsOf(source) {
1107
1289
  * Generated bundle index. Do not edit.
1108
1290
  */
1109
1291
 
1110
- export { DEFAULT_THEME, NUCLEUS_CONFIG, NUCLEUS_DARK_SELECTOR, NUCLEUS_FORCE_BEARER, NUCLEUS_LANGS, NUCLEUS_SKIP_AUTH, NucleusAuth, NucleusLanguage, NucleusMark, NucleusPreset, NucleusSessionAuth, NucleusShell, NucleusStarfield, SHELL_COPY, SessionService, THEMES, THEME_LABELS, ThemeService, UserSyncService, isNucleusConfigured, isThemePreference, loadNucleusConfig, nucleusAuthInterceptor, ownsUrl, provideNucleus, provideNucleusTheme, themeById };
1292
+ export { DEFAULT_THEME, NUCLEUS_CONFIG, NUCLEUS_DARK_SELECTOR, NUCLEUS_FORCE_BEARER, NUCLEUS_LANGS, NUCLEUS_LOGOUT_PARAM, NUCLEUS_SKIP_AUTH, NucleusAuth, NucleusLanguage, NucleusMark, NucleusPreset, NucleusSessionAuth, NucleusShell, NucleusStarfield, SHELL_COPY, SessionService, THEMES, THEME_LABELS, ThemeService, UserSyncService, consumeNucleusLogout, isNucleusConfigured, isThemePreference, loadNucleusConfig, nucleusAuthInterceptor, nucleusLogoutUrl, ownsUrl, provideNucleus, provideNucleusTheme, themeById };
1111
1293
  //# sourceMappingURL=nucleus-suite-pe-core.mjs.map