+ );
+}
diff --git a/src/components/PillSelect/index.ts b/src/components/PillSelect/index.ts
new file mode 100644
index 00000000..0d573a1a
--- /dev/null
+++ b/src/components/PillSelect/index.ts
@@ -0,0 +1,5 @@
+export {
+ PillSelect,
+ type PillSelectProps,
+ type PillSelectOption,
+} from './PillSelect';
diff --git a/src/index.ts b/src/index.ts
index bf9ba28c..2fb8c2fe 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -139,6 +139,7 @@ export * from './components/ScheduleCalendar';
export * from './components/SchedulePicker';
export * from './components/ScrollArea';
export * from './components/Select';
+export * from './components/PillSelect';
export * from './components/ServiceAccordion';
export * from './components/ServiceBadge';
export * from './components/ServiceCard';
From 6d802581b1b9e907f454a91075c8dafa05b771c4 Mon Sep 17 00:00:00 2001
From: Aditya Damerla <75409196+aditya-damerla128@users.noreply.github.com>
Date: Thu, 2 Jul 2026 15:41:06 -0400
Subject: [PATCH 02/12] feat: pill-first tool call, hide flag, safelist +
review fixes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- MCPToolCall: default shows only the status pill; clicking it reveals the
whole box (result, links, raw params) in one click with a "Show details"
tooltip. Input summary now rides in the pill while running
("Creating patient · John Smith"); success shows duration instead.
- Add `hidden` prop to turn the tool-call display off entirely (mirrors the
Ozwell widget debug flag).
- tailwind-preset: safelist the AI pill arbitrary utilities, the violet
thinking-pill classes, and the green/red status pill colors so Tailwind 3
consumers get them.
- CollapsiblePill: don't mutate internal state when controlled; add optional
title (tooltip).
- PillSelect: no trailing colon when empty; disable the pill when there are
no options.
- Stories: add PillOnly and Hidden; refresh control docs.
---
src/components/AI/CollapsiblePill.tsx | 10 +-
src/components/AI/MCPToolCall.stories.tsx | 34 +++-
src/components/AI/MCPToolCall.tsx | 221 +++++++++++-----------
src/components/PillSelect/PillSelect.tsx | 11 +-
src/tailwind-preset.ts | 47 +++++
5 files changed, 201 insertions(+), 122 deletions(-)
diff --git a/src/components/AI/CollapsiblePill.tsx b/src/components/AI/CollapsiblePill.tsx
index 5ac58ff5..a780caf8 100644
--- a/src/components/AI/CollapsiblePill.tsx
+++ b/src/components/AI/CollapsiblePill.tsx
@@ -11,6 +11,8 @@ export interface CollapsiblePillProps {
onOpenChange?: (open: boolean) => void;
pillClassName?: string;
className?: string;
+ /** Native tooltip shown on hover (e.g. "Show details") */
+ title?: string;
children?: React.ReactNode;
}
@@ -23,14 +25,17 @@ export function CollapsiblePill({
onOpenChange,
pillClassName,
className,
+ title,
children,
}: CollapsiblePillProps) {
+ const isControlled = controlledOpen !== undefined;
const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
- const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen;
+ const isOpen = isControlled ? controlledOpen : internalOpen;
const toggle = () => {
const next = !isOpen;
- setInternalOpen(next);
+ // Only own the state when uncontrolled; a controlled consumer drives it.
+ if (!isControlled) setInternalOpen(next);
onOpenChange?.(next);
};
@@ -40,6 +45,7 @@ export function CollapsiblePill({
type="button"
onClick={toggle}
aria-expanded={isOpen}
+ title={title}
className={cn(
'inline-flex items-center gap-1.5 border font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1',
density === 'condensed'
diff --git a/src/components/AI/MCPToolCall.stories.tsx b/src/components/AI/MCPToolCall.stories.tsx
index 27f8922d..80e31873 100644
--- a/src/components/AI/MCPToolCall.stories.tsx
+++ b/src/components/AI/MCPToolCall.stories.tsx
@@ -52,13 +52,14 @@ const meta: Meta = {
},
collapsible: {
control: 'boolean',
- description: 'Whether the technical details can be collapsed/expanded.',
+ description:
+ 'Whether the raw parameters can be collapsed/expanded (the "Show details" toggle inside the box).',
table: { defaultValue: { summary: 'true' } },
},
defaultCollapsed: {
control: 'boolean',
description:
- 'Initial collapsed state. `true` hides the technical details by default.',
+ 'Initial state. `true` shows only the pill; the box (result + params) opens when the pill is clicked.',
table: { defaultValue: { summary: 'true' } },
},
compact: {
@@ -66,6 +67,12 @@ const meta: Meta = {
description:
'Render a condensed single-line variant, ideal for dense lists.',
},
+ hidden: {
+ control: 'boolean',
+ description:
+ 'Hide the whole tool-call display (mirrors the Ozwell widget debug flag).',
+ table: { defaultValue: { summary: 'false' } },
+ },
onLinkClick: {
action: 'onLinkClick',
description: 'Called when a resource link in the result is clicked.',
@@ -116,3 +123,26 @@ export const Compact: Story = {
collapsible: false,
},
};
+
+/**
+ * Pill only by default — the box (result + params) stays hidden until the pill
+ * is clicked. While running, the pill shows the input summary
+ * (e.g. "Creating patient · John Smith").
+ */
+export const PillOnly: Story = {
+ args: {
+ toolCall: pendingToolCall,
+ defaultCollapsed: true,
+ },
+};
+
+/**
+ * `hidden` turns the whole display off at once — the equivalent of the Ozwell
+ * widget's debug flag for toggling tool-call visibility.
+ */
+export const Hidden: Story = {
+ args: {
+ toolCall: successToolCall,
+ hidden: true,
+ },
+};
diff --git a/src/components/AI/MCPToolCall.tsx b/src/components/AI/MCPToolCall.tsx
index 72c6fb01..b8da367d 100644
--- a/src/components/AI/MCPToolCall.tsx
+++ b/src/components/AI/MCPToolCall.tsx
@@ -7,6 +7,7 @@
import * as React from 'react';
import { cva } from 'class-variance-authority';
import { cn } from '../../utils/cn';
+import { CollapsiblePill } from './CollapsiblePill';
import type {
MCPToolCall,
MCPToolStatus,
@@ -655,12 +656,14 @@ export interface MCPToolCallDisplayProps {
toolCall: MCPToolCall;
/** Compact (condensed) sizing — tighter pill and box */
compact?: boolean;
- /** Whether to show parameters (in detailed view) */
+ /** Whether to show the raw parameters ("Show details") inside the box */
showParameters?: boolean;
- /** Whether the component is collapsible to show details */
+ /** Whether the raw parameters can be collapsed/expanded inside the box */
collapsible?: boolean;
- /** Default collapsed state (true = hide details) */
+ /** Default collapsed state (true = pill only, box hidden until clicked) */
defaultCollapsed?: boolean;
+ /** Hide the whole tool-call display (mirrors Ozwell's debug flag) */
+ hidden?: boolean;
/** Callback when a resource link is clicked */
onLinkClick?: (link: MCPResourceLink) => void;
/** Additional class name */
@@ -677,11 +680,10 @@ export function MCPToolCallDisplay({
collapsible = true,
defaultCollapsed = true,
compact,
+ hidden = false,
onLinkClick,
className,
}: MCPToolCallDisplayProps) {
- const [showDetails, setShowDetails] = React.useState(!defaultCollapsed);
-
const formatDuration = (ms?: number) => {
if (!ms) return null;
if (ms < 1000) return `${ms}ms`;
@@ -690,126 +692,119 @@ export function MCPToolCallDisplay({
const friendlyName = getToolFriendlyName(toolCall.toolName, toolCall.status);
const durationLabel = formatDuration(toolCall.duration);
- const pillLabel = durationLabel
- ? `${friendlyName} · ${durationLabel}`
- : friendlyName;
const paramSummary = getParameterSummary(
toolCall.toolName,
toolCall.parameters
);
- return (
-
- {/* Status Pill Header — standalone, above the box */}
-
-
- {pillLabel}
-
+ // While running, show the input summary in the pill (e.g. "Creating patient
+ // · John Smith"). On success the name already appears as the result link, so
+ // show the duration instead to avoid duplication.
+ const detailBit =
+ toolCall.status === 'success'
+ ? durationLabel
+ : (paramSummary ?? durationLabel);
+ const pillLabel = detailBit ? `${friendlyName} · ${detailBit}` : friendlyName;
- {/* Detail box — result, links, and expandable params */}
-