@franzzemen/brokenstock-shell 0.0.2 → 0.0.4

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.
@@ -23,6 +23,8 @@ import * as i4 from '@angular/material/input';
23
23
  import { MatInput, MatInputModule } from '@angular/material/input';
24
24
  import * as i6 from '@angular/material/button';
25
25
  import { MatButtonModule } from '@angular/material/button';
26
+ import * as i7 from '@angular/material/checkbox';
27
+ import { MatCheckboxModule } from '@angular/material/checkbox';
26
28
  import * as i1$2 from '@angular/material/dialog';
27
29
  import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
28
30
 
@@ -45,8 +47,11 @@ function provideShellConfig(config) {
45
47
  }
46
48
 
47
49
  class ThinkingService {
48
- #activeCount = signal(0, ...(ngDevMode ? [{ debugName: "#activeCount" }] : /* istanbul ignore next */ []));
49
- isThinking = computed(() => this.#activeCount() > 0, ...(ngDevMode ? [{ debugName: "isThinking" }] : /* istanbul ignore next */ []));
50
+ constructor() {
51
+ this.#activeCount = signal(0, ...(ngDevMode ? [{ debugName: "#activeCount" }] : /* istanbul ignore next */ []));
52
+ this.isThinking = computed(() => this.#activeCount() > 0, ...(ngDevMode ? [{ debugName: "isThinking" }] : /* istanbul ignore next */ []));
53
+ }
54
+ #activeCount;
50
55
  startThinking() {
51
56
  this.#activeCount.update(c => c + 1);
52
57
  }
@@ -56,8 +61,8 @@ class ThinkingService {
56
61
  reset() {
57
62
  this.#activeCount.set(0);
58
63
  }
59
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThinkingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
60
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThinkingService, providedIn: 'root' });
64
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThinkingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
65
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThinkingService, providedIn: 'root' }); }
61
66
  }
62
67
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThinkingService, decorators: [{
63
68
  type: Injectable,
@@ -65,17 +70,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
65
70
  }] });
66
71
 
67
72
  class AuthenticationService {
68
- authenticated = signal(false, ...(ngDevMode ? [{ debugName: "authenticated" }] : /* istanbul ignore next */ []));
69
- authenticating = signal(false, ...(ngDevMode ? [{ debugName: "authenticating" }] : /* istanbul ignore next */ []));
70
- userSession = signal(undefined, ...(ngDevMode ? [{ debugName: "userSession" }] : /* istanbul ignore next */ []));
71
- effectivePermissions = signal({}, ...(ngDevMode ? [{ debugName: "effectivePermissions" }] : /* istanbul ignore next */ []));
72
- conflictSessions = signal(undefined, ...(ngDevMode ? [{ debugName: "conflictSessions" }] : /* istanbul ignore next */ []));
73
- #refreshing = null;
74
- #auth = 'Bearer ..';
75
- #httpClient = inject(HttpClient);
76
- #thinkingService = inject(ThinkingService);
77
- #router = inject(Router);
78
- #authUrls = inject(SHELL_AUTH_URLS);
73
+ constructor() {
74
+ this.authenticated = signal(false, ...(ngDevMode ? [{ debugName: "authenticated" }] : /* istanbul ignore next */ []));
75
+ this.authenticating = signal(false, ...(ngDevMode ? [{ debugName: "authenticating" }] : /* istanbul ignore next */ []));
76
+ this.userSession = signal(undefined, ...(ngDevMode ? [{ debugName: "userSession" }] : /* istanbul ignore next */ []));
77
+ this.effectivePermissions = signal({}, ...(ngDevMode ? [{ debugName: "effectivePermissions" }] : /* istanbul ignore next */ []));
78
+ this.conflictSessions = signal(undefined, ...(ngDevMode ? [{ debugName: "conflictSessions" }] : /* istanbul ignore next */ []));
79
+ this.#refreshing = null;
80
+ this.#auth = 'Bearer ..';
81
+ this.#httpClient = inject(HttpClient);
82
+ this.#thinkingService = inject(ThinkingService);
83
+ this.#router = inject(Router);
84
+ this.#authUrls = inject(SHELL_AUTH_URLS);
85
+ }
86
+ #refreshing;
87
+ #auth;
88
+ #httpClient;
89
+ #thinkingService;
90
+ #router;
91
+ #authUrls;
79
92
  get auth() {
80
93
  return this.#auth;
81
94
  }
@@ -211,8 +224,8 @@ class AuthenticationService {
211
224
  this.#auth = 'Bearer ..';
212
225
  this.#router.navigate(['/login']).catch(console.error);
213
226
  }
214
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: AuthenticationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
215
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: AuthenticationService, providedIn: 'root' });
227
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: AuthenticationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
228
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: AuthenticationService, providedIn: 'root' }); }
216
229
  }
217
230
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: AuthenticationService, decorators: [{
218
231
  type: Injectable,
@@ -267,15 +280,17 @@ const authInterceptor = (req, next) => {
267
280
  };
268
281
 
269
282
  class RegexValidatorDirective {
270
- regex = input(/^[A-Z0-9._%+-@!?]{4,64}$/i, ...(ngDevMode ? [{ debugName: "regex" }] : /* istanbul ignore next */ []));
283
+ constructor() {
284
+ this.regex = input(/^[A-Z0-9._%+-@!?]{4,64}$/i, ...(ngDevMode ? [{ debugName: "regex" }] : /* istanbul ignore next */ []));
285
+ }
271
286
  validate(control) {
272
287
  if (!control.value || !this.regex().test(control.value)) {
273
288
  return { regexValidator: { value: control.value } };
274
289
  }
275
290
  return null;
276
291
  }
277
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: RegexValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
278
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.7", type: RegexValidatorDirective, isStandalone: true, selector: "[regexValidator]", inputs: { regex: { classPropertyName: "regex", publicName: "regex", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: NG_VALIDATORS, useExisting: RegexValidatorDirective, multi: true }], ngImport: i0 });
292
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: RegexValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
293
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.7", type: RegexValidatorDirective, isStandalone: true, selector: "[regexValidator]", inputs: { regex: { classPropertyName: "regex", publicName: "regex", isSignal: true, isRequired: false, transformFunction: null } }, providers: [{ provide: NG_VALIDATORS, useExisting: RegexValidatorDirective, multi: true }], ngImport: i0 }); }
279
294
  }
280
295
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: RegexValidatorDirective, decorators: [{
281
296
  type: Directive,
@@ -286,10 +301,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
286
301
  }], propDecorators: { regex: [{ type: i0.Input, args: [{ isSignal: true, alias: "regex", required: false }] }] } });
287
302
 
288
303
  class ThemeService {
289
- mode = signal('dark', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
290
- isDark = computed(() => this.mode() === 'dark', ...(ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []));
291
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
292
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThemeService, providedIn: 'root' });
304
+ constructor() {
305
+ this.mode = signal('dark', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
306
+ this.isDark = computed(() => this.mode() === 'dark', ...(ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []));
307
+ }
308
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
309
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThemeService, providedIn: 'root' }); }
293
310
  }
294
311
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ThemeService, decorators: [{
295
312
  type: Injectable,
@@ -297,12 +314,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
297
314
  }] });
298
315
 
299
316
  class TerminalFocusService {
300
- focusRequested = signal(0, ...(ngDevMode ? [{ debugName: "focusRequested" }] : /* istanbul ignore next */ []));
317
+ constructor() {
318
+ this.focusRequested = signal(0, ...(ngDevMode ? [{ debugName: "focusRequested" }] : /* istanbul ignore next */ []));
319
+ }
301
320
  requestFocus() {
302
321
  this.focusRequested.update(v => v + 1);
303
322
  }
304
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: TerminalFocusService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
305
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: TerminalFocusService, providedIn: 'root' });
323
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: TerminalFocusService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
324
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: TerminalFocusService, providedIn: 'root' }); }
306
325
  }
307
326
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: TerminalFocusService, decorators: [{
308
327
  type: Injectable,
@@ -310,22 +329,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
310
329
  }] });
311
330
 
312
331
  class KeyboardShortcutService {
313
- #groups = new Map();
314
- #activeGroupId = signal(null, ...(ngDevMode ? [{ debugName: "#activeGroupId" }] : /* istanbul ignore next */ []));
315
- #document = inject(DOCUMENT);
316
- #zone = inject(NgZone);
317
- #focusService = inject(TerminalFocusService);
318
- helpVisible = signal(false, ...(ngDevMode ? [{ debugName: "helpVisible" }] : /* istanbul ignore next */ []));
319
- activeShortcuts = computed(() => {
320
- const groupId = this.#activeGroupId();
321
- if (!groupId)
322
- return null;
323
- const shortcuts = this.#groups.get(groupId);
324
- if (!shortcuts)
325
- return null;
326
- return { groupId, shortcuts };
327
- }, ...(ngDevMode ? [{ debugName: "activeShortcuts" }] : /* istanbul ignore next */ []));
332
+ #groups;
333
+ #activeGroupId;
334
+ #document;
335
+ #zone;
336
+ #focusService;
328
337
  constructor() {
338
+ this.#groups = new Map();
339
+ this.#activeGroupId = signal(null, ...(ngDevMode ? [{ debugName: "#activeGroupId" }] : /* istanbul ignore next */ []));
340
+ this.#document = inject(DOCUMENT);
341
+ this.#zone = inject(NgZone);
342
+ this.#focusService = inject(TerminalFocusService);
343
+ this.helpVisible = signal(false, ...(ngDevMode ? [{ debugName: "helpVisible" }] : /* istanbul ignore next */ []));
344
+ this.activeShortcuts = computed(() => {
345
+ const groupId = this.#activeGroupId();
346
+ if (!groupId)
347
+ return null;
348
+ const shortcuts = this.#groups.get(groupId);
349
+ if (!shortcuts)
350
+ return null;
351
+ return { groupId, shortcuts };
352
+ }, ...(ngDevMode ? [{ debugName: "activeShortcuts" }] : /* istanbul ignore next */ []));
329
353
  this.#zone.runOutsideAngular(() => {
330
354
  this.#document.addEventListener('keydown', (event) => {
331
355
  this.#handleKeydown(event);
@@ -380,8 +404,8 @@ class KeyboardShortcutService {
380
404
  }
381
405
  return false;
382
406
  }
383
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KeyboardShortcutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
384
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KeyboardShortcutService, providedIn: 'root' });
407
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KeyboardShortcutService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
408
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KeyboardShortcutService, providedIn: 'root' }); }
385
409
  }
386
410
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: KeyboardShortcutService, decorators: [{
387
411
  type: Injectable,
@@ -389,8 +413,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
389
413
  }], ctorParameters: () => [] });
390
414
 
391
415
  class CommandRegistryService {
392
- commands = new Map();
393
416
  constructor() {
417
+ this.commands = new Map();
394
418
  const appCommands = inject(SHELL_COMMANDS, { optional: true }) ?? [];
395
419
  for (const cmd of appCommands) {
396
420
  this.register(cmd);
@@ -421,8 +445,8 @@ class CommandRegistryService {
421
445
  .filter(name => name.startsWith(prefix) && name !== prefix)
422
446
  .sort();
423
447
  }
424
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
425
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandRegistryService, providedIn: 'root' });
448
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
449
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandRegistryService, providedIn: 'root' }); }
426
450
  }
427
451
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandRegistryService, decorators: [{
428
452
  type: Injectable,
@@ -446,22 +470,22 @@ const HELP_USAGE = 'help [command]\n\n' +
446
470
 
447
471
  const CommandInputSchema = z.string().trim().min(1, 'Command cannot be empty');
448
472
  class CommandService {
449
- history = signal([], ...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
450
- currentInput = signal('', ...(ngDevMode ? [{ debugName: "currentInput" }] : /* istanbul ignore next */ []));
451
- isProcessing = signal(false, ...(ngDevMode ? [{ debugName: "isProcessing" }] : /* istanbul ignore next */ []));
452
- authService = inject(AuthenticationService);
453
- registry = inject(CommandRegistryService);
454
473
  constructor() {
474
+ this.history = signal([], ...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
475
+ this.currentInput = signal('', ...(ngDevMode ? [{ debugName: "currentInput" }] : /* istanbul ignore next */ []));
476
+ this.isProcessing = signal(false, ...(ngDevMode ? [{ debugName: "isProcessing" }] : /* istanbul ignore next */ []));
477
+ this.authService = inject(AuthenticationService);
478
+ this.registry = inject(CommandRegistryService);
479
+ this.lastResult = computed(() => {
480
+ const h = this.history();
481
+ return h.length > 0 ? h[h.length - 1].result : null;
482
+ }, ...(ngDevMode ? [{ debugName: "lastResult" }] : /* istanbul ignore next */ []));
455
483
  effect(() => {
456
484
  if (!this.authService.authenticated()) {
457
485
  this.history.set([]);
458
486
  }
459
487
  });
460
488
  }
461
- lastResult = computed(() => {
462
- const h = this.history();
463
- return h.length > 0 ? h[h.length - 1].result : null;
464
- }, ...(ngDevMode ? [{ debugName: "lastResult" }] : /* istanbul ignore next */ []));
465
489
  echoCommand(input) {
466
490
  if (this.#coalesce(input))
467
491
  return;
@@ -585,8 +609,8 @@ class CommandService {
585
609
  return { success: false, message: `Unknown command: ${name}`, timestamp: new Date() };
586
610
  return { success: true, message: def.usage, timestamp: new Date() };
587
611
  }
588
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
589
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandService, providedIn: 'root' });
612
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
613
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandService, providedIn: 'root' }); }
590
614
  }
591
615
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandService, decorators: [{
592
616
  type: Injectable,
@@ -594,22 +618,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
594
618
  }], ctorParameters: () => [] });
595
619
 
596
620
  class StatusBar {
597
- #authService = inject(AuthenticationService);
598
- #focusService = inject(TerminalFocusService);
599
- #commandService = inject(CommandService);
600
- isAuthenticated = this.#authService.authenticated;
601
- loginStatus = computed(() => this.#authService.authenticating()
602
- ? 'Authenticating...'
603
- : this.#authService.authenticated()
604
- ? 'Logged in'
605
- : 'Not logged in', ...(ngDevMode ? [{ debugName: "loginStatus" }] : /* istanbul ignore next */ []));
621
+ constructor() {
622
+ this.#authService = inject(AuthenticationService);
623
+ this.#focusService = inject(TerminalFocusService);
624
+ this.#commandService = inject(CommandService);
625
+ this.isAuthenticated = this.#authService.authenticated;
626
+ this.loginStatus = computed(() => this.#authService.authenticating()
627
+ ? 'Authenticating...'
628
+ : this.#authService.authenticated()
629
+ ? 'Logged in'
630
+ : 'Not logged in', ...(ngDevMode ? [{ debugName: "loginStatus" }] : /* istanbul ignore next */ []));
631
+ }
632
+ #authService;
633
+ #focusService;
634
+ #commandService;
606
635
  logout() {
607
636
  this.#authService.logout();
608
637
  this.#commandService.echoCommand('logout');
609
638
  this.#focusService.requestFocus();
610
639
  }
611
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: StatusBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
612
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: StatusBar, isStandalone: true, selector: "shell-status-bar", ngImport: i0, template: "<div class=\"status-bar\">\n <div class=\"status-item\">\n <span class=\"status-label text-secondary-info\">Status:</span>\n @if (isAuthenticated()) {\n <a class=\"status-value text-interactive logout-link\" (click)=\"logout()\">Log Out</a>\n } @else {\n <span class=\"status-value text-loss\">{{ loginStatus() }}</span>\n }\n </div>\n @if (isAuthenticated()) {\n <ng-content />\n }\n</div>\n", styles: [".status-bar{display:flex;align-items:center;gap:24px;padding:0 16px;height:100%;background-color:var(--color-surface);border-bottom:1px solid var(--color-surface-border);font-size:.8rem}.status-item{display:flex;align-items:baseline;gap:6px}.status-label{font-weight:500;text-transform:uppercase;letter-spacing:.05em;font-size:.7rem}.logout-link{cursor:pointer;text-decoration:underline}.logout-link:hover{opacity:.8}\n"] });
640
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: StatusBar, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
641
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: StatusBar, isStandalone: true, selector: "shell-status-bar", ngImport: i0, template: "<div class=\"status-bar\">\n <div class=\"status-item\">\n <span class=\"status-label text-secondary-info\">Status:</span>\n @if (isAuthenticated()) {\n <a class=\"status-value text-interactive logout-link\" (click)=\"logout()\">Log Out</a>\n } @else {\n <span class=\"status-value text-loss\">{{ loginStatus() }}</span>\n }\n </div>\n @if (isAuthenticated()) {\n <ng-content />\n }\n</div>\n", styles: [".status-bar{display:flex;align-items:center;gap:24px;padding:0 16px;height:100%;background-color:var(--color-surface);border-bottom:1px solid var(--color-surface-border);font-size:.8rem}.status-item{display:flex;align-items:baseline;gap:6px}.status-label{font-weight:500;text-transform:uppercase;letter-spacing:.05em;font-size:.7rem}.logout-link{cursor:pointer;text-decoration:underline}.logout-link:hover{opacity:.8}\n"] }); }
613
642
  }
614
643
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: StatusBar, decorators: [{
615
644
  type: Component,
@@ -617,12 +646,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
617
646
  }] });
618
647
 
619
648
  class SideMenu {
620
- #focusService = inject(TerminalFocusService);
621
- #commandService = inject(CommandService);
622
- #shortcutService = inject(KeyboardShortcutService);
623
- hasShortcuts = this.#shortcutService.activeShortcuts;
624
- #allMenuItems = inject(SHELL_MENU_ITEMS, { optional: true }) ?? [];
625
- menuItems = computed(() => this.#allMenuItems.filter(item => !item.visible || item.visible()), ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
649
+ constructor() {
650
+ this.#focusService = inject(TerminalFocusService);
651
+ this.#commandService = inject(CommandService);
652
+ this.#shortcutService = inject(KeyboardShortcutService);
653
+ this.hasShortcuts = this.#shortcutService.activeShortcuts;
654
+ this.#allMenuItems = inject(SHELL_MENU_ITEMS, { optional: true }) ?? [];
655
+ this.menuItems = computed(() => this.#allMenuItems.filter(item => !item.visible || item.visible()), ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
656
+ }
657
+ #focusService;
658
+ #commandService;
659
+ #shortcutService;
660
+ #allMenuItems;
626
661
  onMenuClick(item) {
627
662
  if (item.command) {
628
663
  this.#commandService.echoCommand(item.command);
@@ -632,8 +667,8 @@ class SideMenu {
632
667
  toggleHelp() {
633
668
  this.#shortcutService.toggleHelp();
634
669
  }
635
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SideMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
636
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: SideMenu, isStandalone: true, selector: "shell-side-menu", ngImport: i0, template: "<nav class=\"side-menu\">\n @for (item of menuItems(); track item.route) {\n <a\n class=\"menu-item\"\n [routerLink]=\"item.route\"\n routerLinkActive=\"active\"\n [matTooltip]=\"item.label\"\n matTooltipPosition=\"right\"\n (click)=\"onMenuClick(item)\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n </a>\n }\n <div class=\"menu-spacer\"></div>\n @if (hasShortcuts()) {\n <button\n class=\"help-button\"\n matTooltip=\"Keyboard shortcuts\"\n matTooltipPosition=\"right\"\n (click)=\"toggleHelp()\"\n >\n <span class=\"help-modifier\">Alt+</span><span class=\"help-key\">?</span>\n </button>\n }\n</nav>\n", styles: [".side-menu{display:flex;flex-direction:column;align-items:center;padding-top:8px;gap:4px;height:100%;background-color:var(--color-surface);border-right:1px solid var(--color-surface-border)}.menu-item{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:8px;color:var(--color-secondary-info);text-decoration:none;transition:background-color .15s,color .15s}.menu-item:hover,.menu-item.active{background-color:var(--color-surface-light);color:var(--color-interactive)}.menu-spacer{flex:1}.help-button{margin-bottom:8px;padding:4px 6px;border:1px solid var(--color-surface-border);border-radius:6px;background:none;font-size:.7rem;font-family:Roboto Mono,monospace;cursor:pointer;color:var(--color-interactive);transition:background-color .15s}.help-button:hover{background-color:var(--color-surface-light)}.help-modifier{opacity:.6}.help-key{font-weight:700}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
670
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SideMenu, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
671
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: SideMenu, isStandalone: true, selector: "shell-side-menu", ngImport: i0, template: "<nav class=\"side-menu\">\n @for (item of menuItems(); track item.route) {\n <a\n class=\"menu-item\"\n [routerLink]=\"item.route\"\n routerLinkActive=\"active\"\n [matTooltip]=\"item.label\"\n matTooltipPosition=\"right\"\n (click)=\"onMenuClick(item)\"\n >\n <mat-icon>{{ item.icon }}</mat-icon>\n </a>\n }\n <div class=\"menu-spacer\"></div>\n @if (hasShortcuts()) {\n <button\n class=\"help-button\"\n matTooltip=\"Keyboard shortcuts\"\n matTooltipPosition=\"right\"\n (click)=\"toggleHelp()\"\n >\n <span class=\"help-modifier\">Alt+</span><span class=\"help-key\">?</span>\n </button>\n }\n</nav>\n", styles: [".side-menu{display:flex;flex-direction:column;align-items:center;padding-top:8px;gap:4px;height:100%;background-color:var(--color-surface);border-right:1px solid var(--color-surface-border)}.menu-item{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:8px;color:var(--color-secondary-info);text-decoration:none;transition:background-color .15s,color .15s}.menu-item:hover,.menu-item.active{background-color:var(--color-surface-light);color:var(--color-interactive)}.menu-spacer{flex:1}.help-button{margin-bottom:8px;padding:4px 6px;border:1px solid var(--color-surface-border);border-radius:6px;background:none;font-size:.7rem;font-family:Roboto Mono,monospace;cursor:pointer;color:var(--color-interactive);transition:background-color .15s}.help-button:hover{background-color:var(--color-surface-light)}.help-modifier{opacity:.6}.help-key{font-weight:700}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] }); }
637
672
  }
638
673
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: SideMenu, decorators: [{
639
674
  type: Component,
@@ -641,11 +676,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
641
676
  }] });
642
677
 
643
678
  class MainContent {
644
- #thinkingService = inject(ThinkingService);
645
- #shortcutService = inject(KeyboardShortcutService);
646
- isThinking = this.#thinkingService.isThinking;
647
- shortcutHelpVisible = this.#shortcutService.helpVisible;
648
- activeShortcuts = this.#shortcutService.activeShortcuts;
679
+ constructor() {
680
+ this.#thinkingService = inject(ThinkingService);
681
+ this.#shortcutService = inject(KeyboardShortcutService);
682
+ this.isThinking = this.#thinkingService.isThinking;
683
+ this.shortcutHelpVisible = this.#shortcutService.helpVisible;
684
+ this.activeShortcuts = this.#shortcutService.activeShortcuts;
685
+ }
686
+ #thinkingService;
687
+ #shortcutService;
649
688
  formatKey(key) {
650
689
  const keyMap = {
651
690
  ArrowUp: '↑', ArrowDown: '↓', ArrowLeft: '←', ArrowRight: '→',
@@ -656,8 +695,8 @@ class MainContent {
656
695
  closeShortcutHelp() {
657
696
  this.#shortcutService.helpVisible.set(false);
658
697
  }
659
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MainContent, deps: [], target: i0.ɵɵFactoryTarget.Component });
660
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: MainContent, isStandalone: true, selector: "shell-main-content", ngImport: i0, template: "<div class=\"main-content\">\n <div class=\"content-area\">\n <router-outlet />\n </div>\n @if (shortcutHelpVisible() && activeShortcuts()) {\n <div class=\"shortcut-overlay\" (click)=\"closeShortcutHelp()\">\n <div class=\"shortcut-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"shortcut-title text-secondary-info\">{{ activeShortcuts()!.groupId }} shortcuts</div>\n @for (s of activeShortcuts()!.shortcuts; track s.key) {\n <div class=\"shortcut-row\">\n <kbd class=\"shortcut-key\">Alt+{{ formatKey(s.key) }}</kbd>\n <span class=\"shortcut-desc text-text-primary\">{{ s.description }}</span>\n </div>\n }\n <div class=\"shortcut-row shortcut-dismiss\">\n <kbd class=\"shortcut-key\">Alt+?</kbd>\n <span class=\"shortcut-desc text-secondary-info\">or</span>\n <kbd class=\"shortcut-key\">Esc</kbd>\n <span class=\"shortcut-desc text-secondary-info\">dismiss</span>\n </div>\n </div>\n </div>\n }\n @if (isThinking()) {\n <div class=\"thinking-overlay\">\n <mat-spinner diameter=\"48\" />\n </div>\n }\n</div>\n", styles: [".main-content{position:relative;height:100%;overflow:hidden}.content-area{height:100%;overflow-y:auto;padding:16px}.shortcut-overlay{position:absolute;inset:0;z-index:15;display:flex;align-items:center;justify-content:center;background-color:#0a0e1480}.shortcut-panel{background-color:var(--color-terminal-bg);border:1px solid var(--color-surface-border);border-radius:6px;padding:14px 18px;font-size:.8rem;box-shadow:0 4px 16px #00000080;min-width:220px}.shortcut-title{font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px;font-weight:500}.shortcut-row{display:flex;align-items:center;gap:10px;padding:3px 0}.shortcut-row.shortcut-dismiss{margin-top:6px;border-top:1px solid var(--color-surface-border);padding-top:8px}.shortcut-key{font-family:Roboto Mono,monospace;font-size:.75rem;background-color:#ffffff14;border:1px solid var(--color-surface-border);border-radius:3px;padding:1px 6px;min-width:70px;text-align:center}.shortcut-desc{font-size:.78rem}.thinking-overlay{position:absolute;inset:0;z-index:20;background-color:#0a0e14b3;display:flex;align-items:center;justify-content:center;pointer-events:all;cursor:wait}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i1$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] });
698
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MainContent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
699
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: MainContent, isStandalone: true, selector: "shell-main-content", ngImport: i0, template: "<div class=\"main-content\">\n <div class=\"content-area\">\n <router-outlet />\n </div>\n @if (shortcutHelpVisible() && activeShortcuts()) {\n <div class=\"shortcut-overlay\" (click)=\"closeShortcutHelp()\">\n <div class=\"shortcut-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"shortcut-title text-secondary-info\">{{ activeShortcuts()!.groupId }} shortcuts</div>\n @for (s of activeShortcuts()!.shortcuts; track s.key) {\n <div class=\"shortcut-row\">\n <kbd class=\"shortcut-key\">Alt+{{ formatKey(s.key) }}</kbd>\n <span class=\"shortcut-desc text-text-primary\">{{ s.description }}</span>\n </div>\n }\n <div class=\"shortcut-row shortcut-dismiss\">\n <kbd class=\"shortcut-key\">Alt+?</kbd>\n <span class=\"shortcut-desc text-secondary-info\">or</span>\n <kbd class=\"shortcut-key\">Esc</kbd>\n <span class=\"shortcut-desc text-secondary-info\">dismiss</span>\n </div>\n </div>\n </div>\n }\n @if (isThinking()) {\n <div class=\"thinking-overlay\">\n <mat-spinner diameter=\"48\" />\n </div>\n }\n</div>\n", styles: [".main-content{position:relative;height:100%;overflow:hidden}.content-area{height:100%;overflow-y:auto;padding:16px}.shortcut-overlay{position:absolute;inset:0;z-index:15;display:flex;align-items:center;justify-content:center;background-color:#0a0e1480}.shortcut-panel{background-color:var(--color-terminal-bg);border:1px solid var(--color-surface-border);border-radius:6px;padding:14px 18px;font-size:.8rem;box-shadow:0 4px 16px #00000080;min-width:220px}.shortcut-title{font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px;font-weight:500}.shortcut-row{display:flex;align-items:center;gap:10px;padding:3px 0}.shortcut-row.shortcut-dismiss{margin-top:6px;border-top:1px solid var(--color-surface-border);padding-top:8px}.shortcut-key{font-family:Roboto Mono,monospace;font-size:.75rem;background-color:#ffffff14;border:1px solid var(--color-surface-border);border-radius:3px;padding:1px 6px;min-width:70px;text-align:center}.shortcut-desc{font-size:.78rem}.thinking-overlay{position:absolute;inset:0;z-index:20;background-color:#0a0e14b3;display:flex;align-items:center;justify-content:center;pointer-events:all;cursor:wait}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i1$1.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }] }); }
661
700
  }
662
701
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MainContent, decorators: [{
663
702
  type: Component,
@@ -665,25 +704,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
665
704
  }] });
666
705
 
667
706
  class CommandTerminal {
668
- commandService = inject(CommandService);
669
- registry = inject(CommandRegistryService);
670
- authService = inject(AuthenticationService);
671
- terminalFocusService = inject(TerminalFocusService);
672
- history = computed(() => [...this.commandService.history()].reverse(), ...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
673
- currentInput = this.commandService.currentInput;
674
- isProcessing = this.commandService.isProcessing;
675
- inputEl = viewChild('terminalInput', ...(ngDevMode ? [{ debugName: "inputEl" }] : /* istanbul ignore next */ []));
676
- tabCycleIndex = -1;
677
- lastTabPrefix = '';
678
- ghostText = computed(() => {
679
- const input = this.currentInput().trim();
680
- if (!input || input.includes(' '))
681
- return '';
682
- const prefix = input.toLowerCase();
683
- const suggestions = this.registry.getSuggestions(prefix, this.authService.authenticated());
684
- return suggestions.length > 0 ? suggestions[0] : '';
685
- }, ...(ngDevMode ? [{ debugName: "ghostText" }] : /* istanbul ignore next */ []));
686
707
  constructor() {
708
+ this.commandService = inject(CommandService);
709
+ this.registry = inject(CommandRegistryService);
710
+ this.authService = inject(AuthenticationService);
711
+ this.terminalFocusService = inject(TerminalFocusService);
712
+ this.history = computed(() => [...this.commandService.history()].reverse(), ...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
713
+ this.currentInput = this.commandService.currentInput;
714
+ this.isProcessing = this.commandService.isProcessing;
715
+ this.inputEl = viewChild('terminalInput', ...(ngDevMode ? [{ debugName: "inputEl" }] : /* istanbul ignore next */ []));
716
+ this.tabCycleIndex = -1;
717
+ this.lastTabPrefix = '';
718
+ this.ghostText = computed(() => {
719
+ const input = this.currentInput().trim();
720
+ if (!input || input.includes(' '))
721
+ return '';
722
+ const prefix = input.toLowerCase();
723
+ const suggestions = this.registry.getSuggestions(prefix, this.authService.authenticated());
724
+ return suggestions.length > 0 ? suggestions[0] : '';
725
+ }, ...(ngDevMode ? [{ debugName: "ghostText" }] : /* istanbul ignore next */ []));
687
726
  afterNextRender(() => this.focusInput());
688
727
  effect(() => {
689
728
  if (!this.isProcessing()) {
@@ -748,8 +787,8 @@ class CommandTerminal {
748
787
  focusInput() {
749
788
  this.inputEl()?.nativeElement.focus();
750
789
  }
751
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandTerminal, deps: [], target: i0.ɵɵFactoryTarget.Component });
752
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: CommandTerminal, isStandalone: true, selector: "shell-command-terminal", viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["terminalInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"terminal\">\n <div class=\"terminal-input-row\">\n <span class=\"prompt\">$</span>\n <div class=\"input-wrapper\">\n @if (ghostText()) {\n <input\n class=\"ghost-input\"\n type=\"text\"\n [value]=\"ghostText()\"\n disabled\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <input\n #terminalInput\n class=\"terminal-input\"\n type=\"text\"\n [ngModel]=\"currentInput()\"\n (ngModelChange)=\"onInputChange($event)\"\n (keydown.enter)=\"onSubmit()\"\n (keydown.tab)=\"onTab($any($event))\"\n placeholder=\"Type a command...\"\n [disabled]=\"isProcessing()\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n />\n </div>\n </div>\n <div class=\"terminal-output\">\n @for (entry of history(); track entry.result.timestamp) {\n @if (entry.input) {\n <div class=\"terminal-line\">\n <span class=\"prompt\">$</span>\n <span class=\"command-text\">{{ entry.input }}@if ((entry.repeatCount ?? 1) > 1) { <span class=\"repeat-count\">(\u00D7{{ entry.repeatCount }})</span>}</span>\n </div>\n }\n @if (entry.result.message) {\n <div class=\"terminal-line\" [class]=\"getSeverityClass(entry.result)\">{{ entry.result.message }}</div>\n }\n }\n </div>\n</div>\n", styles: [".terminal{display:flex;flex-direction:column;height:100%;background-color:var(--color-terminal-bg);border-top:1px solid var(--color-surface-border);font-family:Roboto Mono,Courier New,monospace;font-size:.85rem}.terminal-output{flex:1;overflow-y:auto;padding:8px 12px;display:flex;flex-direction:column}.terminal-line{line-height:1.6;color:var(--color-text-primary);white-space:pre-wrap}.terminal-line.error{color:var(--color-loss)}.terminal-line.warning{color:var(--color-warning)}.terminal-line.info{color:var(--color-info)}.prompt{color:var(--color-terminal-prompt);margin-right:8px;font-weight:700}.command-text{color:var(--color-terminal-text)}.repeat-count{color:var(--color-secondary-info);opacity:.6;margin-left:4px}.terminal-input-row{display:flex;align-items:center;padding:8px 12px;border-bottom:1px solid var(--color-surface-border)}.input-wrapper{flex:1;display:grid}.ghost-input,.terminal-input{grid-area:1 / 1;font-family:inherit;font-size:inherit;line-height:normal;margin:0;padding:0;border:none;background:transparent;outline:none;-webkit-appearance:none;appearance:none;width:100%}.ghost-input{color:var(--color-secondary-info);opacity:.3;pointer-events:none}.terminal-input{color:var(--color-terminal-text);caret-color:var(--color-terminal-prompt)}.terminal-input::placeholder{color:var(--color-secondary-info);opacity:.5}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
790
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandTerminal, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
791
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: CommandTerminal, isStandalone: true, selector: "shell-command-terminal", viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["terminalInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"terminal\">\n <div class=\"terminal-input-row\">\n <span class=\"prompt\">$</span>\n <div class=\"input-wrapper\">\n @if (ghostText()) {\n <input\n class=\"ghost-input\"\n type=\"text\"\n [value]=\"ghostText()\"\n disabled\n tabindex=\"-1\"\n aria-hidden=\"true\"\n />\n }\n <input\n #terminalInput\n class=\"terminal-input\"\n type=\"text\"\n [ngModel]=\"currentInput()\"\n (ngModelChange)=\"onInputChange($event)\"\n (keydown.enter)=\"onSubmit()\"\n (keydown.tab)=\"onTab($any($event))\"\n placeholder=\"Type a command...\"\n [disabled]=\"isProcessing()\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n />\n </div>\n </div>\n <div class=\"terminal-output\">\n @for (entry of history(); track entry.result.timestamp) {\n @if (entry.input) {\n <div class=\"terminal-line\">\n <span class=\"prompt\">$</span>\n <span class=\"command-text\">{{ entry.input }}@if ((entry.repeatCount ?? 1) > 1) { <span class=\"repeat-count\">(\u00D7{{ entry.repeatCount }})</span>}</span>\n </div>\n }\n @if (entry.result.message) {\n <div class=\"terminal-line\" [class]=\"getSeverityClass(entry.result)\">{{ entry.result.message }}</div>\n }\n }\n </div>\n</div>\n", styles: [".terminal{display:flex;flex-direction:column;height:100%;background-color:var(--color-terminal-bg);border-top:1px solid var(--color-surface-border);font-family:Roboto Mono,Courier New,monospace;font-size:.85rem}.terminal-output{flex:1;overflow-y:auto;padding:8px 12px;display:flex;flex-direction:column}.terminal-line{line-height:1.6;color:var(--color-text-primary);white-space:pre-wrap}.terminal-line.error{color:var(--color-loss)}.terminal-line.warning{color:var(--color-warning)}.terminal-line.info{color:var(--color-info)}.prompt{color:var(--color-terminal-prompt);margin-right:8px;font-weight:700}.command-text{color:var(--color-terminal-text)}.repeat-count{color:var(--color-secondary-info);opacity:.6;margin-left:4px}.terminal-input-row{display:flex;align-items:center;padding:8px 12px;border-bottom:1px solid var(--color-surface-border)}.input-wrapper{flex:1;display:grid}.ghost-input,.terminal-input{grid-area:1 / 1;font-family:inherit;font-size:inherit;line-height:normal;margin:0;padding:0;border:none;background:transparent;outline:none;-webkit-appearance:none;appearance:none;width:100%}.ghost-input{color:var(--color-secondary-info);opacity:.3;pointer-events:none}.terminal-input{color:var(--color-terminal-text);caret-color:var(--color-terminal-prompt)}.terminal-input::placeholder{color:var(--color-secondary-info);opacity:.5}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] }); }
753
792
  }
754
793
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: CommandTerminal, decorators: [{
755
794
  type: Component,
@@ -760,23 +799,30 @@ const MIN_TERMINAL_HEIGHT = 80;
760
799
  const MAX_TERMINAL_RATIO = 0.6;
761
800
  const DEFAULT_TERMINAL_HEIGHT = 160;
762
801
  class Shell {
763
- #thinkingService = inject(ThinkingService);
764
- isThinking = this.#thinkingService.isThinking;
765
- terminalHeight = signal(`${DEFAULT_TERMINAL_HEIGHT}px`, ...(ngDevMode ? [{ debugName: "terminalHeight" }] : /* istanbul ignore next */ []));
766
- isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
767
- #startY = 0;
768
- #startHeight = DEFAULT_TERMINAL_HEIGHT;
769
- #onMouseMove = (event) => {
770
- const deltaY = event.clientY - this.#startY;
771
- const maxHeight = Math.floor(window.innerHeight * MAX_TERMINAL_RATIO);
772
- const newHeight = Math.min(maxHeight, Math.max(MIN_TERMINAL_HEIGHT, this.#startHeight - deltaY));
773
- this.terminalHeight.set(`${newHeight}px`);
774
- };
775
- #onMouseUp = () => {
776
- this.isDragging.set(false);
777
- document.removeEventListener('mousemove', this.#onMouseMove);
778
- document.removeEventListener('mouseup', this.#onMouseUp);
779
- };
802
+ constructor() {
803
+ this.#thinkingService = inject(ThinkingService);
804
+ this.isThinking = this.#thinkingService.isThinking;
805
+ this.terminalHeight = signal(`${DEFAULT_TERMINAL_HEIGHT}px`, ...(ngDevMode ? [{ debugName: "terminalHeight" }] : /* istanbul ignore next */ []));
806
+ this.isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
807
+ this.#startY = 0;
808
+ this.#startHeight = DEFAULT_TERMINAL_HEIGHT;
809
+ this.#onMouseMove = (event) => {
810
+ const deltaY = event.clientY - this.#startY;
811
+ const maxHeight = Math.floor(window.innerHeight * MAX_TERMINAL_RATIO);
812
+ const newHeight = Math.min(maxHeight, Math.max(MIN_TERMINAL_HEIGHT, this.#startHeight - deltaY));
813
+ this.terminalHeight.set(`${newHeight}px`);
814
+ };
815
+ this.#onMouseUp = () => {
816
+ this.isDragging.set(false);
817
+ document.removeEventListener('mousemove', this.#onMouseMove);
818
+ document.removeEventListener('mouseup', this.#onMouseUp);
819
+ };
820
+ }
821
+ #thinkingService;
822
+ #startY;
823
+ #startHeight;
824
+ #onMouseMove;
825
+ #onMouseUp;
780
826
  onResizeStart(event) {
781
827
  event.preventDefault();
782
828
  this.#startY = event.clientY;
@@ -785,29 +831,30 @@ class Shell {
785
831
  document.addEventListener('mousemove', this.#onMouseMove);
786
832
  document.addEventListener('mouseup', this.#onMouseUp);
787
833
  }
788
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: Shell, deps: [], target: i0.ɵɵFactoryTarget.Component });
789
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: Shell, isStandalone: true, selector: "shell-root", ngImport: i0, template: "<div class=\"shell\" [style.--terminal-height]=\"terminalHeight()\" [class.is-dragging]=\"isDragging()\">\n <shell-status-bar class=\"shell-status\" [class.disabled-zone]=\"isThinking()\" />\n <shell-side-menu class=\"shell-menu\" [class.disabled-zone]=\"isThinking()\" />\n <shell-main-content class=\"shell-main\" />\n <div class=\"shell-resize\" [class.dragging]=\"isDragging()\" (mousedown)=\"onResizeStart($event)\"></div>\n <shell-command-terminal class=\"shell-terminal\" [class.disabled-zone]=\"isThinking()\" />\n</div>\n", styles: [".shell{display:grid;grid-template-areas:\"status status\" \"menu main\" \"resize resize\" \"terminal terminal\";grid-template-columns:56px 1fr;grid-template-rows:48px 1fr 4px var(--terminal-height, 160px);height:100vh;width:100vw;overflow:hidden;background-color:var(--color-surface-dark)}.shell-status{grid-area:status}.shell-menu{grid-area:menu}.shell-main{grid-area:main;overflow:hidden}.shell-resize{grid-area:resize;cursor:row-resize;background-color:var(--color-surface-border);transition:background-color .15s ease}.shell-resize:hover,.shell-resize.dragging{background-color:var(--color-interactive)}.shell-terminal{grid-area:terminal}.shell.is-dragging{user-select:none;-webkit-user-select:none}.disabled-zone{pointer-events:none;opacity:.4;transition:opacity .2s ease}\n"], dependencies: [{ kind: "component", type: StatusBar, selector: "shell-status-bar" }, { kind: "component", type: SideMenu, selector: "shell-side-menu" }, { kind: "component", type: MainContent, selector: "shell-main-content" }, { kind: "component", type: CommandTerminal, selector: "shell-command-terminal" }] });
834
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: Shell, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
835
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.7", type: Shell, isStandalone: true, selector: "shell-root", ngImport: i0, template: "<div class=\"shell\" [style.--terminal-height]=\"terminalHeight()\" [class.is-dragging]=\"isDragging()\">\n <shell-status-bar class=\"shell-status\" [class.disabled-zone]=\"isThinking()\">\n <ng-content />\n </shell-status-bar>\n <shell-side-menu class=\"shell-menu\" [class.disabled-zone]=\"isThinking()\" />\n <shell-main-content class=\"shell-main\" />\n <div class=\"shell-resize\" [class.dragging]=\"isDragging()\" (mousedown)=\"onResizeStart($event)\"></div>\n <shell-command-terminal class=\"shell-terminal\" [class.disabled-zone]=\"isThinking()\" />\n</div>\n", styles: [".shell{display:grid;grid-template-areas:\"status status\" \"menu main\" \"resize resize\" \"terminal terminal\";grid-template-columns:56px 1fr;grid-template-rows:48px 1fr 4px var(--terminal-height, 160px);height:100vh;width:100vw;overflow:hidden;background-color:var(--color-surface-dark)}.shell-status{grid-area:status}.shell-menu{grid-area:menu}.shell-main{grid-area:main;overflow:hidden}.shell-resize{grid-area:resize;cursor:row-resize;background-color:var(--color-surface-border);transition:background-color .15s ease}.shell-resize:hover,.shell-resize.dragging{background-color:var(--color-interactive)}.shell-terminal{grid-area:terminal}.shell.is-dragging{user-select:none;-webkit-user-select:none}.disabled-zone{pointer-events:none;opacity:.4;transition:opacity .2s ease}\n"], dependencies: [{ kind: "component", type: StatusBar, selector: "shell-status-bar" }, { kind: "component", type: SideMenu, selector: "shell-side-menu" }, { kind: "component", type: MainContent, selector: "shell-main-content" }, { kind: "component", type: CommandTerminal, selector: "shell-command-terminal" }] }); }
790
836
  }
791
837
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: Shell, decorators: [{
792
838
  type: Component,
793
- args: [{ selector: 'shell-root', imports: [StatusBar, SideMenu, MainContent, CommandTerminal], template: "<div class=\"shell\" [style.--terminal-height]=\"terminalHeight()\" [class.is-dragging]=\"isDragging()\">\n <shell-status-bar class=\"shell-status\" [class.disabled-zone]=\"isThinking()\" />\n <shell-side-menu class=\"shell-menu\" [class.disabled-zone]=\"isThinking()\" />\n <shell-main-content class=\"shell-main\" />\n <div class=\"shell-resize\" [class.dragging]=\"isDragging()\" (mousedown)=\"onResizeStart($event)\"></div>\n <shell-command-terminal class=\"shell-terminal\" [class.disabled-zone]=\"isThinking()\" />\n</div>\n", styles: [".shell{display:grid;grid-template-areas:\"status status\" \"menu main\" \"resize resize\" \"terminal terminal\";grid-template-columns:56px 1fr;grid-template-rows:48px 1fr 4px var(--terminal-height, 160px);height:100vh;width:100vw;overflow:hidden;background-color:var(--color-surface-dark)}.shell-status{grid-area:status}.shell-menu{grid-area:menu}.shell-main{grid-area:main;overflow:hidden}.shell-resize{grid-area:resize;cursor:row-resize;background-color:var(--color-surface-border);transition:background-color .15s ease}.shell-resize:hover,.shell-resize.dragging{background-color:var(--color-interactive)}.shell-terminal{grid-area:terminal}.shell.is-dragging{user-select:none;-webkit-user-select:none}.disabled-zone{pointer-events:none;opacity:.4;transition:opacity .2s ease}\n"] }]
839
+ args: [{ selector: 'shell-root', imports: [StatusBar, SideMenu, MainContent, CommandTerminal], template: "<div class=\"shell\" [style.--terminal-height]=\"terminalHeight()\" [class.is-dragging]=\"isDragging()\">\n <shell-status-bar class=\"shell-status\" [class.disabled-zone]=\"isThinking()\">\n <ng-content />\n </shell-status-bar>\n <shell-side-menu class=\"shell-menu\" [class.disabled-zone]=\"isThinking()\" />\n <shell-main-content class=\"shell-main\" />\n <div class=\"shell-resize\" [class.dragging]=\"isDragging()\" (mousedown)=\"onResizeStart($event)\"></div>\n <shell-command-terminal class=\"shell-terminal\" [class.disabled-zone]=\"isThinking()\" />\n</div>\n", styles: [".shell{display:grid;grid-template-areas:\"status status\" \"menu main\" \"resize resize\" \"terminal terminal\";grid-template-columns:56px 1fr;grid-template-rows:48px 1fr 4px var(--terminal-height, 160px);height:100vh;width:100vw;overflow:hidden;background-color:var(--color-surface-dark)}.shell-status{grid-area:status}.shell-menu{grid-area:menu}.shell-main{grid-area:main;overflow:hidden}.shell-resize{grid-area:resize;cursor:row-resize;background-color:var(--color-surface-border);transition:background-color .15s ease}.shell-resize:hover,.shell-resize.dragging{background-color:var(--color-interactive)}.shell-terminal{grid-area:terminal}.shell.is-dragging{user-select:none;-webkit-user-select:none}.disabled-zone{pointer-events:none;opacity:.4;transition:opacity .2s ease}\n"] }]
794
840
  }] });
795
841
 
796
842
  class Login {
797
- authenticationService;
798
- router;
799
- model = { username: '', password: '' };
800
- submitted = false;
801
- hide = signal(true, ...(ngDevMode ? [{ debugName: "hide" }] : /* istanbul ignore next */ []));
802
- errors = [];
803
- USERNAME_REGEX = USERNAME_REGEX;
804
- PASSWORD_REGEX = PASSWORD_REGEX;
805
- appName = inject(SHELL_APP_NAME, { optional: true }) ?? 'Application';
806
- #commandService = inject(CommandService);
807
- #terminalFocusService = inject(TerminalFocusService);
843
+ #commandService;
844
+ #terminalFocusService;
808
845
  constructor(authenticationService, router) {
809
846
  this.authenticationService = authenticationService;
810
847
  this.router = router;
848
+ this.model = { username: '', password: '' };
849
+ this.submitted = false;
850
+ this.hide = signal(true, ...(ngDevMode ? [{ debugName: "hide" }] : /* istanbul ignore next */ []));
851
+ this.takeover = signal(true, ...(ngDevMode ? [{ debugName: "takeover" }] : /* istanbul ignore next */ []));
852
+ this.errors = [];
853
+ this.USERNAME_REGEX = USERNAME_REGEX;
854
+ this.PASSWORD_REGEX = PASSWORD_REGEX;
855
+ this.appName = inject(SHELL_APP_NAME, { optional: true }) ?? 'Application';
856
+ this.#commandService = inject(CommandService);
857
+ this.#terminalFocusService = inject(TerminalFocusService);
811
858
  }
812
859
  onAutofill(event, inputEl) {
813
860
  if (event.isAutofilled) {
@@ -827,7 +874,7 @@ class Login {
827
874
  this.submitted = true;
828
875
  const result = validateCredentials(this.model);
829
876
  if (result === true) {
830
- this.#doLogin(false);
877
+ this.#doLogin(this.takeover());
831
878
  }
832
879
  else {
833
880
  for (let ndx = 0; ndx < result.length; ndx++) {
@@ -864,8 +911,8 @@ class Login {
864
911
  this.submitted = false;
865
912
  });
866
913
  }
867
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: Login, deps: [{ token: AuthenticationService }, { token: i2$1.Router }], target: i0.ɵɵFactoryTarget.Component });
868
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: Login, isStandalone: true, selector: "shell-login", ngImport: i0, template: "@let usernameRegex = USERNAME_REGEX;\n@let passwordRegex = PASSWORD_REGEX;\n<div class=\"login-container\">\n <div class=\"login-header\">\n <h2 class=\"text-text-primary\">{{ appName }}</h2>\n <p class=\"text-secondary-info\">Sign in to continue</p>\n </div>\n <form #loginForm=\"ngForm\" (ngSubmit)=\"onSubmit($event)\" class=\"login-form\">\n <mat-form-field appearance=\"outline\" class=\"login-field\">\n <mat-label>Username</mat-label>\n <input\n #usernameInput\n matInput\n placeholder=\"Enter username\"\n [(ngModel)]=\"model.username\"\n name=\"username\"\n [disabled]=\"submitted\"\n required\n regexValidator\n [regex]=\"usernameRegex\"\n (cdkAutofill)=\"onAutofill($event, usernameInput)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"login-field\">\n <mat-label>Password</mat-label>\n <input\n #passwordInput\n matInput\n placeholder=\"Enter password\"\n [type]=\"hide() ? 'password' : 'text'\"\n [(ngModel)]=\"model.password\"\n name=\"password\"\n [disabled]=\"submitted\"\n regexValidator\n [regex]=\"passwordRegex\"\n (cdkAutofill)=\"onAutofill($event, passwordInput)\"\n />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"clickEvent($event)\"\n [attr.aria-label]=\"'Hide password'\"\n [attr.aria-pressed]=\"hide()\"\n >\n <mat-icon>{{ hide() ? 'visibility_off' : 'visibility' }}</mat-icon>\n </button>\n </mat-form-field>\n <button mat-flat-button type=\"submit\" [disabled]=\"submitted\" class=\"login-button\">\n Login\n </button>\n </form>\n @if (errors.length > 0) {\n <div class=\"login-errors\">\n @for (error of errors; track error.ndx) {\n <div class=\"login-error text-loss\">{{ error.message }}</div>\n }\n </div>\n }\n @if (authenticationService.conflictSessions(); as sessions) {\n <div class=\"login-conflict\">\n @for (session of sessions; track $index) {\n <p class=\"text-secondary-info\">\n Already logged in from <strong>{{ session.appContext }}</strong>\n since {{ session.startEpoch | date:'short' }}.\n </p>\n }\n <p class=\"text-secondary-info\">Log in here and end that session?</p>\n <div class=\"login-conflict-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"onCancelConflict()\">Cancel</button>\n <button mat-flat-button type=\"button\" (click)=\"onTakeOver()\">Take Over</button>\n </div>\n </div>\n }\n</div>\n", styles: [".login-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;text-align:center}.login-header{margin-bottom:32px}.login-header h2{font-size:1.5rem;font-weight:300;margin-bottom:8px}.login-form{display:flex;flex-direction:column;align-items:center;gap:8px;width:320px}.login-field{width:100%;text-align:left}.login-button{margin-top:8px;width:100%}.login-errors{margin-top:16px;max-width:320px}.login-conflict{margin-top:16px;max-width:320px;padding:12px 16px;border-radius:4px;background-color:var(--color-surface-light);font-size:.85rem;text-align:left}.login-conflict p{margin:0 0 8px}.login-conflict-actions{display:flex;gap:8px;margin-top:12px;justify-content:flex-end}.login-error{padding:8px 12px;margin-bottom:4px;font-size:.85rem;border-radius:4px;background-color:var(--color-surface-light)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i5.CdkAutofill, selector: "[cdkAutofill]", outputs: ["cdkAutofill"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: RegexValidatorDirective, selector: "[regexValidator]", inputs: ["regex"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "pipe", type: DatePipe, name: "date" }] });
914
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: Login, deps: [{ token: AuthenticationService }, { token: i2$1.Router }], target: i0.ɵɵFactoryTarget.Component }); }
915
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: Login, isStandalone: true, selector: "shell-login", ngImport: i0, template: "@let usernameRegex = USERNAME_REGEX;\n@let passwordRegex = PASSWORD_REGEX;\n<div class=\"login-container\">\n <div class=\"login-header\">\n <h2 class=\"text-text-primary\">{{ appName }}</h2>\n <p class=\"text-secondary-info\">Sign in to continue</p>\n </div>\n <form #loginForm=\"ngForm\" (ngSubmit)=\"onSubmit($event)\" class=\"login-form\">\n <mat-form-field appearance=\"outline\" class=\"login-field\">\n <mat-label>Username</mat-label>\n <input\n #usernameInput\n matInput\n placeholder=\"Enter username\"\n [(ngModel)]=\"model.username\"\n name=\"username\"\n [disabled]=\"submitted\"\n required\n regexValidator\n [regex]=\"usernameRegex\"\n (cdkAutofill)=\"onAutofill($event, usernameInput)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"login-field\">\n <mat-label>Password</mat-label>\n <input\n #passwordInput\n matInput\n placeholder=\"Enter password\"\n [type]=\"hide() ? 'password' : 'text'\"\n [(ngModel)]=\"model.password\"\n name=\"password\"\n [disabled]=\"submitted\"\n regexValidator\n [regex]=\"passwordRegex\"\n (cdkAutofill)=\"onAutofill($event, passwordInput)\"\n />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"clickEvent($event)\"\n [attr.aria-label]=\"'Hide password'\"\n [attr.aria-pressed]=\"hide()\"\n >\n <mat-icon>{{ hide() ? 'visibility_off' : 'visibility' }}</mat-icon>\n </button>\n </mat-form-field>\n <mat-checkbox\n class=\"login-takeover\"\n name=\"takeover\"\n [checked]=\"takeover()\"\n (change)=\"takeover.set($event.checked)\"\n [disabled]=\"submitted\"\n >\n Takeover session if logged in elsewhere\n </mat-checkbox>\n <button mat-flat-button type=\"submit\" [disabled]=\"submitted\" class=\"login-button\">\n Login\n </button>\n </form>\n @if (errors.length > 0) {\n <div class=\"login-errors\">\n @for (error of errors; track error.ndx) {\n <div class=\"login-error text-loss\">{{ error.message }}</div>\n }\n </div>\n }\n @if (authenticationService.conflictSessions(); as sessions) {\n <div class=\"login-conflict\">\n @for (session of sessions; track $index) {\n <p class=\"text-secondary-info\">\n Already logged in from <strong>{{ session.appContext }}</strong>\n since {{ session.startEpoch | date:'short' }}.\n </p>\n }\n <p class=\"text-secondary-info\">Log in here and end that session?</p>\n <div class=\"login-conflict-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"onCancelConflict()\">Cancel</button>\n <button mat-flat-button type=\"button\" (click)=\"onTakeOver()\">Take Over</button>\n </div>\n </div>\n }\n</div>\n", styles: [".login-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;text-align:center}.login-header{margin-bottom:32px}.login-header h2{font-size:1.5rem;font-weight:300;margin-bottom:8px}.login-form{display:flex;flex-direction:column;align-items:center;gap:8px;width:320px}.login-field{width:100%;text-align:left}.login-takeover{align-self:flex-start;margin-top:4px}.login-button{margin-top:8px;width:100%}.login-errors{margin-top:16px;max-width:320px}.login-conflict{margin-top:16px;max-width:320px;padding:12px 16px;border-radius:4px;background-color:var(--color-surface-light);font-size:.85rem;text-align:left}.login-conflict p{margin:0 0 8px}.login-conflict-actions{display:flex;gap:8px;margin-top:12px;justify-content:flex-end}.login-error{padding:8px 12px;margin-bottom:4px;font-size:.85rem;border-radius:4px;background-color:var(--color-surface-light)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i5.CdkAutofill, selector: "[cdkAutofill]", outputs: ["cdkAutofill"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i7.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: RegexValidatorDirective, selector: "[regexValidator]", inputs: ["regex"] }, { kind: "ngmodule", type: TextFieldModule }, { kind: "pipe", type: DatePipe, name: "date" }] }); }
869
916
  }
870
917
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: Login, decorators: [{
871
918
  type: Component,
@@ -876,18 +923,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
876
923
  MatInput,
877
924
  MatInputModule,
878
925
  MatButtonModule,
926
+ MatCheckboxModule,
879
927
  MatIconModule,
880
928
  RegexValidatorDirective,
881
929
  TextFieldModule,
882
930
  DatePipe,
883
- ], template: "@let usernameRegex = USERNAME_REGEX;\n@let passwordRegex = PASSWORD_REGEX;\n<div class=\"login-container\">\n <div class=\"login-header\">\n <h2 class=\"text-text-primary\">{{ appName }}</h2>\n <p class=\"text-secondary-info\">Sign in to continue</p>\n </div>\n <form #loginForm=\"ngForm\" (ngSubmit)=\"onSubmit($event)\" class=\"login-form\">\n <mat-form-field appearance=\"outline\" class=\"login-field\">\n <mat-label>Username</mat-label>\n <input\n #usernameInput\n matInput\n placeholder=\"Enter username\"\n [(ngModel)]=\"model.username\"\n name=\"username\"\n [disabled]=\"submitted\"\n required\n regexValidator\n [regex]=\"usernameRegex\"\n (cdkAutofill)=\"onAutofill($event, usernameInput)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"login-field\">\n <mat-label>Password</mat-label>\n <input\n #passwordInput\n matInput\n placeholder=\"Enter password\"\n [type]=\"hide() ? 'password' : 'text'\"\n [(ngModel)]=\"model.password\"\n name=\"password\"\n [disabled]=\"submitted\"\n regexValidator\n [regex]=\"passwordRegex\"\n (cdkAutofill)=\"onAutofill($event, passwordInput)\"\n />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"clickEvent($event)\"\n [attr.aria-label]=\"'Hide password'\"\n [attr.aria-pressed]=\"hide()\"\n >\n <mat-icon>{{ hide() ? 'visibility_off' : 'visibility' }}</mat-icon>\n </button>\n </mat-form-field>\n <button mat-flat-button type=\"submit\" [disabled]=\"submitted\" class=\"login-button\">\n Login\n </button>\n </form>\n @if (errors.length > 0) {\n <div class=\"login-errors\">\n @for (error of errors; track error.ndx) {\n <div class=\"login-error text-loss\">{{ error.message }}</div>\n }\n </div>\n }\n @if (authenticationService.conflictSessions(); as sessions) {\n <div class=\"login-conflict\">\n @for (session of sessions; track $index) {\n <p class=\"text-secondary-info\">\n Already logged in from <strong>{{ session.appContext }}</strong>\n since {{ session.startEpoch | date:'short' }}.\n </p>\n }\n <p class=\"text-secondary-info\">Log in here and end that session?</p>\n <div class=\"login-conflict-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"onCancelConflict()\">Cancel</button>\n <button mat-flat-button type=\"button\" (click)=\"onTakeOver()\">Take Over</button>\n </div>\n </div>\n }\n</div>\n", styles: [".login-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;text-align:center}.login-header{margin-bottom:32px}.login-header h2{font-size:1.5rem;font-weight:300;margin-bottom:8px}.login-form{display:flex;flex-direction:column;align-items:center;gap:8px;width:320px}.login-field{width:100%;text-align:left}.login-button{margin-top:8px;width:100%}.login-errors{margin-top:16px;max-width:320px}.login-conflict{margin-top:16px;max-width:320px;padding:12px 16px;border-radius:4px;background-color:var(--color-surface-light);font-size:.85rem;text-align:left}.login-conflict p{margin:0 0 8px}.login-conflict-actions{display:flex;gap:8px;margin-top:12px;justify-content:flex-end}.login-error{padding:8px 12px;margin-bottom:4px;font-size:.85rem;border-radius:4px;background-color:var(--color-surface-light)}\n"] }]
931
+ ], template: "@let usernameRegex = USERNAME_REGEX;\n@let passwordRegex = PASSWORD_REGEX;\n<div class=\"login-container\">\n <div class=\"login-header\">\n <h2 class=\"text-text-primary\">{{ appName }}</h2>\n <p class=\"text-secondary-info\">Sign in to continue</p>\n </div>\n <form #loginForm=\"ngForm\" (ngSubmit)=\"onSubmit($event)\" class=\"login-form\">\n <mat-form-field appearance=\"outline\" class=\"login-field\">\n <mat-label>Username</mat-label>\n <input\n #usernameInput\n matInput\n placeholder=\"Enter username\"\n [(ngModel)]=\"model.username\"\n name=\"username\"\n [disabled]=\"submitted\"\n required\n regexValidator\n [regex]=\"usernameRegex\"\n (cdkAutofill)=\"onAutofill($event, usernameInput)\"\n />\n </mat-form-field>\n <mat-form-field appearance=\"outline\" class=\"login-field\">\n <mat-label>Password</mat-label>\n <input\n #passwordInput\n matInput\n placeholder=\"Enter password\"\n [type]=\"hide() ? 'password' : 'text'\"\n [(ngModel)]=\"model.password\"\n name=\"password\"\n [disabled]=\"submitted\"\n regexValidator\n [regex]=\"passwordRegex\"\n (cdkAutofill)=\"onAutofill($event, passwordInput)\"\n />\n <button\n mat-icon-button\n matSuffix\n type=\"button\"\n (click)=\"clickEvent($event)\"\n [attr.aria-label]=\"'Hide password'\"\n [attr.aria-pressed]=\"hide()\"\n >\n <mat-icon>{{ hide() ? 'visibility_off' : 'visibility' }}</mat-icon>\n </button>\n </mat-form-field>\n <mat-checkbox\n class=\"login-takeover\"\n name=\"takeover\"\n [checked]=\"takeover()\"\n (change)=\"takeover.set($event.checked)\"\n [disabled]=\"submitted\"\n >\n Takeover session if logged in elsewhere\n </mat-checkbox>\n <button mat-flat-button type=\"submit\" [disabled]=\"submitted\" class=\"login-button\">\n Login\n </button>\n </form>\n @if (errors.length > 0) {\n <div class=\"login-errors\">\n @for (error of errors; track error.ndx) {\n <div class=\"login-error text-loss\">{{ error.message }}</div>\n }\n </div>\n }\n @if (authenticationService.conflictSessions(); as sessions) {\n <div class=\"login-conflict\">\n @for (session of sessions; track $index) {\n <p class=\"text-secondary-info\">\n Already logged in from <strong>{{ session.appContext }}</strong>\n since {{ session.startEpoch | date:'short' }}.\n </p>\n }\n <p class=\"text-secondary-info\">Log in here and end that session?</p>\n <div class=\"login-conflict-actions\">\n <button mat-stroked-button type=\"button\" (click)=\"onCancelConflict()\">Cancel</button>\n <button mat-flat-button type=\"button\" (click)=\"onTakeOver()\">Take Over</button>\n </div>\n </div>\n }\n</div>\n", styles: [".login-container{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;text-align:center}.login-header{margin-bottom:32px}.login-header h2{font-size:1.5rem;font-weight:300;margin-bottom:8px}.login-form{display:flex;flex-direction:column;align-items:center;gap:8px;width:320px}.login-field{width:100%;text-align:left}.login-takeover{align-self:flex-start;margin-top:4px}.login-button{margin-top:8px;width:100%}.login-errors{margin-top:16px;max-width:320px}.login-conflict{margin-top:16px;max-width:320px;padding:12px 16px;border-radius:4px;background-color:var(--color-surface-light);font-size:.85rem;text-align:left}.login-conflict p{margin:0 0 8px}.login-conflict-actions{display:flex;gap:8px;margin-top:12px;justify-content:flex-end}.login-error{padding:8px 12px;margin-bottom:4px;font-size:.85rem;border-radius:4px;background-color:var(--color-surface-light)}\n"] }]
884
932
  }], ctorParameters: () => [{ type: AuthenticationService }, { type: i2$1.Router }] });
885
933
 
886
934
  class ConfirmDialog {
887
- data = inject(MAT_DIALOG_DATA);
888
- dialogRef = inject((MatDialogRef));
889
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ConfirmDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
890
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: ConfirmDialog, isStandalone: true, selector: "shell-confirm-dialog", ngImport: i0, template: `
935
+ constructor() {
936
+ this.data = inject(MAT_DIALOG_DATA);
937
+ this.dialogRef = inject((MatDialogRef));
938
+ }
939
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ConfirmDialog, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
940
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.7", type: ConfirmDialog, isStandalone: true, selector: "shell-confirm-dialog", ngImport: i0, template: `
891
941
  <h2 mat-dialog-title>{{ data.title }}</h2>
892
942
  <mat-dialog-content>
893
943
  <p>{{ data.message }}</p>
@@ -899,7 +949,7 @@ class ConfirmDialog {
899
949
  <button mat-button (click)="dialogRef.close(false)">{{ data.cancelLabel ?? 'Cancel' }}</button>
900
950
  <button mat-flat-button color="warn" (click)="dialogRef.close(true)">{{ data.confirmLabel ?? 'Confirm' }}</button>
901
951
  </mat-dialog-actions>
902
- `, isInline: true, styles: [":host{display:block}.warning-message{color:var(--color-warning);margin-top:8px}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$2.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$2.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$2.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }] });
952
+ `, isInline: true, styles: [":host{display:block}.warning-message{color:var(--color-warning);margin-top:8px}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$2.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$2.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$2.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }] }); }
903
953
  }
904
954
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ConfirmDialog, decorators: [{
905
955
  type: Component,