prompt-language-shell 1.0.2 → 1.0.4

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/dist/Main.js CHANGED
@@ -8,10 +8,19 @@ import { getMissingConfigKeys } from './configuration/schema.js';
8
8
  import { createConfigStepsFromSchema } from './configuration/steps.js';
9
9
  import { unflattenConfig } from './configuration/transformation.js';
10
10
  import { createAnthropicService } from './services/anthropic.js';
11
- import { createCommand, createConfig, createFeedback, createMessage, createWelcome, } from './services/components.js';
11
+ import { createCommand, createConfig, createFeedback, createHelp, createMessage, createWelcome, } from './services/components.js';
12
12
  import { registerGlobalShortcut } from './services/keyboard.js';
13
13
  import { initializeLogger, setDebugLevel } from './services/logger.js';
14
14
  import { Workflow } from './components/Workflow.js';
15
+ /**
16
+ * Check if command matches help variations (help, --help, -h)
17
+ */
18
+ function isHelpCommand(cmd) {
19
+ if (!cmd)
20
+ return false;
21
+ const normalized = cmd.toLowerCase().trim();
22
+ return (normalized === 'help' || normalized === '--help' || normalized === '-h');
23
+ }
15
24
  export const Main = ({ app, command, serviceFactory = createAnthropicService, }) => {
16
25
  const [service, setService] = useState(null);
17
26
  const [initialQueue, setInitialQueue] = useState(null);
@@ -106,6 +115,10 @@ export const Main = ({ app, command, serviceFactory = createAnthropicService, })
106
115
  }),
107
116
  ]);
108
117
  }
118
+ else if (service && isHelpCommand(command)) {
119
+ // Help command - show help screen directly (no LLM)
120
+ setInitialQueue([createHelp({ app })]);
121
+ }
109
122
  else if (service && command) {
110
123
  // Valid service exists and command provided - execute command
111
124
  setInitialQueue([createCommand({ command, service })]);
@@ -16,6 +16,7 @@ import { Refinement, RefinementView } from './controllers/Refinement.js';
16
16
  import { Report } from './views/Report.js';
17
17
  import { Schedule, ScheduleView } from './controllers/Schedule.js';
18
18
  import { Validate, ValidateView } from './controllers/Validate.js';
19
+ import { Help } from './views/Help.js';
19
20
  import { Welcome } from './views/Welcome.js';
20
21
  /**
21
22
  * Render a simple component (no lifecycle management)
@@ -26,6 +27,10 @@ export const SimpleComponent = memo(function SimpleComponent({ def, }) {
26
27
  const { props, status } = def;
27
28
  return _jsx(Welcome, { ...props, status: status });
28
29
  }
30
+ case ComponentName.Help: {
31
+ const { props, status } = def;
32
+ return _jsx(Help, { ...props, status: status });
33
+ }
29
34
  case ComponentName.Feedback: {
30
35
  const { props, status } = def;
31
36
  return _jsx(Feedback, { ...props, status: status });
@@ -157,6 +162,7 @@ export const TimelineComponent = ({ def, }) => {
157
162
  switch (def.name) {
158
163
  // Simple components render as-is
159
164
  case ComponentName.Welcome:
165
+ case ComponentName.Help:
160
166
  case ComponentName.Feedback:
161
167
  case ComponentName.Message:
162
168
  case ComponentName.Debug:
@@ -8,9 +8,31 @@ import { createConfigStepsFromSchema } from '../../configuration/steps.js';
8
8
  import { saveConfigLabels } from '../../configuration/labels.js';
9
9
  import { DebugLevel } from '../../configuration/types.js';
10
10
  import { useInput } from '../../services/keyboard.js';
11
- import { ConfigView, StepType } from '../views/Config.js';
11
+ import { StepType } from '../views/Config.js';
12
+ import { Setting } from './Setting.js';
12
13
  import { Spinner } from '../views/Spinner.js';
13
14
  export { ConfigView, StepType, } from '../views/Config.js';
15
+ function isTextStep(step) {
16
+ return step.type === StepType.Text;
17
+ }
18
+ function isSelectionStep(step) {
19
+ return step.type === StepType.Selection;
20
+ }
21
+ function initializeStepValues(steps) {
22
+ const values = {};
23
+ for (const step of steps) {
24
+ const configKey = step.path || step.key;
25
+ if (isTextStep(step)) {
26
+ if (step.value !== null) {
27
+ values[configKey] = step.value;
28
+ }
29
+ }
30
+ else if (isSelectionStep(step)) {
31
+ values[configKey] = step.options[step.defaultIndex].value;
32
+ }
33
+ }
34
+ return values;
35
+ }
14
36
  /**
15
37
  * Resolve query to config steps via CONFIGURE tool
16
38
  */
@@ -48,61 +70,19 @@ export function Config(props) {
48
70
  const isActive = status === ComponentStatus.Active;
49
71
  const [steps, setSteps] = useState(initialSteps || []);
50
72
  const [resolving, setResolving] = useState(!initialSteps?.length && !!query);
51
- const [step, setStep] = useState(0);
52
- const [values, setValues] = useState(() => {
53
- const initial = {};
54
- (initialSteps || []).forEach((stepConfig) => {
55
- const configKey = stepConfig.path || stepConfig.key;
56
- switch (stepConfig.type) {
57
- case StepType.Text:
58
- if (stepConfig.value !== null) {
59
- initial[configKey] = stepConfig.value;
60
- }
61
- break;
62
- case StepType.Selection:
63
- initial[configKey] =
64
- stepConfig.options[stepConfig.defaultIndex].value;
65
- break;
66
- }
67
- });
68
- return initial;
69
- });
70
- const [inputValue, setInputValue] = useState('');
71
- const [selectedIndex, setSelectedIndex] = useState(() => {
72
- if (!initialSteps?.length)
73
- return 0;
74
- const first = initialSteps[0];
75
- return first.type === StepType.Selection ? first.defaultIndex : 0;
76
- });
77
- // Resolve query to steps
73
+ const [currentStep, setCurrentStep] = useState(0);
74
+ const [values, setValues] = useState(() => initializeStepValues(initialSteps || []));
78
75
  useEffect(() => {
79
76
  if (!isActive || !query || !service || initialSteps?.length)
80
77
  return;
81
78
  resolveQueryToSteps(query, service)
82
79
  .then((result) => {
83
- // Add debug components to timeline if present
84
80
  if (result.debug.length) {
85
81
  workflowHandlers.addToTimeline(...result.debug);
86
82
  }
87
83
  setSteps(result.steps);
88
84
  setResolving(false);
89
- // Initialize values for resolved steps
90
- const initial = {};
91
- result.steps.forEach((stepConfig) => {
92
- const configKey = stepConfig.path || stepConfig.key;
93
- switch (stepConfig.type) {
94
- case StepType.Text:
95
- if (stepConfig.value !== null) {
96
- initial[configKey] = stepConfig.value;
97
- }
98
- break;
99
- case StepType.Selection:
100
- initial[configKey] =
101
- stepConfig.options[stepConfig.defaultIndex].value;
102
- break;
103
- }
104
- });
105
- setValues(initial);
85
+ setValues(initializeStepValues(result.steps));
106
86
  })
107
87
  .catch((err) => {
108
88
  setResolving(false);
@@ -119,96 +99,39 @@ export function Config(props) {
119
99
  lifecycleHandlers,
120
100
  workflowHandlers,
121
101
  ]);
122
- // Update inputValue and selectedIndex when step changes
123
- useEffect(() => {
124
- if (isActive && step < steps.length) {
125
- const stepConfig = steps[step];
126
- const configKey = stepConfig.path || stepConfig.key;
127
- setInputValue(values[configKey] || '');
128
- if (stepConfig.type === StepType.Selection) {
129
- setSelectedIndex(stepConfig.defaultIndex);
130
- }
102
+ const handleEscape = () => {
103
+ requestHandlers.onCompleted({
104
+ values,
105
+ completedStep: currentStep,
106
+ selectedIndex: 0,
107
+ steps,
108
+ });
109
+ if (onAborted) {
110
+ onAborted('configuration');
111
+ }
112
+ else {
113
+ lifecycleHandlers.completeActive(createFeedback({
114
+ type: FeedbackType.Aborted,
115
+ message: 'Configuration cancelled.',
116
+ }));
131
117
  }
132
- }, [step, isActive, steps, values]);
133
- const normalizeValue = (value) => {
134
- if (value === null || value === undefined)
135
- return '';
136
- return value.replace(/\n/g, '').trim();
137
118
  };
138
119
  useInput((_, key) => {
139
- if (!isActive || step >= steps.length)
120
+ if (!isActive || currentStep >= steps.length)
140
121
  return;
141
- const currentStepConfig = steps[step];
142
122
  if (key.escape) {
143
- const configKey = currentStepConfig.path || currentStepConfig.key;
144
- let currentValue = '';
145
- switch (currentStepConfig.type) {
146
- case StepType.Text:
147
- currentValue = inputValue || values[configKey] || '';
148
- break;
149
- case StepType.Selection:
150
- currentValue = values[configKey] || '';
151
- break;
152
- }
153
- const finalValues = currentValue
154
- ? { ...values, [configKey]: currentValue }
155
- : values;
156
- requestHandlers.onCompleted({
157
- values: finalValues,
158
- completedStep: step,
159
- selectedIndex,
160
- steps,
161
- });
162
- if (onAborted) {
163
- onAborted('configuration');
164
- }
165
- else {
166
- lifecycleHandlers.completeActive(createFeedback({
167
- type: FeedbackType.Aborted,
168
- message: 'Configuration cancelled.',
169
- }));
170
- }
171
- return;
172
- }
173
- if (currentStepConfig.type === StepType.Selection) {
174
- if (key.tab) {
175
- setSelectedIndex((prev) => (prev + 1) % currentStepConfig.options.length);
176
- }
177
- else if (key.return) {
178
- handleSubmit(currentStepConfig.options[selectedIndex].value);
179
- }
123
+ handleEscape();
180
124
  }
181
125
  }, { isActive });
182
- const handleSubmit = (value) => {
183
- const currentStepConfig = steps[step];
184
- let finalValue = '';
185
- switch (currentStepConfig.type) {
186
- case StepType.Selection:
187
- finalValue = value;
188
- break;
189
- case StepType.Text: {
190
- const normalizedInput = normalizeValue(value);
191
- if (normalizedInput && currentStepConfig.validate(normalizedInput)) {
192
- finalValue = normalizedInput;
193
- }
194
- else if (currentStepConfig.value &&
195
- currentStepConfig.validate(currentStepConfig.value)) {
196
- finalValue = currentStepConfig.value;
197
- }
198
- break;
199
- }
200
- }
201
- if (!finalValue)
202
- return;
203
- const configKey = currentStepConfig.path || currentStepConfig.key;
204
- const newValues = { ...values, [configKey]: finalValue };
126
+ const handleEntrySubmit = (stepConfig, value) => {
127
+ const configKey = stepConfig.path || stepConfig.key;
128
+ const newValues = { ...values, [configKey]: value };
205
129
  setValues(newValues);
206
- setInputValue('');
207
- if (step === steps.length - 1) {
130
+ if (currentStep === steps.length - 1) {
208
131
  requestHandlers.onCompleted({
209
132
  values: newValues,
210
133
  completedStep: steps.length,
211
- selectedIndex,
134
+ selectedIndex: 0,
212
135
  steps,
213
136
  });
214
137
  try {
@@ -224,14 +147,25 @@ export function Config(props) {
224
147
  message: error instanceof Error ? error.message : 'Configuration failed',
225
148
  }));
226
149
  }
227
- setStep(steps.length);
150
+ setCurrentStep(steps.length);
228
151
  }
229
152
  else {
230
- setStep(step + 1);
153
+ setCurrentStep(currentStep + 1);
231
154
  }
232
155
  };
233
156
  if (resolving) {
234
157
  return (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { children: "Resolving configuration... " }), _jsx(Spinner, {})] }));
235
158
  }
236
- return (_jsx(ConfigView, { steps: steps, state: { values, completedStep: step, selectedIndex }, status: status, debug: debug, onInputChange: setInputValue, onInputSubmit: handleSubmit }));
159
+ return (_jsx(Box, { flexDirection: "column", marginLeft: 1, children: steps.map((stepConfig, index) => {
160
+ const isStepActive = index === currentStep && isActive;
161
+ const isCompleted = index < currentStep;
162
+ const wasAborted = index === currentStep && !isActive;
163
+ const shouldShow = isCompleted || isStepActive || wasAborted;
164
+ if (!shouldShow)
165
+ return null;
166
+ const configKey = stepConfig.path || stepConfig.key;
167
+ return (_jsx(Box, { marginTop: index === 0 ? 0 : 1, children: _jsx(Setting, { step: stepConfig, initialValue: values[configKey] || '', isActive: isStepActive, debug: debug, onSubmit: (value) => {
168
+ handleEntrySubmit(stepConfig, value);
169
+ } }) }, configKey));
170
+ }) }));
237
171
  }
@@ -19,7 +19,7 @@ export function Confirm({ message, status, requestHandlers, onConfirmed, onCance
19
19
  requestHandlers.onCompleted(finalState);
20
20
  onCancelled();
21
21
  }
22
- else if (key.tab) {
22
+ else if (key.tab || key.leftArrow || key.rightArrow) {
23
23
  // Toggle between Yes (0) and No (1)
24
24
  setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
25
25
  }
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useReducer, useRef } from 'react';
3
- import { ComponentStatus, } from '../../types/components.js';
2
+ import { useCallback, useEffect, useReducer, useRef, useState } from 'react';
3
+ import { ComponentStatus, OutputSource, } from '../../types/components.js';
4
4
  import { useInput } from '../../services/keyboard.js';
5
5
  import { formatErrorMessage, getExecutionErrorMessage, } from '../../services/messages.js';
6
- import { ExecutionStatus } from '../../services/shell.js';
6
+ import { ExecutionStatus, killCurrentProcess, writeStdin, } from '../../services/shell.js';
7
7
  import { ELAPSED_UPDATE_INTERVAL, ensureMinimumTime, } from '../../services/timing.js';
8
8
  import { handleTaskCompletion, handleTaskFailure, } from '../../execution/handlers.js';
9
9
  import { processTasks } from '../../execution/processing.js';
@@ -43,6 +43,9 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
43
43
  });
44
44
  // Ref to track if current task execution is cancelled
45
45
  const cancelledRef = useRef(false);
46
+ // Toggle stdin input visibility with / key
47
+ const [showStdinInput, setShowStdinInput] = useState(false);
48
+ const showStdinInputRef = useRef(false);
46
49
  const { error, tasks, message, hasProcessed, completionMessage, summary } = localState;
47
50
  // Derive current task index from tasks
48
51
  const currentTaskIndex = getCurrentTaskIndex(tasks);
@@ -75,9 +78,17 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
75
78
  clearInterval(interval);
76
79
  };
77
80
  }, [runningTask?.startTime, isExecuting, currentTaskIndex]);
78
- // Handle cancel - state already in reducer, just need to update final output
81
+ // Hide stdin input when not executing
82
+ useEffect(() => {
83
+ if (!isExecuting) {
84
+ showStdinInputRef.current = false;
85
+ setShowStdinInput(false);
86
+ }
87
+ }, [isExecuting]);
88
+ // Handle cancel - kill the running process and update final output
79
89
  const handleCancel = useCallback(() => {
80
90
  cancelledRef.current = true;
91
+ killCurrentProcess();
81
92
  dispatch({ type: ExecuteActionType.CancelExecution });
82
93
  // Build final state with current output for the running task
83
94
  const updatedTasks = tasks.map((task) => {
@@ -103,9 +114,30 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
103
114
  requestHandlers.onCompleted(finalState);
104
115
  requestHandlers.onAborted('execution');
105
116
  }, [message, summary, tasks, requestHandlers]);
106
- useInput((_, key) => {
117
+ // Handle stdin submission: write the line to the running process
118
+ const handleStdinSubmit = useCallback((value) => {
119
+ writeStdin(value + '\n');
120
+ outputRef.current.chunks.push({
121
+ text: `\n> ${value}\n`,
122
+ timestamp: Date.now(),
123
+ source: OutputSource.Stdin,
124
+ });
125
+ showStdinInputRef.current = false;
126
+ setShowStdinInput(false);
127
+ }, []);
128
+ useInput((input, key) => {
107
129
  if (key.escape && (isLoading || isExecuting)) {
108
- handleCancel();
130
+ if (showStdinInputRef.current) {
131
+ showStdinInputRef.current = false;
132
+ setShowStdinInput(false);
133
+ }
134
+ else {
135
+ handleCancel();
136
+ }
137
+ }
138
+ if (input === '/' && isExecuting && !showStdinInputRef.current) {
139
+ showStdinInputRef.current = true;
140
+ setShowStdinInput(true);
109
141
  }
110
142
  }, { isActive: (isLoading || isExecuting) && isActive });
111
143
  // Process tasks to get commands from AI
@@ -210,6 +242,8 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
210
242
  });
211
243
  // Reset output ref for new task
212
244
  outputRef.current = { chunks: [], currentMemory: undefined };
245
+ showStdinInputRef.current = false;
246
+ setShowStdinInput(false);
213
247
  // Merge workdir into command
214
248
  const command = workdirRef.current
215
249
  ? { ...currentTask.command, workdir: workdirRef.current }
@@ -217,8 +251,10 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
217
251
  void executeTask(command, currentTaskIndex, {
218
252
  onUpdate: (output) => {
219
253
  if (!cancelledRef.current) {
254
+ // Preserve stdin chunks that were added locally
255
+ const stdinChunks = outputRef.current.chunks.filter((c) => c.source === OutputSource.Stdin);
220
256
  outputRef.current = {
221
- chunks: output.chunks,
257
+ chunks: [...output.chunks, ...stdinChunks],
222
258
  currentMemory: output.currentMemory,
223
259
  };
224
260
  }
@@ -230,11 +266,13 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
230
266
  if (execOutput.workdir) {
231
267
  workdirRef.current = execOutput.workdir;
232
268
  }
269
+ // Preserve stdin chunks in final output
270
+ const stdinChunks = outputRef.current.chunks.filter((c) => c.source === OutputSource.Stdin);
233
271
  const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
234
272
  ? {
235
273
  ...task,
236
274
  output: {
237
- chunks: execOutput.chunks,
275
+ chunks: [...execOutput.chunks, ...stdinChunks],
238
276
  },
239
277
  }
240
278
  : task);
@@ -256,11 +294,13 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
256
294
  if (execOutput.workdir) {
257
295
  workdirRef.current = execOutput.workdir;
258
296
  }
297
+ // Preserve stdin chunks in final output
298
+ const stdinChunks = outputRef.current.chunks.filter((c) => c.source === OutputSource.Stdin);
259
299
  const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
260
300
  ? {
261
301
  ...task,
262
302
  output: {
263
- chunks: execOutput.chunks,
303
+ chunks: [...execOutput.chunks, ...stdinChunks],
264
304
  },
265
305
  error: execOutput.error || undefined,
266
306
  }
@@ -287,5 +327,5 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
287
327
  lifecycleHandlers,
288
328
  workflowHandlers,
289
329
  ]);
290
- return (_jsx(ExecuteView, { isLoading: isLoading, isExecuting: isExecuting, isActive: isActive, error: error, message: message, tasks: tasks, completionMessage: completionMessage, showTasks: showTasks, upcoming: upcoming, label: label }));
330
+ return (_jsx(ExecuteView, { isLoading: isLoading, isExecuting: isExecuting, isActive: isActive, error: error, message: message, tasks: tasks, completionMessage: completionMessage, showTasks: showTasks, upcoming: upcoming, label: label, onStdinSubmit: showStdinInput ? handleStdinSubmit : undefined }));
291
331
  }
@@ -61,21 +61,13 @@ export function Schedule({ message, tasks, status, debug = DebugLevel.None, requ
61
61
  requestHandlers.onAborted('task selection');
62
62
  return;
63
63
  }
64
- if (key.downArrow) {
65
- setHighlightedIndex((prev) => {
66
- if (prev === null) {
67
- return 0; // Select first
68
- }
69
- return (prev + 1) % optionsCount; // Wrap around
70
- });
64
+ if (key.downArrow || key.rightArrow || key.tab) {
65
+ setHighlightedIndex((prev) => prev === null ? 0 : (prev + 1) % optionsCount);
71
66
  }
72
- else if (key.upArrow) {
73
- setHighlightedIndex((prev) => {
74
- if (prev === null) {
75
- return optionsCount - 1; // Select last
76
- }
77
- return (prev - 1 + optionsCount) % optionsCount; // Wrap around
78
- });
67
+ else if (key.upArrow || key.leftArrow) {
68
+ setHighlightedIndex((prev) => prev === null
69
+ ? optionsCount - 1
70
+ : (prev - 1 + optionsCount) % optionsCount);
79
71
  }
80
72
  else if (key.return && highlightedIndex !== null) {
81
73
  // Record the selection for this group
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { useInput } from '../../services/keyboard.js';
4
+ import { StepType } from '../views/Config.js';
5
+ import { SettingView } from '../views/Setting.js';
6
+ function isSelectionStep(step) {
7
+ return step.type === StepType.Selection;
8
+ }
9
+ function normalizeTextValue(value) {
10
+ if (value === null || value === undefined)
11
+ return '';
12
+ return value.replace(/\n/g, '').trim();
13
+ }
14
+ export function Setting({ step, initialValue, isActive, debug, onSubmit, }) {
15
+ const [inputValue, setInputValue] = useState(initialValue);
16
+ const [selectedIndex, setSelectedIndex] = useState(() => {
17
+ if (isSelectionStep(step)) {
18
+ const index = step.options.findIndex((opt) => opt.value === initialValue);
19
+ return index >= 0 ? index : step.defaultIndex;
20
+ }
21
+ return 0;
22
+ });
23
+ const handleInputChange = (value) => {
24
+ setInputValue(value);
25
+ };
26
+ const submitValue = (value) => {
27
+ setInputValue(value);
28
+ onSubmit(value);
29
+ };
30
+ const handleInputSubmit = (value) => {
31
+ if (step.type === StepType.Text) {
32
+ const normalizedInput = normalizeTextValue(value);
33
+ if (normalizedInput && step.validate(normalizedInput)) {
34
+ submitValue(normalizedInput);
35
+ return;
36
+ }
37
+ if (step.value && step.validate(step.value)) {
38
+ submitValue(step.value);
39
+ return;
40
+ }
41
+ }
42
+ else {
43
+ submitValue(value);
44
+ }
45
+ };
46
+ const handleSelectionNavigation = (key) => {
47
+ if (!isSelectionStep(step))
48
+ return;
49
+ const len = step.options.length;
50
+ if (key.tab || key.rightArrow) {
51
+ setSelectedIndex((prev) => (prev + 1) % len);
52
+ }
53
+ else if (key.leftArrow) {
54
+ setSelectedIndex((prev) => (prev - 1 + len) % len);
55
+ }
56
+ else if (key.return) {
57
+ submitValue(step.options[selectedIndex].value);
58
+ }
59
+ };
60
+ useInput((_, key) => {
61
+ if (!isActive || !isSelectionStep(step))
62
+ return;
63
+ handleSelectionNavigation(key);
64
+ }, { isActive: isActive && isSelectionStep(step) });
65
+ return (_jsx(SettingView, { step: step, value: inputValue, selectedIndex: selectedIndex, isActive: isActive, debug: debug, onInputChange: handleInputChange, onInputSubmit: handleInputSubmit }));
66
+ }
@@ -1,106 +1,19 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from 'react';
3
- import { Box, Text, useFocus } from 'ink';
4
- import TextInput from 'ink-text-input';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box } from 'ink';
5
3
  import { ComponentStatus } from '../../types/components.js';
6
- import { Colors } from '../../services/colors.js';
7
4
  import { DebugLevel } from '../../configuration/types.js';
8
- import { useInput } from '../../services/keyboard.js';
9
- /**
10
- * Get postfix with debug brackets if debug is enabled
11
- * Info: {key} | Verbose: {key} entry
12
- */
13
- function getPostfix(text, debugLevel) {
14
- if (debugLevel === DebugLevel.None || !text) {
15
- return '';
16
- }
17
- if (debugLevel === DebugLevel.Info) {
18
- return `{${text}}`;
19
- }
20
- return `{${text}} entry`;
21
- }
5
+ import { SettingView } from './Setting.js';
22
6
  export var StepType;
23
7
  (function (StepType) {
24
8
  StepType["Text"] = "text";
25
9
  StepType["Selection"] = "selection";
26
10
  })(StepType || (StepType = {}));
27
- function TextStep({ value, placeholder, validate, onChange, onSubmit, }) {
28
- const [inputValue, setInputValue] = useState(value);
29
- const [validationFailed, setValidationFailed] = useState(false);
30
- const { isFocused } = useFocus({ autoFocus: true });
31
- // Sync internal state with prop changes
32
- useEffect(() => {
33
- setInputValue(value);
34
- }, [value]);
35
- const handleChange = (newValue) => {
36
- setInputValue(newValue);
37
- onChange(newValue);
38
- if (validationFailed) {
39
- setValidationFailed(false);
40
- }
41
- };
42
- const handleSubmit = (value) => {
43
- // Use placeholder if input is empty
44
- const finalValue = value || placeholder || '';
45
- if (!validate(finalValue)) {
46
- setValidationFailed(true);
47
- return;
48
- }
49
- onSubmit(finalValue);
50
- };
51
- // Handle input manually when validation fails
52
- useInput((input, key) => {
53
- if (!validationFailed)
54
- return;
55
- if (key.return) {
56
- handleSubmit(inputValue);
57
- }
58
- else if (key.backspace || key.delete) {
59
- const newValue = inputValue.slice(0, -1);
60
- handleChange(newValue);
61
- }
62
- else if (!key.ctrl && !key.meta && input) {
63
- const newValue = inputValue + input;
64
- handleChange(newValue);
65
- }
66
- }, { isActive: validationFailed });
67
- // When validation fails, show colored text
68
- if (validationFailed) {
69
- return (_jsxs(Text, { color: Colors.Status.Error, children: [inputValue || placeholder, isFocused && _jsx(Text, { inverse: true, children: " " })] }));
70
- }
71
- return (_jsx(TextInput, { value: inputValue, onChange: handleChange, onSubmit: handleSubmit, placeholder: placeholder }));
72
- }
73
- function SelectionStep({ options, selectedIndex, isCurrentStep, }) {
74
- return (_jsx(Box, { children: options.map((option, optIndex) => {
75
- const isSelected = optIndex === selectedIndex;
76
- return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { dimColor: !isSelected || !isCurrentStep, bold: isSelected, children: option.label }) }, option.value));
77
- }) }));
78
- }
79
- export const ConfigView = ({ steps, state, status, debug = DebugLevel.None, onInputChange, onInputSubmit, }) => {
11
+ /**
12
+ * Config view: Multi-step configuration form for timeline display
13
+ */
14
+ export const ConfigView = ({ steps, state, status, debug = DebugLevel.None, }) => {
80
15
  const isActive = status === ComponentStatus.Active;
81
16
  const { values, completedStep, selectedIndex } = state;
82
- const renderStepInput = (stepConfig, isCurrentStep) => {
83
- const configKey = stepConfig.path || stepConfig.key;
84
- const displayValue = values[configKey];
85
- switch (stepConfig.type) {
86
- case StepType.Text:
87
- if (isCurrentStep && onInputChange && onInputSubmit) {
88
- return (_jsx(TextStep, { value: values[configKey] || '', placeholder: stepConfig.value || undefined, validate: stepConfig.validate, onChange: onInputChange, onSubmit: onInputSubmit }));
89
- }
90
- return (_jsx(Text, { dimColor: true, wrap: "truncate-end", children: displayValue || '' }));
91
- case StepType.Selection: {
92
- if (!isCurrentStep) {
93
- const option = stepConfig.options.find((opt) => opt.value === displayValue);
94
- return _jsx(Text, { dimColor: true, children: option?.label || '' });
95
- }
96
- return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep: true }));
97
- }
98
- default: {
99
- const _exhaustiveCheck = stepConfig;
100
- throw new Error('Unsupported step type');
101
- }
102
- }
103
- };
104
17
  return (_jsx(Box, { flexDirection: "column", marginLeft: 1, children: steps.map((stepConfig, index) => {
105
18
  const isCurrentStep = index === completedStep && isActive;
106
19
  const isCompleted = index < completedStep;
@@ -109,7 +22,7 @@ export const ConfigView = ({ steps, state, status, debug = DebugLevel.None, onIn
109
22
  if (!shouldShow) {
110
23
  return null;
111
24
  }
112
- const postfix = getPostfix(stepConfig.path, debug);
113
- return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), postfix && _jsx(Text, { color: Colors.Type.Config, children: postfix })] }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: Colors.Action.Select, dimColor: !isCurrentStep, children: ">" }), _jsx(Text, { children: " " }), renderStepInput(stepConfig, isCurrentStep)] })] }, stepConfig.path || stepConfig.key));
25
+ const configKey = stepConfig.path || stepConfig.key;
26
+ return (_jsx(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: _jsx(SettingView, { step: stepConfig, value: values[configKey] || '', selectedIndex: selectedIndex, isActive: isCurrentStep, debug: debug }) }, configKey));
114
27
  }) }));
115
28
  };