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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.96-beta.38",
3
+ "version": "0.1.96-beta.39",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
- import { renderMarkdownLine } from '../utils/markdown';
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
- // Multi-line markdown: split and render each line
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
- {renderMarkdownLine(line.text, line.id)}
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 to prevent memory bloat)
306
- const allLines = [...systemMessages, ...eventLines].slice(-200);
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 > 8) {
311
- const toCommit = newCommitted.slice(0, -8);
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 > 100 ? rawCommitted.slice(-100) : rawCommitted;
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
- return content.split('\n').slice(0, maxLines);
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
  }
@@ -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 = 150;
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