2025-05-22 10:36:44 -07:00
|
|
|
|
/**
|
|
|
|
|
|
* @license
|
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2025-11-04 16:21:00 -08:00
|
|
|
|
import { useRef, useCallback } from 'react';
|
2025-08-26 00:04:53 +02:00
|
|
|
|
import type React from 'react';
|
2025-05-22 10:36:44 -07:00
|
|
|
|
import { Box, Text } from 'ink';
|
2025-09-10 10:57:07 -07:00
|
|
|
|
import { theme } from '../semantic-colors.js';
|
2025-08-26 00:04:53 +02:00
|
|
|
|
import type { ConsoleMessageItem } from '../types.js';
|
2025-11-04 16:21:00 -08:00
|
|
|
|
import {
|
|
|
|
|
|
ScrollableList,
|
|
|
|
|
|
type ScrollableListRef,
|
|
|
|
|
|
} from './shared/ScrollableList.js';
|
2025-05-22 10:36:44 -07:00
|
|
|
|
|
|
|
|
|
|
interface DetailedMessagesDisplayProps {
|
|
|
|
|
|
messages: ConsoleMessageItem[];
|
2025-06-19 20:17:23 +00:00
|
|
|
|
maxHeight: number | undefined;
|
|
|
|
|
|
width: number;
|
2025-11-04 16:21:00 -08:00
|
|
|
|
hasFocus: boolean;
|
2025-05-22 10:36:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const DetailedMessagesDisplay: React.FC<
|
|
|
|
|
|
DetailedMessagesDisplayProps
|
2025-11-04 16:21:00 -08:00
|
|
|
|
> = ({ messages, maxHeight, width, hasFocus }) => {
|
|
|
|
|
|
const scrollableListRef = useRef<ScrollableListRef<ConsoleMessageItem>>(null);
|
|
|
|
|
|
|
|
|
|
|
|
const borderAndPadding = 4;
|
|
|
|
|
|
|
|
|
|
|
|
const estimatedItemHeight = useCallback(
|
|
|
|
|
|
(index: number) => {
|
|
|
|
|
|
const msg = messages[index];
|
|
|
|
|
|
if (!msg) {
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
const iconAndSpace = 2;
|
|
|
|
|
|
const textWidth = width - borderAndPadding - iconAndSpace;
|
|
|
|
|
|
if (textWidth <= 0) {
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
const lines = Math.ceil((msg.content?.length || 1) / textWidth);
|
|
|
|
|
|
return Math.max(1, lines);
|
|
|
|
|
|
},
|
|
|
|
|
|
[width, messages],
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2025-05-22 10:36:44 -07:00
|
|
|
|
if (messages.length === 0) {
|
2025-11-04 16:21:00 -08:00
|
|
|
|
return null;
|
2025-05-22 10:36:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Box
|
|
|
|
|
|
flexDirection="column"
|
|
|
|
|
|
marginTop={1}
|
|
|
|
|
|
borderStyle="round"
|
2025-09-10 10:57:07 -07:00
|
|
|
|
borderColor={theme.border.default}
|
2025-11-04 16:21:00 -08:00
|
|
|
|
paddingLeft={1}
|
2025-06-19 20:17:23 +00:00
|
|
|
|
width={width}
|
2025-11-04 16:21:00 -08:00
|
|
|
|
height={maxHeight}
|
|
|
|
|
|
flexShrink={0}
|
|
|
|
|
|
flexGrow={0}
|
|
|
|
|
|
overflow="hidden"
|
2025-05-22 10:36:44 -07:00
|
|
|
|
>
|
|
|
|
|
|
<Box marginBottom={1}>
|
2025-09-10 10:57:07 -07:00
|
|
|
|
<Text bold color={theme.text.primary}>
|
2025-10-28 12:10:40 -07:00
|
|
|
|
Debug Console <Text color={theme.text.secondary}>(F12 to close)</Text>
|
2025-05-22 10:36:44 -07:00
|
|
|
|
</Text>
|
|
|
|
|
|
</Box>
|
2025-11-04 16:21:00 -08:00
|
|
|
|
<Box height={maxHeight} width={width - borderAndPadding}>
|
|
|
|
|
|
<ScrollableList
|
|
|
|
|
|
ref={scrollableListRef}
|
|
|
|
|
|
data={messages}
|
|
|
|
|
|
renderItem={({ item: msg }: { item: ConsoleMessageItem }) => {
|
|
|
|
|
|
let textColor = theme.text.primary;
|
|
|
|
|
|
let icon = 'ℹ'; // Information source (ℹ)
|
2025-05-22 10:36:44 -07:00
|
|
|
|
|
2025-11-04 16:21:00 -08:00
|
|
|
|
switch (msg.type) {
|
|
|
|
|
|
case 'warn':
|
|
|
|
|
|
textColor = theme.status.warning;
|
|
|
|
|
|
icon = '⚠'; // Warning sign (⚠)
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'error':
|
|
|
|
|
|
textColor = theme.status.error;
|
|
|
|
|
|
icon = '✖'; // Heavy multiplication x (✖)
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'debug':
|
|
|
|
|
|
textColor = theme.text.secondary; // Or theme.text.secondary
|
|
|
|
|
|
icon = '🔍'; // Left-pointing magnifying glass (🔍)
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'log':
|
|
|
|
|
|
default:
|
|
|
|
|
|
// Default textColor and icon are already set
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-05-22 10:36:44 -07:00
|
|
|
|
|
2025-11-04 16:21:00 -08:00
|
|
|
|
return (
|
|
|
|
|
|
<Box flexDirection="row">
|
|
|
|
|
|
<Text color={textColor}>{icon} </Text>
|
|
|
|
|
|
<Text color={textColor} wrap="wrap">
|
|
|
|
|
|
{msg.content}
|
|
|
|
|
|
{msg.count && msg.count > 1 && (
|
|
|
|
|
|
<Text color={theme.text.secondary}> (x{msg.count})</Text>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Text>
|
|
|
|
|
|
</Box>
|
|
|
|
|
|
);
|
|
|
|
|
|
}}
|
|
|
|
|
|
keyExtractor={(item, index) => `${item.content}-${index}`}
|
|
|
|
|
|
estimatedItemHeight={estimatedItemHeight}
|
|
|
|
|
|
hasFocus={hasFocus}
|
|
|
|
|
|
initialScrollIndex={Number.MAX_SAFE_INTEGER}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Box>
|
2025-05-22 10:36:44 -07:00
|
|
|
|
</Box>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|