From a7a9d57c07cfecccac82e8a420afe4a13edd8bab Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Fri, 25 Sep 2026 20:11:37 -0700 Subject: [PATCH 1/5] refactor: accept undefined on optional props for exactOptionalPropertyTypes --- src/hooks/use-controllable-state.ts | 4 ++-- src/hooks/use-dismissable-popover.ts | 4 ++-- src/ui/agent-turn.tsx | 2 +- src/ui/artifact-notice.tsx | 2 +- src/ui/chart-frame.tsx | 2 +- src/ui/dialog.tsx | 2 +- src/ui/message-bubble.tsx | 4 ++-- src/ui/parts-renderer.tsx | 4 ++-- src/ui/provider-mark.tsx | 2 +- src/ui/reasoning-block.tsx | 2 +- src/ui/sidebar-item-row.tsx | 2 +- src/ui/tooltip.tsx | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/hooks/use-controllable-state.ts b/src/hooks/use-controllable-state.ts index c21330df..725fd65f 100644 --- a/src/hooks/use-controllable-state.ts +++ b/src/hooks/use-controllable-state.ts @@ -4,10 +4,10 @@ export type ControllableStateAction = T | ((previous: T) => T); export type UseControllableStateOptions = { /** Controlled value. Supply this with `onChange` to lift state to a parent. */ - readonly value?: T; + readonly value?: T | undefined; /** Seeds the hook's own state when `value` is not supplied. */ readonly defaultValue: T; - readonly onChange?: (value: T) => void; + readonly onChange?: ((value: T) => void) | undefined; /** Identifies the caller in the dev-mode controlled/uncontrolled warning below. */ readonly name: string; }; diff --git a/src/hooks/use-dismissable-popover.ts b/src/hooks/use-dismissable-popover.ts index 633ec513..4fa40a77 100644 --- a/src/hooks/use-dismissable-popover.ts +++ b/src/hooks/use-dismissable-popover.ts @@ -7,8 +7,8 @@ import { export type UseDismissablePopoverOptions = { /** Controlled open flag. Supply this with `onOpenChange` to lift state to a parent. */ - readonly open?: boolean; - readonly onOpenChange?: (open: boolean) => void; + readonly open?: boolean | undefined; + readonly onOpenChange?: ((open: boolean) => void) | undefined; /** Seeds the hook's own state when `open` is not supplied. */ readonly defaultOpen?: boolean; /** Escape closes the popover and returns focus to the trigger. Default `true`. */ diff --git a/src/ui/agent-turn.tsx b/src/ui/agent-turn.tsx index c47dd88c..9d60cdf3 100644 --- a/src/ui/agent-turn.tsx +++ b/src/ui/agent-turn.tsx @@ -19,7 +19,7 @@ export type AgentTurnProps = { readonly identity: AgentIdentity; /** Rich body for the answer — a markdown renderer. */ readonly children?: ReactNode; - readonly now?: number; + readonly now?: number | undefined; /** e.g. "Thought for 4s". Shown on the reasoning block once it stops streaming. */ readonly reasoningDurationLabel?: string; readonly className?: string; diff --git a/src/ui/artifact-notice.tsx b/src/ui/artifact-notice.tsx index 3f121165..cf202191 100644 --- a/src/ui/artifact-notice.tsx +++ b/src/ui/artifact-notice.tsx @@ -21,7 +21,7 @@ export function ArtifactNotice({ /** Overrides the pending/empty wording — use for a specific failure. */ message, }: { - readonly pending?: boolean; + readonly pending?: boolean | undefined; readonly message?: string; }) { if (message !== undefined) { diff --git a/src/ui/chart-frame.tsx b/src/ui/chart-frame.tsx index 9385cee4..377db4cd 100644 --- a/src/ui/chart-frame.tsx +++ b/src/ui/chart-frame.tsx @@ -42,7 +42,7 @@ export type ChartFrameProps = { readonly table: ChartTable; /** The plot itself — an ``. */ readonly children: ReactNode; - readonly className?: string; + readonly className?: string | undefined; }; /** diff --git a/src/ui/dialog.tsx b/src/ui/dialog.tsx index 538c4cfa..2ed42c43 100644 --- a/src/ui/dialog.tsx +++ b/src/ui/dialog.tsx @@ -54,7 +54,7 @@ export function DialogContent({ side = "center", ...props }: React.ComponentProps & { - side?: DialogSide; + side?: DialogSide | undefined; }) { return ( diff --git a/src/ui/message-bubble.tsx b/src/ui/message-bubble.tsx index 02d0c136..11f9995e 100644 --- a/src/ui/message-bubble.tsx +++ b/src/ui/message-bubble.tsx @@ -8,8 +8,8 @@ import { cn } from "../lib/utils.js"; export type MessageBubbleProps = { readonly message: ChatMessage; /** Retry affordance for a failed send. Omit and a failure is only reported. */ - readonly onRetry?: () => void; - readonly now?: number; + readonly onRetry?: (() => void) | undefined; + readonly now?: number | undefined; /** Rich body — a markdown renderer. Defaults to the message's plain text. */ readonly children?: ReactNode; readonly className?: string; diff --git a/src/ui/parts-renderer.tsx b/src/ui/parts-renderer.tsx index fd40205f..36adf7d4 100644 --- a/src/ui/parts-renderer.tsx +++ b/src/ui/parts-renderer.tsx @@ -76,7 +76,7 @@ function ReasoningPartView({ durationMs, }: { text: string; - durationMs?: number; + durationMs?: number | undefined; }) { const [open, setOpen] = useState(false); if (text.trim().length === 0) return null; @@ -132,7 +132,7 @@ function FilePartView({ }: { name: string; mediaType: string; - url?: string; + url?: string | undefined; }) { const content = ( <> diff --git a/src/ui/provider-mark.tsx b/src/ui/provider-mark.tsx index 13bffece..c0055bc5 100644 --- a/src/ui/provider-mark.tsx +++ b/src/ui/provider-mark.tsx @@ -17,7 +17,7 @@ export type ProviderMarkProps = { */ readonly logo?: ReactNode; readonly size?: "sm" | "md"; - readonly className?: string; + readonly className?: string | undefined; }; const SIZE_CLASS = { diff --git a/src/ui/reasoning-block.tsx b/src/ui/reasoning-block.tsx index 17ebada4..c19ee730 100644 --- a/src/ui/reasoning-block.tsx +++ b/src/ui/reasoning-block.tsx @@ -15,7 +15,7 @@ export type ReasoningBlockProps = { /** Summary once idle when no `durationLabel` is supplied. Defaults to "Thinking". */ readonly idleLabel?: string; /** e.g. "Thought for 4s". Becomes the summary once reasoning has stopped streaming. */ - readonly durationLabel?: string; + readonly durationLabel?: string | undefined; readonly defaultOpen?: boolean; /** * Controlled open state. Pair with `onOpenChange` to lift it to a parent — diff --git a/src/ui/sidebar-item-row.tsx b/src/ui/sidebar-item-row.tsx index 0be312a2..80f87af0 100644 --- a/src/ui/sidebar-item-row.tsx +++ b/src/ui/sidebar-item-row.tsx @@ -20,7 +20,7 @@ export type SidebarItemRowProps = { * one more frame (~150ms) so the leave transition gets to play. List * membership is the consumer's data — nothing here manages it for you. */ - readonly leaving?: boolean; + readonly leaving?: boolean | undefined; readonly onSelect?: () => void; readonly className?: string; }; diff --git a/src/ui/tooltip.tsx b/src/ui/tooltip.tsx index 58971203..ca95cb56 100644 --- a/src/ui/tooltip.tsx +++ b/src/ui/tooltip.tsx @@ -34,7 +34,7 @@ export function InfoTooltip({ }: InfoTooltipProps) { return ( - +