refactor atom components to use .withProps() subcomponents and extract inline code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-03-24 10:44:06 -04:00
commit 04c23094a4
7 changed files with 168 additions and 82 deletions

View file

@ -16,11 +16,31 @@ function formatJson(content: string): string {
}
}
function buildDataUri(mimeType: string, content: string): string {
return `data:${mimeType};base64,${content}`;
}
function formatContent(type: "text" | "json", content: string): string {
return type === "json" ? formatJson(content) : content;
}
const ContentWrapper = Flex.withProps({
pos: "relative",
direction: "column",
});
const CopyOverlay = Flex.withProps({
pos: "absolute",
top: 4,
right: 4,
});
const PreviewImage = Image.withProps({
alt: "Content preview",
maw: 400,
radius: "md",
});
export function ContentViewer({
type,
content,
@ -34,26 +54,21 @@ export function ContentViewer({
{(type === "json" || type === "text") && (
<ContentWrapper>
<Code block p={36}>
{type === "json" ? formatJson(content) : content}
{formatContent(type, content)}
</Code>
{showCopy && (
<Flex pos="absolute" top={4} right={4}>
<CopyOverlay>
<CopyButton value={content} />
</Flex>
</CopyOverlay>
)}
</ContentWrapper>
)}
{type === "image" && (
<Image
src={`data:${mimeType || "image/png"};base64,${content}`}
alt="Content preview"
maw={400}
radius="md"
/>
<PreviewImage src={buildDataUri(mimeType || "image/png", content)} />
)}
{type === "audio" && (
<audio controls>
<source src={`data:${mimeType || "audio/wav"};base64,${content}`} />
<source src={buildDataUri(mimeType || "audio/wav", content)} />
</audio>
)}
</Stack>

View file

@ -9,6 +9,46 @@ export interface InlineErrorProps {
docLink?: string;
}
const HeaderRow = Group.withProps({
justify: "space-between",
wrap: "nowrap",
});
const MessageGroup = Group.withProps({
gap: "sm",
wrap: "nowrap",
});
const ErrorMessage = Text.withProps({
size: "sm",
fw: 500,
c: "red",
});
const RetryText = Text.withProps({
size: "sm",
c: "dimmed",
});
const ExpandButton = Button.withProps({
variant: "transparent",
size: "compact-xs",
c: "var(--inspector-text-primary)",
});
const DocLinkButton = Button.withProps({
variant: "light",
size: "xs",
component: "a",
target: "_blank",
});
function formatRetryLabel(retryCount: number, maxRetries?: number): string {
return maxRetries !== undefined
? `Retry attempt ${retryCount} of ${maxRetries}`
: `Retry attempt ${retryCount}`;
}
export function InlineError({
message,
details,
@ -22,42 +62,26 @@ export function InlineError({
return (
<Alert color="red" variant="light">
<Stack gap="xs">
<Group justify="space-between" wrap="nowrap">
<Group gap="sm" wrap="nowrap">
<Text size="sm" fw={500} c="red">
{message}
</Text>
<HeaderRow>
<MessageGroup>
<ErrorMessage>{message}</ErrorMessage>
{retryCount !== undefined && (
<Text size="sm" c="dimmed">
Retry attempt {retryCount}
{maxRetries !== undefined && ` of ${maxRetries}`}
</Text>
<RetryText>{formatRetryLabel(retryCount, maxRetries)}</RetryText>
)}
</Group>
</MessageGroup>
{hasExpandable && (
<Button
variant="transparent"
size="compact-xs"
c="var(--inspector-text-primary)"
onClick={() => setExpanded((v) => !v)}
>
<ExpandButton onClick={() => setExpanded((v) => !v)}>
{expanded ? "Show less" : "Show more"}
</Button>
</ExpandButton>
)}
</Group>
</HeaderRow>
<Collapse in={expanded}>
<Stack gap="xs">
{details && <Text size="sm">{details}</Text>}
{docLink && (
<Button
variant="light"
size="xs"
component="a"
href={docLink}
target="_blank"
>
<DocLinkButton href={docLink}>
View Troubleshooting Guide
</Button>
</DocLinkButton>
)}
</Stack>
</Collapse>

View file

@ -1,17 +1,27 @@
import { Button, Flex, Group, Text } from "@mantine/core";
import { Button, Group, Paper, Text } from "@mantine/core";
export interface ListChangedIndicatorProps {
visible: boolean;
onRefresh: () => void;
}
const Dot = Flex.withProps({
const Dot = Paper.withProps({
w: 8,
h: 8,
style: { borderRadius: "50%" },
radius: "xl",
bg: "var(--inspector-status-connecting)",
});
const UpdateLabel = Text.withProps({
size: "sm",
c: "dimmed",
});
const RefreshButton = Button.withProps({
size: "xs",
variant: "light",
});
export function ListChangedIndicator({
visible,
onRefresh,
@ -21,12 +31,8 @@ export function ListChangedIndicator({
return (
<Group gap="xs">
<Dot />
<Text size="sm" c="dimmed">
List updated
</Text>
<Button size="xs" variant="light" onClick={onRefresh}>
↻ Refresh
</Button>
<UpdateLabel>List updated</UpdateLabel>
<RefreshButton onClick={onRefresh}>↻ Refresh</RefreshButton>
</Group>
);
}

View file

@ -54,24 +54,37 @@ function isBoldLevel(level: LogLevel): boolean {
return level === "alert" || level === "emergency";
}
function getBoldWeight(level: LogLevel): number | undefined {
return isBoldLevel(level) ? 700 : undefined;
}
function formatLogger(logger: string): string {
return `[${logger}]`;
}
const TimestampText = Text.withProps({
size: "sm",
ff: "monospace",
c: "dimmed",
});
const LoggerText = Text.withProps({
size: "xs",
c: "dimmed",
});
export function LogEntry({ timestamp, level, message, logger }: LogEntryProps) {
return (
<Group gap="sm" wrap="nowrap">
<Text size="sm" ff="monospace" c="dimmed">
{timestamp}
</Text>
<TimestampText>{timestamp}</TimestampText>
<Badge
color={levelBadgeColor[level]}
variant={levelBadgeVariant[level]}
fw={isBoldLevel(level) ? 700 : undefined}
fw={getBoldWeight(level)}
>
{level}
</Badge>
{logger && (
<Text size="xs" c="dimmed">
[{logger}]
</Text>
)}
{logger && <LoggerText>{formatLogger(logger)}</LoggerText>}
<Text size="sm" c={levelMessageColor[level]}>
{message}
</Text>

View file

@ -9,6 +9,35 @@ export interface MessageBubbleProps {
audioContent?: { data: string; mimeType: string };
}
function buildDataUri(mimeType: string, data: string): string {
return `data:${mimeType};base64,${data}`;
}
function formatRoleLabel(index: number, role: string): string {
return `[${index}] role: ${role}`;
}
function formatQuotedContent(content: string): string {
return `"${content}"`;
}
const BubbleContainer = Paper.withProps({
p: "md",
radius: "md",
withBorder: true,
});
const RoleLabel = Text.withProps({
size: "xs",
c: "dimmed",
});
const PreviewImage = Image.withProps({
maw: 300,
radius: "sm",
mt: "xs",
});
export function MessageBubble({
index,
role,
@ -17,32 +46,27 @@ export function MessageBubble({
audioContent,
}: MessageBubbleProps) {
return (
<Paper p="md" radius="md" withBorder>
<BubbleContainer>
<Stack gap="xs">
<Group justify="space-between">
<Text size="xs" c="dimmed">
[{index}] role: {role}
</Text>
<RoleLabel>{formatRoleLabel(index, role)}</RoleLabel>
<CopyButton value={content} />
</Group>
<Text size="sm">&quot;{content}&quot;</Text>
<Text size="sm">{formatQuotedContent(content)}</Text>
{imageContent && (
<Image
src={`data:${imageContent.mimeType};base64,${imageContent.data}`}
maw={300}
radius="sm"
mt="xs"
<PreviewImage
src={buildDataUri(imageContent.mimeType, imageContent.data)}
/>
)}
{audioContent && (
<audio controls>
<source
src={`data:${audioContent.mimeType};base64,${audioContent.data}`}
src={buildDataUri(audioContent.mimeType, audioContent.data)}
type={audioContent.mimeType}
/>
</audio>
)}
</Stack>
</Paper>
</BubbleContainer>
);
}

View file

@ -6,6 +6,20 @@ export interface ProgressDisplayProps {
elapsed?: string;
}
function formatPercent(progress: number): string {
return `${progress}%`;
}
const ProgressLabel = Text.withProps({
size: "sm",
c: "dimmed",
});
const ElapsedText = Text.withProps({
size: "xs",
c: "dimmed",
});
export function ProgressDisplay({
progress,
description,
@ -14,21 +28,11 @@ export function ProgressDisplay({
return (
<Stack gap="xs">
<Group justify="space-between">
{description && (
<Text size="sm" c="dimmed">
{description}
</Text>
)}
<Text size="sm" c="dimmed">
{progress}%
</Text>
{description && <ProgressLabel>{description}</ProgressLabel>}
<ProgressLabel>{formatPercent(progress)}</ProgressLabel>
</Group>
<Progress value={progress} size="sm" />
{elapsed && (
<Text size="xs" c="dimmed">
{elapsed}
</Text>
)}
{elapsed && <ElapsedText>{elapsed}</ElapsedText>}
</Stack>
);
}

View file

@ -1,4 +1,4 @@
import { Flex, Group, Text } from "@mantine/core";
import { Group, Paper, Text } from "@mantine/core";
export interface StatusIndicatorProps {
status: "connected" | "connecting" | "disconnected" | "failed";
@ -30,10 +30,10 @@ function getLabel(
}
}
const Dot = Flex.withProps({
const Dot = Paper.withProps({
w: 10,
h: 10,
style: { borderRadius: "50%" },
radius: "xl",
});
export function StatusIndicator({