aiexecode 1.0.66 → 1.0.69
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.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/config_template/settings.json +1 -3
- package/index.js +46 -71
- package/package.json +1 -12
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +1 -1
- package/payload_viewer/web_server.js +0 -163
- package/prompts/completion_judge.txt +11 -7
- package/src/ai_based/completion_judge.js +97 -6
- package/src/ai_based/orchestrator.js +71 -3
- package/src/ai_based/pip_package_installer.js +14 -12
- package/src/ai_based/pip_package_lookup.js +13 -10
- package/src/commands/apikey.js +8 -34
- package/src/commands/help.js +3 -4
- package/src/commands/model.js +17 -74
- package/src/commands/reasoning_effort.js +1 -1
- package/src/config/feature_flags.js +0 -12
- package/src/{ui → frontend}/App.js +23 -25
- package/src/frontend/README.md +81 -0
- package/src/{ui/components/SuggestionsDisplay.js → frontend/components/AutocompleteMenu.js} +3 -3
- package/src/{ui/components/HistoryItemDisplay.js → frontend/components/ConversationItem.js} +37 -89
- package/src/{ui → frontend}/components/CurrentModelView.js +3 -5
- package/src/{ui → frontend}/components/Footer.js +4 -6
- package/src/{ui → frontend}/components/Header.js +2 -5
- package/src/{ui/components/InputPrompt.js → frontend/components/Input.js} +16 -54
- package/src/frontend/components/ModelListView.js +106 -0
- package/src/{ui → frontend}/components/ModelUpdatedView.js +3 -5
- package/src/{ui → frontend}/components/SessionSpinner.js +3 -3
- package/src/{ui → frontend}/components/SetupWizard.js +8 -101
- package/src/{ui → frontend}/components/ToolApprovalPrompt.js +16 -14
- package/src/frontend/design/themeColors.js +42 -0
- package/src/{ui → frontend}/index.js +7 -7
- package/src/frontend/utils/inputBuffer.js +441 -0
- package/src/{ui/utils/markdownRenderer.js → frontend/utils/markdownParser.js} +3 -3
- package/src/{ui/utils/ConsolePatcher.js → frontend/utils/outputRedirector.js} +9 -9
- package/src/{ui/utils/codeColorizer.js → frontend/utils/syntaxHighlighter.js} +2 -3
- package/src/system/ai_request.js +145 -595
- package/src/system/code_executer.js +111 -16
- package/src/system/file_integrity.js +5 -7
- package/src/system/log.js +3 -3
- package/src/system/mcp_integration.js +15 -13
- package/src/system/output_helper.js +0 -20
- package/src/system/session.js +97 -23
- package/src/system/session_memory.js +2 -82
- package/src/system/system_info.js +1 -1
- package/src/system/ui_events.js +0 -43
- package/src/tools/code_editor.js +17 -2
- package/src/tools/file_reader.js +17 -2
- package/src/tools/glob.js +9 -1
- package/src/tools/response_message.js +0 -2
- package/src/tools/ripgrep.js +9 -1
- package/src/tools/web_downloader.js +9 -1
- package/src/util/config.js +3 -8
- package/src/util/debug_log.js +4 -11
- package/src/util/mcp_config_manager.js +3 -5
- package/src/util/output_formatter.js +0 -47
- package/src/util/prompt_loader.js +3 -4
- package/src/util/safe_fs.js +60 -0
- package/src/util/setup_wizard.js +1 -3
- package/src/util/text_formatter.js +0 -86
- package/src/config/claude_models.js +0 -195
- package/src/ui/README.md +0 -208
- package/src/ui/api.js +0 -167
- package/src/ui/components/AgenticProgressDisplay.js +0 -126
- package/src/ui/components/Composer.js +0 -55
- package/src/ui/components/LoadingIndicator.js +0 -54
- package/src/ui/components/ModelListView.js +0 -214
- package/src/ui/components/Notifications.js +0 -55
- package/src/ui/components/StreamingIndicator.js +0 -36
- package/src/ui/contexts/AppContext.js +0 -25
- package/src/ui/contexts/StreamingContext.js +0 -20
- package/src/ui/contexts/UIStateContext.js +0 -117
- package/src/ui/example-usage.js +0 -180
- package/src/ui/hooks/useTerminalResize.js +0 -39
- package/src/ui/themes/semantic-tokens.js +0 -73
- package/src/ui/utils/text-buffer.js +0 -975
- /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → 7FgHZugvVp3pn1XStUYUJ}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → 7FgHZugvVp3pn1XStUYUJ}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{t0WTsjXST7ISD1Boa6ifx → 7FgHZugvVp3pn1XStUYUJ}/_ssgManifest.js +0 -0
- /package/src/{ui → frontend}/components/BlankLine.js +0 -0
- /package/src/{ui → frontend}/components/FileDiffViewer.js +0 -0
- /package/src/{ui → frontend}/components/HelpView.js +0 -0
- /package/src/{ui → frontend}/hooks/useCompletion.js +0 -0
- /package/src/{ui → frontend}/hooks/useKeypress.js +0 -0
- /package/src/{ui → frontend}/utils/diffUtils.js +0 -0
- /package/src/{ui → frontend}/utils/renderInkComponent.js +0 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Buffer - Advanced text buffer for multi-line input with cursor management
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import stringWidth from 'string-width';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Convert string to code points array
|
|
9
|
+
*/
|
|
10
|
+
function toCodePoints(str) {
|
|
11
|
+
return Array.from(str);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get length in code points
|
|
16
|
+
*/
|
|
17
|
+
export function cpLen(str) {
|
|
18
|
+
return toCodePoints(str).length;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Slice string by code points
|
|
23
|
+
*/
|
|
24
|
+
export function cpSlice(str, start, end) {
|
|
25
|
+
const cps = toCodePoints(str);
|
|
26
|
+
return cps.slice(start, end).join('');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Hook to create and manage text buffer using useReducer
|
|
31
|
+
*/
|
|
32
|
+
import { useReducer, useMemo, useCallback, useState, useEffect, useRef } from 'react';
|
|
33
|
+
|
|
34
|
+
function textBufferReducer(state, action) {
|
|
35
|
+
switch (action.type) {
|
|
36
|
+
case 'set_text': {
|
|
37
|
+
const newLines = action.payload ? action.payload.split('\n') : [''];
|
|
38
|
+
return {
|
|
39
|
+
...state,
|
|
40
|
+
lines: newLines,
|
|
41
|
+
cursor: [0, 0]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
case 'insert_text': {
|
|
45
|
+
const { text } = action.payload;
|
|
46
|
+
const [row, col] = state.cursor;
|
|
47
|
+
const newLines = [...state.lines];
|
|
48
|
+
const line = newLines[row] || '';
|
|
49
|
+
const before = cpSlice(line, 0, col);
|
|
50
|
+
const after = cpSlice(line, col);
|
|
51
|
+
newLines[row] = before + text + after;
|
|
52
|
+
return {
|
|
53
|
+
...state,
|
|
54
|
+
lines: newLines,
|
|
55
|
+
cursor: [row, col + cpLen(text)]
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
case 'move_cursor': {
|
|
59
|
+
const { direction } = action.payload;
|
|
60
|
+
const [row, col] = state.cursor;
|
|
61
|
+
const line = state.lines[row] || '';
|
|
62
|
+
let newCursor = [row, col];
|
|
63
|
+
|
|
64
|
+
switch (direction) {
|
|
65
|
+
case 'left':
|
|
66
|
+
if (col > 0) {
|
|
67
|
+
newCursor = [row, col - 1];
|
|
68
|
+
} else if (row > 0) {
|
|
69
|
+
newCursor = [row - 1, cpLen(state.lines[row - 1] || '')];
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case 'right':
|
|
73
|
+
if (col < cpLen(line)) {
|
|
74
|
+
newCursor = [row, col + 1];
|
|
75
|
+
} else if (row < state.lines.length - 1) {
|
|
76
|
+
newCursor = [row + 1, 0];
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
case 'up':
|
|
80
|
+
if (row > 0) {
|
|
81
|
+
const prevLineLen = cpLen(state.lines[row - 1] || '');
|
|
82
|
+
newCursor = [row - 1, Math.min(col, prevLineLen)];
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case 'down':
|
|
86
|
+
if (row < state.lines.length - 1) {
|
|
87
|
+
const nextLineLen = cpLen(state.lines[row + 1] || '');
|
|
88
|
+
newCursor = [row + 1, Math.min(col, nextLineLen)];
|
|
89
|
+
}
|
|
90
|
+
break;
|
|
91
|
+
case 'home':
|
|
92
|
+
newCursor = [row, 0];
|
|
93
|
+
break;
|
|
94
|
+
case 'end':
|
|
95
|
+
newCursor = [row, cpLen(line)];
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
...state,
|
|
101
|
+
cursor: newCursor
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
case 'backspace': {
|
|
105
|
+
const [row, col] = state.cursor;
|
|
106
|
+
if (col === 0 && row === 0) return state;
|
|
107
|
+
|
|
108
|
+
const newLines = [...state.lines];
|
|
109
|
+
let newCursor = [row, col];
|
|
110
|
+
|
|
111
|
+
if (col > 0) {
|
|
112
|
+
const line = newLines[row] || '';
|
|
113
|
+
newLines[row] = cpSlice(line, 0, col - 1) + cpSlice(line, col);
|
|
114
|
+
newCursor = [row, col - 1];
|
|
115
|
+
} else if (row > 0) {
|
|
116
|
+
const currentLine = newLines[row] || '';
|
|
117
|
+
const prevLine = newLines[row - 1] || '';
|
|
118
|
+
const prevLineLen = cpLen(prevLine);
|
|
119
|
+
newLines[row - 1] = prevLine + currentLine;
|
|
120
|
+
newLines.splice(row, 1);
|
|
121
|
+
newCursor = [row - 1, prevLineLen];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
...state,
|
|
126
|
+
lines: newLines,
|
|
127
|
+
cursor: newCursor
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
case 'delete': {
|
|
131
|
+
const [row, col] = state.cursor;
|
|
132
|
+
const newLines = [...state.lines];
|
|
133
|
+
const line = newLines[row] || '';
|
|
134
|
+
const lineLen = cpLen(line);
|
|
135
|
+
|
|
136
|
+
if (col < lineLen) {
|
|
137
|
+
newLines[row] = cpSlice(line, 0, col) + cpSlice(line, col + 1);
|
|
138
|
+
} else if (row < state.lines.length - 1) {
|
|
139
|
+
const nextLine = newLines[row + 1] || '';
|
|
140
|
+
newLines[row] = line + nextLine;
|
|
141
|
+
newLines.splice(row + 1, 1);
|
|
142
|
+
} else {
|
|
143
|
+
return state;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
...state,
|
|
148
|
+
lines: newLines
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
case 'newline': {
|
|
152
|
+
const [row, col] = state.cursor;
|
|
153
|
+
const newLines = [...state.lines];
|
|
154
|
+
const line = newLines[row] || '';
|
|
155
|
+
const before = cpSlice(line, 0, col);
|
|
156
|
+
const after = cpSlice(line, col);
|
|
157
|
+
newLines[row] = before;
|
|
158
|
+
newLines.splice(row + 1, 0, after);
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
...state,
|
|
162
|
+
lines: newLines,
|
|
163
|
+
cursor: [row + 1, 0]
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
case 'kill_line_right': {
|
|
167
|
+
const [row, col] = state.cursor;
|
|
168
|
+
const newLines = [...state.lines];
|
|
169
|
+
const line = newLines[row] || '';
|
|
170
|
+
newLines[row] = cpSlice(line, 0, col);
|
|
171
|
+
return {
|
|
172
|
+
...state,
|
|
173
|
+
lines: newLines
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
case 'kill_line_left': {
|
|
177
|
+
const [row, col] = state.cursor;
|
|
178
|
+
const newLines = [...state.lines];
|
|
179
|
+
const line = newLines[row] || '';
|
|
180
|
+
newLines[row] = cpSlice(line, col);
|
|
181
|
+
return {
|
|
182
|
+
...state,
|
|
183
|
+
lines: newLines,
|
|
184
|
+
cursor: [row, 0]
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
case 'delete_word_left': {
|
|
188
|
+
const [row, col] = state.cursor;
|
|
189
|
+
if (col === 0) return state;
|
|
190
|
+
|
|
191
|
+
const newLines = [...state.lines];
|
|
192
|
+
const line = newLines[row] || '';
|
|
193
|
+
const before = cpSlice(line, 0, col);
|
|
194
|
+
const after = cpSlice(line, col);
|
|
195
|
+
|
|
196
|
+
const trimmed = before.trimEnd();
|
|
197
|
+
const lastSpace = trimmed.lastIndexOf(' ');
|
|
198
|
+
const newCol = lastSpace >= 0 ? lastSpace + 1 : 0;
|
|
199
|
+
|
|
200
|
+
newLines[row] = cpSlice(line, 0, newCol) + after;
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
...state,
|
|
204
|
+
lines: newLines,
|
|
205
|
+
cursor: [row, newCol]
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
case 'set_viewport': {
|
|
209
|
+
return {
|
|
210
|
+
...state,
|
|
211
|
+
viewport: action.payload
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
default:
|
|
215
|
+
return state;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function useTextBuffer(options) {
|
|
220
|
+
const { initialText = '', viewport } = options;
|
|
221
|
+
|
|
222
|
+
const initialState = useMemo(() => ({
|
|
223
|
+
lines: initialText ? initialText.split('\n') : [''],
|
|
224
|
+
cursor: [0, 0],
|
|
225
|
+
viewport: viewport || { width: 80, height: 10 }
|
|
226
|
+
}), [initialText, viewport]);
|
|
227
|
+
|
|
228
|
+
const [state, dispatch] = useReducer(textBufferReducer, initialState);
|
|
229
|
+
|
|
230
|
+
// Update viewport when it changes
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
if (viewport && (viewport.width !== state.viewport.width || viewport.height !== state.viewport.height)) {
|
|
233
|
+
dispatch({ type: 'set_viewport', payload: viewport });
|
|
234
|
+
}
|
|
235
|
+
}, [viewport, state.viewport]);
|
|
236
|
+
|
|
237
|
+
// Calculate visual layout
|
|
238
|
+
const visualLayout = useMemo(() => {
|
|
239
|
+
const allVisualLines = [];
|
|
240
|
+
const visualToLogicalMap = [];
|
|
241
|
+
const viewportWidth = state.viewport.width;
|
|
242
|
+
|
|
243
|
+
state.lines.forEach((line, logicalRow) => {
|
|
244
|
+
if (!line) {
|
|
245
|
+
allVisualLines.push('');
|
|
246
|
+
visualToLogicalMap.push([logicalRow, 0]);
|
|
247
|
+
} else {
|
|
248
|
+
const wrapped = wrapLine(line, viewportWidth);
|
|
249
|
+
wrapped.forEach((segment, i) => {
|
|
250
|
+
allVisualLines.push(segment);
|
|
251
|
+
visualToLogicalMap.push([logicalRow, i * viewportWidth]);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return { allVisualLines, visualToLogicalMap };
|
|
257
|
+
}, [state.lines, state.viewport.width]);
|
|
258
|
+
|
|
259
|
+
// Calculate visual cursor
|
|
260
|
+
const visualCursor = useMemo(() => {
|
|
261
|
+
const [row, col] = state.cursor;
|
|
262
|
+
const line = state.lines[row] || '';
|
|
263
|
+
const wrappedLines = wrapLine(line, state.viewport.width);
|
|
264
|
+
|
|
265
|
+
let remainingCol = col;
|
|
266
|
+
let visualRow = 0;
|
|
267
|
+
|
|
268
|
+
for (let i = 0; i < row; i++) {
|
|
269
|
+
visualRow += wrapLine(state.lines[i], state.viewport.width).length;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
for (let i = 0; i < wrappedLines.length; i++) {
|
|
273
|
+
const lineLen = cpLen(wrappedLines[i]);
|
|
274
|
+
if (remainingCol <= lineLen) {
|
|
275
|
+
return [visualRow + i, remainingCol];
|
|
276
|
+
}
|
|
277
|
+
remainingCol -= lineLen;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return [visualRow + wrappedLines.length - 1, cpLen(wrappedLines[wrappedLines.length - 1])];
|
|
281
|
+
}, [state.cursor, state.lines, state.viewport.width]);
|
|
282
|
+
|
|
283
|
+
const [visualScrollRow, setVisualScrollRow] = useState(0);
|
|
284
|
+
|
|
285
|
+
// Update scroll position - memoize the calculation to avoid unnecessary state updates
|
|
286
|
+
const computedScrollRow = useMemo(() => {
|
|
287
|
+
const { height } = state.viewport;
|
|
288
|
+
const totalVisualLines = visualLayout.allVisualLines.length;
|
|
289
|
+
const maxScrollStart = Math.max(0, totalVisualLines - height);
|
|
290
|
+
let newVisualScrollRow = visualScrollRow;
|
|
291
|
+
|
|
292
|
+
// Only adjust scroll if cursor is out of viewport
|
|
293
|
+
if (visualCursor[0] < visualScrollRow) {
|
|
294
|
+
newVisualScrollRow = visualCursor[0];
|
|
295
|
+
} else if (visualCursor[0] >= visualScrollRow + height) {
|
|
296
|
+
newVisualScrollRow = visualCursor[0] - height + 1;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
newVisualScrollRow = Math.min(Math.max(newVisualScrollRow, 0), maxScrollStart);
|
|
300
|
+
return newVisualScrollRow;
|
|
301
|
+
}, [visualCursor, visualScrollRow, state.viewport.height, visualLayout.allVisualLines.length]);
|
|
302
|
+
|
|
303
|
+
// Only update state when scroll actually changes
|
|
304
|
+
useEffect(() => {
|
|
305
|
+
if (computedScrollRow !== visualScrollRow) {
|
|
306
|
+
setVisualScrollRow(computedScrollRow);
|
|
307
|
+
}
|
|
308
|
+
}, [computedScrollRow, visualScrollRow]);
|
|
309
|
+
|
|
310
|
+
// Create stable callbacks with useCallback
|
|
311
|
+
const setText = useCallback((text) => {
|
|
312
|
+
dispatch({ type: 'set_text', payload: text });
|
|
313
|
+
}, []);
|
|
314
|
+
|
|
315
|
+
const insertText = useCallback((text) => {
|
|
316
|
+
dispatch({ type: 'insert_text', payload: { text } });
|
|
317
|
+
}, []);
|
|
318
|
+
|
|
319
|
+
const move = useCallback((direction) => {
|
|
320
|
+
dispatch({ type: 'move_cursor', payload: { direction } });
|
|
321
|
+
}, []);
|
|
322
|
+
|
|
323
|
+
const backspace = useCallback(() => {
|
|
324
|
+
dispatch({ type: 'backspace' });
|
|
325
|
+
}, []);
|
|
326
|
+
|
|
327
|
+
const deleteChar = useCallback(() => {
|
|
328
|
+
dispatch({ type: 'delete' });
|
|
329
|
+
}, []);
|
|
330
|
+
|
|
331
|
+
const newline = useCallback(() => {
|
|
332
|
+
dispatch({ type: 'newline' });
|
|
333
|
+
}, []);
|
|
334
|
+
|
|
335
|
+
const killLineRight = useCallback(() => {
|
|
336
|
+
dispatch({ type: 'kill_line_right' });
|
|
337
|
+
}, []);
|
|
338
|
+
|
|
339
|
+
const killLineLeft = useCallback(() => {
|
|
340
|
+
dispatch({ type: 'kill_line_left' });
|
|
341
|
+
}, []);
|
|
342
|
+
|
|
343
|
+
const deleteWordLeft = useCallback(() => {
|
|
344
|
+
dispatch({ type: 'delete_word_left' });
|
|
345
|
+
}, []);
|
|
346
|
+
|
|
347
|
+
const handleInput = useCallback((key) => {
|
|
348
|
+
if (key.sequence && key.sequence.length > 0 && !key.ctrl && !key.meta) {
|
|
349
|
+
if (key.sequence.includes('\n') || key.sequence.includes('\r')) {
|
|
350
|
+
const normalized = key.sequence.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
351
|
+
const parts = normalized.split('\n');
|
|
352
|
+
parts.forEach((part, i) => {
|
|
353
|
+
if (part) dispatch({ type: 'insert_text', payload: { text: part } });
|
|
354
|
+
if (i < parts.length - 1) dispatch({ type: 'newline' });
|
|
355
|
+
});
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const isPrintable = key.sequence.split('').every(char => {
|
|
360
|
+
const code = char.charCodeAt(0);
|
|
361
|
+
return (code >= 32 && code <= 126) || code >= 128;
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
if (isPrintable) {
|
|
365
|
+
dispatch({ type: 'insert_text', payload: { text: key.sequence } });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}, []);
|
|
369
|
+
|
|
370
|
+
const text = useMemo(() => state.lines.join('\n'), [state.lines]);
|
|
371
|
+
|
|
372
|
+
const viewportVisualLines = useMemo(() => {
|
|
373
|
+
return visualLayout.allVisualLines.slice(visualScrollRow, visualScrollRow + state.viewport.height);
|
|
374
|
+
}, [visualLayout.allVisualLines, visualScrollRow, state.viewport.height]);
|
|
375
|
+
|
|
376
|
+
// Return stable buffer object using useRef to maintain reference identity
|
|
377
|
+
const bufferRef = useRef(null);
|
|
378
|
+
|
|
379
|
+
if (!bufferRef.current) {
|
|
380
|
+
bufferRef.current = {
|
|
381
|
+
get lines() { return state.lines; },
|
|
382
|
+
get cursor() { return state.cursor; },
|
|
383
|
+
get text() { return text; },
|
|
384
|
+
get allVisualLines() { return visualLayout.allVisualLines; },
|
|
385
|
+
get viewportVisualLines() { return viewportVisualLines; },
|
|
386
|
+
get visualCursor() { return visualCursor; },
|
|
387
|
+
get visualScrollRow() { return visualScrollRow; },
|
|
388
|
+
get visualToLogicalMap() { return visualLayout.visualToLogicalMap; },
|
|
389
|
+
get viewport() { return state.viewport; },
|
|
390
|
+
setText,
|
|
391
|
+
insertText,
|
|
392
|
+
move,
|
|
393
|
+
backspace,
|
|
394
|
+
delete: deleteChar,
|
|
395
|
+
newline,
|
|
396
|
+
killLineRight,
|
|
397
|
+
killLineLeft,
|
|
398
|
+
deleteWordLeft,
|
|
399
|
+
handleInput
|
|
400
|
+
};
|
|
401
|
+
} else {
|
|
402
|
+
// Update getters to return current values without changing object reference
|
|
403
|
+
Object.defineProperties(bufferRef.current, {
|
|
404
|
+
lines: { get: () => state.lines, enumerable: true, configurable: true },
|
|
405
|
+
cursor: { get: () => state.cursor, enumerable: true, configurable: true },
|
|
406
|
+
text: { get: () => text, enumerable: true, configurable: true },
|
|
407
|
+
allVisualLines: { get: () => visualLayout.allVisualLines, enumerable: true, configurable: true },
|
|
408
|
+
viewportVisualLines: { get: () => viewportVisualLines, enumerable: true, configurable: true },
|
|
409
|
+
visualCursor: { get: () => visualCursor, enumerable: true, configurable: true },
|
|
410
|
+
visualScrollRow: { get: () => visualScrollRow, enumerable: true, configurable: true },
|
|
411
|
+
visualToLogicalMap: { get: () => visualLayout.visualToLogicalMap, enumerable: true, configurable: true },
|
|
412
|
+
viewport: { get: () => state.viewport, enumerable: true, configurable: true }
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return bufferRef.current;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function wrapLine(line, viewportWidth) {
|
|
420
|
+
if (!line) return [''];
|
|
421
|
+
|
|
422
|
+
const result = [];
|
|
423
|
+
const cps = toCodePoints(line);
|
|
424
|
+
let currentLine = '';
|
|
425
|
+
let currentWidth = 0;
|
|
426
|
+
|
|
427
|
+
for (const cp of cps) {
|
|
428
|
+
const cpWidth = stringWidth(cp);
|
|
429
|
+
if (currentWidth + cpWidth > viewportWidth) {
|
|
430
|
+
result.push(currentLine);
|
|
431
|
+
currentLine = cp;
|
|
432
|
+
currentWidth = cpWidth;
|
|
433
|
+
} else {
|
|
434
|
+
currentLine += cp;
|
|
435
|
+
currentWidth += cpWidth;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
result.push(currentLine);
|
|
440
|
+
return result.length > 0 ? result : [''];
|
|
441
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Markdown
|
|
3
|
-
* 마크다운 텍스트를 터미널 스타일로 렌더링
|
|
2
|
+
* Markdown Parser
|
|
3
|
+
* 마크다운 텍스트를 파싱하여 터미널 스타일로 렌더링
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import { Text, Box } from 'ink';
|
|
8
|
-
import { theme } from '../
|
|
8
|
+
import { theme } from '../design/themeColors.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* 마크다운 텍스트를 파싱하여 Ink 컴포넌트로 변환
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Output Redirector - Redirects console output to prevent interfering with Ink rendering
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import util from 'util';
|
|
6
6
|
import { createDebugLogger } from '../../util/debug_log.js';
|
|
7
7
|
|
|
8
|
-
const debugLog = createDebugLogger('ui_utils.log', '
|
|
8
|
+
const debugLog = createDebugLogger('ui_utils.log', 'OutputRedirector');
|
|
9
9
|
|
|
10
|
-
export class
|
|
10
|
+
export class OutputRedirector {
|
|
11
11
|
constructor() {
|
|
12
|
-
debugLog('
|
|
12
|
+
debugLog('OutputRedirector constructor called');
|
|
13
13
|
this.originalConsoleLog = console.log;
|
|
14
14
|
this.originalConsoleWarn = console.warn;
|
|
15
15
|
this.originalConsoleError = console.error;
|
|
@@ -18,24 +18,24 @@ export class ConsolePatcher {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
patch() {
|
|
21
|
-
debugLog('
|
|
21
|
+
debugLog('OutputRedirector patching console methods');
|
|
22
22
|
// Redirect console methods to stderr to prevent interference with Ink's stdout rendering
|
|
23
23
|
console.log = this.patchConsoleMethod(this.originalConsoleError);
|
|
24
24
|
console.warn = this.patchConsoleMethod(this.originalConsoleError);
|
|
25
25
|
console.error = this.patchConsoleMethod(this.originalConsoleError);
|
|
26
26
|
console.debug = this.patchConsoleMethod(this.originalConsoleError);
|
|
27
|
-
console.info = this.patchConsoleMethod(this.
|
|
28
|
-
debugLog('
|
|
27
|
+
console.info = this.patchConsoleMethod(this.originalConsoleInfo);
|
|
28
|
+
debugLog('OutputRedirector patch completed');
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
cleanup() {
|
|
32
|
-
debugLog('
|
|
32
|
+
debugLog('OutputRedirector cleanup called');
|
|
33
33
|
console.log = this.originalConsoleLog;
|
|
34
34
|
console.warn = this.originalConsoleWarn;
|
|
35
35
|
console.error = this.originalConsoleError;
|
|
36
36
|
console.debug = this.originalConsoleDebug;
|
|
37
37
|
console.info = this.originalConsoleInfo;
|
|
38
|
-
debugLog('
|
|
38
|
+
debugLog('OutputRedirector cleanup completed');
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
patchConsoleMethod(originalMethod) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Syntax Highlighter - Syntax highlighting for code blocks
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
@@ -7,7 +7,6 @@ import { Text, Box } from 'ink';
|
|
|
7
7
|
import { common, createLowlight } from 'lowlight';
|
|
8
8
|
|
|
9
9
|
const lowlight = createLowlight(common);
|
|
10
|
-
function consolelog() { }
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Theme color mapping for syntax highlighting
|
|
@@ -130,7 +129,7 @@ export function colorizeCode(code, language, showLineNumbers = true) {
|
|
|
130
129
|
})
|
|
131
130
|
);
|
|
132
131
|
} catch (error) {
|
|
133
|
-
|
|
132
|
+
// Error highlighting code - silently ignore
|
|
134
133
|
|
|
135
134
|
// Fallback to plain text
|
|
136
135
|
const lines = codeToHighlight.split('\n');
|