summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/components/ConsolePatcher.tsx
blob: 7070fbe4ad5739fad759269ec1eb501454517c6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import React, { useState, useEffect, Key } from 'react';
import { Box, Text } from 'ink';
import util from 'util';

interface ConsoleMessage {
  id: Key;
  type: 'log' | 'warn' | 'error';
  content: string;
}

// Using a module-level counter for unique IDs.
// This ensures IDs are unique across messages.
let messageIdCounter = 0;

export const ConsoleOutput: React.FC = () => {
  const [messages, setMessages] = useState<ConsoleMessage[]>([]);

  useEffect(() => {
    const originalConsoleLog = console.log;
    const originalConsoleWarn = console.warn;
    const originalConsoleError = console.error;

    const formatArgs = (args: unknown[]): string => util.format(...args);
    const addMessage = (type: 'log' | 'warn' | 'error', args: unknown[]) => {
      setMessages((prevMessages) => [
        ...prevMessages,
        {
          id: `console-msg-${messageIdCounter++}`,
          type,
          content: formatArgs(args),
        },
      ]);
    };

    // It's patching time
    console.log = (...args: unknown[]) => addMessage('log', args);
    console.warn = (...args: unknown[]) => addMessage('warn', args);
    console.error = (...args: unknown[]) => addMessage('error', args);

    return () => {
      console.log = originalConsoleLog;
      console.warn = originalConsoleWarn;
      console.error = originalConsoleError;
    };
  }, []);

  return (
    <Box flexDirection="column">
      {messages.map((msg) => {
        const textProps: { color?: string } = {};
        let prefix = '';

        switch (msg.type) {
          case 'warn':
            textProps.color = 'yellow';
            prefix = 'WARN: ';
            break;
          case 'error':
            textProps.color = 'red';
            prefix = 'ERROR: ';
            break;
          case 'log':
          default:
            prefix = 'LOG: ';
            break;
        }

        return (
          <Box key={msg.id}>
            <Text {...textProps}>
              {prefix}
              {msg.content}
            </Text>
          </Box>
        );
      })}
    </Box>
  );
};