@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,584 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask User Overlay V2
|
|
3
|
+
*
|
|
4
|
+
* Modal overlay for presenting multi-question forms to the user.
|
|
5
|
+
* Uses BaseOverlayV2 for TerminalUI integration.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Multiple questions with navigation (Left/Right or Tab)
|
|
9
|
+
* - Options to select from with arrow keys
|
|
10
|
+
* - Custom text input option
|
|
11
|
+
* - Multi-select support
|
|
12
|
+
* - Progress indicator showing answered questions
|
|
13
|
+
* - Submit tab at the end
|
|
14
|
+
*/
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import { BaseOverlayV2, renderBorder } from '../../base/index.js';
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// Overlay Implementation
|
|
19
|
+
// =============================================================================
|
|
20
|
+
export class AskUserOverlayV2 extends BaseOverlayV2 {
|
|
21
|
+
type = 'inline';
|
|
22
|
+
id = 'ask-user-overlay-v2';
|
|
23
|
+
questions;
|
|
24
|
+
context;
|
|
25
|
+
constructor(options) {
|
|
26
|
+
// Check if first question should auto-start in typing mode
|
|
27
|
+
const firstQ = options.questions[0];
|
|
28
|
+
const firstQAutoType = firstQ !== undefined &&
|
|
29
|
+
(firstQ.options?.length ?? 0) === 0 &&
|
|
30
|
+
firstQ.allowCustom !== false &&
|
|
31
|
+
firstQ.multiSelect !== true;
|
|
32
|
+
super({
|
|
33
|
+
currentQuestion: 0,
|
|
34
|
+
selectedIndex: 0,
|
|
35
|
+
inputBuffer: '',
|
|
36
|
+
isTypingCustom: firstQAutoType,
|
|
37
|
+
answers: {},
|
|
38
|
+
multiSelections: new Set(),
|
|
39
|
+
warningMessage: '',
|
|
40
|
+
isOnSubmitTab: false,
|
|
41
|
+
});
|
|
42
|
+
this.questions = options.questions;
|
|
43
|
+
this.context = options.context;
|
|
44
|
+
this.minHeight = 20; // Prevent jitter with multi-question forms
|
|
45
|
+
}
|
|
46
|
+
renderContent(context) {
|
|
47
|
+
const s = context.styles;
|
|
48
|
+
const lines = [];
|
|
49
|
+
const cols = context.width;
|
|
50
|
+
const border = renderBorder(cols, s);
|
|
51
|
+
// Header
|
|
52
|
+
lines.push(border);
|
|
53
|
+
lines.push(' ' + s.primaryBold('Questions'));
|
|
54
|
+
lines.push('');
|
|
55
|
+
// Tab bar
|
|
56
|
+
lines.push(...this.renderTabBar(s));
|
|
57
|
+
lines.push('');
|
|
58
|
+
// Current question or Submit tab
|
|
59
|
+
if (this.state.isOnSubmitTab) {
|
|
60
|
+
lines.push(...this.renderSubmitTab(s));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
lines.push(...this.renderQuestion(s));
|
|
64
|
+
}
|
|
65
|
+
// Footer with instructions
|
|
66
|
+
lines.push(...this.renderInstructions(s));
|
|
67
|
+
// Warning message if present
|
|
68
|
+
if (this.state.warningMessage) {
|
|
69
|
+
lines.push('');
|
|
70
|
+
lines.push(s.warning(` ! ${this.state.warningMessage}`));
|
|
71
|
+
}
|
|
72
|
+
// Bottom border
|
|
73
|
+
lines.push(border);
|
|
74
|
+
return lines;
|
|
75
|
+
}
|
|
76
|
+
handleKey(key) {
|
|
77
|
+
// Ctrl+C always cancels
|
|
78
|
+
if (key.ctrl && key.name === 'c') {
|
|
79
|
+
return this.close(this.getResult());
|
|
80
|
+
}
|
|
81
|
+
// Handle Submit tab first
|
|
82
|
+
if (this.state.isOnSubmitTab) {
|
|
83
|
+
return this.handleSubmitTabKey(key);
|
|
84
|
+
}
|
|
85
|
+
// Handle typing mode
|
|
86
|
+
if (this.state.isTypingCustom) {
|
|
87
|
+
return this.handleTypingKey(key);
|
|
88
|
+
}
|
|
89
|
+
// Handle navigation mode
|
|
90
|
+
return this.handleNavigationKey(key);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Summary shown after overlay closes.
|
|
94
|
+
*/
|
|
95
|
+
getCloseSummary(result) {
|
|
96
|
+
const s = this.getStyles();
|
|
97
|
+
const answeredCount = Object.keys(result.answers).length;
|
|
98
|
+
const skippedCount = result.skipped.length;
|
|
99
|
+
if (answeredCount === 0) {
|
|
100
|
+
return s.muted('Questions: ') + s.warning('Cancelled');
|
|
101
|
+
}
|
|
102
|
+
if (skippedCount > 0) {
|
|
103
|
+
return s.muted('Questions: ') + s.success(`${String(answeredCount)} answered`) + s.muted(`, ${String(skippedCount)} skipped`);
|
|
104
|
+
}
|
|
105
|
+
return s.muted('Questions: ') + s.success(`${String(answeredCount)} answered`);
|
|
106
|
+
}
|
|
107
|
+
// ===========================================================================
|
|
108
|
+
// Private: Rendering Helpers
|
|
109
|
+
// ===========================================================================
|
|
110
|
+
renderTabBar(s) {
|
|
111
|
+
const lines = [];
|
|
112
|
+
let tabLine = ' ';
|
|
113
|
+
for (let i = 0; i < this.questions.length; i++) {
|
|
114
|
+
const q = this.questions[i];
|
|
115
|
+
const hasAnswer = q.id in this.state.answers;
|
|
116
|
+
const isCurrent = i === this.state.currentQuestion && !this.state.isOnSubmitTab;
|
|
117
|
+
const label = q.header || q.id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
118
|
+
if (isCurrent) {
|
|
119
|
+
tabLine += s.selected(` ${label} `) + ' ';
|
|
120
|
+
}
|
|
121
|
+
else if (hasAnswer) {
|
|
122
|
+
tabLine += s.success(`* ${label}`) + ' ';
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
tabLine += s.muted(` ${label} `) + ' ';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Add Submit tab
|
|
129
|
+
const allAnswered = this.questions.every((q) => q.id in this.state.answers);
|
|
130
|
+
if (this.state.isOnSubmitTab) {
|
|
131
|
+
tabLine += s.selected(' Submit ');
|
|
132
|
+
}
|
|
133
|
+
else if (allAnswered) {
|
|
134
|
+
tabLine += s.success(' Submit ');
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
tabLine += s.muted(' Submit ');
|
|
138
|
+
}
|
|
139
|
+
lines.push(tabLine);
|
|
140
|
+
return lines;
|
|
141
|
+
}
|
|
142
|
+
renderQuestion(s) {
|
|
143
|
+
const lines = [];
|
|
144
|
+
const question = this.questions[this.state.currentQuestion];
|
|
145
|
+
const questionIndex = this.state.currentQuestion;
|
|
146
|
+
const totalQuestions = this.questions.length;
|
|
147
|
+
// Context if provided (only on first question)
|
|
148
|
+
if (this.context && questionIndex === 0) {
|
|
149
|
+
lines.push(s.muted(' ' + this.context));
|
|
150
|
+
lines.push('');
|
|
151
|
+
}
|
|
152
|
+
// Question number and text
|
|
153
|
+
lines.push(chalk.bold(` [${String(questionIndex + 1)}/${String(totalQuestions)}] ${question.question}`));
|
|
154
|
+
lines.push('');
|
|
155
|
+
// Options
|
|
156
|
+
const options = question.options ?? [];
|
|
157
|
+
const allowCustom = question.allowCustom !== false;
|
|
158
|
+
const isMultiSelect = question.multiSelect === true;
|
|
159
|
+
const customIndex = options.length;
|
|
160
|
+
// Get existing answer info
|
|
161
|
+
const { answeredOptions, customAnswerValue } = this.getAnswerInfo(question);
|
|
162
|
+
for (let i = 0; i < options.length; i++) {
|
|
163
|
+
const isCursor = this.state.selectedIndex === i;
|
|
164
|
+
const isMultiSelected = isMultiSelect && this.state.multiSelections.has(i);
|
|
165
|
+
const isAnswered = answeredOptions.has(i);
|
|
166
|
+
const prefix = isCursor ? ' > ' : ' ';
|
|
167
|
+
let marker = '';
|
|
168
|
+
if (isMultiSelect) {
|
|
169
|
+
marker = isMultiSelected ? '[*] ' : '[ ] ';
|
|
170
|
+
}
|
|
171
|
+
else if (isAnswered) {
|
|
172
|
+
marker = '* ';
|
|
173
|
+
}
|
|
174
|
+
const label = `${String(i + 1)}. ${options[i]}`;
|
|
175
|
+
if (isCursor) {
|
|
176
|
+
lines.push(s.primary(prefix + marker + label));
|
|
177
|
+
}
|
|
178
|
+
else if (isMultiSelected || isAnswered) {
|
|
179
|
+
lines.push(s.success(prefix + marker + label));
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
lines.push(s.muted(prefix + marker + label));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Custom option
|
|
186
|
+
if (allowCustom) {
|
|
187
|
+
const isCursor = this.state.selectedIndex === customIndex;
|
|
188
|
+
const prefix = isCursor ? ' > ' : ' ';
|
|
189
|
+
const hasCustomAnswer = customAnswerValue !== '' || this.state.inputBuffer !== '';
|
|
190
|
+
const displayValue = this.state.inputBuffer || customAnswerValue;
|
|
191
|
+
if (this.state.isTypingCustom) {
|
|
192
|
+
lines.push(s.primary(prefix + 'Custom: ' + this.state.inputBuffer + '_'));
|
|
193
|
+
}
|
|
194
|
+
else if (hasCustomAnswer) {
|
|
195
|
+
const marker = isMultiSelect ? '[*] ' : '* ';
|
|
196
|
+
const label = `${String(options.length + 1)}. Custom: "${displayValue}"`;
|
|
197
|
+
lines.push(isCursor ? s.primary(prefix + marker + label) : s.success(prefix + marker + label));
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
const label = `${String(options.length + 1)}. Type something custom...`;
|
|
201
|
+
lines.push(isCursor ? s.primary(prefix + label) : s.muted(prefix + label));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return lines;
|
|
205
|
+
}
|
|
206
|
+
renderSubmitTab(s) {
|
|
207
|
+
const lines = [];
|
|
208
|
+
lines.push('');
|
|
209
|
+
lines.push(chalk.bold(' Ready to submit?'));
|
|
210
|
+
lines.push('');
|
|
211
|
+
const answeredCount = Object.keys(this.state.answers).length;
|
|
212
|
+
const totalCount = this.questions.length;
|
|
213
|
+
const allAnswered = answeredCount === totalCount;
|
|
214
|
+
if (allAnswered) {
|
|
215
|
+
lines.push(s.success(` * All ${String(totalCount)} questions answered`));
|
|
216
|
+
lines.push('');
|
|
217
|
+
lines.push(' ' + s.muted('Press Enter to submit your answers.'));
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
const unansweredCount = totalCount - answeredCount;
|
|
221
|
+
lines.push(s.warning(` ! ${String(unansweredCount)} of ${String(totalCount)} questions not answered`));
|
|
222
|
+
lines.push('');
|
|
223
|
+
lines.push(' ' + s.muted('Unanswered:'));
|
|
224
|
+
for (const q of this.questions) {
|
|
225
|
+
if (!(q.id in this.state.answers)) {
|
|
226
|
+
const label = q.header || q.id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
227
|
+
lines.push(' ' + s.muted(`- ${label}`));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
lines.push('');
|
|
231
|
+
lines.push(' ' + s.muted('Press Left or Tab to go back and answer.'));
|
|
232
|
+
}
|
|
233
|
+
return lines;
|
|
234
|
+
}
|
|
235
|
+
renderInstructions(s) {
|
|
236
|
+
const lines = [];
|
|
237
|
+
lines.push('');
|
|
238
|
+
if (this.state.isOnSubmitTab) {
|
|
239
|
+
lines.push(s.muted(' Enter Submit all | Left/Tab Go back | Esc Cancel'));
|
|
240
|
+
}
|
|
241
|
+
else if (this.state.isTypingCustom) {
|
|
242
|
+
const currentQ = this.questions[this.state.currentQuestion];
|
|
243
|
+
if (currentQ.multiSelect === true) {
|
|
244
|
+
lines.push(s.muted(' Enter Done typing | Up Navigate | Esc Clear custom'));
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
lines.push(s.muted(' Enter Submit | Esc Cancel custom'));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
const currentQ = this.questions[this.state.currentQuestion];
|
|
252
|
+
if (currentQ.multiSelect === true) {
|
|
253
|
+
lines.push(s.muted(' Up/Down Navigate | Space Toggle | Enter Confirm | Tab Next | Esc Cancel'));
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
lines.push(s.muted(' Up/Down Navigate | Enter Select | Tab Next | Esc Cancel'));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return lines;
|
|
260
|
+
}
|
|
261
|
+
// ===========================================================================
|
|
262
|
+
// Private: Key Handlers
|
|
263
|
+
// ===========================================================================
|
|
264
|
+
handleSubmitTabKey(key) {
|
|
265
|
+
if (key.name === 'escape') {
|
|
266
|
+
return this.close(this.getResult());
|
|
267
|
+
}
|
|
268
|
+
if (key.name === 'left' || key.name === 'tab') {
|
|
269
|
+
this.prevQuestion();
|
|
270
|
+
return this.rerender();
|
|
271
|
+
}
|
|
272
|
+
if (key.name === 'return') {
|
|
273
|
+
if (this.tryComplete()) {
|
|
274
|
+
return this.close(this.getResult());
|
|
275
|
+
}
|
|
276
|
+
// tryComplete sets warning and navigates to first unanswered
|
|
277
|
+
return this.rerender();
|
|
278
|
+
}
|
|
279
|
+
return this.noAction();
|
|
280
|
+
}
|
|
281
|
+
handleTypingKey(key) {
|
|
282
|
+
const currentQ = this.questions[this.state.currentQuestion];
|
|
283
|
+
const options = currentQ.options ?? [];
|
|
284
|
+
if (key.name === 'escape') {
|
|
285
|
+
this.state.isTypingCustom = false;
|
|
286
|
+
this.state.inputBuffer = '';
|
|
287
|
+
return this.rerender();
|
|
288
|
+
}
|
|
289
|
+
if (key.name === 'up' && options.length > 0) {
|
|
290
|
+
this.state.isTypingCustom = false;
|
|
291
|
+
this.state.inputBuffer = '';
|
|
292
|
+
this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
|
|
293
|
+
return this.rerender();
|
|
294
|
+
}
|
|
295
|
+
if (key.name === 'down') {
|
|
296
|
+
// Custom is last option, down arrow does nothing
|
|
297
|
+
return this.noAction();
|
|
298
|
+
}
|
|
299
|
+
if (key.name === 'return') {
|
|
300
|
+
const isMultiSelect = currentQ.multiSelect === true;
|
|
301
|
+
if (isMultiSelect) {
|
|
302
|
+
// In multi-select: Enter just confirms the custom text and exits typing mode
|
|
303
|
+
// The custom option stays selected, user can continue selecting other options
|
|
304
|
+
if (this.state.inputBuffer.trim()) {
|
|
305
|
+
// Custom text entered - keep it and exit typing mode
|
|
306
|
+
this.state.isTypingCustom = false;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
// No text - deselect custom option and exit typing mode
|
|
310
|
+
this.state.multiSelections.delete(options.length);
|
|
311
|
+
this.state.isTypingCustom = false;
|
|
312
|
+
}
|
|
313
|
+
return this.rerender();
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
// Single-select: submit answer and move to next question
|
|
317
|
+
if (this.submitAnswer()) {
|
|
318
|
+
this.state.warningMessage = '';
|
|
319
|
+
this.nextQuestion();
|
|
320
|
+
}
|
|
321
|
+
return this.rerender();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (key.name === 'backspace') {
|
|
325
|
+
this.state.inputBuffer = this.state.inputBuffer.slice(0, -1);
|
|
326
|
+
return this.rerender();
|
|
327
|
+
}
|
|
328
|
+
// Printable character
|
|
329
|
+
if (key.char && key.char.length === 1 && !key.ctrl && !key.meta) {
|
|
330
|
+
this.state.inputBuffer += key.char;
|
|
331
|
+
return this.rerender();
|
|
332
|
+
}
|
|
333
|
+
return this.noAction();
|
|
334
|
+
}
|
|
335
|
+
handleNavigationKey(key) {
|
|
336
|
+
const optionCount = this.getOptionCount();
|
|
337
|
+
const currentQ = this.questions[this.state.currentQuestion];
|
|
338
|
+
const isMultiSelect = currentQ.multiSelect === true;
|
|
339
|
+
const options = currentQ.options ?? [];
|
|
340
|
+
const allowCustom = currentQ.allowCustom !== false;
|
|
341
|
+
const customIndex = options.length;
|
|
342
|
+
if (key.name === 'escape') {
|
|
343
|
+
return this.close(this.getResult());
|
|
344
|
+
}
|
|
345
|
+
if (key.name === 'up') {
|
|
346
|
+
this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
|
|
347
|
+
if (!isMultiSelect && allowCustom && this.state.selectedIndex === customIndex) {
|
|
348
|
+
this.state.isTypingCustom = true;
|
|
349
|
+
}
|
|
350
|
+
return this.rerender();
|
|
351
|
+
}
|
|
352
|
+
if (key.name === 'down') {
|
|
353
|
+
this.state.selectedIndex = Math.min(optionCount - 1, this.state.selectedIndex + 1);
|
|
354
|
+
if (!isMultiSelect && allowCustom && this.state.selectedIndex === customIndex) {
|
|
355
|
+
this.state.isTypingCustom = true;
|
|
356
|
+
}
|
|
357
|
+
return this.rerender();
|
|
358
|
+
}
|
|
359
|
+
if (key.name === 'left') {
|
|
360
|
+
this.prevQuestion();
|
|
361
|
+
return this.rerender();
|
|
362
|
+
}
|
|
363
|
+
if (key.name === 'right' || key.name === 'tab') {
|
|
364
|
+
this.nextQuestion();
|
|
365
|
+
return this.rerender();
|
|
366
|
+
}
|
|
367
|
+
if (key.name === 'space' && isMultiSelect) {
|
|
368
|
+
const isOnCustom = allowCustom && this.state.selectedIndex === customIndex;
|
|
369
|
+
if (this.state.multiSelections.has(this.state.selectedIndex)) {
|
|
370
|
+
// Deselect
|
|
371
|
+
this.state.multiSelections.delete(this.state.selectedIndex);
|
|
372
|
+
if (isOnCustom) {
|
|
373
|
+
this.state.inputBuffer = '';
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
// Select
|
|
378
|
+
this.state.multiSelections.add(this.state.selectedIndex);
|
|
379
|
+
if (isOnCustom) {
|
|
380
|
+
// Start typing mode for custom input
|
|
381
|
+
this.state.isTypingCustom = true;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return this.rerender();
|
|
385
|
+
}
|
|
386
|
+
if (key.name === 'return') {
|
|
387
|
+
if (this.submitAnswer()) {
|
|
388
|
+
this.state.warningMessage = '';
|
|
389
|
+
this.nextQuestion();
|
|
390
|
+
}
|
|
391
|
+
return this.rerender();
|
|
392
|
+
}
|
|
393
|
+
// Number keys 1-9 reserved for tab navigation (jump to question N)
|
|
394
|
+
if (key.char && key.char >= '1' && key.char <= '9') {
|
|
395
|
+
const tabIndex = parseInt(key.char, 10) - 1;
|
|
396
|
+
if (tabIndex < this.questions.length) {
|
|
397
|
+
this.state.currentQuestion = tabIndex;
|
|
398
|
+
this.state.isOnSubmitTab = false;
|
|
399
|
+
this.restoreFromAnswer();
|
|
400
|
+
return this.rerender();
|
|
401
|
+
}
|
|
402
|
+
else if (tabIndex === this.questions.length) {
|
|
403
|
+
// Jump to Submit tab
|
|
404
|
+
this.state.isOnSubmitTab = true;
|
|
405
|
+
this.state.isTypingCustom = false;
|
|
406
|
+
this.state.inputBuffer = '';
|
|
407
|
+
return this.rerender();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return this.noAction();
|
|
411
|
+
}
|
|
412
|
+
// ===========================================================================
|
|
413
|
+
// Private: Helper Methods
|
|
414
|
+
// ===========================================================================
|
|
415
|
+
getResult() {
|
|
416
|
+
const skipped = this.questions.filter((q) => !(q.id in this.state.answers)).map((q) => q.id);
|
|
417
|
+
return {
|
|
418
|
+
answers: this.state.answers,
|
|
419
|
+
skipped,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
getOptionCount() {
|
|
423
|
+
const q = this.questions[this.state.currentQuestion];
|
|
424
|
+
const optionCount = q.options?.length ?? 0;
|
|
425
|
+
const allowCustom = q.allowCustom !== false;
|
|
426
|
+
return allowCustom ? optionCount + 1 : optionCount;
|
|
427
|
+
}
|
|
428
|
+
getAnswerInfo(question) {
|
|
429
|
+
const options = question.options ?? [];
|
|
430
|
+
const isMultiSelect = question.multiSelect === true;
|
|
431
|
+
const answeredOptions = new Set();
|
|
432
|
+
let customAnswerValue = '';
|
|
433
|
+
if (question.id in this.state.answers) {
|
|
434
|
+
const existingAnswer = this.state.answers[question.id];
|
|
435
|
+
if (isMultiSelect && Array.isArray(existingAnswer)) {
|
|
436
|
+
for (const ans of existingAnswer) {
|
|
437
|
+
const idx = options.indexOf(ans);
|
|
438
|
+
if (idx >= 0) {
|
|
439
|
+
answeredOptions.add(idx);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
customAnswerValue = ans;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
else if (typeof existingAnswer === 'string') {
|
|
447
|
+
const idx = options.indexOf(existingAnswer);
|
|
448
|
+
if (idx >= 0) {
|
|
449
|
+
answeredOptions.add(idx);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
customAnswerValue = existingAnswer;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return { answeredOptions, customAnswerValue };
|
|
457
|
+
}
|
|
458
|
+
restoreFromAnswer() {
|
|
459
|
+
const q = this.questions[this.state.currentQuestion];
|
|
460
|
+
const options = q.options ?? [];
|
|
461
|
+
const isMultiSelect = q.multiSelect === true;
|
|
462
|
+
const allowCustom = q.allowCustom !== false;
|
|
463
|
+
// Reset state first
|
|
464
|
+
this.state.selectedIndex = 0;
|
|
465
|
+
this.state.inputBuffer = '';
|
|
466
|
+
this.state.isTypingCustom = false;
|
|
467
|
+
this.state.multiSelections.clear();
|
|
468
|
+
if (!(q.id in this.state.answers)) {
|
|
469
|
+
// No existing answer - check if we should auto-type
|
|
470
|
+
if (options.length === 0 && allowCustom && !isMultiSelect) {
|
|
471
|
+
this.state.isTypingCustom = true;
|
|
472
|
+
}
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const existingAnswer = this.state.answers[q.id];
|
|
476
|
+
if (isMultiSelect && Array.isArray(existingAnswer)) {
|
|
477
|
+
for (const answer of existingAnswer) {
|
|
478
|
+
const idx = options.indexOf(answer);
|
|
479
|
+
if (idx >= 0) {
|
|
480
|
+
this.state.multiSelections.add(idx);
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
this.state.multiSelections.add(options.length);
|
|
484
|
+
this.state.inputBuffer = answer;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (this.state.multiSelections.size > 0) {
|
|
488
|
+
this.state.selectedIndex = Math.min(...this.state.multiSelections);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
else if (typeof existingAnswer === 'string') {
|
|
492
|
+
const idx = options.indexOf(existingAnswer);
|
|
493
|
+
if (idx >= 0) {
|
|
494
|
+
this.state.selectedIndex = idx;
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
this.state.selectedIndex = options.length;
|
|
498
|
+
this.state.inputBuffer = existingAnswer;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
nextQuestion() {
|
|
503
|
+
if (this.state.isOnSubmitTab) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (this.state.currentQuestion < this.questions.length - 1) {
|
|
507
|
+
this.state.currentQuestion++;
|
|
508
|
+
this.restoreFromAnswer();
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
// On last question - move to Submit tab
|
|
512
|
+
this.state.isOnSubmitTab = true;
|
|
513
|
+
this.state.isTypingCustom = false;
|
|
514
|
+
this.state.inputBuffer = '';
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
prevQuestion() {
|
|
518
|
+
if (this.state.isOnSubmitTab) {
|
|
519
|
+
this.state.isOnSubmitTab = false;
|
|
520
|
+
this.restoreFromAnswer();
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (this.state.currentQuestion > 0) {
|
|
524
|
+
this.state.currentQuestion--;
|
|
525
|
+
this.restoreFromAnswer();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
tryComplete() {
|
|
529
|
+
const unanswered = this.questions.filter((q) => !(q.id in this.state.answers));
|
|
530
|
+
if (unanswered.length === 0) {
|
|
531
|
+
this.state.warningMessage = '';
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
// Navigate to first unanswered question and show warning
|
|
535
|
+
const firstUnansweredIndex = this.questions.findIndex((q) => !(q.id in this.state.answers));
|
|
536
|
+
this.state.warningMessage = `${String(unanswered.length)} question${unanswered.length > 1 ? 's' : ''} unanswered. Please answer all questions.`;
|
|
537
|
+
this.state.currentQuestion = firstUnansweredIndex;
|
|
538
|
+
this.state.isOnSubmitTab = false;
|
|
539
|
+
this.restoreFromAnswer();
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
submitAnswer() {
|
|
543
|
+
const q = this.questions[this.state.currentQuestion];
|
|
544
|
+
const options = q.options ?? [];
|
|
545
|
+
const allowCustom = q.allowCustom !== false;
|
|
546
|
+
const isMultiSelect = q.multiSelect === true;
|
|
547
|
+
const customIndex = options.length;
|
|
548
|
+
if (isMultiSelect) {
|
|
549
|
+
const selectedAnswers = [];
|
|
550
|
+
for (const idx of this.state.multiSelections) {
|
|
551
|
+
if (idx < options.length) {
|
|
552
|
+
selectedAnswers.push(options[idx]);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (this.state.multiSelections.has(customIndex) && this.state.inputBuffer.trim()) {
|
|
556
|
+
selectedAnswers.push(this.state.inputBuffer.trim());
|
|
557
|
+
}
|
|
558
|
+
if (selectedAnswers.length > 0) {
|
|
559
|
+
this.state.answers[q.id] = selectedAnswers;
|
|
560
|
+
return true;
|
|
561
|
+
}
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
if (this.state.isTypingCustom) {
|
|
566
|
+
const custom = this.state.inputBuffer.trim();
|
|
567
|
+
if (custom) {
|
|
568
|
+
this.state.answers[q.id] = custom;
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
else if (this.state.selectedIndex < options.length) {
|
|
574
|
+
this.state.answers[q.id] = options[this.state.selectedIndex];
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
577
|
+
else if (allowCustom && this.state.selectedIndex === customIndex) {
|
|
578
|
+
this.state.isTypingCustom = true;
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ask User Simple Overlay V2
|
|
3
|
+
*
|
|
4
|
+
* Simplified modal overlay for single-question input.
|
|
5
|
+
* Uses BaseOverlayV2 for TerminalUI integration.
|
|
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 { BaseOverlayV2 } from '../../base/index.js';
|
|
14
|
+
import type { RenderContext, OverlayAction, KeyEvent } from '../types.js';
|
|
15
|
+
interface SimpleState {
|
|
16
|
+
/** Selected option index */
|
|
17
|
+
selectedIndex: number;
|
|
18
|
+
/** Custom input buffer for free-text */
|
|
19
|
+
inputBuffer: string;
|
|
20
|
+
/** Whether currently typing custom input */
|
|
21
|
+
isTypingCustom: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface AskUserSimpleOptionsV2 {
|
|
24
|
+
question: string;
|
|
25
|
+
options?: string[];
|
|
26
|
+
allowCustom?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface AskUserSimpleResultV2 {
|
|
29
|
+
answer: string;
|
|
30
|
+
skipped: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare class AskUserSimpleOverlayV2 extends BaseOverlayV2<SimpleState, AskUserSimpleResultV2> {
|
|
33
|
+
readonly type: "inline";
|
|
34
|
+
readonly id = "ask-user-simple-overlay-v2";
|
|
35
|
+
private readonly question;
|
|
36
|
+
private readonly choices;
|
|
37
|
+
private readonly allowCustom;
|
|
38
|
+
constructor(options: AskUserSimpleOptionsV2);
|
|
39
|
+
protected renderContent(context: RenderContext): string[];
|
|
40
|
+
handleKey(key: KeyEvent): OverlayAction<AskUserSimpleResultV2>;
|
|
41
|
+
/**
|
|
42
|
+
* Summary shown after overlay closes.
|
|
43
|
+
*/
|
|
44
|
+
getCloseSummary(result: AskUserSimpleResultV2): string | null;
|
|
45
|
+
}
|
|
46
|
+
export {};
|