import type { ReactNode } from "react"; import { t } from "../../i18n"; import { blendHex } from "../../theme/colors"; import { useThemeColors } from "../../theme/theme-context"; import { Box, Text, TextAttributes } from "../../ui"; import { truncateToDisplayWidth } from "../../utils/format"; import { headerCase } from "./header-case"; export { Prose } from "./prose"; export type { ProseProps } from "./prose"; export interface SectionHeadingProps { title: string; marginTop?: number; width?: number; wrap?: boolean; } export function SectionHeading({ title, marginTop = 0, width, wrap = false }: SectionHeadingProps) { const colors = useThemeColors(); return ( {wrap || width === undefined ? headerCase(t(title)) : truncateToDisplayWidth(headerCase(t(title)), width)} ); } export interface SectionProps extends SectionHeadingProps { children: ReactNode; } export function Section({ title, children, width, marginTop = 1, wrap }: SectionProps) { return ( {children} ); } export interface KeyValueRowProps { label: string; value: string; detail?: string; color?: string; width?: number; labelWidth?: number; emphasis?: boolean; } /** Aligned labels and values; a bounded row reserves room for an optional detail. */ export function KeyValueRow({ label, value, detail, color, width, labelWidth, emphasis = true }: KeyValueRowProps) { const colors = useThemeColors(); const rowWidth = width === undefined ? undefined : Math.max(0, Math.floor(width)); const preferredLabelWidth = Math.max(0, labelWidth ?? (rowWidth === undefined ? 14 : Math.min(12, Math.max(8, Math.floor(rowWidth * 0.32))))); const labelColumns = rowWidth === undefined ? preferredLabelWidth : Math.min(preferredLabelWidth, Math.max(0, rowWidth - 1)); const availableWidth = rowWidth === undefined ? undefined : rowWidth - labelColumns; const valueWidth = availableWidth === undefined ? undefined : detail ? Math.min(availableWidth, Math.max(8, Math.floor(rowWidth! * 0.42))) : availableWidth; const detailWidth = availableWidth === undefined ? undefined : availableWidth - valueWidth!; return ( {t(label)} {valueWidth === undefined ? value : truncateToDisplayWidth(value, valueWidth)} {detail && (detailWidth === undefined || detailWidth > 0) && ( {detailWidth === undefined ? ` ${detail}` : truncateToDisplayWidth(detail, detailWidth)} )} ); } export interface BadgeProps { label: string; tone?: "neutral" | "accent" | "positive" | "negative" | "warning"; /** For a domain scale, such as a sentiment score or chart series. */ color?: string; variant?: "subtle" | "solid"; } export function Badge({ label, tone = "neutral", color, variant = "subtle" }: BadgeProps) { const colors = useThemeColors(); const accent = color ?? (tone === "neutral" ? colors.textDim : tone === "accent" ? colors.borderFocused : colors[tone]); const solid = variant === "solid"; const neutral = solid && tone === "neutral" && !color; return ( {t(label)} ); } export interface DividerProps { width?: number | `${number}%`; height?: number | `${number}%`; orientation?: "horizontal" | "vertical"; } /** Host borders draw terminal rules and real CSS borders without text glyphs in the DOM. */ export function Divider({ width, height, orientation = "horizontal" }: DividerProps) { const colors = useThemeColors(); const vertical = orientation === "vertical"; return ( ); }