feat(apollo-wind): Autopilot Chat pattern (spike)#899
Conversation
Part of a spike to bring shadcn's new chat component family (MessageScroller, Message, Bubble, Attachment, Marker) into apollo-wind for an "Autopilot chat" Patterns page. Message and Bubble are ported and self-contained (no new dependencies). Paused here: MessageScroller needs a new package, @shadcn/react, which this repo's pnpm minimumReleaseAge policy currently blocks (published 2026-06-30, clears the 14-day policy window around 2026-07-14). Attachment, Marker, MessageScroller, the Autopilot chat story, and Patterns sidebar ordering are not yet built.
…rimitives Completes the shadcn chat component port started with Message/Bubble: - MessageScroller (+ Provider/Viewport/Content/Item/Button) wraps the newly-added @shadcn/react package with apollo-wind styling. Handles auto-scroll-to-latest with a fade-in "scroll to latest" button once the viewport is scrolled away from the live edge. - Attachment (+ Media/Content/Title/Description/Actions/Action/Trigger/ Group) and Marker (+ Icon/Content), ported from shadcn's registry source. - Extends InputGroupAddon with block-start/block-end alignment (a toolbar row stacked below content instead of beside it), which shadcn's current InputGroup supports but apollo-wind's earlier port didn't. The container switches to a column layout via a `has-[]:` variant, forced with `!` since conditional variants and unconditional utilities on the same property aren't reconciled by class merging. - Adds the `shimmer` and `scroll-fade-x`/`scroll-fade-y` Tailwind utilities these components need, scoped to only what's used (the source registry ships direction variants and size modifiers nothing here uses). @shadcn/react was vetted (provenance, publisher, release diff, integrity) and added to minimumReleaseAgeExclude pending the 14-day quarantine window.
Assembles the ported primitives into a Patterns/Autopilot Chat page for coworker feedback, mirroring the legacy Material ApChat's Embedded/ FullScreen layout modes and header/composer structure: - Components: Message/Bubble, Attachment, Marker, and MessageScroller shown in isolation as a reference for the pieces the panel is built from. - Layout Embedded: the panel docked bottom-right over a mock host page (fixed, shadow-xl), matching the legacy demo harness's exact positioning convention (that positioning is a host-page choice, not enforced by the panel itself). - Layout Fullscreen: the same panel with its size cap and chrome stripped so it fills the page, matching the legacy FullScreen mode's approach of expanding rather than repositioning. The header (title + "Preview" badge, New chat/Settings/History/Expand- Collapse/Close actions) and composer (Attach/Resource/Agent-mode) mirror the legacy ApChat's actual structure rather than the generic shadcn demo this was initially scaffolded from. The transcript itself is a scripted, local-state conversation (no @ai-sdk/react) since this is a static design- system demo, not a wired chat backend. Known gaps, tracked but not addressed here: no unit tests yet for the new primitives (every sibling in ui/ has one), and a config-driven "Showcase" page isn't feasible until there's a real service layer behind this pattern — the legacy Showcase's controls drive an actual AutopilotChatService; ours is presentation-only.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Dependency License Review
License distribution
Excluded packages
|
There was a problem hiding this comment.
Pull request overview
This PR spikes an “Autopilot Chat” pattern in apollo-wind by porting shadcn’s newer chat primitives and composing them into Storybook pattern stories that mirror the legacy ApChat embedded/fullscreen layouts.
Changes:
- Added new chat-related UI primitives (
Attachment,Marker,Message,Bubble,MessageScroller) and exported them from the package public API. - Added Tailwind CSS utilities for scroll-fade masks and shimmer states used by the new primitives.
- Extended
InputGroupAddonwithblock-start/block-endalignment to support a 2-row composer layout, and added@shadcn/react@0.2.1as a dependency (with a workspace release-age exclusion).
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
pnpm-workspace.yaml |
Adds @shadcn/react to minimumReleaseAgeExclude with a pinned version/reason. |
pnpm-lock.yaml |
Locks @shadcn/react@0.2.1 resolution and snapshot entries. |
packages/apollo-wind/package.json |
Adds runtime dependency on @shadcn/react@0.2.1. |
packages/apollo-wind/src/styles/tailwind.consumer.css |
Introduces scroll-fade-x/y and shimmer utilities needed by the new chat primitives. |
packages/apollo-wind/src/index.ts |
Re-exports new chat primitives and reorganizes package public exports. |
packages/apollo-wind/src/components/ui/index.ts |
Adds barrel exports for the new primitives. |
packages/apollo-wind/src/components/ui/input-group.tsx |
Adds block-aligned addon support to enable multi-row input groups. |
packages/apollo-wind/src/components/ui/message.tsx |
Adds Message layout primitives (group/avatar/content/header/footer). |
packages/apollo-wind/src/components/ui/bubble.tsx |
Adds Bubble primitives and reaction positioning variants. |
packages/apollo-wind/src/components/ui/marker.tsx |
Adds marker/separator primitive used for “Today” dividers and similar labels. |
packages/apollo-wind/src/components/ui/message-scroller.tsx |
Wraps @shadcn/react/message-scroller with apollo-wind styling and exports hooks/components. |
packages/apollo-wind/src/components/ui/attachment.tsx |
Adds attachment card primitives and horizontal attachment scroller group. |
packages/apollo-wind/src/components/ui/autopilot-chat.stories.tsx |
Adds the Storybook “Patterns/Autopilot Chat” stories (components gallery + embedded/fullscreen layouts). |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
| <span | ||
| data-slot="marker-content" | ||
| className={cn( | ||
| 'min-w-0 wrap-break-word group-data-[variant=separator]/marker:flex-none group-data-[variant=separator]/marker:text-center [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground', |
| <div | ||
| data-slot="attachment-group" | ||
| className={cn( | ||
| 'flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start', |
| className={cn( | ||
| 'absolute bottom-3 left-1/2 z-10 flex -translate-x-1/2 items-center justify-center gap-1.5 rounded-full border border-border-subtle bg-card px-3 py-1.5 text-xs font-medium text-foreground shadow-md transition-opacity hover:bg-surface-overlay data-[active=false]:opacity-0', | ||
| className | ||
| )} |
| import { Slot } from '@radix-ui/react-slot'; | ||
| import { cva, type VariantProps } from 'class-variance-authority'; | ||
| import * as React from 'react'; | ||
| import { Button } from '@/components/ui/button'; | ||
| import { cn } from '@/lib'; |
| import { Slot } from '@radix-ui/react-slot'; | ||
| import { cva, type VariantProps } from 'class-variance-authority'; | ||
| import * as React from 'react'; | ||
| import { cn } from '@/lib'; | ||
|
|
| import * as React from 'react'; | ||
| import { cn } from '@/lib'; | ||
|
|
||
| const MessageGroup = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( | ||
| ({ className, ...props }, ref) => ( |
| import { Slot } from '@radix-ui/react-slot'; | ||
| import { cva, type VariantProps } from 'class-variance-authority'; | ||
| import * as React from 'react'; | ||
| import { cn } from '@/lib'; | ||
|
|
| 'use client'; | ||
|
|
||
| import { MessageScroller as MessageScrollerPrimitive } from '@shadcn/react/message-scroller'; | ||
| import { ChevronDown } from 'lucide-react'; | ||
| import * as React from 'react'; | ||
| import { cn } from '@/lib'; |
| // Block-aligned addons (toolbar rows) stack the group into a column instead of | ||
| // sitting inline. `!` is required: these must win over the unconditional height/ | ||
| // items-center utilities below regardless of Tailwind's generated rule order, | ||
| // since has-[]: variants and plain utilities aren't reconciled by class merging. | ||
| 'has-[>[data-align=block-start]]:!h-auto has-[>[data-align=block-start]]:!flex-col has-[>[data-align=block-start]]:!items-stretch', | ||
| 'has-[>[data-align=block-end]]:!h-auto has-[>[data-align=block-end]]:!flex-col has-[>[data-align=block-end]]:!items-stretch', | ||
| // Size (mirrors Input's own size scale, since Input's box chrome moves up to this wrapper) |
📊 Coverage + size by packagePer-package coverage and bundle size on this PR. New-line coverage = of the source lines this PR adds or changes, the % hit by tests.
"Coverage" is each package's own |
Adds a "Wind vs Material" page to Patterns/Autopilot Chat clarifying when to use each: apollo-wind's primitives for presentational work (mockups, custom chat UIs, no MUI/Emotion dependency), Material's ApChat for fully wired product integration (real AutopilotChatService backing models, agent modes, resources, streaming, history, mode switching). Includes a capability comparison table and links to where each lives.
| <div | ||
| ref={ref} | ||
| className={cn( | ||
| 'flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-data-[align=end]/message:justify-end', | ||
| className | ||
| )} | ||
| {...props} | ||
| /> |
| <span | ||
| data-slot="marker-content" | ||
| className={cn( | ||
| 'min-w-0 wrap-break-word group-data-[variant=separator]/marker:flex-none group-data-[variant=separator]/marker:text-center [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground', |
| <div | ||
| data-slot="attachment-group" | ||
| className={cn( | ||
| 'flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start', |
| import { Slot } from '@radix-ui/react-slot'; | ||
| import { cva, type VariantProps } from 'class-variance-authority'; | ||
| import * as React from 'react'; | ||
| import { Button } from '@/components/ui/button'; | ||
| import { cn } from '@/lib'; |
| import { Slot } from '@radix-ui/react-slot'; | ||
| import { cva, type VariantProps } from 'class-variance-authority'; | ||
| import * as React from 'react'; | ||
| import { cn } from '@/lib'; | ||
|
|
| import { Slot } from '@radix-ui/react-slot'; | ||
| import { cva, type VariantProps } from 'class-variance-authority'; | ||
| import * as React from 'react'; | ||
| import { cn } from '@/lib'; | ||
|
|
| import * as React from 'react'; | ||
| import { cn } from '@/lib'; | ||
|
|
||
| const MessageGroup = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( | ||
| ({ className, ...props }, ref) => ( |
| 'use client'; | ||
|
|
||
| import { MessageScroller as MessageScrollerPrimitive } from '@shadcn/react/message-scroller'; | ||
| import { ChevronDown } from 'lucide-react'; | ||
| import * as React from 'react'; | ||
| import { cn } from '@/lib'; | ||
|
|
||
| export { | ||
| useMessageScroller, | ||
| useMessageScrollerScrollable, | ||
| useMessageScrollerVisibility, | ||
| } from '@shadcn/react/message-scroller'; |
Summary
Spike porting shadcn's new chat component family into apollo-wind and assembling it into a Patterns page for feedback, mirroring the legacy Material
ApChat's Embedded/FullScreen layout modes and header/composer structure.Message/Bubble(already merged in a prior commit), plus newMessageScroller,Attachment, andMarker, all ported from shadcn's registry/npm package and restyled to apollo-wind's tokens.InputGroupgets realblock-start/block-endaddon support — a genuine gap vs. shadcn's current upstream API, needed for the composer's two-row (content + toolbar) layout, fixed properly rather than worked around.Components(primitives in isolation),Layout Embedded,Layout Fullscreen— the last two mirror the legacyApChat's actual mode-dependent container behavior (docked bottom-right vs. fills the page), with the header and composer restructured to match the legacy component's real structure (title + "Preview" badge, New chat/Settings/History/Expand-Collapse/Close actions; Attach/Resource/Agent-mode composer controls) rather than the generic shadcn demo this was initially scaffolded from.Known gaps (tracked, not addressed in this PR)
Attachment,Marker,MessageScroller(orMessage/Bubblefrom the prior commit) — every sibling component inpackages/apollo-wind/src/components/ui/has a.test.tsx; this breaks that pattern and should be closed before this graduates past spike status.AttachmentTrigger,AttachmentActions/AttachmentAction, andBubbleReactionsare exported but not demonstrated anywhere yet.ApChat's) isn't feasible yet — the legacy version's controls drive a realAutopilotChatService; this pattern is presentation-only with a hardcoded scripted transcript. Would need a real service layer first.packages/apollo-wind/vitest.config.ts's coverage thresholds are placed directly undercoverageinstead ofcoverage.thresholds, so Vitest 4 silently ignores them — confirmed live, package coverage sits at 38% andvitest run --coveragestill exits 0. Worth a separate fix, but retroactively affects every package, not just this one.Test plan
pnpm build,pnpm lint,pnpm format:check,tsc --noEmitall pass forapollo-wind(verified locally)Patterns → Autopilot Chatin Storybook:Components,Layout Embedded,Layout Fullscreen