ralph-cli-sandboxed 0.3.0 → 0.4.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/dist/commands/action.d.ts +7 -0
- package/dist/commands/action.js +276 -0
- package/dist/commands/chat.js +95 -7
- package/dist/commands/config.js +6 -18
- package/dist/commands/fix-config.d.ts +4 -0
- package/dist/commands/fix-config.js +388 -0
- package/dist/commands/help.js +17 -0
- package/dist/commands/init.js +89 -2
- package/dist/commands/listen.js +50 -9
- package/dist/commands/prd.js +2 -2
- package/dist/config/languages.json +4 -0
- package/dist/index.js +4 -0
- package/dist/providers/telegram.d.ts +6 -2
- package/dist/providers/telegram.js +68 -2
- package/dist/templates/macos-scripts.d.ts +42 -0
- package/dist/templates/macos-scripts.js +448 -0
- package/dist/tui/ConfigEditor.d.ts +7 -0
- package/dist/tui/ConfigEditor.js +313 -0
- package/dist/tui/components/ArrayEditor.d.ts +22 -0
- package/dist/tui/components/ArrayEditor.js +193 -0
- package/dist/tui/components/BooleanToggle.d.ts +19 -0
- package/dist/tui/components/BooleanToggle.js +43 -0
- package/dist/tui/components/EditorPanel.d.ts +50 -0
- package/dist/tui/components/EditorPanel.js +232 -0
- package/dist/tui/components/HelpPanel.d.ts +13 -0
- package/dist/tui/components/HelpPanel.js +69 -0
- package/dist/tui/components/JsonSnippetEditor.d.ts +24 -0
- package/dist/tui/components/JsonSnippetEditor.js +380 -0
- package/dist/tui/components/KeyValueEditor.d.ts +34 -0
- package/dist/tui/components/KeyValueEditor.js +261 -0
- package/dist/tui/components/ObjectEditor.d.ts +23 -0
- package/dist/tui/components/ObjectEditor.js +227 -0
- package/dist/tui/components/PresetSelector.d.ts +23 -0
- package/dist/tui/components/PresetSelector.js +58 -0
- package/dist/tui/components/Preview.d.ts +18 -0
- package/dist/tui/components/Preview.js +190 -0
- package/dist/tui/components/ScrollableContainer.d.ts +38 -0
- package/dist/tui/components/ScrollableContainer.js +77 -0
- package/dist/tui/components/SectionNav.d.ts +31 -0
- package/dist/tui/components/SectionNav.js +130 -0
- package/dist/tui/components/StringEditor.d.ts +21 -0
- package/dist/tui/components/StringEditor.js +29 -0
- package/dist/tui/hooks/useConfig.d.ts +16 -0
- package/dist/tui/hooks/useConfig.js +89 -0
- package/dist/tui/hooks/useTerminalSize.d.ts +21 -0
- package/dist/tui/hooks/useTerminalSize.js +48 -0
- package/dist/tui/utils/presets.d.ts +52 -0
- package/dist/tui/utils/presets.js +191 -0
- package/dist/tui/utils/validation.d.ts +49 -0
- package/dist/tui/utils/validation.js +198 -0
- package/dist/utils/chat-client.d.ts +31 -1
- package/dist/utils/chat-client.js +27 -1
- package/dist/utils/config.d.ts +7 -2
- package/docs/MACOS-DEVELOPMENT.md +435 -0
- package/package.json +1 -1
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
3
|
+
import { Box, Text, useInput } from "ink";
|
|
4
|
+
import { CONFIG_SECTIONS } from "./SectionNav.js";
|
|
5
|
+
import { getFieldErrors, hasFieldError } from "../utils/validation.js";
|
|
6
|
+
/**
|
|
7
|
+
* Get the value at a dot-notation path from an object.
|
|
8
|
+
*/
|
|
9
|
+
export function getValueAtPath(obj, path) {
|
|
10
|
+
const parts = path.split(".");
|
|
11
|
+
let current = obj;
|
|
12
|
+
for (const part of parts) {
|
|
13
|
+
if (current === null || current === undefined) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
if (typeof current === "object") {
|
|
17
|
+
current = current[part];
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return current;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Determine the type of a field based on its current value.
|
|
27
|
+
*/
|
|
28
|
+
export function inferFieldType(value) {
|
|
29
|
+
if (value === null || value === undefined) {
|
|
30
|
+
return "unknown";
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === "string") {
|
|
33
|
+
return "string";
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === "boolean") {
|
|
36
|
+
return "boolean";
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === "number") {
|
|
39
|
+
return "number";
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
return "array";
|
|
43
|
+
}
|
|
44
|
+
if (typeof value === "object") {
|
|
45
|
+
return "object";
|
|
46
|
+
}
|
|
47
|
+
return "unknown";
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the display value for a field.
|
|
51
|
+
*/
|
|
52
|
+
function getDisplayValue(value, type) {
|
|
53
|
+
if (value === undefined || value === null) {
|
|
54
|
+
return "(not set)";
|
|
55
|
+
}
|
|
56
|
+
switch (type) {
|
|
57
|
+
case "string":
|
|
58
|
+
return value || "(empty)";
|
|
59
|
+
case "boolean":
|
|
60
|
+
return value ? "true" : "false";
|
|
61
|
+
case "number":
|
|
62
|
+
return String(value);
|
|
63
|
+
case "array":
|
|
64
|
+
const arr = value;
|
|
65
|
+
return `[${arr.length} items]`;
|
|
66
|
+
case "object":
|
|
67
|
+
const keys = Object.keys(value);
|
|
68
|
+
return `{${keys.length} keys}`;
|
|
69
|
+
default:
|
|
70
|
+
return String(value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Convert a field path to a human-readable label.
|
|
75
|
+
*/
|
|
76
|
+
function pathToLabel(path) {
|
|
77
|
+
const parts = path.split(".");
|
|
78
|
+
const lastPart = parts[parts.length - 1];
|
|
79
|
+
// Convert camelCase to Title Case with spaces
|
|
80
|
+
return lastPart
|
|
81
|
+
.replace(/([A-Z])/g, " $1")
|
|
82
|
+
.replace(/^./, (str) => str.toUpperCase())
|
|
83
|
+
.trim();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* EditorPanel component displays fields for the selected config section.
|
|
87
|
+
* It shows a breadcrumb of the current path and lists editable fields.
|
|
88
|
+
* Supports scrolling for long content with Page Up/Down and scroll indicators.
|
|
89
|
+
*/
|
|
90
|
+
export function EditorPanel({ config, selectedSection, selectedField, onSelectField, onBack, isFocused = true, validationErrors = [], maxHeight = 12, }) {
|
|
91
|
+
const [highlightedIndex, setHighlightedIndex] = useState(0);
|
|
92
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
93
|
+
// Get the current section definition
|
|
94
|
+
const currentSection = useMemo(() => {
|
|
95
|
+
return CONFIG_SECTIONS.find((s) => s.id === selectedSection);
|
|
96
|
+
}, [selectedSection]);
|
|
97
|
+
// Build field schemas from section definition
|
|
98
|
+
const fields = useMemo(() => {
|
|
99
|
+
if (!currentSection || !config) {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
return currentSection.fields.map((fieldPath) => {
|
|
103
|
+
const value = getValueAtPath(config, fieldPath);
|
|
104
|
+
const type = inferFieldType(value);
|
|
105
|
+
return {
|
|
106
|
+
path: fieldPath,
|
|
107
|
+
label: pathToLabel(fieldPath),
|
|
108
|
+
type,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
}, [currentSection, config]);
|
|
112
|
+
const totalFields = fields.length;
|
|
113
|
+
// Reset highlighted index and scroll when section changes
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
setHighlightedIndex(0);
|
|
116
|
+
setScrollOffset(0);
|
|
117
|
+
}, [selectedSection]);
|
|
118
|
+
// Auto-scroll to keep highlighted item visible
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (highlightedIndex < scrollOffset) {
|
|
121
|
+
setScrollOffset(highlightedIndex);
|
|
122
|
+
}
|
|
123
|
+
else if (highlightedIndex >= scrollOffset + maxHeight) {
|
|
124
|
+
setScrollOffset(highlightedIndex - maxHeight + 1);
|
|
125
|
+
}
|
|
126
|
+
}, [highlightedIndex, scrollOffset, maxHeight]);
|
|
127
|
+
// Navigation handlers
|
|
128
|
+
const handleNavigateUp = useCallback(() => {
|
|
129
|
+
setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : totalFields - 1));
|
|
130
|
+
}, [totalFields]);
|
|
131
|
+
const handleNavigateDown = useCallback(() => {
|
|
132
|
+
setHighlightedIndex((prev) => (prev < totalFields - 1 ? prev + 1 : 0));
|
|
133
|
+
}, [totalFields]);
|
|
134
|
+
const handlePageUp = useCallback(() => {
|
|
135
|
+
const newIndex = Math.max(0, highlightedIndex - maxHeight);
|
|
136
|
+
setHighlightedIndex(newIndex);
|
|
137
|
+
}, [highlightedIndex, maxHeight]);
|
|
138
|
+
const handlePageDown = useCallback(() => {
|
|
139
|
+
const newIndex = Math.min(totalFields - 1, highlightedIndex + maxHeight);
|
|
140
|
+
setHighlightedIndex(newIndex);
|
|
141
|
+
}, [highlightedIndex, maxHeight, totalFields]);
|
|
142
|
+
const handleSelect = useCallback(() => {
|
|
143
|
+
const field = fields[highlightedIndex];
|
|
144
|
+
if (field) {
|
|
145
|
+
onSelectField(field.path, false);
|
|
146
|
+
}
|
|
147
|
+
}, [highlightedIndex, fields, onSelectField]);
|
|
148
|
+
// Handle selecting field with JSON editor
|
|
149
|
+
const handleSelectJson = useCallback(() => {
|
|
150
|
+
const field = fields[highlightedIndex];
|
|
151
|
+
if (field) {
|
|
152
|
+
// JSON editor is useful for arrays, objects, and unknown types
|
|
153
|
+
const isComplexType = field.type === "array" || field.type === "object" || field.type === "unknown";
|
|
154
|
+
onSelectField(field.path, isComplexType);
|
|
155
|
+
}
|
|
156
|
+
}, [highlightedIndex, fields, onSelectField]);
|
|
157
|
+
// Handle keyboard input
|
|
158
|
+
useInput((input, key) => {
|
|
159
|
+
if (!isFocused)
|
|
160
|
+
return;
|
|
161
|
+
// j/k or arrow keys for navigation
|
|
162
|
+
if (input === "j" || key.downArrow) {
|
|
163
|
+
handleNavigateDown();
|
|
164
|
+
}
|
|
165
|
+
else if (input === "k" || key.upArrow) {
|
|
166
|
+
handleNavigateUp();
|
|
167
|
+
}
|
|
168
|
+
else if (key.pageUp) {
|
|
169
|
+
handlePageUp();
|
|
170
|
+
}
|
|
171
|
+
else if (key.pageDown) {
|
|
172
|
+
handlePageDown();
|
|
173
|
+
}
|
|
174
|
+
else if (key.return) {
|
|
175
|
+
handleSelect();
|
|
176
|
+
}
|
|
177
|
+
else if (input === "J") {
|
|
178
|
+
// J (uppercase) to edit as JSON
|
|
179
|
+
handleSelectJson();
|
|
180
|
+
}
|
|
181
|
+
else if (key.escape) {
|
|
182
|
+
onBack();
|
|
183
|
+
}
|
|
184
|
+
}, { isActive: isFocused });
|
|
185
|
+
// Build breadcrumb path
|
|
186
|
+
const breadcrumb = useMemo(() => {
|
|
187
|
+
const parts = [currentSection?.label || selectedSection];
|
|
188
|
+
if (selectedField) {
|
|
189
|
+
// Add nested path components
|
|
190
|
+
const fieldParts = selectedField.split(".");
|
|
191
|
+
// Skip the first part if it matches section (e.g., "docker" in "docker.ports")
|
|
192
|
+
const startIdx = fieldParts[0] === selectedSection ? 1 : 0;
|
|
193
|
+
for (let i = startIdx; i < fieldParts.length; i++) {
|
|
194
|
+
parts.push(pathToLabel(fieldParts[i]));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return parts.join(" > ");
|
|
198
|
+
}, [currentSection, selectedSection, selectedField]);
|
|
199
|
+
// Calculate visible fields based on scroll offset
|
|
200
|
+
const visibleFields = useMemo(() => {
|
|
201
|
+
const endIndex = Math.min(scrollOffset + maxHeight, totalFields);
|
|
202
|
+
return fields.slice(scrollOffset, endIndex);
|
|
203
|
+
}, [fields, scrollOffset, maxHeight, totalFields]);
|
|
204
|
+
// Check if we have overflow
|
|
205
|
+
const canScrollUp = scrollOffset > 0;
|
|
206
|
+
const canScrollDown = scrollOffset + maxHeight < totalFields;
|
|
207
|
+
const hasOverflow = totalFields > maxHeight;
|
|
208
|
+
// Render loading state
|
|
209
|
+
if (!config) {
|
|
210
|
+
return (_jsx(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, flexGrow: 1, children: _jsx(Text, { dimColor: true, children: "Loading configuration..." }) }));
|
|
211
|
+
}
|
|
212
|
+
// Render empty section
|
|
213
|
+
if (!currentSection || fields.length === 0) {
|
|
214
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, flexGrow: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: breadcrumb }) }), _jsx(Text, { dimColor: true, children: "No fields in this section" })] }));
|
|
215
|
+
}
|
|
216
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, flexGrow: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: breadcrumb }), hasOverflow && (_jsxs(Text, { dimColor: true, children: [" (", highlightedIndex + 1, "/", totalFields, ")"] }))] }), hasOverflow && (_jsx(Box, { children: _jsx(Text, { color: canScrollUp ? "cyan" : "gray", dimColor: !canScrollUp, children: canScrollUp ? "▲ more" : "" }) })), visibleFields.map((field) => {
|
|
217
|
+
const actualIndex = fields.findIndex((f) => f.path === field.path);
|
|
218
|
+
const isHighlighted = actualIndex === highlightedIndex;
|
|
219
|
+
const value = getValueAtPath(config, field.path);
|
|
220
|
+
const displayValue = getDisplayValue(value, field.type);
|
|
221
|
+
const fieldHasError = hasFieldError(validationErrors, field.path);
|
|
222
|
+
const fieldErrors = getFieldErrors(validationErrors, field.path);
|
|
223
|
+
// Color based on field type, but red if there's an error
|
|
224
|
+
const typeColor = fieldHasError ? "red"
|
|
225
|
+
: field.type === "array" ? "yellow"
|
|
226
|
+
: field.type === "object" ? "magenta"
|
|
227
|
+
: field.type === "boolean" ? "blue"
|
|
228
|
+
: undefined;
|
|
229
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: isHighlighted ? "cyan" : fieldHasError ? "red" : undefined, children: isHighlighted ? "▸ " : fieldHasError ? "✗ " : " " }), _jsx(Text, { bold: isHighlighted, color: fieldHasError ? "red" : isHighlighted ? "cyan" : undefined, inverse: isHighlighted, children: field.label }), _jsx(Text, { dimColor: true, children: ": " }), _jsx(Text, { color: typeColor, dimColor: value === undefined || value === null, children: displayValue }), (field.type === "array" || field.type === "object") && (_jsx(Text, { dimColor: true, children: " \u2192" }))] }), fieldHasError && fieldErrors.length > 0 && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: "red", dimColor: true, children: fieldErrors[0].message }) }))] }, field.path));
|
|
230
|
+
}), hasOverflow && (_jsx(Box, { children: _jsx(Text, { color: canScrollDown ? "cyan" : "gray", dimColor: !canScrollDown, children: canScrollDown ? "▼ more" : "" }) })), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["j/k: navigate | Enter: edit | J: edit as JSON", hasOverflow && " | PgUp/Dn: scroll", " | Esc: back"] }) })] }));
|
|
231
|
+
}
|
|
232
|
+
export default EditorPanel;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface HelpPanelProps {
|
|
3
|
+
/** Whether the help panel is visible */
|
|
4
|
+
visible: boolean;
|
|
5
|
+
/** Callback to close the help panel */
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* HelpPanel displays all keyboard shortcuts for the config editor.
|
|
10
|
+
* Toggle with the ? key.
|
|
11
|
+
*/
|
|
12
|
+
export declare function HelpPanel({ visible, onClose, }: HelpPanelProps): React.ReactElement | null;
|
|
13
|
+
export default HelpPanel;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
/**
|
|
4
|
+
* All keyboard shortcuts organized by category.
|
|
5
|
+
*/
|
|
6
|
+
const SHORTCUT_GROUPS = [
|
|
7
|
+
{
|
|
8
|
+
title: "Navigation",
|
|
9
|
+
shortcuts: [
|
|
10
|
+
{ keys: "j / ↓", description: "Move down" },
|
|
11
|
+
{ keys: "k / ↑", description: "Move up" },
|
|
12
|
+
{ keys: "h / ←", description: "Move to nav panel" },
|
|
13
|
+
{ keys: "l / →", description: "Move to editor panel" },
|
|
14
|
+
{ keys: "PgUp / PgDn", description: "Scroll page up/down" },
|
|
15
|
+
{ keys: "Enter", description: "Select / Edit" },
|
|
16
|
+
{ keys: "J", description: "Edit as JSON (complex fields)" },
|
|
17
|
+
{ keys: "Esc", description: "Go back / Cancel" },
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
title: "Actions",
|
|
22
|
+
shortcuts: [
|
|
23
|
+
{ keys: "S", description: "Save configuration" },
|
|
24
|
+
{ keys: "Q", description: "Quit editor" },
|
|
25
|
+
{ keys: "Tab", description: "Toggle JSON preview" },
|
|
26
|
+
{ keys: "p", description: "Open presets (Chat/Notifications)" },
|
|
27
|
+
{ keys: "?", description: "Toggle this help panel" },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
title: "JSON Editor",
|
|
32
|
+
shortcuts: [
|
|
33
|
+
{ keys: "e / Enter", description: "Enter edit mode" },
|
|
34
|
+
{ keys: "s", description: "Save changes" },
|
|
35
|
+
{ keys: "c", description: "Copy to clipboard" },
|
|
36
|
+
{ keys: "f", description: "Format JSON" },
|
|
37
|
+
{ keys: "j/k", description: "Scroll preview" },
|
|
38
|
+
{ keys: "PgUp / PgDn", description: "Page scroll" },
|
|
39
|
+
{ keys: "Esc", description: "Cancel" },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
title: "Array/Object Editor",
|
|
44
|
+
shortcuts: [
|
|
45
|
+
{ keys: "e / Enter", description: "Edit selected item" },
|
|
46
|
+
{ keys: "d / Delete", description: "Delete item/entry" },
|
|
47
|
+
{ keys: "Space / Tab", description: "Expand (objects)" },
|
|
48
|
+
{ keys: "J/K (Shift)", description: "Reorder (arrays)" },
|
|
49
|
+
{ keys: "s", description: "Save changes" },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
/**
|
|
54
|
+
* HelpPanel displays all keyboard shortcuts for the config editor.
|
|
55
|
+
* Toggle with the ? key.
|
|
56
|
+
*/
|
|
57
|
+
export function HelpPanel({ visible, onClose, }) {
|
|
58
|
+
// Handle keyboard input to close help
|
|
59
|
+
useInput((input, key) => {
|
|
60
|
+
if (input === "?" || key.escape) {
|
|
61
|
+
onClose();
|
|
62
|
+
}
|
|
63
|
+
}, { isActive: visible });
|
|
64
|
+
if (!visible) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "double", borderColor: "cyan", paddingX: 2, paddingY: 1, children: [_jsx(Box, { marginBottom: 1, justifyContent: "center", children: _jsx(Text, { bold: true, color: "cyan", children: "Keyboard Shortcuts" }) }), _jsx(Box, { flexDirection: "row", gap: 4, children: SHORTCUT_GROUPS.map((group, groupIndex) => (_jsxs(Box, { flexDirection: "column", marginRight: 2, children: [_jsx(Text, { bold: true, color: "yellow", underline: true, children: group.title }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: group.shortcuts.map((shortcut, index) => (_jsxs(Box, { children: [_jsx(Box, { width: 16, children: _jsx(Text, { color: "green", children: shortcut.keys }) }), _jsx(Text, { children: shortcut.description })] }, index))) })] }, groupIndex))) }), _jsx(Box, { marginTop: 1, justifyContent: "center", children: _jsx(Text, { dimColor: true, children: "Press ? or Esc to close" }) })] }));
|
|
68
|
+
}
|
|
69
|
+
export default HelpPanel;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface JsonSnippetEditorProps {
|
|
3
|
+
/** The label to display for this field */
|
|
4
|
+
label: string;
|
|
5
|
+
/** The current JSON value (will be stringified for editing) */
|
|
6
|
+
value: unknown;
|
|
7
|
+
/** Called when the user confirms the edit */
|
|
8
|
+
onConfirm: (newValue: unknown) => void;
|
|
9
|
+
/** Called when the user cancels the edit (Esc) */
|
|
10
|
+
onCancel: () => void;
|
|
11
|
+
/** Whether this editor has focus */
|
|
12
|
+
isFocused?: boolean;
|
|
13
|
+
/** Maximum height for the content area (for scrolling) */
|
|
14
|
+
maxHeight?: number;
|
|
15
|
+
/** Maximum width for the preview (terminal columns) */
|
|
16
|
+
maxWidth?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* JsonSnippetEditor component for editing complex nested JSON sections.
|
|
20
|
+
* Supports copy/paste and live JSON validation with syntax highlighting.
|
|
21
|
+
* Used for MCP servers, actions, skills, and other complex configs.
|
|
22
|
+
*/
|
|
23
|
+
export declare function JsonSnippetEditor({ label, value, onConfirm, onCancel, isFocused, maxHeight, maxWidth, }: JsonSnippetEditorProps): React.ReactElement;
|
|
24
|
+
export default JsonSnippetEditor;
|