claude-code-best/src/components/LogoV2/Feed.tsx
y574444354 c5c6b50c9e feat: 品牌化首页为 CoStrict 风格
- 品牌名从 Claude Code 改为 CoStrict
- 版本号更新为 v4.0.1
- 首页边框、标题、分割线、小人等元素统一改为蓝色
- 简单模式(CondensedLogo)添加蓝色圆角边框
- 小人(Clawd)替换为 CoStrict ASCII art 大字标志
- Feed 标题(Recent activity/What's new)改为蓝色
2026-04-09 14:25:15 +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>
);
}