snow-ai 0.3.16 → 0.3.17
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
-
import { cpSlice
|
|
3
|
+
import { cpSlice } from '../../utils/textUtils.js';
|
|
4
4
|
import CommandPanel from './CommandPanel.js';
|
|
5
5
|
import FileList from './FileList.js';
|
|
6
6
|
import { useInputBuffer } from '../../hooks/useInputBuffer.js';
|
|
@@ -181,101 +181,18 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
|
|
|
181
181
|
return React.createElement(Text, null, char);
|
|
182
182
|
}
|
|
183
183
|
}, [hasFocus]);
|
|
184
|
-
// Render content with cursor
|
|
184
|
+
// Render content with cursor (treat all text including placeholders as plain text)
|
|
185
185
|
const renderContent = useCallback(() => {
|
|
186
186
|
if (buffer.text.length > 0) {
|
|
187
|
-
// 使用buffer
|
|
187
|
+
// 使用buffer的内部文本,将占位符当作普通文本处理
|
|
188
188
|
const displayText = buffer.text;
|
|
189
189
|
const cursorPos = buffer.getCursorPosition();
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const hasOnlyNewlines = /^[\n\s]*$/.test(displayText.replace(focusTokenPattern, ''));
|
|
197
|
-
const isFocusNoise = cleanedText.length === 0 && !hasOnlyNewlines;
|
|
198
|
-
if (hasPastePlaceholder || hasImagePlaceholder || isFocusNoise) {
|
|
199
|
-
const atCursor = (() => {
|
|
200
|
-
const charInfo = buffer.getCharAtCursor();
|
|
201
|
-
return charInfo.char === '\n' ? ' ' : charInfo.char;
|
|
202
|
-
})();
|
|
203
|
-
// 分割文本并高亮占位符(粘贴和图片)
|
|
204
|
-
const parts = displayText.split(/(\[Paste \d+ characters #\d+\]|\[image #\d+\])/);
|
|
205
|
-
// 先构建带位置信息的数据结构
|
|
206
|
-
const partsWithPosition = [];
|
|
207
|
-
let processedLength = 0;
|
|
208
|
-
for (let i = 0; i < parts.length; i++) {
|
|
209
|
-
const part = parts[i] || '';
|
|
210
|
-
const isPastePlaceholder = !!part.match(/^\[Paste \d+ characters #\d+\]$/);
|
|
211
|
-
const isImagePlaceholder = !!part.match(/^\[image #\d+\]$/);
|
|
212
|
-
const partStart = processedLength;
|
|
213
|
-
const partEnd = processedLength + cpLen(part);
|
|
214
|
-
processedLength = partEnd;
|
|
215
|
-
if (part.length > 0) {
|
|
216
|
-
partsWithPosition.push({
|
|
217
|
-
part,
|
|
218
|
-
partStart,
|
|
219
|
-
partEnd,
|
|
220
|
-
isPastePlaceholder,
|
|
221
|
-
isImagePlaceholder,
|
|
222
|
-
originalIndex: i,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
let cursorRendered = false;
|
|
227
|
-
const elements = [];
|
|
228
|
-
let elementKey = 0;
|
|
229
|
-
for (const item of partsWithPosition) {
|
|
230
|
-
const { part, partStart, partEnd, isPastePlaceholder, isImagePlaceholder, originalIndex } = item;
|
|
231
|
-
const isPlaceholder = isPastePlaceholder || isImagePlaceholder;
|
|
232
|
-
// 检查光标是否在这个部分
|
|
233
|
-
if (cursorPos >= partStart && cursorPos < partEnd) {
|
|
234
|
-
cursorRendered = true;
|
|
235
|
-
const beforeCursorInPart = cpSlice(part, 0, cursorPos - partStart);
|
|
236
|
-
const afterCursorInPart = cpSlice(part, cursorPos - partStart + 1);
|
|
237
|
-
if (isPlaceholder) {
|
|
238
|
-
if (beforeCursorInPart) {
|
|
239
|
-
elements.push(React.createElement(Text, { key: `${originalIndex}-before`, color: isImagePlaceholder ? 'magenta' : 'cyan', dimColor: true }, beforeCursorInPart));
|
|
240
|
-
}
|
|
241
|
-
elements.push(React.createElement(React.Fragment, { key: `cursor-${elementKey++}` }, renderCursor(atCursor)));
|
|
242
|
-
if (afterCursorInPart) {
|
|
243
|
-
elements.push(React.createElement(Text, { key: `${originalIndex}-after`, color: isImagePlaceholder ? 'magenta' : 'cyan', dimColor: true }, afterCursorInPart));
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
if (beforeCursorInPart) {
|
|
248
|
-
elements.push(React.createElement(Text, { key: `${originalIndex}-before` }, beforeCursorInPart));
|
|
249
|
-
}
|
|
250
|
-
elements.push(React.createElement(React.Fragment, { key: `cursor-${elementKey++}` }, renderCursor(atCursor)));
|
|
251
|
-
if (afterCursorInPart) {
|
|
252
|
-
elements.push(React.createElement(Text, { key: `${originalIndex}-after` }, afterCursorInPart));
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
else {
|
|
257
|
-
if (isPlaceholder) {
|
|
258
|
-
elements.push(React.createElement(Text, { key: originalIndex, color: isImagePlaceholder ? 'magenta' : 'cyan', dimColor: true }, part));
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
elements.push(React.createElement(Text, { key: originalIndex }, part));
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if (!cursorRendered) {
|
|
266
|
-
elements.push(React.createElement(React.Fragment, { key: `cursor-final` }, renderCursor(' ')));
|
|
267
|
-
}
|
|
268
|
-
return React.createElement(React.Fragment, null, elements);
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
271
|
-
// 普通文本渲染
|
|
272
|
-
const charInfo = buffer.getCharAtCursor();
|
|
273
|
-
const atCursor = charInfo.char === '\n' ? ' ' : charInfo.char;
|
|
274
|
-
return (React.createElement(Text, null,
|
|
275
|
-
cpSlice(displayText, 0, cursorPos),
|
|
276
|
-
renderCursor(atCursor),
|
|
277
|
-
cpSlice(displayText, cursorPos + 1)));
|
|
278
|
-
}
|
|
190
|
+
const charInfo = buffer.getCharAtCursor();
|
|
191
|
+
const atCursor = charInfo.char === '\n' ? ' ' : charInfo.char;
|
|
192
|
+
return (React.createElement(Text, null,
|
|
193
|
+
cpSlice(displayText, 0, cursorPos),
|
|
194
|
+
renderCursor(atCursor),
|
|
195
|
+
cpSlice(displayText, cursorPos + 1)));
|
|
279
196
|
}
|
|
280
197
|
else {
|
|
281
198
|
return (React.createElement(React.Fragment, null,
|