@compilr-dev/cli 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -12
- package/dist/agent.d.ts +74 -1
- package/dist/agent.js +259 -76
- package/dist/anchors/index.d.ts +9 -0
- package/dist/anchors/index.js +9 -0
- package/dist/anchors/project-anchors.d.ts +79 -0
- package/dist/anchors/project-anchors.js +202 -0
- package/dist/commands/handler-types.d.ts +68 -0
- package/dist/commands/handler-types.js +8 -0
- package/dist/commands/handlers/agent-commands.d.ts +13 -0
- package/dist/commands/handlers/agent-commands.js +305 -0
- package/dist/commands/handlers/design-commands.d.ts +15 -0
- package/dist/commands/handlers/design-commands.js +334 -0
- package/dist/commands/handlers/index.d.ts +20 -0
- package/dist/commands/handlers/index.js +43 -0
- package/dist/commands/handlers/overlay-commands.d.ts +21 -0
- package/dist/commands/handlers/overlay-commands.js +287 -0
- package/dist/commands/handlers/project-commands.d.ts +11 -0
- package/dist/commands/handlers/project-commands.js +167 -0
- package/dist/commands/handlers/simple-commands.d.ts +19 -0
- package/dist/commands/handlers/simple-commands.js +144 -0
- package/dist/commands/index.d.ts +2 -1
- package/dist/commands/registry.d.ts +50 -0
- package/dist/commands/registry.js +75 -0
- package/dist/commands-v2/handlers/context.d.ts +13 -0
- package/dist/commands-v2/handlers/context.js +348 -0
- package/dist/commands-v2/handlers/core.d.ts +13 -0
- package/dist/commands-v2/handlers/core.js +165 -0
- package/dist/commands-v2/handlers/debug.d.ts +11 -0
- package/dist/commands-v2/handlers/debug.js +159 -0
- package/dist/commands-v2/handlers/index.d.ts +12 -0
- package/dist/commands-v2/handlers/index.js +24 -0
- package/dist/commands-v2/handlers/project.d.ts +22 -0
- package/dist/commands-v2/handlers/project.js +814 -0
- package/dist/commands-v2/handlers/settings.d.ts +15 -0
- package/dist/commands-v2/handlers/settings.js +235 -0
- package/dist/commands-v2/index.d.ts +13 -0
- package/dist/commands-v2/index.js +15 -0
- package/dist/commands-v2/registry.d.ts +37 -0
- package/dist/commands-v2/registry.js +80 -0
- package/dist/commands-v2/types.d.ts +75 -0
- package/dist/commands-v2/types.js +7 -0
- package/dist/commands.js +110 -7
- package/dist/index.js +288 -29
- package/dist/input-handlers/index.d.ts +7 -0
- package/dist/input-handlers/index.js +7 -0
- package/dist/input-handlers/memory-handler.d.ts +26 -0
- package/dist/input-handlers/memory-handler.js +68 -0
- package/dist/repl-helpers.d.ts +63 -0
- package/dist/repl-helpers.js +318 -0
- package/dist/repl-v2.d.ts +155 -0
- package/dist/repl-v2.js +774 -0
- package/dist/repl.d.ts +32 -4
- package/dist/repl.js +250 -977
- package/dist/settings/index.d.ts +23 -0
- package/dist/settings/index.js +48 -0
- package/dist/settings/paths.d.ts +110 -0
- package/dist/settings/paths.js +264 -0
- package/dist/templates/compilr-md.js +7 -4
- package/dist/templates/index.js +3 -4
- package/dist/themes/colors.js +3 -1
- package/dist/themes/registry.d.ts +5 -36
- package/dist/themes/registry.js +11 -95
- package/dist/themes/types.d.ts +3 -38
- package/dist/themes/types.js +2 -2
- package/dist/tools/anchor-tools.d.ts +31 -0
- package/dist/tools/anchor-tools.js +255 -0
- package/dist/tools/backlog-wrappers.d.ts +54 -0
- package/dist/tools/backlog-wrappers.js +338 -0
- package/dist/tools/backlog.js +1 -1
- package/dist/tools/db-tools.d.ts +65 -0
- package/dist/tools/db-tools.js +19 -0
- package/dist/tools/document-db.d.ts +43 -0
- package/dist/tools/document-db.js +220 -0
- package/dist/tools/project-db.d.ts +102 -0
- package/dist/tools/project-db.js +370 -0
- package/dist/tools/workitem-db.d.ts +103 -0
- package/dist/tools/workitem-db.js +549 -0
- package/dist/tools.js +13 -3
- package/dist/ui/agents-overlay-v2.d.ts +43 -0
- package/dist/ui/agents-overlay-v2.js +809 -0
- package/dist/ui/agents-overlay.d.ts +5 -5
- package/dist/ui/agents-overlay.js +782 -420
- package/dist/ui/anchors-overlay.d.ts +12 -0
- package/dist/ui/anchors-overlay.js +775 -0
- package/dist/ui/arch-type-overlay.d.ts +1 -6
- package/dist/ui/arch-type-overlay.js +175 -203
- package/dist/ui/ask-user-overlay-v2.d.ts +26 -0
- package/dist/ui/ask-user-overlay-v2.js +555 -0
- package/dist/ui/ask-user-overlay.d.ts +2 -2
- package/dist/ui/ask-user-overlay.js +443 -535
- package/dist/ui/ask-user-simple-overlay-v2.d.ts +25 -0
- package/dist/ui/ask-user-simple-overlay-v2.js +215 -0
- package/dist/ui/ask-user-simple-overlay.d.ts +2 -2
- package/dist/ui/ask-user-simple-overlay.js +182 -209
- package/dist/ui/backlog-overlay.d.ts +16 -1
- package/dist/ui/backlog-overlay.js +525 -659
- package/dist/ui/base/index.d.ts +26 -0
- package/dist/ui/base/index.js +33 -0
- package/dist/ui/base/inline-overlay-utils.d.ts +217 -0
- package/dist/ui/base/inline-overlay-utils.js +320 -0
- package/dist/ui/base/inline-overlay.d.ts +159 -0
- package/dist/ui/base/inline-overlay.js +257 -0
- package/dist/ui/base/key-utils.d.ts +15 -0
- package/dist/ui/base/key-utils.js +30 -0
- package/dist/ui/base/overlay-base-v2.d.ts +193 -0
- package/dist/ui/base/overlay-base-v2.js +246 -0
- package/dist/ui/base/overlay-base.d.ts +156 -0
- package/dist/ui/base/overlay-base.js +238 -0
- package/dist/ui/base/overlay-lifecycle.d.ts +65 -0
- package/dist/ui/base/overlay-lifecycle.js +159 -0
- package/dist/ui/base/overlay-types.d.ts +185 -0
- package/dist/ui/base/overlay-types.js +7 -0
- package/dist/ui/base/render-utils.d.ts +8 -0
- package/dist/ui/base/render-utils.js +11 -0
- package/dist/ui/base/screen-stack.d.ts +148 -0
- package/dist/ui/base/screen-stack.js +184 -0
- package/dist/ui/base/tabbed-list-overlay-v2.d.ts +103 -0
- package/dist/ui/base/tabbed-list-overlay-v2.js +317 -0
- package/dist/ui/base/tabbed-list-overlay.d.ts +153 -0
- package/dist/ui/base/tabbed-list-overlay.js +369 -0
- package/dist/ui/commands-overlay-v2.d.ts +33 -0
- package/dist/ui/commands-overlay-v2.js +441 -0
- package/dist/ui/commands-overlay.d.ts +7 -2
- package/dist/ui/commands-overlay.js +384 -355
- package/dist/ui/config-overlay.d.ts +5 -4
- package/dist/ui/config-overlay.js +243 -513
- package/dist/ui/conversation.d.ts +75 -4
- package/dist/ui/conversation.js +374 -161
- package/dist/ui/docs-overlay.d.ts +17 -0
- package/dist/ui/docs-overlay.js +303 -0
- package/dist/ui/ephemeral.d.ts +1 -1
- package/dist/ui/ephemeral.js +1 -1
- package/dist/ui/features/index.d.ts +34 -0
- package/dist/ui/features/index.js +34 -0
- package/dist/ui/features/input-feature.d.ts +85 -0
- package/dist/ui/features/input-feature.js +238 -0
- package/dist/ui/features/list-feature.d.ts +155 -0
- package/dist/ui/features/list-feature.js +244 -0
- package/dist/ui/features/pagination-feature.d.ts +154 -0
- package/dist/ui/features/pagination-feature.js +238 -0
- package/dist/ui/features/search-feature.d.ts +148 -0
- package/dist/ui/features/search-feature.js +185 -0
- package/dist/ui/features/tab-feature.d.ts +194 -0
- package/dist/ui/features/tab-feature.js +307 -0
- package/dist/ui/footer-v2.d.ts +222 -0
- package/dist/ui/footer-v2.js +1349 -0
- package/dist/ui/footer.d.ts +107 -0
- package/dist/ui/footer.js +359 -67
- package/dist/ui/guardrail-overlay.d.ts +29 -0
- package/dist/ui/guardrail-overlay.js +145 -0
- package/dist/ui/help-overlay-v2.d.ts +34 -0
- package/dist/ui/help-overlay-v2.js +309 -0
- package/dist/ui/help-overlay.d.ts +16 -0
- package/dist/ui/help-overlay.js +316 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -3
- package/dist/ui/init-overlay-v2.d.ts +34 -0
- package/dist/ui/init-overlay-v2.js +600 -0
- package/dist/ui/init-overlay.d.ts +12 -2
- package/dist/ui/init-overlay.js +349 -270
- package/dist/ui/input-prompt-v2.d.ts +1 -0
- package/dist/ui/input-prompt-v2.js +14 -6
- package/dist/ui/input-prompt.d.ts +116 -33
- package/dist/ui/input-prompt.js +536 -337
- package/dist/ui/iteration-limit-overlay-v2.d.ts +21 -0
- package/dist/ui/iteration-limit-overlay-v2.js +114 -0
- package/dist/ui/iteration-limit-overlay.d.ts +2 -2
- package/dist/ui/iteration-limit-overlay.js +92 -128
- package/dist/ui/keys-overlay-v2.d.ts +41 -0
- package/dist/ui/keys-overlay-v2.js +248 -0
- package/dist/ui/keys-overlay.d.ts +1 -0
- package/dist/ui/keys-overlay.js +203 -141
- package/dist/ui/line-utils.d.ts +88 -0
- package/dist/ui/line-utils.js +150 -0
- package/dist/ui/live-region.d.ts +161 -0
- package/dist/ui/live-region.js +387 -0
- package/dist/ui/mascot/expressions.d.ts +32 -0
- package/dist/ui/mascot/expressions.js +213 -0
- package/dist/ui/mascot/index.d.ts +8 -0
- package/dist/ui/mascot/index.js +8 -0
- package/dist/ui/mascot/renderer.d.ts +19 -0
- package/dist/ui/mascot/renderer.js +97 -0
- package/dist/ui/mascot-overlay-v2.d.ts +41 -0
- package/dist/ui/mascot-overlay-v2.js +138 -0
- package/dist/ui/mascot-overlay.d.ts +21 -0
- package/dist/ui/mascot-overlay.js +146 -0
- package/dist/ui/model-overlay-v2.d.ts +49 -0
- package/dist/ui/model-overlay-v2.js +118 -0
- package/dist/ui/model-overlay.d.ts +27 -0
- package/dist/ui/model-overlay.js +221 -0
- package/dist/ui/model-warning-overlay.js +3 -5
- package/dist/ui/new-overlay.d.ts +34 -0
- package/dist/ui/new-overlay.js +604 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/agents-overlay-v2.js +825 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/anchors-overlay-v2.js +783 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.d.ts +37 -0
- package/dist/ui/overlay/impl/arch-type-overlay-v2.js +240 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.d.ts +72 -0
- package/dist/ui/overlay/impl/ask-user-overlay-v2.js +584 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/ask-user-simple-overlay-v2.js +204 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.d.ts +49 -0
- package/dist/ui/overlay/impl/backlog-overlay-v2.js +642 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.d.ts +33 -0
- package/dist/ui/overlay/impl/commands-overlay-v2.js +441 -0
- package/dist/ui/overlay/impl/config-overlay-v2.d.ts +100 -0
- package/dist/ui/overlay/impl/config-overlay-v2.js +654 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.d.ts +55 -0
- package/dist/ui/overlay/impl/dashboard-overlay-v2.js +359 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.d.ts +45 -0
- package/dist/ui/overlay/impl/docs-overlay-v2.js +114 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/document-detail-overlay-v2.js +1071 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.d.ts +43 -0
- package/dist/ui/overlay/impl/guardrail-overlay-v2.js +114 -0
- package/dist/ui/overlay/impl/help-overlay-v2.d.ts +34 -0
- package/dist/ui/overlay/impl/help-overlay-v2.js +309 -0
- package/dist/ui/overlay/impl/init-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/init-overlay-v2.js +593 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.d.ts +25 -0
- package/dist/ui/overlay/impl/init-setup-overlay-v2.js +97 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.d.ts +35 -0
- package/dist/ui/overlay/impl/iteration-limit-overlay-v2.js +105 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/keys-overlay-v2.js +248 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.d.ts +41 -0
- package/dist/ui/overlay/impl/mascot-overlay-v2.js +138 -0
- package/dist/ui/overlay/impl/model-overlay-v2.d.ts +49 -0
- package/dist/ui/overlay/impl/model-overlay-v2.js +118 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.d.ts +46 -0
- package/dist/ui/overlay/impl/model-warning-overlay-v2.js +132 -0
- package/dist/ui/overlay/impl/new-overlay-v2.d.ts +77 -0
- package/dist/ui/overlay/impl/new-overlay-v2.js +593 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/permission-overlay-v2.js +380 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.d.ts +36 -0
- package/dist/ui/overlay/impl/projects-overlay-v2.js +499 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.d.ts +42 -0
- package/dist/ui/overlay/impl/theme-overlay-v2.js +135 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.d.ts +47 -0
- package/dist/ui/overlay/impl/tools-overlay-v2.js +218 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.d.ts +31 -0
- package/dist/ui/overlay/impl/tutorial-overlay-v2.js +1035 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.d.ts +80 -0
- package/dist/ui/overlay/impl/workflow-overlay-v2.js +637 -0
- package/dist/ui/overlay/index.d.ts +33 -0
- package/dist/ui/overlay/index.js +35 -0
- package/dist/ui/overlay/key-utils.d.ts +6 -0
- package/dist/ui/overlay/key-utils.js +6 -0
- package/dist/ui/overlay/overlay-types.d.ts +128 -0
- package/dist/ui/overlay/overlay-types.js +22 -0
- package/dist/ui/overlay/types.d.ts +135 -0
- package/dist/ui/overlay/types.js +22 -0
- package/dist/ui/overlays/help-overlay-v2.d.ts +28 -0
- package/dist/ui/overlays/help-overlay-v2.js +198 -0
- package/dist/ui/overlays/index.d.ts +11 -0
- package/dist/ui/overlays/index.js +11 -0
- package/dist/ui/overlays.d.ts +0 -4
- package/dist/ui/overlays.js +0 -444
- package/dist/ui/permission-overlay-v2.d.ts +36 -0
- package/dist/ui/permission-overlay-v2.js +380 -0
- package/dist/ui/permission-overlay.d.ts +1 -1
- package/dist/ui/permission-overlay.js +186 -298
- package/dist/ui/projects-overlay.d.ts +19 -0
- package/dist/ui/projects-overlay.js +484 -0
- package/dist/ui/providers/types.d.ts +178 -0
- package/dist/ui/providers/types.js +9 -0
- package/dist/ui/render-modes.d.ts +36 -0
- package/dist/ui/render-modes.js +44 -0
- package/dist/ui/startup-menu.d.ts +36 -0
- package/dist/ui/startup-menu.js +236 -0
- package/dist/ui/subagent-renderer.d.ts +117 -0
- package/dist/ui/subagent-renderer.js +334 -0
- package/dist/ui/terminal-codes.d.ts +94 -0
- package/dist/ui/terminal-codes.js +124 -0
- package/dist/ui/terminal-renderer.d.ts +221 -0
- package/dist/ui/terminal-renderer.js +751 -0
- package/dist/ui/terminal-ui.d.ts +463 -0
- package/dist/ui/terminal-ui.js +2296 -0
- package/dist/ui/terminal.d.ts +20 -0
- package/dist/ui/terminal.js +72 -0
- package/dist/ui/theme-overlay-v2.d.ts +42 -0
- package/dist/ui/theme-overlay-v2.js +135 -0
- package/dist/ui/theme-overlay.d.ts +24 -0
- package/dist/ui/theme-overlay.js +127 -0
- package/dist/ui/todo-zone.js +53 -25
- package/dist/ui/tool-formatters.d.ts +16 -0
- package/dist/ui/tool-formatters.js +516 -0
- package/dist/ui/tools-overlay-v2.d.ts +47 -0
- package/dist/ui/tools-overlay-v2.js +218 -0
- package/dist/ui/tools-overlay.d.ts +10 -2
- package/dist/ui/tools-overlay.js +172 -220
- package/dist/ui/tutorial-overlay-v2.d.ts +31 -0
- package/dist/ui/tutorial-overlay-v2.js +1035 -0
- package/dist/ui/tutorial-overlay.d.ts +1 -0
- package/dist/ui/tutorial-overlay.js +400 -302
- package/dist/ui/workflow-overlay.d.ts +22 -0
- package/dist/ui/workflow-overlay.js +636 -0
- package/dist/utils/debug-log.d.ts +28 -0
- package/dist/utils/debug-log.js +57 -0
- package/dist/utils/model-tiers.js +1 -1
- package/dist/utils/path-safety.d.ts +56 -0
- package/dist/utils/path-safety.js +239 -0
- package/dist/workflow/guided-mode-injector.d.ts +42 -0
- package/dist/workflow/guided-mode-injector.js +191 -0
- package/dist/workflow/index.d.ts +8 -0
- package/dist/workflow/index.js +8 -0
- package/dist/workflow/step-criteria.d.ts +62 -0
- package/dist/workflow/step-criteria.js +150 -0
- package/dist/workflow/step-tracker.d.ts +92 -0
- package/dist/workflow/step-tracker.js +141 -0
- package/package.json +12 -5
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask User Simple Overlay (Refactored)
|
|
3
|
+
*
|
|
4
|
+
* Simplified modal overlay for single-question input.
|
|
5
|
+
* Uses InlineOverlay base class for consistent lifecycle management.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Single question (no tabs, no navigation)
|
|
9
|
+
* - Options to select from with arrow keys
|
|
10
|
+
* - Custom text input option
|
|
11
|
+
* - Simple, clean UI
|
|
12
|
+
*/
|
|
13
|
+
export interface AskUserSimpleOptions {
|
|
14
|
+
question: string;
|
|
15
|
+
options?: string[];
|
|
16
|
+
allowCustom?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface AskUserSimpleResult {
|
|
19
|
+
answer: string;
|
|
20
|
+
skipped: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Show the simple ask user overlay
|
|
24
|
+
*/
|
|
25
|
+
export declare function showAskUserSimpleOverlay(options: AskUserSimpleOptions): Promise<AskUserSimpleResult>;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask User Simple Overlay (Refactored)
|
|
3
|
+
*
|
|
4
|
+
* Simplified modal overlay for single-question input.
|
|
5
|
+
* Uses InlineOverlay base class for consistent lifecycle management.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Single question (no tabs, no navigation)
|
|
9
|
+
* - Options to select from with arrow keys
|
|
10
|
+
* - Custom text input option
|
|
11
|
+
* - Simple, clean UI
|
|
12
|
+
*/
|
|
13
|
+
import chalk from 'chalk';
|
|
14
|
+
import { getStyles } from '../themes/index.js';
|
|
15
|
+
import { InlineOverlay } from './base/inline-overlay.js';
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// Overlay Implementation
|
|
18
|
+
// =============================================================================
|
|
19
|
+
class AskUserSimpleOverlayImpl extends InlineOverlay {
|
|
20
|
+
question;
|
|
21
|
+
choices;
|
|
22
|
+
allowCustom;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
super();
|
|
25
|
+
this.question = options.question;
|
|
26
|
+
this.choices = options.options ?? [];
|
|
27
|
+
this.allowCustom = options.allowCustom ?? true;
|
|
28
|
+
}
|
|
29
|
+
getInitialState() {
|
|
30
|
+
// If no predefined choices and custom is allowed, auto-start in typing mode
|
|
31
|
+
const autoTyping = this.choices.length === 0 && this.allowCustom;
|
|
32
|
+
return {
|
|
33
|
+
selectedIndex: 0,
|
|
34
|
+
inputBuffer: '',
|
|
35
|
+
isTypingCustom: autoTyping,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
render() {
|
|
39
|
+
const s = getStyles();
|
|
40
|
+
const lines = [];
|
|
41
|
+
const cols = this.getTerminalWidth();
|
|
42
|
+
const border = s.muted('─'.repeat(Math.max(1, cols - 1)));
|
|
43
|
+
// Header
|
|
44
|
+
lines.push(border);
|
|
45
|
+
lines.push(' ' + s.primaryBold('Sketch'));
|
|
46
|
+
lines.push('');
|
|
47
|
+
// Question
|
|
48
|
+
lines.push(chalk.bold(` ${this.question}`));
|
|
49
|
+
lines.push('');
|
|
50
|
+
// Options
|
|
51
|
+
const customIndex = this.choices.length;
|
|
52
|
+
for (let i = 0; i < this.choices.length; i++) {
|
|
53
|
+
const isCursor = this.state.selectedIndex === i && !this.state.isTypingCustom;
|
|
54
|
+
const prefix = isCursor ? ' ❯ ' : ' ';
|
|
55
|
+
const num = `${String(i + 1)}. `;
|
|
56
|
+
if (isCursor) {
|
|
57
|
+
lines.push(s.primary(prefix + num + this.choices[i]));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
lines.push(s.muted(prefix + num + this.choices[i]));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Custom input option (if allowed)
|
|
64
|
+
if (this.allowCustom) {
|
|
65
|
+
const isCursor = this.state.selectedIndex === customIndex && !this.state.isTypingCustom;
|
|
66
|
+
const prefix = isCursor ? ' ❯ ' : ' ';
|
|
67
|
+
const num = `${String(this.choices.length + 1)}. `;
|
|
68
|
+
if (this.state.isTypingCustom) {
|
|
69
|
+
// Show input field
|
|
70
|
+
lines.push(s.primary(prefix + num + 'Custom: ') + s.primaryBold(this.state.inputBuffer + '▋'));
|
|
71
|
+
}
|
|
72
|
+
else if (isCursor) {
|
|
73
|
+
lines.push(s.primary(prefix + num + 'Type something else...'));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
lines.push(s.muted(prefix + num + 'Type something else...'));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Footer
|
|
80
|
+
lines.push('');
|
|
81
|
+
if (this.state.isTypingCustom) {
|
|
82
|
+
lines.push(s.muted(' Enter Submit · Esc Cancel'));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
lines.push(s.muted(' ↑↓ Navigate · Enter Select · s Skip · Esc Cancel'));
|
|
86
|
+
}
|
|
87
|
+
// Bottom border
|
|
88
|
+
lines.push(border);
|
|
89
|
+
return lines;
|
|
90
|
+
}
|
|
91
|
+
handleKey(data) {
|
|
92
|
+
const customIndex = this.choices.length;
|
|
93
|
+
const optionCount = this.allowCustom ? this.choices.length + 1 : this.choices.length;
|
|
94
|
+
// Ctrl+C always cancels
|
|
95
|
+
if (this.isCtrlCKey(data)) {
|
|
96
|
+
return { type: 'close', result: { answer: '', skipped: true } };
|
|
97
|
+
}
|
|
98
|
+
// If typing custom input
|
|
99
|
+
if (this.state.isTypingCustom) {
|
|
100
|
+
if (this.isEscapeKey(data)) {
|
|
101
|
+
// Cancel custom input
|
|
102
|
+
this.state.isTypingCustom = false;
|
|
103
|
+
this.state.inputBuffer = '';
|
|
104
|
+
return { type: 'continue' };
|
|
105
|
+
}
|
|
106
|
+
if (this.isUpArrowKey(data) && this.choices.length > 0) {
|
|
107
|
+
// Exit typing mode and navigate up (only if there are other options)
|
|
108
|
+
this.state.isTypingCustom = false;
|
|
109
|
+
this.state.inputBuffer = '';
|
|
110
|
+
this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
|
|
111
|
+
return { type: 'continue' };
|
|
112
|
+
}
|
|
113
|
+
if (this.isDownArrowKey(data)) {
|
|
114
|
+
// Exit typing mode (but stay on custom since it's last option)
|
|
115
|
+
// This effectively does nothing but we handle it to be consistent
|
|
116
|
+
return { type: 'continue' };
|
|
117
|
+
}
|
|
118
|
+
if (this.isEnterKey(data)) {
|
|
119
|
+
// Submit custom input
|
|
120
|
+
const custom = this.state.inputBuffer.trim();
|
|
121
|
+
if (custom) {
|
|
122
|
+
return { type: 'close', result: { answer: custom, skipped: false } };
|
|
123
|
+
}
|
|
124
|
+
// Empty - stay in custom mode
|
|
125
|
+
return { type: 'continue' };
|
|
126
|
+
}
|
|
127
|
+
if (this.isBackspaceKey(data)) {
|
|
128
|
+
this.state.inputBuffer = this.state.inputBuffer.slice(0, -1);
|
|
129
|
+
return { type: 'continue' };
|
|
130
|
+
}
|
|
131
|
+
// Printable character
|
|
132
|
+
const char = this.getPrintableChar(data);
|
|
133
|
+
if (char) {
|
|
134
|
+
this.state.inputBuffer += char;
|
|
135
|
+
return { type: 'continue' };
|
|
136
|
+
}
|
|
137
|
+
return { type: 'continue' };
|
|
138
|
+
}
|
|
139
|
+
// Navigation mode
|
|
140
|
+
if (this.isEscapeKey(data)) {
|
|
141
|
+
// Cancel overlay
|
|
142
|
+
return { type: 'close', result: { answer: '', skipped: true } };
|
|
143
|
+
}
|
|
144
|
+
// Skip question (s key)
|
|
145
|
+
const keyStr = data.toString();
|
|
146
|
+
if (keyStr.toLowerCase() === 's') {
|
|
147
|
+
return { type: 'close', result: { answer: '', skipped: true } };
|
|
148
|
+
}
|
|
149
|
+
if (this.isUpArrowKey(data)) {
|
|
150
|
+
this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
|
|
151
|
+
// Auto-enable typing when navigating to custom option
|
|
152
|
+
if (this.allowCustom && this.state.selectedIndex === customIndex) {
|
|
153
|
+
this.state.isTypingCustom = true;
|
|
154
|
+
}
|
|
155
|
+
return { type: 'continue' };
|
|
156
|
+
}
|
|
157
|
+
if (this.isDownArrowKey(data)) {
|
|
158
|
+
this.state.selectedIndex = Math.min(optionCount - 1, this.state.selectedIndex + 1);
|
|
159
|
+
// Auto-enable typing when navigating to custom option
|
|
160
|
+
if (this.allowCustom && this.state.selectedIndex === customIndex) {
|
|
161
|
+
this.state.isTypingCustom = true;
|
|
162
|
+
}
|
|
163
|
+
return { type: 'continue' };
|
|
164
|
+
}
|
|
165
|
+
if (this.isEnterKey(data)) {
|
|
166
|
+
if (this.state.selectedIndex < this.choices.length) {
|
|
167
|
+
// Selected a predefined option
|
|
168
|
+
return { type: 'close', result: { answer: this.choices[this.state.selectedIndex], skipped: false } };
|
|
169
|
+
}
|
|
170
|
+
else if (this.allowCustom && this.state.selectedIndex === customIndex) {
|
|
171
|
+
// Enter custom input mode
|
|
172
|
+
this.state.isTypingCustom = true;
|
|
173
|
+
return { type: 'continue' };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Number keys 1-9
|
|
177
|
+
if (data.length === 1 && data[0] >= 0x31 && data[0] <= 0x39) {
|
|
178
|
+
const numIndex = data[0] - 0x31; // 0-indexed
|
|
179
|
+
if (numIndex < optionCount) {
|
|
180
|
+
if (numIndex < this.choices.length) {
|
|
181
|
+
// Select predefined option directly
|
|
182
|
+
return { type: 'close', result: { answer: this.choices[numIndex], skipped: false } };
|
|
183
|
+
}
|
|
184
|
+
else if (this.allowCustom && numIndex === customIndex) {
|
|
185
|
+
// Enter custom mode
|
|
186
|
+
this.state.selectedIndex = customIndex;
|
|
187
|
+
this.state.isTypingCustom = true;
|
|
188
|
+
return { type: 'continue' };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return { type: 'continue' };
|
|
193
|
+
}
|
|
194
|
+
getCleanupSummary(result) {
|
|
195
|
+
const s = getStyles();
|
|
196
|
+
if (result.skipped) {
|
|
197
|
+
return s.muted('Sketch: ') + s.warning('Skipped');
|
|
198
|
+
}
|
|
199
|
+
// Truncate long answers
|
|
200
|
+
const displayAnswer = result.answer.length > 50
|
|
201
|
+
? result.answer.substring(0, 47) + '...'
|
|
202
|
+
: result.answer;
|
|
203
|
+
return s.muted('Sketch: ') + s.success(displayAnswer);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// =============================================================================
|
|
207
|
+
// Main Export
|
|
208
|
+
// =============================================================================
|
|
209
|
+
/**
|
|
210
|
+
* Show the simple ask user overlay
|
|
211
|
+
*/
|
|
212
|
+
export async function showAskUserSimpleOverlay(options) {
|
|
213
|
+
const overlay = new AskUserSimpleOverlayImpl(options);
|
|
214
|
+
return overlay.show();
|
|
215
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ask User Simple Overlay
|
|
2
|
+
* Ask User Simple Overlay (Refactored)
|
|
3
3
|
*
|
|
4
4
|
* Simplified modal overlay for single-question input.
|
|
5
|
-
*
|
|
5
|
+
* Uses InlineOverlay base class for consistent lifecycle management.
|
|
6
6
|
*
|
|
7
7
|
* Features:
|
|
8
8
|
* - Single question (no tabs, no navigation)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Ask User Simple Overlay
|
|
2
|
+
* Ask User Simple Overlay (Refactored)
|
|
3
3
|
*
|
|
4
4
|
* Simplified modal overlay for single-question input.
|
|
5
|
-
*
|
|
5
|
+
* Uses InlineOverlay base class for consistent lifecycle management.
|
|
6
6
|
*
|
|
7
7
|
* Features:
|
|
8
8
|
* - Single question (no tabs, no navigation)
|
|
@@ -11,73 +11,197 @@
|
|
|
11
11
|
* - Simple, clean UI
|
|
12
12
|
*/
|
|
13
13
|
import chalk from 'chalk';
|
|
14
|
-
import * as terminal from './terminal.js';
|
|
15
14
|
import { getStyles } from '../themes/index.js';
|
|
15
|
+
import { InlineOverlay } from './base/inline-overlay.js';
|
|
16
16
|
// =============================================================================
|
|
17
|
-
//
|
|
17
|
+
// Overlay Implementation
|
|
18
18
|
// =============================================================================
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
class AskUserSimpleOverlayImpl extends InlineOverlay {
|
|
20
|
+
question;
|
|
21
|
+
choices;
|
|
22
|
+
allowCustom;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
super();
|
|
25
|
+
this.question = options.question;
|
|
26
|
+
this.choices = options.options ?? [];
|
|
27
|
+
this.allowCustom = options.allowCustom ?? true;
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
getInitialState() {
|
|
30
|
+
// If no predefined choices and custom is allowed, auto-start in typing mode
|
|
31
|
+
const autoTyping = this.choices.length === 0 && this.allowCustom;
|
|
32
|
+
return {
|
|
33
|
+
selectedIndex: 0,
|
|
34
|
+
inputBuffer: '',
|
|
35
|
+
isTypingCustom: autoTyping,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
render() {
|
|
39
|
+
const s = getStyles();
|
|
40
|
+
const lines = [];
|
|
41
|
+
const cols = this.getTerminalWidth();
|
|
42
|
+
const border = s.muted('─'.repeat(Math.max(1, cols - 1)));
|
|
43
|
+
// Header
|
|
44
|
+
lines.push(border);
|
|
45
|
+
lines.push(' ' + s.primaryBold('Sketch'));
|
|
46
|
+
lines.push('');
|
|
47
|
+
// Question
|
|
48
|
+
lines.push(chalk.bold(` ${this.question}`));
|
|
49
|
+
lines.push('');
|
|
50
|
+
// Options
|
|
51
|
+
const customIndex = this.choices.length;
|
|
52
|
+
for (let i = 0; i < this.choices.length; i++) {
|
|
53
|
+
const isCursor = this.state.selectedIndex === i && !this.state.isTypingCustom;
|
|
54
|
+
const prefix = isCursor ? ' ❯ ' : ' ';
|
|
55
|
+
const num = `${String(i + 1)}. `;
|
|
56
|
+
if (isCursor) {
|
|
57
|
+
lines.push(s.primary(prefix + num + this.choices[i]));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
lines.push(s.muted(prefix + num + this.choices[i]));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Custom input option (if allowed)
|
|
64
|
+
if (this.allowCustom) {
|
|
65
|
+
const isCursor = this.state.selectedIndex === customIndex && !this.state.isTypingCustom;
|
|
66
|
+
const prefix = isCursor ? ' ❯ ' : ' ';
|
|
67
|
+
const num = `${String(this.choices.length + 1)}. `;
|
|
68
|
+
if (this.state.isTypingCustom) {
|
|
69
|
+
// Show input field
|
|
70
|
+
lines.push(s.primary(prefix + num + 'Custom: ') + s.primaryBold(this.state.inputBuffer + '▋'));
|
|
71
|
+
}
|
|
72
|
+
else if (isCursor) {
|
|
73
|
+
lines.push(s.primary(prefix + num + 'Type something else...'));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
lines.push(s.muted(prefix + num + 'Type something else...'));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Footer
|
|
80
|
+
lines.push('');
|
|
81
|
+
if (this.state.isTypingCustom) {
|
|
82
|
+
lines.push(s.muted(' Enter Submit · Esc Cancel'));
|
|
43
83
|
}
|
|
44
84
|
else {
|
|
45
|
-
lines.push(s.muted(
|
|
85
|
+
lines.push(s.muted(' ↑↓ Navigate · Enter Select · s Skip · Esc Cancel'));
|
|
46
86
|
}
|
|
87
|
+
// Bottom border
|
|
88
|
+
lines.push(border);
|
|
89
|
+
return lines;
|
|
47
90
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Show input field
|
|
55
|
-
lines.push(s.primary(prefix + num + 'Custom: ') + s.primaryBold(state.inputBuffer + '▋'));
|
|
91
|
+
handleKey(data) {
|
|
92
|
+
const customIndex = this.choices.length;
|
|
93
|
+
const optionCount = this.allowCustom ? this.choices.length + 1 : this.choices.length;
|
|
94
|
+
// Ctrl+C always cancels
|
|
95
|
+
if (this.isCtrlCKey(data)) {
|
|
96
|
+
return { type: 'close', result: { answer: '', skipped: true } };
|
|
56
97
|
}
|
|
57
|
-
|
|
58
|
-
|
|
98
|
+
// If typing custom input
|
|
99
|
+
if (this.state.isTypingCustom) {
|
|
100
|
+
if (this.isEscapeKey(data)) {
|
|
101
|
+
// Cancel custom input
|
|
102
|
+
this.state.isTypingCustom = false;
|
|
103
|
+
this.state.inputBuffer = '';
|
|
104
|
+
return { type: 'continue' };
|
|
105
|
+
}
|
|
106
|
+
if (this.isUpArrowKey(data) && this.choices.length > 0) {
|
|
107
|
+
// Exit typing mode and navigate up (only if there are other options)
|
|
108
|
+
this.state.isTypingCustom = false;
|
|
109
|
+
this.state.inputBuffer = '';
|
|
110
|
+
this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
|
|
111
|
+
return { type: 'continue' };
|
|
112
|
+
}
|
|
113
|
+
if (this.isDownArrowKey(data)) {
|
|
114
|
+
// Exit typing mode (but stay on custom since it's last option)
|
|
115
|
+
// This effectively does nothing but we handle it to be consistent
|
|
116
|
+
return { type: 'continue' };
|
|
117
|
+
}
|
|
118
|
+
if (this.isEnterKey(data)) {
|
|
119
|
+
// Submit custom input
|
|
120
|
+
const custom = this.state.inputBuffer.trim();
|
|
121
|
+
if (custom) {
|
|
122
|
+
return { type: 'close', result: { answer: custom, skipped: false } };
|
|
123
|
+
}
|
|
124
|
+
// Empty - stay in custom mode
|
|
125
|
+
return { type: 'continue' };
|
|
126
|
+
}
|
|
127
|
+
if (this.isBackspaceKey(data)) {
|
|
128
|
+
this.state.inputBuffer = this.state.inputBuffer.slice(0, -1);
|
|
129
|
+
return { type: 'continue' };
|
|
130
|
+
}
|
|
131
|
+
// Printable character
|
|
132
|
+
const char = this.getPrintableChar(data);
|
|
133
|
+
if (char) {
|
|
134
|
+
this.state.inputBuffer += char;
|
|
135
|
+
return { type: 'continue' };
|
|
136
|
+
}
|
|
137
|
+
return { type: 'continue' };
|
|
59
138
|
}
|
|
60
|
-
|
|
61
|
-
|
|
139
|
+
// Navigation mode
|
|
140
|
+
if (this.isEscapeKey(data)) {
|
|
141
|
+
// Cancel overlay
|
|
142
|
+
return { type: 'close', result: { answer: '', skipped: true } };
|
|
62
143
|
}
|
|
144
|
+
// Skip question (s key)
|
|
145
|
+
const keyStr = data.toString();
|
|
146
|
+
if (keyStr.toLowerCase() === 's') {
|
|
147
|
+
return { type: 'close', result: { answer: '', skipped: true } };
|
|
148
|
+
}
|
|
149
|
+
if (this.isUpArrowKey(data)) {
|
|
150
|
+
this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
|
|
151
|
+
// Auto-enable typing when navigating to custom option
|
|
152
|
+
if (this.allowCustom && this.state.selectedIndex === customIndex) {
|
|
153
|
+
this.state.isTypingCustom = true;
|
|
154
|
+
}
|
|
155
|
+
return { type: 'continue' };
|
|
156
|
+
}
|
|
157
|
+
if (this.isDownArrowKey(data)) {
|
|
158
|
+
this.state.selectedIndex = Math.min(optionCount - 1, this.state.selectedIndex + 1);
|
|
159
|
+
// Auto-enable typing when navigating to custom option
|
|
160
|
+
if (this.allowCustom && this.state.selectedIndex === customIndex) {
|
|
161
|
+
this.state.isTypingCustom = true;
|
|
162
|
+
}
|
|
163
|
+
return { type: 'continue' };
|
|
164
|
+
}
|
|
165
|
+
if (this.isEnterKey(data)) {
|
|
166
|
+
if (this.state.selectedIndex < this.choices.length) {
|
|
167
|
+
// Selected a predefined option
|
|
168
|
+
return { type: 'close', result: { answer: this.choices[this.state.selectedIndex], skipped: false } };
|
|
169
|
+
}
|
|
170
|
+
else if (this.allowCustom && this.state.selectedIndex === customIndex) {
|
|
171
|
+
// Enter custom input mode
|
|
172
|
+
this.state.isTypingCustom = true;
|
|
173
|
+
return { type: 'continue' };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Number keys 1-9
|
|
177
|
+
if (data.length === 1 && data[0] >= 0x31 && data[0] <= 0x39) {
|
|
178
|
+
const numIndex = data[0] - 0x31; // 0-indexed
|
|
179
|
+
if (numIndex < optionCount) {
|
|
180
|
+
if (numIndex < this.choices.length) {
|
|
181
|
+
// Select predefined option directly
|
|
182
|
+
return { type: 'close', result: { answer: this.choices[numIndex], skipped: false } };
|
|
183
|
+
}
|
|
184
|
+
else if (this.allowCustom && numIndex === customIndex) {
|
|
185
|
+
// Enter custom mode
|
|
186
|
+
this.state.selectedIndex = customIndex;
|
|
187
|
+
this.state.isTypingCustom = true;
|
|
188
|
+
return { type: 'continue' };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return { type: 'continue' };
|
|
63
193
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
// Pad with empty lines to maintain consistent height
|
|
75
|
-
while (lines.length < targetLineCount) {
|
|
76
|
-
lines.push('');
|
|
194
|
+
getCleanupSummary(result) {
|
|
195
|
+
const s = getStyles();
|
|
196
|
+
if (result.skipped) {
|
|
197
|
+
return s.muted('Sketch: ') + s.warning('Skipped');
|
|
198
|
+
}
|
|
199
|
+
// Truncate long answers
|
|
200
|
+
const displayAnswer = result.answer.length > 50
|
|
201
|
+
? result.answer.substring(0, 47) + '...'
|
|
202
|
+
: result.answer;
|
|
203
|
+
return s.muted('Sketch: ') + s.success(displayAnswer);
|
|
77
204
|
}
|
|
78
|
-
// Render all lines
|
|
79
|
-
terminal.write(lines.join('\n'));
|
|
80
|
-
return lines.length;
|
|
81
205
|
}
|
|
82
206
|
// =============================================================================
|
|
83
207
|
// Main Export
|
|
@@ -86,157 +210,6 @@ function render(question, options, allowCustom, state, previousLineCount = 0, ta
|
|
|
86
210
|
* Show the simple ask user overlay
|
|
87
211
|
*/
|
|
88
212
|
export async function showAskUserSimpleOverlay(options) {
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
const autoTyping = choices.length === 0 && allowCustom;
|
|
92
|
-
const state = {
|
|
93
|
-
selectedIndex: autoTyping ? 0 : 0,
|
|
94
|
-
inputBuffer: '',
|
|
95
|
-
isTypingCustom: autoTyping,
|
|
96
|
-
cancelled: false,
|
|
97
|
-
};
|
|
98
|
-
let lineCount = 0;
|
|
99
|
-
let maxLineCount = 0;
|
|
100
|
-
// NOTE: Footer is already paused by the caller (index.ts handler calls sharedState.pauseFooter)
|
|
101
|
-
// Do NOT call pauseForOverlay() here - it causes double-pause issues
|
|
102
|
-
// The caller's pause already clears the footer from screen
|
|
103
|
-
// Text output is also buffered by repl.ts (not flushed until overlay closes)
|
|
104
|
-
// Ensure we start from a fresh line (like config-overlay does)
|
|
105
|
-
terminal.writeLine('');
|
|
106
|
-
terminal.hideCursor();
|
|
107
|
-
const wasRawMode = process.stdin.isRaw;
|
|
108
|
-
terminal.enableRawMode();
|
|
109
|
-
// Initial render
|
|
110
|
-
lineCount = render(question, choices, allowCustom, state, 0);
|
|
111
|
-
maxLineCount = Math.max(maxLineCount, lineCount);
|
|
112
|
-
// Helper: Get total option count
|
|
113
|
-
const getOptionCount = () => {
|
|
114
|
-
return allowCustom ? choices.length + 1 : choices.length;
|
|
115
|
-
};
|
|
116
|
-
return new Promise((resolve) => {
|
|
117
|
-
const cleanup = () => {
|
|
118
|
-
terminal.clearLinesAbove(maxLineCount);
|
|
119
|
-
terminal.writeLine('');
|
|
120
|
-
terminal.showCursor();
|
|
121
|
-
if (!wasRawMode) {
|
|
122
|
-
terminal.disableRawMode();
|
|
123
|
-
}
|
|
124
|
-
process.stdin.removeListener('data', handleData);
|
|
125
|
-
// NOTE: Footer resume is handled by the caller (index.ts) in the finally block
|
|
126
|
-
};
|
|
127
|
-
const handleData = (data) => {
|
|
128
|
-
const isEscape = data.length === 1 && data[0] === 0x1b;
|
|
129
|
-
const isUpArrow = data.length === 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x41;
|
|
130
|
-
const isDownArrow = data.length === 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x42;
|
|
131
|
-
const isCtrlC = data.length === 1 && data[0] === 0x03;
|
|
132
|
-
const isEnter = data.length === 1 && (data[0] === 0x0d || data[0] === 0x0a);
|
|
133
|
-
const isBackspace = data.length === 1 && (data[0] === 0x7f || data[0] === 0x08);
|
|
134
|
-
const isS = data.length === 1 && (data[0] === 0x73 || data[0] === 0x53); // 's' or 'S'
|
|
135
|
-
// Ctrl+C always cancels
|
|
136
|
-
if (isCtrlC) {
|
|
137
|
-
cleanup();
|
|
138
|
-
resolve({ answer: '', skipped: true });
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
// If typing custom input
|
|
142
|
-
if (state.isTypingCustom) {
|
|
143
|
-
if (isEscape) {
|
|
144
|
-
// Cancel custom input
|
|
145
|
-
state.isTypingCustom = false;
|
|
146
|
-
state.inputBuffer = '';
|
|
147
|
-
}
|
|
148
|
-
else if (isUpArrow && choices.length > 0) {
|
|
149
|
-
// Exit typing mode and navigate up (only if there are other options)
|
|
150
|
-
state.isTypingCustom = false;
|
|
151
|
-
state.inputBuffer = '';
|
|
152
|
-
state.selectedIndex = Math.max(0, state.selectedIndex - 1);
|
|
153
|
-
}
|
|
154
|
-
else if (isDownArrow) {
|
|
155
|
-
// Exit typing mode (but stay on custom since it's last option)
|
|
156
|
-
// This effectively does nothing but we handle it to be consistent
|
|
157
|
-
}
|
|
158
|
-
else if (isEnter) {
|
|
159
|
-
// Submit custom input
|
|
160
|
-
const custom = state.inputBuffer.trim();
|
|
161
|
-
if (custom) {
|
|
162
|
-
cleanup();
|
|
163
|
-
resolve({ answer: custom, skipped: false });
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
// Empty - stay in custom mode
|
|
167
|
-
}
|
|
168
|
-
else if (isBackspace) {
|
|
169
|
-
state.inputBuffer = state.inputBuffer.slice(0, -1);
|
|
170
|
-
}
|
|
171
|
-
else if (data.length === 1 && data[0] >= 0x20 && data[0] < 0x7f) {
|
|
172
|
-
// Printable character
|
|
173
|
-
state.inputBuffer += String.fromCharCode(data[0]);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
// Navigation mode
|
|
178
|
-
const optionCount = getOptionCount();
|
|
179
|
-
const customIndex = choices.length;
|
|
180
|
-
if (isEscape) {
|
|
181
|
-
// Cancel overlay
|
|
182
|
-
cleanup();
|
|
183
|
-
resolve({ answer: '', skipped: true });
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
else if (isS) {
|
|
187
|
-
// Skip question (only in navigation mode, not when typing)
|
|
188
|
-
cleanup();
|
|
189
|
-
resolve({ answer: '', skipped: true });
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
else if (isUpArrow) {
|
|
193
|
-
state.selectedIndex = Math.max(0, state.selectedIndex - 1);
|
|
194
|
-
// Auto-enable typing when navigating to custom option
|
|
195
|
-
if (allowCustom && state.selectedIndex === customIndex) {
|
|
196
|
-
state.isTypingCustom = true;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
else if (isDownArrow) {
|
|
200
|
-
state.selectedIndex = Math.min(optionCount - 1, state.selectedIndex + 1);
|
|
201
|
-
// Auto-enable typing when navigating to custom option
|
|
202
|
-
if (allowCustom && state.selectedIndex === customIndex) {
|
|
203
|
-
state.isTypingCustom = true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else if (isEnter) {
|
|
207
|
-
if (state.selectedIndex < choices.length) {
|
|
208
|
-
// Selected a predefined option
|
|
209
|
-
cleanup();
|
|
210
|
-
resolve({ answer: choices[state.selectedIndex], skipped: false });
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
else if (allowCustom && state.selectedIndex === customIndex) {
|
|
214
|
-
// Enter custom input mode
|
|
215
|
-
state.isTypingCustom = true;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
else if (data.length === 1 && data[0] >= 0x31 && data[0] <= 0x39) {
|
|
219
|
-
// Number keys 1-9
|
|
220
|
-
const numIndex = data[0] - 0x31; // 0-indexed
|
|
221
|
-
if (numIndex < optionCount) {
|
|
222
|
-
if (numIndex < choices.length) {
|
|
223
|
-
// Select predefined option directly
|
|
224
|
-
cleanup();
|
|
225
|
-
resolve({ answer: choices[numIndex], skipped: false });
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
else if (allowCustom && numIndex === customIndex) {
|
|
229
|
-
// Enter custom mode
|
|
230
|
-
state.selectedIndex = customIndex;
|
|
231
|
-
state.isTypingCustom = true;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
// Re-render
|
|
237
|
-
lineCount = render(question, choices, allowCustom, state, maxLineCount, maxLineCount);
|
|
238
|
-
maxLineCount = Math.max(maxLineCount, lineCount);
|
|
239
|
-
};
|
|
240
|
-
process.stdin.on('data', handleData);
|
|
241
|
-
});
|
|
213
|
+
const overlay = new AskUserSimpleOverlayImpl(options);
|
|
214
|
+
return overlay.show();
|
|
242
215
|
}
|