ideacode 1.2.4 → 1.2.5

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.
Files changed (2) hide show
  1. package/dist/repl.js +55 -25
  2. package/package.json +1 -1
package/dist/repl.js CHANGED
@@ -281,30 +281,6 @@ function orbitDots(frame) {
281
281
  .map((ch, i) => (i === activeIndex ? colors.gray(ch) : colors.mutedDark(ch)))
282
282
  .join("");
283
283
  }
284
- const LoadingStatus = React.memo(function LoadingStatus({ active, label, }) {
285
- const [frame, setFrame] = useState(0);
286
- const startedAtRef = useRef(null);
287
- useEffect(() => {
288
- if (!active) {
289
- startedAtRef.current = null;
290
- return;
291
- }
292
- if (startedAtRef.current == null) {
293
- startedAtRef.current = Date.now();
294
- setFrame(0);
295
- }
296
- const anim = setInterval(() => setFrame((n) => n + 1), LOADING_TICK_MS);
297
- return () => {
298
- clearInterval(anim);
299
- };
300
- }, [active]);
301
- if (!active)
302
- return _jsx(Text, { color: inkColors.textSecondary, children: "\u00A0" });
303
- const startedAt = startedAtRef.current ?? Date.now();
304
- const elapsedSeconds = Math.max(0, (Date.now() - startedAt) / 1000);
305
- const elapsedText = elapsedSeconds < 10 ? `${elapsedSeconds.toFixed(1)}s` : `${Math.floor(elapsedSeconds)}s`;
306
- return (_jsxs(Text, { color: inkColors.textSecondary, children: [" ", orbitDots(frame), " ", colors.gray(label), " ", colors.gray(elapsedText)] }));
307
- });
308
284
  export function Repl({ apiKey, cwd, onQuit }) {
309
285
  const { rows: termRows, columns: termColumns } = useTerminalSize();
310
286
  // Big ASCII art logo for ideacode
@@ -403,6 +379,10 @@ export function Repl({ apiKey, cwd, onQuit }) {
403
379
  }, [cwd, onQuit]);
404
380
  const [loading, setLoading] = useState(false);
405
381
  const [loadingLabel, setLoadingLabel] = useState("Thinking…");
382
+ const loadingActiveRef = useRef(false);
383
+ const loadingLabelRef = useRef(loadingLabel);
384
+ const loadingFooterLinesRef = useRef(2);
385
+ const loadingRenderRef = useRef(null);
406
386
  const cursorBlinkOn = true;
407
387
  const [showPalette, setShowPalette] = useState(false);
408
388
  const [paletteIndex, setPaletteIndex] = useState(0);
@@ -428,6 +408,55 @@ export function Repl({ apiKey, cwd, onQuit }) {
428
408
  process.stdout.write("\x1b[?1006l\x1b[?1000l");
429
409
  };
430
410
  }, []);
411
+ useEffect(() => {
412
+ loadingActiveRef.current = loading;
413
+ loadingLabelRef.current = loadingLabel;
414
+ if (!process.stdout.isTTY)
415
+ return;
416
+ const clearLoadingLine = () => {
417
+ const up = Math.max(1, loadingFooterLinesRef.current);
418
+ try {
419
+ writeSync(process.stdout.fd, `\x1b7\x1b[${up}A\r\x1b[2K\x1b8`);
420
+ }
421
+ catch {
422
+ // Best effort only.
423
+ }
424
+ };
425
+ if (!loading) {
426
+ if (loadingRenderRef.current) {
427
+ clearInterval(loadingRenderRef.current);
428
+ loadingRenderRef.current = null;
429
+ }
430
+ clearLoadingLine();
431
+ return;
432
+ }
433
+ const startedAt = Date.now();
434
+ let frame = 0;
435
+ const renderTick = () => {
436
+ if (!loadingActiveRef.current || !process.stdout.isTTY)
437
+ return;
438
+ const elapsedSeconds = Math.max(0, (Date.now() - startedAt) / 1000);
439
+ const elapsedText = elapsedSeconds < 10 ? `${elapsedSeconds.toFixed(1)}s` : `${Math.floor(elapsedSeconds)}s`;
440
+ const line = ` ${orbitDots(frame)} ${colors.gray(loadingLabelRef.current)} ${colors.gray(elapsedText)}`;
441
+ const up = Math.max(1, loadingFooterLinesRef.current);
442
+ try {
443
+ writeSync(process.stdout.fd, `\x1b7\x1b[${up}A\r\x1b[2K${line}\x1b8`);
444
+ }
445
+ catch {
446
+ // Best effort only.
447
+ }
448
+ frame = (frame + 1) % 6;
449
+ };
450
+ renderTick();
451
+ loadingRenderRef.current = setInterval(renderTick, LOADING_TICK_MS);
452
+ return () => {
453
+ if (loadingRenderRef.current) {
454
+ clearInterval(loadingRenderRef.current);
455
+ loadingRenderRef.current = null;
456
+ }
457
+ clearLoadingLine();
458
+ };
459
+ }, [loading, loadingLabel]);
431
460
  const estimatedTokens = useMemo(() => estimateTokens(messages, undefined), [messages]);
432
461
  const contextWindowK = useMemo(() => {
433
462
  const ctx = modelList.find((m) => m.id === currentModel)?.context_length;
@@ -1173,7 +1202,8 @@ export function Repl({ apiKey, cwd, onQuit }) {
1173
1202
  return (_jsxs(Box, { flexDirection: "column", height: termRows, overflow: "hidden", children: [_jsx(Box, { height: topPad }), _jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: leftPad }), _jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: inkColors.primary, paddingX: 2, paddingY: 1, width: paletteModalWidth, minHeight: paletteModalHeight, children: [_jsx(Text, { bold: true, children: " Command palette " }), COMMANDS.map((c, i) => (_jsxs(Text, { color: i === paletteIndex ? inkColors.primary : undefined, children: [i === paletteIndex ? "› " : " ", c.cmd, _jsxs(Text, { color: inkColors.textSecondary, children: [" \u2014 ", c.desc] })] }, c.cmd))), _jsxs(Text, { color: paletteIndex === COMMANDS.length ? inkColors.primary : undefined, children: [paletteIndex === COMMANDS.length ? "› " : " ", "Cancel (Esc)"] }), _jsx(Text, { color: inkColors.textSecondary, children: " \u2191/\u2193 select, Enter confirm, Esc close " })] })] }), _jsx(Box, { flexGrow: 1 })] }));
1174
1203
  }
1175
1204
  const footerLines = suggestionBoxLines + 1 + stableInputLineCount;
1176
- return (_jsxs(Box, { flexDirection: "column", height: termRows, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, minHeight: 0, overflow: "hidden", children: [_jsx(LogViewport, { lines: visibleLogLines, startIndex: logStartIndex, height: logViewportHeight }), _jsx(Box, { flexDirection: "row", marginTop: 1, marginBottom: 0, children: _jsx(LoadingStatus, { active: loading, label: loadingLabel }) })] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, height: footerLines, children: [showSlashSuggestions && (_jsxs(Box, { flexDirection: "column", marginBottom: 0, paddingLeft: 2, borderStyle: "single", borderColor: inkColors.textDisabled, children: [filteredSlashCommands.length === 0 ? (_jsx(Text, { color: inkColors.textSecondary, children: " No match " })) : ([...filteredSlashCommands].reverse().map((c, rev) => {
1205
+ loadingFooterLinesRef.current = footerLines;
1206
+ return (_jsxs(Box, { flexDirection: "column", height: termRows, overflow: "hidden", children: [_jsxs(Box, { flexDirection: "column", flexGrow: 1, minHeight: 0, overflow: "hidden", children: [_jsx(LogViewport, { lines: visibleLogLines, startIndex: logStartIndex, height: logViewportHeight }), _jsx(Box, { flexDirection: "row", marginTop: 1, marginBottom: 0, children: _jsx(Text, { color: inkColors.textSecondary, children: "\u00A0" }) })] }), _jsxs(Box, { flexDirection: "column", flexShrink: 0, height: footerLines, children: [showSlashSuggestions && (_jsxs(Box, { flexDirection: "column", marginBottom: 0, paddingLeft: 2, borderStyle: "single", borderColor: inkColors.textDisabled, children: [filteredSlashCommands.length === 0 ? (_jsx(Text, { color: inkColors.textSecondary, children: " No match " })) : ([...filteredSlashCommands].reverse().map((c, rev) => {
1177
1207
  const i = filteredSlashCommands.length - 1 - rev;
1178
1208
  return (_jsxs(Text, { color: i === clampedSlashIndex ? inkColors.primary : undefined, children: [i === clampedSlashIndex ? "› " : " ", c.cmd, _jsxs(Text, { color: inkColors.textSecondary, children: [" \u2014 ", c.desc] })] }, c.cmd));
1179
1209
  })), _jsx(Text, { color: inkColors.textSecondary, children: " Commands (\u2191/\u2193 select, Enter run, Esc clear) " })] })), cursorInAtSegment && !showSlashSuggestions && (_jsxs(Box, { flexDirection: "column", marginBottom: 0, paddingLeft: 2, borderStyle: "single", borderColor: inkColors.textDisabled, children: [filteredFilePaths.length === 0 ? (_jsxs(Text, { color: inkColors.textSecondary, children: [" ", hasCharsAfterAt ? "No match" : "Type to search files", " "] })) : ([...filteredFilePaths].reverse().map((p, rev) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ideacode",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "CLI TUI for AI agents via OpenRouter — agentic loop, tools, markdown",
5
5
  "type": "module",
6
6
  "repository": {