@franzzemen/brokenstock-shell 0.9.0 → 0.9.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.
@@ -3010,8 +3010,64 @@ const HELP_USAGE = 'help [command]\n\n' +
3010
3010
  ' Arguments:\n' +
3011
3011
  ' [command] Optional command name to show detailed help for';
3012
3012
 
3013
+ /*
3014
+ Created by Franz Zemen 2026-10-18
3015
+ License Type: UNLICENSED
3016
+
3017
+ A question a command leaves open in the terminal (live-trading-foundation.prd.md D24).
3018
+
3019
+ A command can ask instead of acting: "Send SIM · Buy 100 NVDA? (Enter or yes to send, Esc to
3020
+ cancel)". The next line typed — including a bare Enter — is the answer and goes to the command,
3021
+ not to the command parser. Esc cancels. The arming code is asked the same way, with the input
3022
+ masked so the code never appears on screen or in the terminal's history.
3023
+
3024
+ Plain logic, no Angular: `CommandService` holds one of these and the terminal reads it.
3025
+ */
3026
+ /** True for the answers that mean yes: a bare Enter, `y`, `yes`. */
3027
+ function isYes(text) {
3028
+ const t = text.trim().toLowerCase();
3029
+ return t === '' || t === 'y' || t === 'yes';
3030
+ }
3031
+ /** What the history shows for an answer: the text, a masked run, or "(Enter)". */
3032
+ function answerEcho(prompt, text) {
3033
+ if (text === '')
3034
+ return '(Enter)';
3035
+ return prompt.masked ? '•'.repeat(text.length) : text;
3036
+ }
3037
+ /** Holds at most one open question. Asking again cancels the one before it. */
3038
+ class PendingPromptState {
3039
+ #current = null;
3040
+ get current() {
3041
+ return this.#current;
3042
+ }
3043
+ ask(prompt) {
3044
+ const before = this.#current;
3045
+ this.#current = prompt;
3046
+ if (before && before !== prompt)
3047
+ before.cancel?.();
3048
+ }
3049
+ /**
3050
+ * Takes the open question and closes it, so an answer that asks again opens a fresh one rather
3051
+ * than being closed afterwards.
3052
+ */
3053
+ take() {
3054
+ const p = this.#current;
3055
+ this.#current = null;
3056
+ return p;
3057
+ }
3058
+ cancel() {
3059
+ const p = this.#current;
3060
+ if (!p)
3061
+ return false;
3062
+ this.#current = null;
3063
+ p.cancel?.();
3064
+ return true;
3065
+ }
3066
+ }
3067
+
3013
3068
  const CommandInputSchema = z.string().trim().min(1, 'Command cannot be empty');
3014
3069
  class CommandService {
3070
+ #prompts;
3015
3071
  constructor() {
3016
3072
  this.history = signal([], /* @ts-ignore */
3017
3073
  ...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
@@ -3021,6 +3077,13 @@ class CommandService {
3021
3077
  ...(ngDevMode ? [{ debugName: "isProcessing" }] : /* istanbul ignore next */ []));
3022
3078
  this.helpOverlay = signal(null, /* @ts-ignore */
3023
3079
  ...(ngDevMode ? [{ debugName: "helpOverlay" }] : /* istanbul ignore next */ []));
3080
+ /**
3081
+ * The question a command left open, or null (live-trading-foundation.prd.md D24). While set, the
3082
+ * next line — a bare Enter included — is its answer, not a command; Esc cancels.
3083
+ */
3084
+ this.pending = signal(null, /* @ts-ignore */
3085
+ ...(ngDevMode ? [{ debugName: "pending" }] : /* istanbul ignore next */ []));
3086
+ this.#prompts = new PendingPromptState();
3024
3087
  this.authService = inject(AuthenticationService);
3025
3088
  this.registry = inject(CommandRegistryService);
3026
3089
  this.router = inject(Router);
@@ -3032,12 +3095,68 @@ class CommandService {
3032
3095
  effect(() => {
3033
3096
  if (!this.authService.authenticated()) {
3034
3097
  this.history.set([]);
3098
+ // A question asked for a session that has ended must not be answerable by the next one.
3099
+ this.#prompts.cancel();
3100
+ this.pending.set(null);
3035
3101
  }
3036
3102
  });
3037
3103
  }
3038
3104
  dismissHelp() {
3039
3105
  this.helpOverlay.set(null);
3040
3106
  }
3107
+ /** Leaves a question open; the next line typed answers it. A question already open is cancelled. */
3108
+ ask(prompt) {
3109
+ this.#prompts.ask(prompt);
3110
+ this.pending.set(prompt);
3111
+ this.history.update(h => [...h, {
3112
+ input: '',
3113
+ result: { success: true, message: prompt.question, timestamp: new Date(), severity: 'info' },
3114
+ }]);
3115
+ }
3116
+ /** Esc: closes the open question, if any. True when there was one. */
3117
+ cancelPending() {
3118
+ if (!this.#prompts.cancel())
3119
+ return false;
3120
+ this.pending.set(null);
3121
+ this.currentInput.set('');
3122
+ this.history.update(h => [...h, {
3123
+ input: '',
3124
+ result: { success: true, message: 'Cancelled — nothing was sent.', timestamp: new Date(), severity: 'info' },
3125
+ }]);
3126
+ return true;
3127
+ }
3128
+ #answer(rawInput) {
3129
+ const prompt = this.#prompts.take();
3130
+ if (!prompt)
3131
+ return;
3132
+ this.pending.set(null);
3133
+ const text = rawInput.trim();
3134
+ const echo = answerEcho(prompt, text);
3135
+ this.currentInput.set('');
3136
+ const record = (result) => {
3137
+ this.history.update(h => [...h, { input: echo, result }]);
3138
+ // An answer may ask again; show the new question after the answer's line.
3139
+ const next = this.#prompts.current;
3140
+ if (next && next !== this.pending())
3141
+ this.pending.set(next);
3142
+ };
3143
+ let out;
3144
+ try {
3145
+ out = prompt.answer(text);
3146
+ }
3147
+ catch (err) {
3148
+ out = { success: false, message: err?.message ?? String(err), timestamp: new Date() };
3149
+ }
3150
+ if (out instanceof Promise) {
3151
+ this.isProcessing.set(true);
3152
+ out
3153
+ .then(record, err => record({ success: false, message: err?.message ?? String(err), timestamp: new Date() }))
3154
+ .finally(() => this.isProcessing.set(false));
3155
+ }
3156
+ else {
3157
+ record(out);
3158
+ }
3159
+ }
3041
3160
  echoCommand(input) {
3042
3161
  if (this.#coalesce(input))
3043
3162
  return;
@@ -3059,6 +3178,11 @@ class CommandService {
3059
3178
  }]);
3060
3179
  }
3061
3180
  execute(rawInput) {
3181
+ // An open question takes the line, a bare Enter included (D24).
3182
+ if (this.#prompts.current) {
3183
+ this.#answer(rawInput);
3184
+ return;
3185
+ }
3062
3186
  const validation = CommandInputSchema.safeParse(rawInput);
3063
3187
  if (!validation.success) {
3064
3188
  this.history.update(h => [...h, {
@@ -3480,6 +3604,8 @@ class CommandTerminal {
3480
3604
  ...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
3481
3605
  this.currentInput = this.commandService.currentInput;
3482
3606
  this.isProcessing = this.commandService.isProcessing;
3607
+ /** The question a command left open (D24): the input answers it; Esc cancels. */
3608
+ this.pending = this.commandService.pending;
3483
3609
  this.inputEl = viewChild('terminalInput', /* @ts-ignore */
3484
3610
  ...(ngDevMode ? [{ debugName: "inputEl" }] : /* istanbul ignore next */ []));
3485
3611
  this.tabCycleIndex = -1;
@@ -3524,7 +3650,8 @@ class CommandTerminal {
3524
3650
  }
3525
3651
  onSubmit() {
3526
3652
  const input = this.currentInput();
3527
- if (!input.trim())
3653
+ // With a question open, a bare Enter is an answer.
3654
+ if (!input.trim() && !this.pending())
3528
3655
  return;
3529
3656
  this.commandService.execute(input);
3530
3657
  this.tabCycleIndex = -1;
@@ -3542,6 +3669,11 @@ class CommandTerminal {
3542
3669
  dismissHelp() {
3543
3670
  this.commandService.dismissHelp();
3544
3671
  }
3672
+ /** Esc: cancels an open question first, otherwise closes the help overlay. */
3673
+ onEscape() {
3674
+ if (!this.commandService.cancelPending())
3675
+ this.dismissHelp();
3676
+ }
3545
3677
  onTab(event) {
3546
3678
  event.preventDefault();
3547
3679
  const input = this.currentInput().trim();
@@ -3591,11 +3723,11 @@ class CommandTerminal {
3591
3723
  this.inputEl()?.nativeElement.focus();
3592
3724
  }
3593
3725
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: CommandTerminal, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3594
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: CommandTerminal, isStandalone: true, selector: "shell-command-terminal", inputs: { focusOnAppear: { classPropertyName: "focusOnAppear", publicName: "focusOnAppear", isSignal: true, isRequired: false, transformFunction: null } }, 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 (keydown.escape)=\"dismissHelp()\"\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: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
3726
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: CommandTerminal, isStandalone: true, selector: "shell-command-terminal", inputs: { focusOnAppear: { classPropertyName: "focusOnAppear", publicName: "focusOnAppear", isSignal: true, isRequired: false, transformFunction: null } }, 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() && !pending()) {\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]=\"pending()?.masked ? 'password' : 'text'\"\n [class.answering]=\"!!pending()\"\n [ngModel]=\"currentInput()\"\n (ngModelChange)=\"onInputChange($event)\"\n (keydown.enter)=\"onSubmit()\"\n (keydown.tab)=\"onTab($any($event))\"\n (keydown.escape)=\"onEscape()\"\n [placeholder]=\"pending()?.question ?? 'Type a command...'\"\n [attr.autocomplete]=\"pending()?.masked ? 'one-time-code' : 'off'\"\n [disabled]=\"isProcessing()\"\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}.terminal-input.answering::placeholder{color:var(--color-neutral);opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
3595
3727
  }
3596
3728
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: CommandTerminal, decorators: [{
3597
3729
  type: Component,
3598
- args: [{ selector: 'shell-command-terminal', imports: [FormsModule], changeDetection: ChangeDetectionStrategy.Eager, 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 (keydown.escape)=\"dismissHelp()\"\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"] }]
3730
+ args: [{ selector: 'shell-command-terminal', imports: [FormsModule], changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"terminal\">\n <div class=\"terminal-input-row\">\n <span class=\"prompt\">$</span>\n <div class=\"input-wrapper\">\n @if (ghostText() && !pending()) {\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]=\"pending()?.masked ? 'password' : 'text'\"\n [class.answering]=\"!!pending()\"\n [ngModel]=\"currentInput()\"\n (ngModelChange)=\"onInputChange($event)\"\n (keydown.enter)=\"onSubmit()\"\n (keydown.tab)=\"onTab($any($event))\"\n (keydown.escape)=\"onEscape()\"\n [placeholder]=\"pending()?.question ?? 'Type a command...'\"\n [attr.autocomplete]=\"pending()?.masked ? 'one-time-code' : 'off'\"\n [disabled]=\"isProcessing()\"\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}.terminal-input.answering::placeholder{color:var(--color-neutral);opacity:1}\n"] }]
3599
3731
  }], ctorParameters: () => [], propDecorators: { focusOnAppear: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusOnAppear", required: false }] }], inputEl: [{ type: i0.ViewChild, args: ['terminalInput', { isSignal: true }] }] } });
3600
3732
 
3601
3733
  const MIN_TERMINAL_HEIGHT = 80;
@@ -4324,5 +4456,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
4324
4456
  * Generated bundle index. Do not edit.
4325
4457
  */
4326
4458
 
4327
- export { AuthenticationService, AuthenticatorError, AuthenticatorSettings, CLEAR_USAGE, CODE_STEP, CodeDialog, CommandRegistryService, CommandService, CommandTerminal, ConfirmDialog, FAQ_CONTENT_LOADER, FAQ_DEV_EDITOR, FAQ_DEV_MODE, FAQ_DEV_WORKSPACE_ROOT, FAQ_PUBLIC_PUBLISHER, FAQ_PUBLIC_SITE_BASE_URL, FaqAuthoringPanel, FaqHistoryService, FaqMarkdownRendererService, FaqModal, FaqService, HELP_USAGE, HttpFaqContentLoader, KeyboardShortcutService, Login, MainContent, PhoneLayoutService, PublicFaqIndex, PublicFaqPage, PublicFaqToc, RecoveryCodes, RegexValidatorDirective, SESSION_TOKEN_KEY, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_PHONE_MEDIA_QUERY, SHELL_POST_LOGIN_ROUTE, SHELL_SHARED_SESSION, SessionTokenStore, Shell, SideMenu, StatusBar, TOO_MANY_ATTEMPTS, TerminalFocusService, TerminalVisibilityService, ThemeService, ThinkingService, audienceAllowed, authGuard, authInterceptor, authenticatorMessage, buildFallbackChain, buildLoginBody, decideRenewal, guestGuard, matchesRoute, openCodeDialog, provideShellConfig, recoveryCodesFile, tokenize };
4459
+ export { AuthenticationService, AuthenticatorError, AuthenticatorSettings, CLEAR_USAGE, CODE_STEP, CodeDialog, CommandRegistryService, CommandService, CommandTerminal, ConfirmDialog, FAQ_CONTENT_LOADER, FAQ_DEV_EDITOR, FAQ_DEV_MODE, FAQ_DEV_WORKSPACE_ROOT, FAQ_PUBLIC_PUBLISHER, FAQ_PUBLIC_SITE_BASE_URL, FaqAuthoringPanel, FaqHistoryService, FaqMarkdownRendererService, FaqModal, FaqService, HELP_USAGE, HttpFaqContentLoader, KeyboardShortcutService, Login, MainContent, PendingPromptState, PhoneLayoutService, PublicFaqIndex, PublicFaqPage, PublicFaqToc, RecoveryCodes, RegexValidatorDirective, SESSION_TOKEN_KEY, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_PHONE_MEDIA_QUERY, SHELL_POST_LOGIN_ROUTE, SHELL_SHARED_SESSION, SessionTokenStore, Shell, SideMenu, StatusBar, TOO_MANY_ATTEMPTS, TerminalFocusService, TerminalVisibilityService, ThemeService, ThinkingService, answerEcho, audienceAllowed, authGuard, authInterceptor, authenticatorMessage, buildFallbackChain, buildLoginBody, decideRenewal, guestGuard, isYes, matchesRoute, openCodeDialog, provideShellConfig, recoveryCodesFile, tokenize };
4328
4460
  //# sourceMappingURL=franzzemen-brokenstock-shell.mjs.map