@raindrop-ai/wizard 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +47 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +117 -0
- package/dist/bin.js.map +1 -0
- package/dist/src/docs/browser.md +105 -0
- package/dist/src/docs/python.md +618 -0
- package/dist/src/docs/typescript.md +584 -0
- package/dist/src/docs/vercel-ai-sdk.md +304 -0
- package/dist/src/lib/agent-interface.d.ts +46 -0
- package/dist/src/lib/agent-interface.js +292 -0
- package/dist/src/lib/agent-interface.js.map +1 -0
- package/dist/src/lib/agent-prompts.d.ts +10 -0
- package/dist/src/lib/agent-prompts.js +49 -0
- package/dist/src/lib/agent-prompts.js.map +1 -0
- package/dist/src/lib/config.d.ts +39 -0
- package/dist/src/lib/config.js +549 -0
- package/dist/src/lib/config.js.map +1 -0
- package/dist/src/lib/constants.d.ts +27 -0
- package/dist/src/lib/constants.js +165 -0
- package/dist/src/lib/constants.js.map +1 -0
- package/dist/src/lib/handlers.d.ts +68 -0
- package/dist/src/lib/handlers.js +420 -0
- package/dist/src/lib/handlers.js.map +1 -0
- package/dist/src/lib/integration-testing.d.ts +44 -0
- package/dist/src/lib/integration-testing.js +123 -0
- package/dist/src/lib/integration-testing.js.map +1 -0
- package/dist/src/lib/mcp.d.ts +14 -0
- package/dist/src/lib/mcp.js +134 -0
- package/dist/src/lib/mcp.js.map +1 -0
- package/dist/src/lib/sdk-messages.d.ts +17 -0
- package/dist/src/lib/sdk-messages.js +278 -0
- package/dist/src/lib/sdk-messages.js.map +1 -0
- package/dist/src/lib/wizard.d.ts +6 -0
- package/dist/src/lib/wizard.js +131 -0
- package/dist/src/lib/wizard.js.map +1 -0
- package/dist/src/run.d.ts +8 -0
- package/dist/src/run.js +53 -0
- package/dist/src/run.js.map +1 -0
- package/dist/src/ui/App.d.ts +15 -0
- package/dist/src/ui/App.js +27 -0
- package/dist/src/ui/App.js.map +1 -0
- package/dist/src/ui/cancellation.d.ts +14 -0
- package/dist/src/ui/cancellation.js +17 -0
- package/dist/src/ui/cancellation.js.map +1 -0
- package/dist/src/ui/components/ClarifyingQuestionsPrompt.d.ts +17 -0
- package/dist/src/ui/components/ClarifyingQuestionsPrompt.js +359 -0
- package/dist/src/ui/components/ClarifyingQuestionsPrompt.js.map +1 -0
- package/dist/src/ui/components/ContinuePrompt.d.ts +14 -0
- package/dist/src/ui/components/ContinuePrompt.js +23 -0
- package/dist/src/ui/components/ContinuePrompt.js.map +1 -0
- package/dist/src/ui/components/DiffDisplay.d.ts +18 -0
- package/dist/src/ui/components/DiffDisplay.js +110 -0
- package/dist/src/ui/components/DiffDisplay.js.map +1 -0
- package/dist/src/ui/components/FeedbackSelectPrompt.d.ts +20 -0
- package/dist/src/ui/components/FeedbackSelectPrompt.js +132 -0
- package/dist/src/ui/components/FeedbackSelectPrompt.js.map +1 -0
- package/dist/src/ui/components/HistoryItemDisplay.d.ts +14 -0
- package/dist/src/ui/components/HistoryItemDisplay.js +140 -0
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -0
- package/dist/src/ui/components/Logo.d.ts +10 -0
- package/dist/src/ui/components/Logo.js +47 -0
- package/dist/src/ui/components/Logo.js.map +1 -0
- package/dist/src/ui/components/OrgInfoBox.d.ts +11 -0
- package/dist/src/ui/components/OrgInfoBox.js +16 -0
- package/dist/src/ui/components/OrgInfoBox.js.map +1 -0
- package/dist/src/ui/components/PendingPrompt.d.ts +18 -0
- package/dist/src/ui/components/PendingPrompt.js +57 -0
- package/dist/src/ui/components/PendingPrompt.js.map +1 -0
- package/dist/src/ui/components/PersistentTextInput.d.ts +21 -0
- package/dist/src/ui/components/PersistentTextInput.js +117 -0
- package/dist/src/ui/components/PersistentTextInput.js.map +1 -0
- package/dist/src/ui/components/PlanApprovalPrompt.d.ts +19 -0
- package/dist/src/ui/components/PlanApprovalPrompt.js +62 -0
- package/dist/src/ui/components/PlanApprovalPrompt.js.map +1 -0
- package/dist/src/ui/components/PromptContainer.d.ts +14 -0
- package/dist/src/ui/components/PromptContainer.js +18 -0
- package/dist/src/ui/components/PromptContainer.js.map +1 -0
- package/dist/src/ui/components/SelectPrompt.d.ts +14 -0
- package/dist/src/ui/components/SelectPrompt.js +62 -0
- package/dist/src/ui/components/SelectPrompt.js.map +1 -0
- package/dist/src/ui/components/SpinnerDisplay.d.ts +13 -0
- package/dist/src/ui/components/SpinnerDisplay.js +11 -0
- package/dist/src/ui/components/SpinnerDisplay.js.map +1 -0
- package/dist/src/ui/components/ToolApprovalPrompt.d.ts +14 -0
- package/dist/src/ui/components/ToolApprovalPrompt.js +142 -0
- package/dist/src/ui/components/ToolApprovalPrompt.js.map +1 -0
- package/dist/src/ui/components/ToolCallDisplay.d.ts +14 -0
- package/dist/src/ui/components/ToolCallDisplay.js +83 -0
- package/dist/src/ui/components/ToolCallDisplay.js.map +1 -0
- package/dist/src/ui/components/WriteKeyDisplay.d.ts +15 -0
- package/dist/src/ui/components/WriteKeyDisplay.js +13 -0
- package/dist/src/ui/components/WriteKeyDisplay.js.map +1 -0
- package/dist/src/ui/contexts/WizardContext.d.ts +210 -0
- package/dist/src/ui/contexts/WizardContext.js +362 -0
- package/dist/src/ui/contexts/WizardContext.js.map +1 -0
- package/dist/src/ui/hooks/useCancellation.d.ts +15 -0
- package/dist/src/ui/hooks/useCancellation.js +25 -0
- package/dist/src/ui/hooks/useCancellation.js.map +1 -0
- package/dist/src/ui/render.d.ts +34 -0
- package/dist/src/ui/render.js +94 -0
- package/dist/src/ui/render.js.map +1 -0
- package/dist/src/ui/types.d.ts +184 -0
- package/dist/src/ui/types.js +6 -0
- package/dist/src/ui/types.js.map +1 -0
- package/dist/src/utils/clack-utils.d.ts +13 -0
- package/dist/src/utils/clack-utils.js +131 -0
- package/dist/src/utils/clack-utils.js.map +1 -0
- package/dist/src/utils/debug.d.ts +13 -0
- package/dist/src/utils/debug.js +47 -0
- package/dist/src/utils/debug.js.map +1 -0
- package/dist/src/utils/environment.d.ts +5 -0
- package/dist/src/utils/environment.js +131 -0
- package/dist/src/utils/environment.js.map +1 -0
- package/dist/src/utils/logging.d.ts +9 -0
- package/dist/src/utils/logging.js +38 -0
- package/dist/src/utils/logging.js.map +1 -0
- package/dist/src/utils/oauth.d.ts +12 -0
- package/dist/src/utils/oauth.js +497 -0
- package/dist/src/utils/oauth.js.map +1 -0
- package/dist/src/utils/package-json-types.d.ts +44 -0
- package/dist/src/utils/package-json-types.js +6 -0
- package/dist/src/utils/package-json-types.js.map +1 -0
- package/dist/src/utils/package-json.d.ts +19 -0
- package/dist/src/utils/package-json.js +22 -0
- package/dist/src/utils/package-json.js.map +1 -0
- package/dist/src/utils/session.d.ts +2 -0
- package/dist/src/utils/session.js +87 -0
- package/dist/src/utils/session.js.map +1 -0
- package/dist/src/utils/types.d.ts +61 -0
- package/dist/src/utils/types.js +2 -0
- package/dist/src/utils/types.js.map +1 -0
- package/dist/src/utils/ui.d.ts +120 -0
- package/dist/src/utils/ui.js +164 -0
- package/dist/src/utils/ui.js.map +1 -0
- package/package.json +140 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clarifying questions prompt component.
|
|
3
|
+
* Displays questions from the AskUserQuestion tool and collects answers.
|
|
4
|
+
* Styled with a stepper progress bar showing question headers.
|
|
5
|
+
* Features inline text input for the "Type something" option.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import type { ClarifyingQuestionsProps } from '../types.js';
|
|
9
|
+
interface ClarifyingQuestionsPromptComponentProps {
|
|
10
|
+
props: ClarifyingQuestionsProps;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Clarifying questions prompt for the AskUserQuestion tool.
|
|
14
|
+
* Supports single-select, multi-select, and inline free-text input.
|
|
15
|
+
*/
|
|
16
|
+
export declare function ClarifyingQuestionsPrompt({ props, }: ClarifyingQuestionsPromptComponentProps): React.ReactElement;
|
|
17
|
+
export default ClarifyingQuestionsPrompt;
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Clarifying questions prompt component.
|
|
4
|
+
* Displays questions from the AskUserQuestion tool and collects answers.
|
|
5
|
+
* Styled with a stepper progress bar showing question headers.
|
|
6
|
+
* Features inline text input for the "Type something" option.
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useMemo, useCallback, useEffect } from 'react';
|
|
9
|
+
import { Box, Text, useInput } from 'ink';
|
|
10
|
+
import TextInput from 'ink-text-input';
|
|
11
|
+
import { useWizard } from '../contexts/WizardContext.js';
|
|
12
|
+
import { PromptContainer } from './PromptContainer.js';
|
|
13
|
+
/**
|
|
14
|
+
* Stepper progress bar component showing question headers with status indicators.
|
|
15
|
+
*/
|
|
16
|
+
function StepperProgress({ questions, currentIndex, answers, }) {
|
|
17
|
+
// Add "Submit" as the final step
|
|
18
|
+
const steps = [...questions.map((q) => q.header), 'Submit'];
|
|
19
|
+
return (_jsx(Box, { marginBottom: 1, children: steps.map((header, idx) => {
|
|
20
|
+
const isCompleted = idx < currentIndex ||
|
|
21
|
+
(idx < questions.length && answers[questions[idx].question]);
|
|
22
|
+
const isCurrent = idx === currentIndex;
|
|
23
|
+
// Determine the marker
|
|
24
|
+
let marker;
|
|
25
|
+
if (isCompleted && !isCurrent) {
|
|
26
|
+
marker = '✓';
|
|
27
|
+
}
|
|
28
|
+
else if (isCurrent) {
|
|
29
|
+
marker = '●';
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
marker = '○';
|
|
33
|
+
}
|
|
34
|
+
// Determine colors
|
|
35
|
+
const markerColor = isCompleted ? 'green' : isCurrent ? 'cyan' : 'gray';
|
|
36
|
+
const textColor = isCurrent ? 'cyan' : isCompleted ? undefined : 'gray';
|
|
37
|
+
const textDim = !isCurrent && !isCompleted;
|
|
38
|
+
return (_jsxs(Box, { children: [idx > 0 && _jsx(Text, { color: "gray", children: " \u2501\u2501\u2501 " }), _jsxs(Box, { children: [_jsx(Text, { color: markerColor, children: marker }), _jsxs(Text, { color: textColor, dimColor: textDim, children: [' ', header] })] })] }, idx));
|
|
39
|
+
}) }));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Clarifying questions prompt for the AskUserQuestion tool.
|
|
43
|
+
* Supports single-select, multi-select, and inline free-text input.
|
|
44
|
+
*/
|
|
45
|
+
export function ClarifyingQuestionsPrompt({ props, }) {
|
|
46
|
+
const { questions } = props;
|
|
47
|
+
const { state, actions } = useWizard();
|
|
48
|
+
const { resolvePending, addItem } = actions;
|
|
49
|
+
const { agentState } = state;
|
|
50
|
+
// Track current question index
|
|
51
|
+
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
|
|
52
|
+
// Track answers for each question
|
|
53
|
+
const [answers, setAnswers] = useState({});
|
|
54
|
+
// Track multi-select selections for current question
|
|
55
|
+
const [multiSelections, setMultiSelections] = useState([]);
|
|
56
|
+
// Track highlighted item index
|
|
57
|
+
const [highlightedIndex, setHighlightedIndex] = useState(0);
|
|
58
|
+
// Track custom text value for "Type something" option
|
|
59
|
+
const [customText, setCustomText] = useState('');
|
|
60
|
+
// Track if we're in typing mode (when Type something is selected and user is typing)
|
|
61
|
+
const [isTypingMode, setIsTypingMode] = useState(false);
|
|
62
|
+
// Track if we're in review mode (after all questions answered)
|
|
63
|
+
const [isReviewMode, setIsReviewMode] = useState(false);
|
|
64
|
+
// Track highlighted option in review mode (0 = Submit, 1 = Cancel)
|
|
65
|
+
const [reviewHighlightedIndex, setReviewHighlightedIndex] = useState(0);
|
|
66
|
+
const currentQuestion = questions[currentQuestionIndex];
|
|
67
|
+
const isLastQuestion = currentQuestionIndex === questions.length - 1;
|
|
68
|
+
const isMultiSelect = currentQuestion?.multiSelect ?? false;
|
|
69
|
+
// Get regular option values (needed before handleCustomTextSubmit)
|
|
70
|
+
const regularOptionValues = useMemo(() => {
|
|
71
|
+
if (!currentQuestion)
|
|
72
|
+
return new Set();
|
|
73
|
+
return new Set(currentQuestion.options.map((opt) => opt.label));
|
|
74
|
+
}, [currentQuestion]);
|
|
75
|
+
// Get custom entries (values in multiSelections that aren't regular options)
|
|
76
|
+
const customEntries = useMemo(() => {
|
|
77
|
+
return multiSelections.filter((v) => !regularOptionValues.has(v));
|
|
78
|
+
}, [multiSelections, regularOptionValues]);
|
|
79
|
+
// Build list items for current question
|
|
80
|
+
const items = useMemo(() => {
|
|
81
|
+
if (!currentQuestion)
|
|
82
|
+
return [];
|
|
83
|
+
const questionItems = currentQuestion.options.map((opt) => ({
|
|
84
|
+
label: opt.label,
|
|
85
|
+
value: opt.label,
|
|
86
|
+
description: opt.description,
|
|
87
|
+
}));
|
|
88
|
+
// Add "Type something" option for free-text input
|
|
89
|
+
questionItems.push({
|
|
90
|
+
label: 'Type something',
|
|
91
|
+
value: '__OTHER__',
|
|
92
|
+
});
|
|
93
|
+
// Add "Submit" option for multi-select (always visible)
|
|
94
|
+
if (isMultiSelect) {
|
|
95
|
+
questionItems.push({
|
|
96
|
+
label: 'Submit',
|
|
97
|
+
value: '__SUBMIT__',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return questionItems;
|
|
101
|
+
}, [currentQuestion, isMultiSelect, multiSelections]);
|
|
102
|
+
// Auto-enter typing mode when "Type something" is highlighted
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
// Only auto-enter typing mode if:
|
|
105
|
+
// 1. Not already in typing mode
|
|
106
|
+
// 2. Not in review mode
|
|
107
|
+
// 3. "Type something" is highlighted
|
|
108
|
+
// 4. No custom entries exist yet (for multi-select)
|
|
109
|
+
if (!isTypingMode &&
|
|
110
|
+
!isReviewMode &&
|
|
111
|
+
items[highlightedIndex]?.value === '__OTHER__' &&
|
|
112
|
+
customEntries.length === 0) {
|
|
113
|
+
setIsTypingMode(true);
|
|
114
|
+
}
|
|
115
|
+
}, [
|
|
116
|
+
highlightedIndex,
|
|
117
|
+
items,
|
|
118
|
+
isTypingMode,
|
|
119
|
+
isReviewMode,
|
|
120
|
+
customEntries.length,
|
|
121
|
+
]);
|
|
122
|
+
// Move to next question or go to review
|
|
123
|
+
const proceedToNext = useCallback((answer) => {
|
|
124
|
+
const newAnswers = {
|
|
125
|
+
...answers,
|
|
126
|
+
[currentQuestion.question]: answer,
|
|
127
|
+
};
|
|
128
|
+
setAnswers(newAnswers);
|
|
129
|
+
if (isLastQuestion) {
|
|
130
|
+
// All questions answered, go to review mode
|
|
131
|
+
setIsReviewMode(true);
|
|
132
|
+
setReviewHighlightedIndex(0);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Move to next question
|
|
136
|
+
setCurrentQuestionIndex((idx) => idx + 1);
|
|
137
|
+
setMultiSelections([]);
|
|
138
|
+
setHighlightedIndex(0);
|
|
139
|
+
setCustomText('');
|
|
140
|
+
setIsTypingMode(false);
|
|
141
|
+
}
|
|
142
|
+
}, [answers, currentQuestion, isLastQuestion]);
|
|
143
|
+
// Submit final answers
|
|
144
|
+
const submitAnswers = useCallback(() => {
|
|
145
|
+
// Add history item showing questions and answers
|
|
146
|
+
addItem({
|
|
147
|
+
type: 'clarifying-questions-result',
|
|
148
|
+
text: "User answered Raindrop wizard's questions:",
|
|
149
|
+
questionsAndAnswers: questions.map((q) => ({
|
|
150
|
+
question: q.question,
|
|
151
|
+
answer: answers[q.question] || '(no answer)',
|
|
152
|
+
})),
|
|
153
|
+
});
|
|
154
|
+
const result = {
|
|
155
|
+
questions,
|
|
156
|
+
answers,
|
|
157
|
+
};
|
|
158
|
+
resolvePending(result);
|
|
159
|
+
}, [questions, answers, resolvePending, addItem]);
|
|
160
|
+
// Cancel and return empty answers, interrupting the agent
|
|
161
|
+
const cancelAnswers = useCallback(() => {
|
|
162
|
+
// Add history item indicating user declined
|
|
163
|
+
addItem({
|
|
164
|
+
type: 'declined-questions',
|
|
165
|
+
text: 'User declined to answer questions',
|
|
166
|
+
});
|
|
167
|
+
// Interrupt the agent
|
|
168
|
+
if (agentState.queryHandle?.interrupt) {
|
|
169
|
+
void agentState.queryHandle.interrupt();
|
|
170
|
+
}
|
|
171
|
+
const result = {
|
|
172
|
+
questions,
|
|
173
|
+
answers: {},
|
|
174
|
+
};
|
|
175
|
+
resolvePending(result);
|
|
176
|
+
}, [questions, resolvePending, addItem, agentState.queryHandle]);
|
|
177
|
+
// Handle custom text submission from TextInput
|
|
178
|
+
const handleCustomTextSubmit = useCallback((value) => {
|
|
179
|
+
if (!value.trim())
|
|
180
|
+
return;
|
|
181
|
+
const trimmedValue = value.trim();
|
|
182
|
+
if (isMultiSelect) {
|
|
183
|
+
// Multi-select: Replace any existing custom entries with the new value
|
|
184
|
+
// This ensures only the latest typed value is kept, not multiple versions
|
|
185
|
+
if (multiSelections.includes(trimmedValue)) {
|
|
186
|
+
// Same value already exists - toggle it off
|
|
187
|
+
setMultiSelections((prev) => prev.filter((v) => v !== trimmedValue));
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// New value - remove old custom entries and add the new one
|
|
191
|
+
setMultiSelections((prev) => {
|
|
192
|
+
const withoutOldCustom = prev.filter((v) => regularOptionValues.has(v));
|
|
193
|
+
return [...withoutOldCustom, trimmedValue];
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// Don't clear text - keep it for further editing or submit
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
// Single-select: Submit immediately with the typed value
|
|
200
|
+
proceedToNext(trimmedValue);
|
|
201
|
+
}
|
|
202
|
+
}, [isMultiSelect, multiSelections, regularOptionValues, proceedToNext]);
|
|
203
|
+
// Handle keyboard navigation and selection
|
|
204
|
+
useInput((input, key) => {
|
|
205
|
+
// Ctrl+C always cancels/declines questions (no matter what mode we're in)
|
|
206
|
+
if (key.ctrl && input === 'c') {
|
|
207
|
+
cancelAnswers();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Review mode has its own navigation
|
|
211
|
+
if (isReviewMode) {
|
|
212
|
+
if (key.escape) {
|
|
213
|
+
// Go back to last question
|
|
214
|
+
setIsReviewMode(false);
|
|
215
|
+
setHighlightedIndex(0);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (key.upArrow) {
|
|
219
|
+
setReviewHighlightedIndex((idx) => Math.max(0, idx - 1));
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (key.downArrow) {
|
|
223
|
+
setReviewHighlightedIndex((idx) => Math.min(1, idx + 1));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (key.return) {
|
|
227
|
+
if (reviewHighlightedIndex === 0) {
|
|
228
|
+
submitAnswers();
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
cancelAnswers();
|
|
232
|
+
}
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
// Escape handling - always active
|
|
238
|
+
if (key.escape) {
|
|
239
|
+
if (isTypingMode) {
|
|
240
|
+
// Exit typing mode
|
|
241
|
+
setIsTypingMode(false);
|
|
242
|
+
}
|
|
243
|
+
else if (currentQuestionIndex > 0) {
|
|
244
|
+
// Go back to previous question
|
|
245
|
+
setCurrentQuestionIndex((idx) => idx - 1);
|
|
246
|
+
setMultiSelections([]);
|
|
247
|
+
setHighlightedIndex(0);
|
|
248
|
+
setCustomText('');
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
// On first question, cancel entirely
|
|
252
|
+
cancelAnswers();
|
|
253
|
+
}
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
// Arrow keys - exit typing mode and navigate
|
|
257
|
+
if (key.upArrow) {
|
|
258
|
+
if (isTypingMode) {
|
|
259
|
+
setIsTypingMode(false);
|
|
260
|
+
}
|
|
261
|
+
setHighlightedIndex((idx) => Math.max(0, idx - 1));
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (key.downArrow) {
|
|
265
|
+
if (isTypingMode) {
|
|
266
|
+
setIsTypingMode(false);
|
|
267
|
+
}
|
|
268
|
+
setHighlightedIndex((idx) => Math.min(items.length - 1, idx + 1));
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
// Skip other keys when typing - TextInput handles them
|
|
272
|
+
if (isTypingMode) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// Enter to select
|
|
276
|
+
if (key.return) {
|
|
277
|
+
const selectedItem = items[highlightedIndex];
|
|
278
|
+
if (!selectedItem)
|
|
279
|
+
return;
|
|
280
|
+
if (selectedItem.value === '__OTHER__') {
|
|
281
|
+
if (isMultiSelect && customEntries.length > 0) {
|
|
282
|
+
// Already has custom entries - toggle (clear) them to uncheck
|
|
283
|
+
setMultiSelections((prev) => prev.filter((v) => !customEntries.includes(v)));
|
|
284
|
+
setCustomText('');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
// No custom entries yet - enter typing mode
|
|
288
|
+
setIsTypingMode(true);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (selectedItem.value === '__SUBMIT__') {
|
|
292
|
+
// Submit with all checked selections only
|
|
293
|
+
if (multiSelections.length > 0) {
|
|
294
|
+
proceedToNext(multiSelections.join(', '));
|
|
295
|
+
}
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (isMultiSelect) {
|
|
299
|
+
// Toggle selection for multi-select
|
|
300
|
+
setMultiSelections((prev) => {
|
|
301
|
+
if (prev.includes(selectedItem.value)) {
|
|
302
|
+
return prev.filter((v) => v !== selectedItem.value);
|
|
303
|
+
}
|
|
304
|
+
return [...prev, selectedItem.value];
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
// Single select - proceed immediately
|
|
309
|
+
proceedToNext(selectedItem.value);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
// Render a single list item
|
|
314
|
+
const renderItem = (item, index) => {
|
|
315
|
+
const isHighlighted = index === highlightedIndex;
|
|
316
|
+
const isSelected = multiSelections.includes(item.value);
|
|
317
|
+
const isTypeOption = item.value === '__OTHER__';
|
|
318
|
+
const isSubmitOption = item.value === '__SUBMIT__';
|
|
319
|
+
const displayNumber = index + 1;
|
|
320
|
+
// Indicator (removed for cleaner UI)
|
|
321
|
+
const indicator = ' ';
|
|
322
|
+
// For "Type something" option with inline text input
|
|
323
|
+
if (isTypeOption) {
|
|
324
|
+
// Show checkbox only for multi-select
|
|
325
|
+
const hasCustomEntries = customEntries.length > 0;
|
|
326
|
+
const checkbox = isMultiSelect
|
|
327
|
+
? hasCustomEntries
|
|
328
|
+
? '[✓] '
|
|
329
|
+
: '[ ] '
|
|
330
|
+
: '';
|
|
331
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: isHighlighted ? 'cyan' : undefined, children: [indicator, displayNumber, ". ", checkbox] }), isTypingMode || customText ? (_jsx(TextInput, { value: customText, onChange: setCustomText, onSubmit: handleCustomTextSubmit, focus: isTypingMode, placeholder: "" })) : (_jsx(Text, { color: isHighlighted ? 'cyan' : undefined, children: item.label }))] }, item.value));
|
|
332
|
+
}
|
|
333
|
+
// Submit option
|
|
334
|
+
if (isSubmitOption) {
|
|
335
|
+
const canSubmit = multiSelections.length > 0;
|
|
336
|
+
const textColor = isHighlighted
|
|
337
|
+
? 'cyan'
|
|
338
|
+
: !canSubmit
|
|
339
|
+
? 'gray'
|
|
340
|
+
: 'green';
|
|
341
|
+
// Calculate spacing to align with checkbox brackets (same as "N. " where N is the number)
|
|
342
|
+
const numberSpacing = `${displayNumber}. `;
|
|
343
|
+
return (_jsx(Box, { children: _jsxs(Text, { color: textColor, bold: true, children: [indicator, ' '.repeat(numberSpacing.length), "Submit"] }) }, item.value));
|
|
344
|
+
}
|
|
345
|
+
// Regular option - show checkbox for multi-select mode
|
|
346
|
+
const checkbox = isMultiSelect ? (isSelected ? '[✓] ' : '[ ] ') : '';
|
|
347
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: isHighlighted ? 'cyan' : undefined, children: [indicator, displayNumber, ". ", checkbox, item.label] }), item.description && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { dimColor: true, children: item.description }) }))] }, item.value));
|
|
348
|
+
};
|
|
349
|
+
if (!currentQuestion && !isReviewMode) {
|
|
350
|
+
return (_jsx(Box, { children: _jsx(Text, { color: "red", children: "No questions to display" }) }));
|
|
351
|
+
}
|
|
352
|
+
// Review mode - show all answers and confirm
|
|
353
|
+
if (isReviewMode) {
|
|
354
|
+
return (_jsxs(PromptContainer, { children: [_jsx(StepperProgress, { questions: questions, currentIndex: questions.length, answers: answers }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Review your answers" }) }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: questions.map((q) => (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "blue", children: "\u25CF " }), _jsx(Text, { children: q.question })] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "green", children: answers[q.question] || '(no answer)' }) })] }, q.question))) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: "Ready to submit your answers?" }) }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { children: _jsxs(Text, { color: reviewHighlightedIndex === 0 ? 'cyan' : 'green', bold: true, children: [reviewHighlightedIndex === 0 ? '› ' : ' ', "1. Submit answers"] }) }), _jsx(Box, { children: _jsxs(Text, { color: reviewHighlightedIndex === 1 ? 'cyan' : undefined, children: [reviewHighlightedIndex === 1 ? '› ' : ' ', "2. Cancel"] }) })] })] }));
|
|
355
|
+
}
|
|
356
|
+
return (_jsxs(PromptContainer, { children: [_jsx(StepperProgress, { questions: questions, currentIndex: currentQuestionIndex, answers: answers }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: currentQuestion.question }) }), _jsx(Box, { flexDirection: "column", children: items.map((item, index) => renderItem(item, index)) })] }));
|
|
357
|
+
}
|
|
358
|
+
export default ClarifyingQuestionsPrompt;
|
|
359
|
+
//# sourceMappingURL=ClarifyingQuestionsPrompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClarifyingQuestionsPrompt.js","sourceRoot":"","sources":["../../../../src/ui/components/ClarifyingQuestionsPrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Clarifying questions prompt component.\n * Displays questions from the AskUserQuestion tool and collects answers.\n * Styled with a stepper progress bar showing question headers.\n * Features inline text input for the \"Type something\" option.\n */\n\nimport React, { useState, useMemo, useCallback, useEffect } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport TextInput from 'ink-text-input';\nimport { useWizard } from '../contexts/WizardContext.js';\nimport { PromptContainer } from './PromptContainer.js';\nimport type {\n ClarifyingQuestionsProps,\n ClarifyingQuestionsResult,\n ClarifyingQuestion,\n} from '../types.js';\n\ninterface ClarifyingQuestionsPromptComponentProps {\n props: ClarifyingQuestionsProps;\n}\n\ninterface ListItem {\n label: string;\n value: string;\n description?: string;\n}\n\n/**\n * Stepper progress bar component showing question headers with status indicators.\n */\nfunction StepperProgress({\n questions,\n currentIndex,\n answers,\n}: {\n questions: ClarifyingQuestion[];\n currentIndex: number;\n answers: Record<string, string>;\n}): React.ReactElement {\n // Add \"Submit\" as the final step\n const steps = [...questions.map((q) => q.header), 'Submit'];\n\n return (\n <Box marginBottom={1}>\n {steps.map((header, idx) => {\n const isCompleted =\n idx < currentIndex ||\n (idx < questions.length && answers[questions[idx].question]);\n const isCurrent = idx === currentIndex;\n\n // Determine the marker\n let marker: string;\n if (isCompleted && !isCurrent) {\n marker = '✓';\n } else if (isCurrent) {\n marker = '●';\n } else {\n marker = '○';\n }\n\n // Determine colors\n const markerColor = isCompleted ? 'green' : isCurrent ? 'cyan' : 'gray';\n const textColor = isCurrent ? 'cyan' : isCompleted ? undefined : 'gray';\n const textDim = !isCurrent && !isCompleted;\n\n return (\n <Box key={idx}>\n {/* Connector line before (except first) */}\n {idx > 0 && <Text color=\"gray\"> ━━━ </Text>}\n {/* Marker and label */}\n <Box>\n <Text color={markerColor}>{marker}</Text>\n <Text color={textColor} dimColor={textDim}>\n {' '}\n {header}\n </Text>\n </Box>\n </Box>\n );\n })}\n </Box>\n );\n}\n\n/**\n * Clarifying questions prompt for the AskUserQuestion tool.\n * Supports single-select, multi-select, and inline free-text input.\n */\nexport function ClarifyingQuestionsPrompt({\n props,\n}: ClarifyingQuestionsPromptComponentProps): React.ReactElement {\n const { questions } = props;\n const { state, actions } = useWizard();\n const { resolvePending, addItem } = actions;\n const { agentState } = state;\n\n // Track current question index\n const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);\n // Track answers for each question\n const [answers, setAnswers] = useState<Record<string, string>>({});\n // Track multi-select selections for current question\n const [multiSelections, setMultiSelections] = useState<string[]>([]);\n // Track highlighted item index\n const [highlightedIndex, setHighlightedIndex] = useState(0);\n // Track custom text value for \"Type something\" option\n const [customText, setCustomText] = useState('');\n // Track if we're in typing mode (when Type something is selected and user is typing)\n const [isTypingMode, setIsTypingMode] = useState(false);\n // Track if we're in review mode (after all questions answered)\n const [isReviewMode, setIsReviewMode] = useState(false);\n // Track highlighted option in review mode (0 = Submit, 1 = Cancel)\n const [reviewHighlightedIndex, setReviewHighlightedIndex] = useState(0);\n\n const currentQuestion = questions[currentQuestionIndex];\n const isLastQuestion = currentQuestionIndex === questions.length - 1;\n const isMultiSelect = currentQuestion?.multiSelect ?? false;\n\n // Get regular option values (needed before handleCustomTextSubmit)\n const regularOptionValues = useMemo(() => {\n if (!currentQuestion) return new Set<string>();\n return new Set(currentQuestion.options.map((opt) => opt.label));\n }, [currentQuestion]);\n\n // Get custom entries (values in multiSelections that aren't regular options)\n const customEntries = useMemo(() => {\n return multiSelections.filter((v) => !regularOptionValues.has(v));\n }, [multiSelections, regularOptionValues]);\n\n // Build list items for current question\n const items: ListItem[] = useMemo(() => {\n if (!currentQuestion) return [];\n\n const questionItems: ListItem[] = currentQuestion.options.map((opt) => ({\n label: opt.label,\n value: opt.label,\n description: opt.description,\n }));\n\n // Add \"Type something\" option for free-text input\n questionItems.push({\n label: 'Type something',\n value: '__OTHER__',\n });\n\n // Add \"Submit\" option for multi-select (always visible)\n if (isMultiSelect) {\n questionItems.push({\n label: 'Submit',\n value: '__SUBMIT__',\n });\n }\n\n return questionItems;\n }, [currentQuestion, isMultiSelect, multiSelections]);\n\n // Auto-enter typing mode when \"Type something\" is highlighted\n useEffect(() => {\n // Only auto-enter typing mode if:\n // 1. Not already in typing mode\n // 2. Not in review mode\n // 3. \"Type something\" is highlighted\n // 4. No custom entries exist yet (for multi-select)\n if (\n !isTypingMode &&\n !isReviewMode &&\n items[highlightedIndex]?.value === '__OTHER__' &&\n customEntries.length === 0\n ) {\n setIsTypingMode(true);\n }\n }, [\n highlightedIndex,\n items,\n isTypingMode,\n isReviewMode,\n customEntries.length,\n ]);\n\n // Move to next question or go to review\n const proceedToNext = useCallback(\n (answer: string) => {\n const newAnswers = {\n ...answers,\n [currentQuestion.question]: answer,\n };\n setAnswers(newAnswers);\n\n if (isLastQuestion) {\n // All questions answered, go to review mode\n setIsReviewMode(true);\n setReviewHighlightedIndex(0);\n } else {\n // Move to next question\n setCurrentQuestionIndex((idx) => idx + 1);\n setMultiSelections([]);\n setHighlightedIndex(0);\n setCustomText('');\n setIsTypingMode(false);\n }\n },\n [answers, currentQuestion, isLastQuestion],\n );\n\n // Submit final answers\n const submitAnswers = useCallback(() => {\n // Add history item showing questions and answers\n addItem({\n type: 'clarifying-questions-result',\n text: \"User answered Raindrop wizard's questions:\",\n questionsAndAnswers: questions.map((q) => ({\n question: q.question,\n answer: answers[q.question] || '(no answer)',\n })),\n });\n\n const result: ClarifyingQuestionsResult = {\n questions,\n answers,\n };\n resolvePending(result);\n }, [questions, answers, resolvePending, addItem]);\n\n // Cancel and return empty answers, interrupting the agent\n const cancelAnswers = useCallback(() => {\n // Add history item indicating user declined\n addItem({\n type: 'declined-questions',\n text: 'User declined to answer questions',\n });\n\n // Interrupt the agent\n if (agentState.queryHandle?.interrupt) {\n void agentState.queryHandle.interrupt();\n }\n\n const result: ClarifyingQuestionsResult = {\n questions,\n answers: {},\n };\n resolvePending(result);\n }, [questions, resolvePending, addItem, agentState.queryHandle]);\n\n // Handle custom text submission from TextInput\n const handleCustomTextSubmit = useCallback(\n (value: string) => {\n if (!value.trim()) return;\n\n const trimmedValue = value.trim();\n\n if (isMultiSelect) {\n // Multi-select: Replace any existing custom entries with the new value\n // This ensures only the latest typed value is kept, not multiple versions\n if (multiSelections.includes(trimmedValue)) {\n // Same value already exists - toggle it off\n setMultiSelections((prev) => prev.filter((v) => v !== trimmedValue));\n } else {\n // New value - remove old custom entries and add the new one\n setMultiSelections((prev) => {\n const withoutOldCustom = prev.filter((v) =>\n regularOptionValues.has(v),\n );\n return [...withoutOldCustom, trimmedValue];\n });\n }\n // Don't clear text - keep it for further editing or submit\n } else {\n // Single-select: Submit immediately with the typed value\n proceedToNext(trimmedValue);\n }\n },\n [isMultiSelect, multiSelections, regularOptionValues, proceedToNext],\n );\n\n // Handle keyboard navigation and selection\n useInput((input, key) => {\n // Ctrl+C always cancels/declines questions (no matter what mode we're in)\n if (key.ctrl && input === 'c') {\n cancelAnswers();\n return;\n }\n\n // Review mode has its own navigation\n if (isReviewMode) {\n if (key.escape) {\n // Go back to last question\n setIsReviewMode(false);\n setHighlightedIndex(0);\n return;\n }\n if (key.upArrow) {\n setReviewHighlightedIndex((idx) => Math.max(0, idx - 1));\n return;\n }\n if (key.downArrow) {\n setReviewHighlightedIndex((idx) => Math.min(1, idx + 1));\n return;\n }\n if (key.return) {\n if (reviewHighlightedIndex === 0) {\n submitAnswers();\n } else {\n cancelAnswers();\n }\n return;\n }\n return;\n }\n\n // Escape handling - always active\n if (key.escape) {\n if (isTypingMode) {\n // Exit typing mode\n setIsTypingMode(false);\n } else if (currentQuestionIndex > 0) {\n // Go back to previous question\n setCurrentQuestionIndex((idx) => idx - 1);\n setMultiSelections([]);\n setHighlightedIndex(0);\n setCustomText('');\n } else {\n // On first question, cancel entirely\n cancelAnswers();\n }\n return;\n }\n\n // Arrow keys - exit typing mode and navigate\n if (key.upArrow) {\n if (isTypingMode) {\n setIsTypingMode(false);\n }\n setHighlightedIndex((idx) => Math.max(0, idx - 1));\n return;\n }\n if (key.downArrow) {\n if (isTypingMode) {\n setIsTypingMode(false);\n }\n setHighlightedIndex((idx) => Math.min(items.length - 1, idx + 1));\n return;\n }\n\n // Skip other keys when typing - TextInput handles them\n if (isTypingMode) {\n return;\n }\n\n // Enter to select\n if (key.return) {\n const selectedItem = items[highlightedIndex];\n if (!selectedItem) return;\n\n if (selectedItem.value === '__OTHER__') {\n if (isMultiSelect && customEntries.length > 0) {\n // Already has custom entries - toggle (clear) them to uncheck\n setMultiSelections((prev) =>\n prev.filter((v) => !customEntries.includes(v)),\n );\n setCustomText('');\n return;\n }\n // No custom entries yet - enter typing mode\n setIsTypingMode(true);\n return;\n }\n\n if (selectedItem.value === '__SUBMIT__') {\n // Submit with all checked selections only\n if (multiSelections.length > 0) {\n proceedToNext(multiSelections.join(', '));\n }\n return;\n }\n\n if (isMultiSelect) {\n // Toggle selection for multi-select\n setMultiSelections((prev) => {\n if (prev.includes(selectedItem.value)) {\n return prev.filter((v) => v !== selectedItem.value);\n }\n return [...prev, selectedItem.value];\n });\n } else {\n // Single select - proceed immediately\n proceedToNext(selectedItem.value);\n }\n }\n });\n\n // Render a single list item\n const renderItem = (item: ListItem, index: number) => {\n const isHighlighted = index === highlightedIndex;\n const isSelected = multiSelections.includes(item.value);\n const isTypeOption = item.value === '__OTHER__';\n const isSubmitOption = item.value === '__SUBMIT__';\n const displayNumber = index + 1;\n\n // Indicator (removed for cleaner UI)\n const indicator = ' ';\n\n // For \"Type something\" option with inline text input\n if (isTypeOption) {\n // Show checkbox only for multi-select\n const hasCustomEntries = customEntries.length > 0;\n const checkbox = isMultiSelect\n ? hasCustomEntries\n ? '[✓] '\n : '[ ] '\n : '';\n\n return (\n <Box key={item.value}>\n <Text color={isHighlighted ? 'cyan' : undefined}>\n {indicator}\n {displayNumber}. {checkbox}\n </Text>\n {isTypingMode || customText ? (\n <TextInput\n value={customText}\n onChange={setCustomText}\n onSubmit={handleCustomTextSubmit}\n focus={isTypingMode}\n placeholder=\"\"\n />\n ) : (\n <Text color={isHighlighted ? 'cyan' : undefined}>{item.label}</Text>\n )}\n </Box>\n );\n }\n\n // Submit option\n if (isSubmitOption) {\n const canSubmit = multiSelections.length > 0;\n const textColor = isHighlighted\n ? 'cyan'\n : !canSubmit\n ? 'gray'\n : 'green';\n // Calculate spacing to align with checkbox brackets (same as \"N. \" where N is the number)\n const numberSpacing = `${displayNumber}. `;\n return (\n <Box key={item.value}>\n <Text color={textColor} bold>\n {indicator}\n {' '.repeat(numberSpacing.length)}Submit\n </Text>\n </Box>\n );\n }\n\n // Regular option - show checkbox for multi-select mode\n const checkbox = isMultiSelect ? (isSelected ? '[✓] ' : '[ ] ') : '';\n\n return (\n <Box key={item.value} flexDirection=\"column\">\n <Text color={isHighlighted ? 'cyan' : undefined}>\n {indicator}\n {displayNumber}. {checkbox}\n {item.label}\n </Text>\n {item.description && (\n <Box marginLeft={4}>\n <Text dimColor>{item.description}</Text>\n </Box>\n )}\n </Box>\n );\n };\n\n if (!currentQuestion && !isReviewMode) {\n return (\n <Box>\n <Text color=\"red\">No questions to display</Text>\n </Box>\n );\n }\n\n // Review mode - show all answers and confirm\n if (isReviewMode) {\n return (\n <PromptContainer>\n {/* Stepper progress bar - show Submit as current */}\n <StepperProgress\n questions={questions}\n currentIndex={questions.length}\n answers={answers}\n />\n\n {/* Review header */}\n <Box marginBottom={1}>\n <Text bold>Review your answers</Text>\n </Box>\n\n {/* List all questions and answers */}\n <Box flexDirection=\"column\" marginBottom={1}>\n {questions.map((q) => (\n <Box key={q.question} flexDirection=\"column\" marginLeft={1}>\n <Box>\n <Text color=\"blue\">● </Text>\n <Text>{q.question}</Text>\n </Box>\n <Box marginLeft={2}>\n <Text color=\"green\">\n {answers[q.question] || '(no answer)'}\n </Text>\n </Box>\n </Box>\n ))}\n </Box>\n\n {/* Confirmation prompt */}\n <Box marginBottom={1}>\n <Text>Ready to submit your answers?</Text>\n </Box>\n\n {/* Submit/Cancel options */}\n <Box flexDirection=\"column\">\n <Box>\n <Text\n color={reviewHighlightedIndex === 0 ? 'cyan' : 'green'}\n bold\n >\n {reviewHighlightedIndex === 0 ? '› ' : ' '}1. Submit answers\n </Text>\n </Box>\n <Box>\n <Text color={reviewHighlightedIndex === 1 ? 'cyan' : undefined}>\n {reviewHighlightedIndex === 1 ? '› ' : ' '}2. Cancel\n </Text>\n </Box>\n </Box>\n </PromptContainer>\n );\n }\n\n return (\n <PromptContainer>\n {/* Stepper progress bar */}\n <StepperProgress\n questions={questions}\n currentIndex={currentQuestionIndex}\n answers={answers}\n />\n\n {/* Question text */}\n <Box marginBottom={1}>\n <Text>{currentQuestion.question}</Text>\n </Box>\n\n {/* Options list */}\n <Box flexDirection=\"column\">\n {items.map((item, index) => renderItem(item, index))}\n </Box>\n </PromptContainer>\n );\n}\n\nexport default ClarifyingQuestionsPrompt;\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Continue button prompt component.
|
|
3
|
+
* Shows a message and waits for user to press Enter to continue.
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
interface ContinuePromptProps {
|
|
7
|
+
message: string;
|
|
8
|
+
buttonLabel?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Continue button prompt that waits for Enter key
|
|
12
|
+
*/
|
|
13
|
+
export declare function ContinuePrompt({ message, buttonLabel, }: ContinuePromptProps): React.ReactElement;
|
|
14
|
+
export default ContinuePrompt;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput } from 'ink';
|
|
3
|
+
import { useWizardActions } from '../contexts/WizardContext.js';
|
|
4
|
+
import { CANCEL_SYMBOL } from '../cancellation.js';
|
|
5
|
+
import { PromptContainer } from './PromptContainer.js';
|
|
6
|
+
/**
|
|
7
|
+
* Continue button prompt that waits for Enter key
|
|
8
|
+
*/
|
|
9
|
+
export function ContinuePrompt({ message, buttonLabel = 'Continue', }) {
|
|
10
|
+
const { resolvePending, addItem } = useWizardActions();
|
|
11
|
+
useInput((input, key) => {
|
|
12
|
+
if (key.ctrl && input === 'c') {
|
|
13
|
+
resolvePending(CANCEL_SYMBOL);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (key.return) {
|
|
17
|
+
resolvePending(true);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return (_jsx(PromptContainer, { children: _jsxs(Box, { children: [_jsxs(Text, { color: "cyan", bold: true, children: ["\u203A", ' '] }), _jsx(Text, { color: "cyan", children: message })] }) }));
|
|
21
|
+
}
|
|
22
|
+
export default ContinuePrompt;
|
|
23
|
+
//# sourceMappingURL=ContinuePrompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContinuePrompt.js","sourceRoot":"","sources":["../../../../src/ui/components/ContinuePrompt.tsx"],"names":[],"mappings":";AAMA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAOvD;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,OAAO,EACP,WAAW,GAAG,UAAU,GACJ;IACpB,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAEvD,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;YAC9B,cAAc,CAAC,aAAa,CAAC,CAAC;YAC9B,OAAO;QACT,CAAC;QAED,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACf,cAAc,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,CACL,KAAC,eAAe,cACd,MAAC,GAAG,eACF,MAAC,IAAI,IAAC,KAAK,EAAC,MAAM,EAAC,IAAI,6BACnB,GAAG,IACA,EACP,KAAC,IAAI,IAAC,KAAK,EAAC,MAAM,YAAE,OAAO,GAAQ,IAC/B,GACU,CACnB,CAAC;AACJ,CAAC;AAED,eAAe,cAAc,CAAC","sourcesContent":["/**\n * Continue button prompt component.\n * Shows a message and waits for user to press Enter to continue.\n */\n\nimport React from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport { useWizardActions } from '../contexts/WizardContext.js';\nimport { CANCEL_SYMBOL } from '../cancellation.js';\nimport { PromptContainer } from './PromptContainer.js';\n\ninterface ContinuePromptProps {\n message: string;\n buttonLabel?: string;\n}\n\n/**\n * Continue button prompt that waits for Enter key\n */\nexport function ContinuePrompt({\n message,\n buttonLabel = 'Continue',\n}: ContinuePromptProps): React.ReactElement {\n const { resolvePending, addItem } = useWizardActions();\n\n useInput((input, key) => {\n if (key.ctrl && input === 'c') {\n resolvePending(CANCEL_SYMBOL);\n return;\n }\n\n if (key.return) {\n resolvePending(true);\n }\n });\n\n return (\n <PromptContainer>\n <Box>\n <Text color=\"cyan\" bold>\n ›{' '}\n </Text>\n <Text color=\"cyan\">{message}</Text>\n </Box>\n </PromptContainer>\n );\n}\n\nexport default ContinuePrompt;\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff display component for showing file changes.
|
|
3
|
+
* Renders unified diff format with syntax highlighting.
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
interface DiffDisplayProps {
|
|
7
|
+
/** The diff content in unified diff format */
|
|
8
|
+
diffContent: string;
|
|
9
|
+
/** The filename being modified */
|
|
10
|
+
fileName?: string;
|
|
11
|
+
/** Maximum height in lines (for scrollable content) */
|
|
12
|
+
maxHeight?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Display a unified diff with colored lines
|
|
16
|
+
*/
|
|
17
|
+
export declare function DiffDisplay({ diffContent, fileName, maxHeight, }: DiffDisplayProps): React.ReactElement;
|
|
18
|
+
export default DiffDisplay;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Diff display component for showing file changes.
|
|
4
|
+
* Renders unified diff format with syntax highlighting.
|
|
5
|
+
*/
|
|
6
|
+
import { useMemo } from 'react';
|
|
7
|
+
import { Box, Text, useStdout } from 'ink';
|
|
8
|
+
/**
|
|
9
|
+
* Parse unified diff format into structured lines
|
|
10
|
+
*/
|
|
11
|
+
function parseDiff(diffContent) {
|
|
12
|
+
const lines = diffContent.split('\n');
|
|
13
|
+
const result = [];
|
|
14
|
+
let newLineNumber = 0;
|
|
15
|
+
const hunkHeaderRegex = /^@@ -\d+,?\d* \+(\d+),?\d* @@/;
|
|
16
|
+
for (const line of lines) {
|
|
17
|
+
// Skip git header lines
|
|
18
|
+
if (line.startsWith('diff --git') || line.startsWith('index ')) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
// File header lines
|
|
22
|
+
if (line.startsWith('--- ') || line.startsWith('+++ ')) {
|
|
23
|
+
// result.push({ type: 'header', content: line }); // Skip file header lines
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
// Hunk header
|
|
27
|
+
const hunkMatch = line.match(hunkHeaderRegex);
|
|
28
|
+
if (hunkMatch) {
|
|
29
|
+
newLineNumber = parseInt(hunkMatch[1], 10) - 1;
|
|
30
|
+
result.push({ type: 'hunk', content: line });
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
// Added line
|
|
34
|
+
if (line.startsWith('+')) {
|
|
35
|
+
newLineNumber++;
|
|
36
|
+
result.push({
|
|
37
|
+
type: 'add',
|
|
38
|
+
lineNumber: newLineNumber,
|
|
39
|
+
content: line.substring(1),
|
|
40
|
+
});
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// Deleted line
|
|
44
|
+
if (line.startsWith('-')) {
|
|
45
|
+
result.push({
|
|
46
|
+
type: 'del',
|
|
47
|
+
content: line.substring(1),
|
|
48
|
+
});
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
// Context line
|
|
52
|
+
if (line.startsWith(' ')) {
|
|
53
|
+
newLineNumber++;
|
|
54
|
+
result.push({
|
|
55
|
+
type: 'context',
|
|
56
|
+
lineNumber: newLineNumber,
|
|
57
|
+
content: line.substring(1),
|
|
58
|
+
});
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Display a unified diff with colored lines
|
|
66
|
+
*/
|
|
67
|
+
export function DiffDisplay({ diffContent, fileName, maxHeight, }) {
|
|
68
|
+
const { stdout } = useStdout();
|
|
69
|
+
const terminalWidth = stdout?.columns || 80;
|
|
70
|
+
const parsedLines = useMemo(() => {
|
|
71
|
+
if (!diffContent || typeof diffContent !== 'string') {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
return parseDiff(diffContent);
|
|
75
|
+
}, [diffContent]);
|
|
76
|
+
// Calculate gutter width based on max line number
|
|
77
|
+
const maxLineNum = useMemo(() => {
|
|
78
|
+
return Math.max(0, ...parsedLines
|
|
79
|
+
.filter((l) => l.lineNumber !== undefined)
|
|
80
|
+
.map((l) => l.lineNumber));
|
|
81
|
+
}, [parsedLines]);
|
|
82
|
+
const gutterWidth = Math.max(4, maxLineNum.toString().length + 1);
|
|
83
|
+
if (parsedLines.length === 0) {
|
|
84
|
+
return (_jsx(Box, { children: _jsx(Text, { dimColor: true, children: "No changes to display" }) }));
|
|
85
|
+
}
|
|
86
|
+
// Limit lines if maxHeight is specified
|
|
87
|
+
const displayLines = maxHeight
|
|
88
|
+
? parsedLines.slice(0, maxHeight)
|
|
89
|
+
: parsedLines;
|
|
90
|
+
const truncated = maxHeight && parsedLines.length > maxHeight;
|
|
91
|
+
return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [fileName && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: fileName }) })), displayLines.map((line, index) => {
|
|
92
|
+
const key = `diff-line-${index}`;
|
|
93
|
+
switch (line.type) {
|
|
94
|
+
case 'header':
|
|
95
|
+
return (_jsx(Box, { children: _jsx(Text, { dimColor: true, children: line.content }) }, key));
|
|
96
|
+
case 'hunk':
|
|
97
|
+
return (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "cyan", children: line.content }) }, key));
|
|
98
|
+
case 'add':
|
|
99
|
+
return (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: (line.lineNumber?.toString() || '').padStart(gutterWidth) }), _jsxs(Text, { color: "green", backgroundColor: "greenBright", children: [' ', "+", ' '] }), _jsx(Text, { color: "green", children: line.content })] }, key));
|
|
100
|
+
case 'del':
|
|
101
|
+
return (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: ''.padStart(gutterWidth) }), _jsxs(Text, { color: "red", backgroundColor: "redBright", children: [' ', "-", ' '] }), _jsx(Text, { color: "red", children: line.content })] }, key));
|
|
102
|
+
case 'context':
|
|
103
|
+
return (_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: (line.lineNumber?.toString() || '').padStart(gutterWidth) }), _jsx(Text, { children: " " }), _jsx(Text, { children: line.content })] }, key));
|
|
104
|
+
default:
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}), truncated && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["... ", parsedLines.length - (maxHeight || 0), " more lines"] }) }))] }));
|
|
108
|
+
}
|
|
109
|
+
export default DiffDisplay;
|
|
110
|
+
//# sourceMappingURL=DiffDisplay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DiffDisplay.js","sourceRoot":"","sources":["../../../../src/ui/components/DiffDisplay.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Diff display component for showing file changes.\n * Renders unified diff format with syntax highlighting.\n */\n\nimport React, { useMemo } from 'react';\nimport { Box, Text, useStdout } from 'ink';\n\ninterface DiffLine {\n type: 'add' | 'del' | 'context' | 'hunk' | 'header';\n lineNumber?: number;\n content: string;\n}\n\ninterface DiffDisplayProps {\n /** The diff content in unified diff format */\n diffContent: string;\n /** The filename being modified */\n fileName?: string;\n /** Maximum height in lines (for scrollable content) */\n maxHeight?: number;\n}\n\n/**\n * Parse unified diff format into structured lines\n */\nfunction parseDiff(diffContent: string): DiffLine[] {\n const lines = diffContent.split('\\n');\n const result: DiffLine[] = [];\n let newLineNumber = 0;\n const hunkHeaderRegex = /^@@ -\\d+,?\\d* \\+(\\d+),?\\d* @@/;\n\n for (const line of lines) {\n // Skip git header lines\n if (line.startsWith('diff --git') || line.startsWith('index ')) {\n continue;\n }\n\n // File header lines\n if (line.startsWith('--- ') || line.startsWith('+++ ')) {\n // result.push({ type: 'header', content: line }); // Skip file header lines\n continue;\n }\n\n // Hunk header\n const hunkMatch = line.match(hunkHeaderRegex);\n if (hunkMatch) {\n newLineNumber = parseInt(hunkMatch[1], 10) - 1;\n result.push({ type: 'hunk', content: line });\n continue;\n }\n\n // Added line\n if (line.startsWith('+')) {\n newLineNumber++;\n result.push({\n type: 'add',\n lineNumber: newLineNumber,\n content: line.substring(1),\n });\n continue;\n }\n\n // Deleted line\n if (line.startsWith('-')) {\n result.push({\n type: 'del',\n content: line.substring(1),\n });\n continue;\n }\n\n // Context line\n if (line.startsWith(' ')) {\n newLineNumber++;\n result.push({\n type: 'context',\n lineNumber: newLineNumber,\n content: line.substring(1),\n });\n continue;\n }\n }\n\n return result;\n}\n\n/**\n * Display a unified diff with colored lines\n */\nexport function DiffDisplay({\n diffContent,\n fileName,\n maxHeight,\n}: DiffDisplayProps): React.ReactElement {\n const { stdout } = useStdout();\n const terminalWidth = stdout?.columns || 80;\n\n const parsedLines = useMemo(() => {\n if (!diffContent || typeof diffContent !== 'string') {\n return [];\n }\n return parseDiff(diffContent);\n }, [diffContent]);\n\n // Calculate gutter width based on max line number\n const maxLineNum = useMemo(() => {\n return Math.max(\n 0,\n ...parsedLines\n .filter((l) => l.lineNumber !== undefined)\n .map((l) => l.lineNumber as number),\n );\n }, [parsedLines]);\n const gutterWidth = Math.max(4, maxLineNum.toString().length + 1);\n\n if (parsedLines.length === 0) {\n return (\n <Box>\n <Text dimColor>No changes to display</Text>\n </Box>\n );\n }\n\n // Limit lines if maxHeight is specified\n const displayLines = maxHeight\n ? parsedLines.slice(0, maxHeight)\n : parsedLines;\n const truncated = maxHeight && parsedLines.length > maxHeight;\n\n return (\n <Box flexDirection=\"column\" width={terminalWidth}>\n {/* File name header */}\n {fileName && (\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n {fileName}\n </Text>\n </Box>\n )}\n\n {/* Diff lines */}\n {displayLines.map((line, index) => {\n const key = `diff-line-${index}`;\n\n switch (line.type) {\n case 'header':\n return (\n <Box key={key}>\n <Text dimColor>{line.content}</Text>\n </Box>\n );\n\n case 'hunk':\n return (\n <Box key={key} marginTop={1}>\n <Text color=\"cyan\">{line.content}</Text>\n </Box>\n );\n\n case 'add':\n return (\n <Box key={key}>\n <Text dimColor>\n {(line.lineNumber?.toString() || '').padStart(gutterWidth)}\n </Text>\n <Text color=\"green\" backgroundColor=\"greenBright\">\n {' '}\n +{' '}\n </Text>\n <Text color=\"green\">{line.content}</Text>\n </Box>\n );\n\n case 'del':\n return (\n <Box key={key}>\n <Text dimColor>{''.padStart(gutterWidth)}</Text>\n <Text color=\"red\" backgroundColor=\"redBright\">\n {' '}\n -{' '}\n </Text>\n <Text color=\"red\">{line.content}</Text>\n </Box>\n );\n\n case 'context':\n return (\n <Box key={key}>\n <Text dimColor>\n {(line.lineNumber?.toString() || '').padStart(gutterWidth)}\n </Text>\n <Text> </Text>\n <Text>{line.content}</Text>\n </Box>\n );\n\n default:\n return null;\n }\n })}\n\n {/* Truncation indicator */}\n {truncated && (\n <Box marginTop={1}>\n <Text dimColor>\n ... {parsedLines.length - (maxHeight || 0)} more lines\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n\nexport default DiffDisplay;\n"]}
|