@franzzemen/brokenstock-shell 0.9.0 → 0.9.2
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,70 @@ 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;
|
|
3071
|
+
/**
|
|
3072
|
+
* True while a command (or an answer) runs. A question it asks is written to the history only
|
|
3073
|
+
* after the command's own lines, so it reads as the last thing said, under the preview it is about.
|
|
3074
|
+
*/
|
|
3075
|
+
#running;
|
|
3076
|
+
#deferredQuestion;
|
|
3015
3077
|
constructor() {
|
|
3016
3078
|
this.history = signal([], /* @ts-ignore */
|
|
3017
3079
|
...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
|
|
@@ -3021,6 +3083,18 @@ class CommandService {
|
|
|
3021
3083
|
...(ngDevMode ? [{ debugName: "isProcessing" }] : /* istanbul ignore next */ []));
|
|
3022
3084
|
this.helpOverlay = signal(null, /* @ts-ignore */
|
|
3023
3085
|
...(ngDevMode ? [{ debugName: "helpOverlay" }] : /* istanbul ignore next */ []));
|
|
3086
|
+
/**
|
|
3087
|
+
* The question a command left open, or null (live-trading-foundation.prd.md D24). While set, the
|
|
3088
|
+
* next line — a bare Enter included — is its answer, not a command; Esc cancels.
|
|
3089
|
+
*/
|
|
3090
|
+
this.pending = signal(null, /* @ts-ignore */
|
|
3091
|
+
...(ngDevMode ? [{ debugName: "pending" }] : /* istanbul ignore next */ []));
|
|
3092
|
+
this.#prompts = new PendingPromptState();
|
|
3093
|
+
/**
|
|
3094
|
+
* True while a command (or an answer) runs. A question it asks is written to the history only
|
|
3095
|
+
* after the command's own lines, so it reads as the last thing said, under the preview it is about.
|
|
3096
|
+
*/
|
|
3097
|
+
this.#running = false;
|
|
3024
3098
|
this.authService = inject(AuthenticationService);
|
|
3025
3099
|
this.registry = inject(CommandRegistryService);
|
|
3026
3100
|
this.router = inject(Router);
|
|
@@ -3032,12 +3106,80 @@ class CommandService {
|
|
|
3032
3106
|
effect(() => {
|
|
3033
3107
|
if (!this.authService.authenticated()) {
|
|
3034
3108
|
this.history.set([]);
|
|
3109
|
+
// A question asked for a session that has ended must not be answerable by the next one.
|
|
3110
|
+
this.#prompts.cancel();
|
|
3111
|
+
this.pending.set(null);
|
|
3035
3112
|
}
|
|
3036
3113
|
});
|
|
3037
3114
|
}
|
|
3038
3115
|
dismissHelp() {
|
|
3039
3116
|
this.helpOverlay.set(null);
|
|
3040
3117
|
}
|
|
3118
|
+
/** Leaves a question open; the next line typed answers it. A question already open is cancelled. */
|
|
3119
|
+
ask(prompt) {
|
|
3120
|
+
this.#prompts.ask(prompt);
|
|
3121
|
+
this.pending.set(prompt);
|
|
3122
|
+
if (this.#running)
|
|
3123
|
+
this.#deferredQuestion = prompt;
|
|
3124
|
+
else
|
|
3125
|
+
this.#writeQuestion(prompt);
|
|
3126
|
+
}
|
|
3127
|
+
#writeQuestion(prompt) {
|
|
3128
|
+
this.history.update(h => [...h, {
|
|
3129
|
+
input: '',
|
|
3130
|
+
result: { success: true, message: prompt.question, timestamp: new Date(), severity: 'info' },
|
|
3131
|
+
}]);
|
|
3132
|
+
}
|
|
3133
|
+
/** After a command's or an answer's own lines: the question it asked, if it is still open. */
|
|
3134
|
+
#finishRun() {
|
|
3135
|
+
this.#running = false;
|
|
3136
|
+
const q = this.#deferredQuestion;
|
|
3137
|
+
this.#deferredQuestion = undefined;
|
|
3138
|
+
if (q && this.#prompts.current === q)
|
|
3139
|
+
this.#writeQuestion(q);
|
|
3140
|
+
}
|
|
3141
|
+
/** Esc: closes the open question, if any. True when there was one. */
|
|
3142
|
+
cancelPending() {
|
|
3143
|
+
if (!this.#prompts.cancel())
|
|
3144
|
+
return false;
|
|
3145
|
+
this.pending.set(null);
|
|
3146
|
+
this.currentInput.set('');
|
|
3147
|
+
this.history.update(h => [...h, {
|
|
3148
|
+
input: '',
|
|
3149
|
+
result: { success: true, message: 'Cancelled — nothing was sent.', timestamp: new Date(), severity: 'info' },
|
|
3150
|
+
}]);
|
|
3151
|
+
return true;
|
|
3152
|
+
}
|
|
3153
|
+
#answer(rawInput) {
|
|
3154
|
+
const prompt = this.#prompts.take();
|
|
3155
|
+
if (!prompt)
|
|
3156
|
+
return;
|
|
3157
|
+
this.pending.set(null);
|
|
3158
|
+
const text = rawInput.trim();
|
|
3159
|
+
const echo = answerEcho(prompt, text);
|
|
3160
|
+
this.currentInput.set('');
|
|
3161
|
+
const record = (result) => {
|
|
3162
|
+
this.history.update(h => [...h, { input: echo, result }]);
|
|
3163
|
+
this.#finishRun();
|
|
3164
|
+
};
|
|
3165
|
+
this.#running = true;
|
|
3166
|
+
let out;
|
|
3167
|
+
try {
|
|
3168
|
+
out = prompt.answer(text);
|
|
3169
|
+
}
|
|
3170
|
+
catch (err) {
|
|
3171
|
+
out = { success: false, message: err?.message ?? String(err), timestamp: new Date() };
|
|
3172
|
+
}
|
|
3173
|
+
if (out instanceof Promise) {
|
|
3174
|
+
this.isProcessing.set(true);
|
|
3175
|
+
out
|
|
3176
|
+
.then(record, err => record({ success: false, message: err?.message ?? String(err), timestamp: new Date() }))
|
|
3177
|
+
.finally(() => this.isProcessing.set(false));
|
|
3178
|
+
}
|
|
3179
|
+
else {
|
|
3180
|
+
record(out);
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3041
3183
|
echoCommand(input) {
|
|
3042
3184
|
if (this.#coalesce(input))
|
|
3043
3185
|
return;
|
|
@@ -3059,6 +3201,11 @@ class CommandService {
|
|
|
3059
3201
|
}]);
|
|
3060
3202
|
}
|
|
3061
3203
|
execute(rawInput) {
|
|
3204
|
+
// An open question takes the line, a bare Enter included (D24).
|
|
3205
|
+
if (this.#prompts.current) {
|
|
3206
|
+
this.#answer(rawInput);
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3062
3209
|
const validation = CommandInputSchema.safeParse(rawInput);
|
|
3063
3210
|
if (!validation.success) {
|
|
3064
3211
|
this.history.update(h => [...h, {
|
|
@@ -3076,7 +3223,14 @@ class CommandService {
|
|
|
3076
3223
|
// masked under `login` stays masked under any other word for it).
|
|
3077
3224
|
const def = this.registry.resolve(command)?.command;
|
|
3078
3225
|
const displayInput = def?.sanitizeInput ? def.sanitizeInput(input) : input;
|
|
3079
|
-
|
|
3226
|
+
this.#running = true;
|
|
3227
|
+
let resultOrPromise;
|
|
3228
|
+
try {
|
|
3229
|
+
resultOrPromise = this.#dispatch(command, parts.slice(1));
|
|
3230
|
+
}
|
|
3231
|
+
catch (err) {
|
|
3232
|
+
resultOrPromise = { success: false, message: err?.message ?? String(err), timestamp: new Date() };
|
|
3233
|
+
}
|
|
3080
3234
|
this.currentInput.set('');
|
|
3081
3235
|
if (resultOrPromise instanceof Promise) {
|
|
3082
3236
|
this.isProcessing.set(true);
|
|
@@ -3090,7 +3244,10 @@ class CommandService {
|
|
|
3090
3244
|
this.history.update(h => [...h, { input: displayInput, result: historyResult, repeatCount: 1 }]);
|
|
3091
3245
|
}
|
|
3092
3246
|
})
|
|
3093
|
-
.finally(() =>
|
|
3247
|
+
.finally(() => {
|
|
3248
|
+
this.#finishRun();
|
|
3249
|
+
this.isProcessing.set(false);
|
|
3250
|
+
});
|
|
3094
3251
|
}
|
|
3095
3252
|
else {
|
|
3096
3253
|
if (resultOrPromise.isHelp && resultOrPromise.message) {
|
|
@@ -3100,6 +3257,7 @@ class CommandService {
|
|
|
3100
3257
|
if (!this.#coalesce(displayInput)) {
|
|
3101
3258
|
this.history.update(h => [...h, { input: displayInput, result: historyResult, repeatCount: 1 }]);
|
|
3102
3259
|
}
|
|
3260
|
+
this.#finishRun();
|
|
3103
3261
|
}
|
|
3104
3262
|
}
|
|
3105
3263
|
#coalesce(input) {
|
|
@@ -3480,6 +3638,8 @@ class CommandTerminal {
|
|
|
3480
3638
|
...(ngDevMode ? [{ debugName: "history" }] : /* istanbul ignore next */ []));
|
|
3481
3639
|
this.currentInput = this.commandService.currentInput;
|
|
3482
3640
|
this.isProcessing = this.commandService.isProcessing;
|
|
3641
|
+
/** The question a command left open (D24): the input answers it; Esc cancels. */
|
|
3642
|
+
this.pending = this.commandService.pending;
|
|
3483
3643
|
this.inputEl = viewChild('terminalInput', /* @ts-ignore */
|
|
3484
3644
|
...(ngDevMode ? [{ debugName: "inputEl" }] : /* istanbul ignore next */ []));
|
|
3485
3645
|
this.tabCycleIndex = -1;
|
|
@@ -3524,7 +3684,8 @@ class CommandTerminal {
|
|
|
3524
3684
|
}
|
|
3525
3685
|
onSubmit() {
|
|
3526
3686
|
const input = this.currentInput();
|
|
3527
|
-
|
|
3687
|
+
// With a question open, a bare Enter is an answer.
|
|
3688
|
+
if (!input.trim() && !this.pending())
|
|
3528
3689
|
return;
|
|
3529
3690
|
this.commandService.execute(input);
|
|
3530
3691
|
this.tabCycleIndex = -1;
|
|
@@ -3542,6 +3703,11 @@ class CommandTerminal {
|
|
|
3542
3703
|
dismissHelp() {
|
|
3543
3704
|
this.commandService.dismissHelp();
|
|
3544
3705
|
}
|
|
3706
|
+
/** Esc: cancels an open question first, otherwise closes the help overlay. */
|
|
3707
|
+
onEscape() {
|
|
3708
|
+
if (!this.commandService.cancelPending())
|
|
3709
|
+
this.dismissHelp();
|
|
3710
|
+
}
|
|
3545
3711
|
onTab(event) {
|
|
3546
3712
|
event.preventDefault();
|
|
3547
3713
|
const input = this.currentInput().trim();
|
|
@@ -3591,11 +3757,11 @@ class CommandTerminal {
|
|
|
3591
3757
|
this.inputEl()?.nativeElement.focus();
|
|
3592
3758
|
}
|
|
3593
3759
|
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)=\"
|
|
3760
|
+
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
3761
|
}
|
|
3596
3762
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: CommandTerminal, decorators: [{
|
|
3597
3763
|
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)=\"
|
|
3764
|
+
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
3765
|
}], ctorParameters: () => [], propDecorators: { focusOnAppear: [{ type: i0.Input, args: [{ isSignal: true, alias: "focusOnAppear", required: false }] }], inputEl: [{ type: i0.ViewChild, args: ['terminalInput', { isSignal: true }] }] } });
|
|
3600
3766
|
|
|
3601
3767
|
const MIN_TERMINAL_HEIGHT = 80;
|
|
@@ -4324,5 +4490,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
4324
4490
|
* Generated bundle index. Do not edit.
|
|
4325
4491
|
*/
|
|
4326
4492
|
|
|
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 };
|
|
4493
|
+
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
4494
|
//# sourceMappingURL=franzzemen-brokenstock-shell.mjs.map
|