claude-code-best/src/components/LogoV2/Feed.tsx
2026-05-01 21:39:30 +08:00

99 lines
2.8 KiB
TypeScript

import * as React from 'react';
import { Box, Text, stringWidth } from '@anthropic/ink';
import { truncate } from '../../utils/format.js';
export type FeedLine = {
text: string;
timestamp?: string;
};
export type FeedConfig = {
title: string;
lines: FeedLine[];
footer?: string;
emptyMessage?: string;
customContent?: { content: React.ReactNode; width: number };
};
type FeedProps = {
config: FeedConfig;
actualWidth: number;
};
export function calculateFeedWidth(config: FeedConfig): number {
const { title, lines, footer, emptyMessage, customContent } = config;
let maxWidth = stringWidth(title);
if (customContent !== undefined) {
maxWidth = Math.max(maxWidth, customContent.width);
} else if (lines.length === 0 && emptyMessage) {
maxWidth = Math.max(maxWidth, stringWidth(emptyMessage));
} else {
const gap = ' ';
const maxTimestampWidth = Math.max(0, ...lines.map(line => (line.timestamp ? stringWidth(line.timestamp) : 0)));
for (const line of lines) {
const timestampWidth = maxTimestampWidth > 0 ? maxTimestampWidth : 0;
const lineWidth = stringWidth(line.text) + (timestampWidth > 0 ? timestampWidth + gap.length : 0);
maxWidth = Math.max(maxWidth, lineWidth);
}
}
if (footer) {
maxWidth = Math.max(maxWidth, stringWidth(footer));
}
return maxWidth;
}
export function Feed({ config, actualWidth }: FeedProps): React.ReactNode {
const { title, lines, footer, emptyMessage, customContent } = config;
const gap = ' ';
const maxTimestampWidth = Math.max(0, ...lines.map(line => (line.timestamp ? stringWidth(line.timestamp) : 0)));
return (
<Box flexDirection="column" width={actualWidth}>
<Text bold color="claudeBlue_FOR_SYSTEM_SPINNER">
{title}
</Text>
{customContent ? (
<>
{customContent.content}
{footer && (
<Text dimColor italic>
{truncate(footer, actualWidth)}
</Text>
)}
</>
) : lines.length === 0 && emptyMessage ? (
<Text dimColor>{truncate(emptyMessage, actualWidth)}</Text>
) : (
<>
{lines.map((line, index) => {
const textWidth = Math.max(10, actualWidth - (maxTimestampWidth > 0 ? maxTimestampWidth + gap.length : 0));
return (
<Text key={index}>
{maxTimestampWidth > 0 && (
<>
<Text dimColor>{(line.timestamp || '').padEnd(maxTimestampWidth)}</Text>
{gap}
</>
)}
<Text>{truncate(line.text, textWidth)}</Text>
</Text>
);
})}
{footer && (
<Text dimColor italic>
{truncate(footer, actualWidth)}
</Text>
)}
</>
)}
</Box>
);
}