tycono 0.1.96-beta.38 → 0.1.96-beta.39
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/package.json
CHANGED
|
@@ -10,7 +10,7 @@ import { Box, Text, Static } from 'ink';
|
|
|
10
10
|
import TextInput from 'ink-text-input';
|
|
11
11
|
import type { SSEEvent } from '../api';
|
|
12
12
|
import { getRoleColor } from '../theme';
|
|
13
|
-
|
|
13
|
+
// Markdown rendering is done inline via regex (no external dependency)
|
|
14
14
|
|
|
15
15
|
const SUPERVISOR_ROLE = 'ceo';
|
|
16
16
|
|
|
@@ -236,29 +236,15 @@ export function summarizeEvent(event: SSEEvent, allRoleIds: string[]): StreamLin
|
|
|
236
236
|
|
|
237
237
|
/** Render a single StreamLine */
|
|
238
238
|
function StreamLineRow({ line }: { line: StreamLine }) {
|
|
239
|
-
//
|
|
240
|
-
if (line.markdown && line.text.includes('\n')) {
|
|
241
|
-
const lines = line.text.split('\n');
|
|
242
|
-
return (
|
|
243
|
-
<Box flexDirection="column">
|
|
244
|
-
{lines.map((l, i) => (
|
|
245
|
-
<Box key={i}>
|
|
246
|
-
{line.indent && <Text> </Text>}
|
|
247
|
-
{line.prefix && i === 0 && (
|
|
248
|
-
<Text color={line.prefixColor} bold>
|
|
249
|
-
{(line.prefix).padEnd(12)}
|
|
250
|
-
</Text>
|
|
251
|
-
)}
|
|
252
|
-
{line.prefix && i > 0 && <Text>{' '.repeat(12)}</Text>}
|
|
253
|
-
{renderMarkdownLine(l, `${line.id}-${i}`)}
|
|
254
|
-
</Box>
|
|
255
|
-
))}
|
|
256
|
-
</Box>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Single line with markdown
|
|
239
|
+
// Markdown: inline formatting only (no multi-line splitting — too many elements)
|
|
261
240
|
if (line.markdown) {
|
|
241
|
+
// Strip markdown markers for terminal display
|
|
242
|
+
const cleaned = line.text
|
|
243
|
+
.replace(/^#{1,4}\s+/gm, '') // ## heading → heading
|
|
244
|
+
.replace(/\*\*(.+?)\*\*/g, '$1') // **bold** → bold
|
|
245
|
+
.replace(/`(.+?)`/g, '$1') // `code` → code
|
|
246
|
+
.replace(/^---+$/gm, '\u2500'.repeat(40)); // --- → line
|
|
247
|
+
|
|
262
248
|
return (
|
|
263
249
|
<Box>
|
|
264
250
|
{line.indent && <Text> </Text>}
|
|
@@ -267,12 +253,11 @@ function StreamLineRow({ line }: { line: StreamLine }) {
|
|
|
267
253
|
{(line.prefix).padEnd(12)}
|
|
268
254
|
</Text>
|
|
269
255
|
)}
|
|
270
|
-
{
|
|
256
|
+
<Text color={line.color} wrap="wrap">{cleaned}</Text>
|
|
271
257
|
</Box>
|
|
272
258
|
);
|
|
273
259
|
}
|
|
274
260
|
|
|
275
|
-
// Plain text (tools, system messages)
|
|
276
261
|
return (
|
|
277
262
|
<Box>
|
|
278
263
|
{line.indent && <Text> </Text>}
|
|
@@ -302,19 +287,19 @@ export const CommandMode: React.FC<CommandModeProps> = ({
|
|
|
302
287
|
if (line) eventLines.push(line);
|
|
303
288
|
}
|
|
304
289
|
|
|
305
|
-
// Merge system messages and event lines (cap total
|
|
306
|
-
const allLines = [...systemMessages, ...eventLines].slice(-
|
|
290
|
+
// Merge system messages and event lines (cap total)
|
|
291
|
+
const allLines = [...systemMessages, ...eventLines].slice(-100);
|
|
307
292
|
|
|
308
293
|
// Split into committed (scrollback) and live (re-rendered)
|
|
309
294
|
const newCommitted = allLines.slice(committedRef.current);
|
|
310
|
-
if (newCommitted.length >
|
|
311
|
-
const toCommit = newCommitted.slice(0, -
|
|
295
|
+
if (newCommitted.length > 6) {
|
|
296
|
+
const toCommit = newCommitted.slice(0, -6);
|
|
312
297
|
committedRef.current += toCommit.length;
|
|
313
298
|
}
|
|
314
299
|
|
|
315
300
|
// Cap committed to prevent Static from holding too many items
|
|
316
301
|
const rawCommitted = allLines.slice(0, committedRef.current);
|
|
317
|
-
const committedLines = rawCommitted.length >
|
|
302
|
+
const committedLines = rawCommitted.length > 50 ? rawCommitted.slice(-50) : rawCommitted;
|
|
318
303
|
const liveLines = allLines.slice(committedRef.current);
|
|
319
304
|
|
|
320
305
|
const handleSubmit = useCallback((value: string) => {
|
|
@@ -91,11 +91,21 @@ function extractWaveFiles(events: SSEEvent[]): string[] {
|
|
|
91
91
|
return Array.from(files);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
/** Read file preview (first N lines) */
|
|
94
|
+
/** Read file preview (first N lines, cached) */
|
|
95
|
+
const fileCache = new Map<string, string[]>();
|
|
95
96
|
function readFilePreview(filePath: string, maxLines: number): string[] {
|
|
97
|
+
const cached = fileCache.get(filePath);
|
|
98
|
+
if (cached) return cached;
|
|
96
99
|
try {
|
|
97
100
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
98
|
-
|
|
101
|
+
const lines = content.split('\n').slice(0, maxLines);
|
|
102
|
+
fileCache.set(filePath, lines);
|
|
103
|
+
// Evict old entries
|
|
104
|
+
if (fileCache.size > 5) {
|
|
105
|
+
const first = fileCache.keys().next().value;
|
|
106
|
+
if (first) fileCache.delete(first);
|
|
107
|
+
}
|
|
108
|
+
return lines;
|
|
99
109
|
} catch {
|
|
100
110
|
return ['(cannot read file)'];
|
|
101
111
|
}
|
package/src/tui/hooks/useSSE.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
9
9
|
import { subscribeToWaveStream, type SSEEvent, type SSEConnection } from '../api';
|
|
10
10
|
|
|
11
|
-
const MAX_EVENTS =
|
|
11
|
+
const MAX_EVENTS = 100;
|
|
12
12
|
const RECONNECT_DELAY_MS = 3000;
|
|
13
13
|
const MAX_RECONNECT_DELAY_MS = 15000;
|
|
14
14
|
const BATCH_INTERVAL_MS = 300; // Throttle: update React state max ~3x/sec
|