diff --git a/apps/www/src/content/docs/ai-elements/chat-panel/demo.ts b/apps/www/src/content/docs/ai-elements/chat-panel/demo.ts
index 3e34072f0..4244b4107 100644
--- a/apps/www/src/content/docs/ai-elements/chat-panel/demo.ts
+++ b/apps/www/src/content/docs/ai-elements/chat-panel/demo.ts
@@ -64,7 +64,7 @@ export const preview = {
{message.role === 'user' ? (
{message.text}
) : (
- {message.text}
+ {message.text}
)}
@@ -151,6 +151,57 @@ export const controlledDemo = {
}`
};
+export const transitionDemo = {
+ type: 'code',
+ code: `function ChatPanelTransitions() {
+ const [transition, setTransition] = React.useState('morph');
+ const [mode, setMode] = React.useState('docked');
+
+ return (
+
+
+
+ {['minimal', 'morph'].map(type => (
+
+ ))}
+
+
+ {['docked', 'floating', 'minimized'].map(next => (
+
+ ))}
+
+
+
+
+
+ Switch modes with either transition to compare them. Morph tweens
+ the panel out of the box it just left; minimal eases it in from its
+ own edge.
+
+
+
+
+ Assistant
+
+
+
+
+
+
+
+
+ transition: {transition}
+
+
+
+
+
+
+
+ );
+}`
+};
+
export const boundedDragDemo = {
type: 'code',
code: `function BoundedDragChatPanel() {
diff --git a/apps/www/src/content/docs/ai-elements/chat-panel/index.mdx b/apps/www/src/content/docs/ai-elements/chat-panel/index.mdx
index cfdfe71df..7c105f172 100644
--- a/apps/www/src/content/docs/ai-elements/chat-panel/index.mdx
+++ b/apps/www/src/content/docs/ai-elements/chat-panel/index.mdx
@@ -5,7 +5,7 @@ source: packages/raystack/components/chat-panel
tag: new
---
-import { preview, controlledDemo, boundedDragDemo, resizeDemo, draggableBubbleDemo, unreadBadgeDemo } from "./demo.ts";
+import { preview, controlledDemo, transitionDemo, boundedDragDemo, resizeDemo, draggableBubbleDemo, unreadBadgeDemo } from "./demo.ts";
@@ -31,9 +31,9 @@ import { ChatPanel } from '@raystack/apsara'
```
-The panel mounts **once** in your layout and morphs between modes with pure
-CSS — no portal, no remount, so chat state (scroll position, focus, streams)
-survives every transition:
+The panel mounts **once** in your layout and switches between modes in place —
+no portal, no remount, so chat state (scroll position, focus, streams) survives
+every transition:
- **`docked`** — an in-flow sidebar (a flex sibling, like `SidePanel`) that
squeezes the main content rather than covering it.
@@ -95,12 +95,44 @@ height.
### Trigger
-The minimized-state corner bubble. Children replace the default chat icon,
+The minimized-state corner bubble. Children replace the default `CoPilotIcon`,
and `draggable` lets the bubble be dragged around the viewport — a completed
drag never triggers the restore click.
+## Mode transitions
+
+Switching mode flips the panel between in-flow and `position: fixed`, which no
+CSS property can tween, so the transform bridges the gap: the panel is rendered
+in its new mode already holding the shape it is coming out of, then eased back
+to rest. `transition` picks which shape that is.
+
+**`minimal`** (default) brings the new mode in from its own edge or corner and
+fades it up: the docked sidebar slides in from the edge it docks to, the
+floating window scales and lifts out of its corner, and the bubble grows out of
+its own. `--rs-duration-normal`.
+
+**`morph`** measures the box the panel is leaving and draws the new mode back
+over it — an inverse `translate` and `scale` off its top-left corner — so the
+panel travels and reshapes into its new place, the way a shared-layout
+animation does. Docking and popping out morph outright. The bubble is a tenth
+of the panel's size, so its two pairs cross-fade as well, and the bubble itself
+only travels — scaling it up to the panel's box would stretch its icon for the
+length of the tween. `--rs-duration-moderate`, because there is real distance to
+cover.
+
+Both use `--rs-ease-out`, so a transition is off the mark on its first frame and
+settles into place rather than easing away from a standstill.
+
+
+
+Both are skipped under `prefers-reduced-motion: reduce`, and neither runs on
+first paint — the transition needs a mode to have changed. Only the individual
+transform properties (`scale`, `translate`) are transitioned, never `transform`
+itself: dragging writes an inline `transform`, which stays instant, so a drag
+never lags the pointer and ending one never replays the transition.
+
## Examples
### Controlled mode
diff --git a/apps/www/src/content/docs/ai-elements/chat-panel/props.ts b/apps/www/src/content/docs/ai-elements/chat-panel/props.ts
index 30f16b75a..4d98ca0b0 100644
--- a/apps/www/src/content/docs/ai-elements/chat-panel/props.ts
+++ b/apps/www/src/content/docs/ai-elements/chat-panel/props.ts
@@ -87,6 +87,14 @@ export interface ChatPanelProps {
*/
dragBoundary?: HTMLElement | React.RefObject;
+ /**
+ * How a mode change animates. `"minimal"` eases the new mode in from its own
+ * edge or corner; `"morph"` measures the box the panel is leaving and tweens
+ * the new mode out of it, so the panel moves and reshapes into place.
+ * @defaultValue "minimal"
+ */
+ transition?: 'minimal' | 'morph';
+
/** Custom CSS class names. */
className?: string;
}
diff --git a/apps/www/src/content/docs/ai-elements/chat/demo.ts b/apps/www/src/content/docs/ai-elements/chat/demo.ts
index c0783ab4c..a3785adc4 100644
--- a/apps/www/src/content/docs/ai-elements/chat/demo.ts
+++ b/apps/www/src/content/docs/ai-elements/chat/demo.ts
@@ -25,7 +25,7 @@ export const preview = {
- I created the task in Design System 2 and assigned it to you.
+ I created the task in Design System 2 and assigned it to you.
@@ -102,7 +102,7 @@ export const streamingDemo = {
{message.align === 'end' ? (
{message.text}
) : (
- {message.text}
+ {message.text}
)}
diff --git a/apps/www/src/content/docs/ai-elements/message/demo.ts b/apps/www/src/content/docs/ai-elements/message/demo.ts
index 6f44dc631..cf8679fec 100644
--- a/apps/www/src/content/docs/ai-elements/message/demo.ts
+++ b/apps/www/src/content/docs/ai-elements/message/demo.ts
@@ -106,7 +106,43 @@ export const bubbleVariantsDemo = {
Bordered bubble on the base background.Accent outline bubble.Danger outline bubble.
+`
+ },
+ {
+ name: 'Ghost',
+ code: `
+ No surface at all — plain body copy.
+ Accent ghost bubble.
+ Danger ghost bubble.`
}
]
};
+
+export const ghostDemo = {
+ type: 'code',
+ code: `
+
+
+ Summarise the release notes for me.
+
+
+
+
+
+
+ Assistant
+
+
+ Three things shipped: the timeline renderer, the AI element set, and a
+ rewrite of the token docs. The ghost bubble spans the whole row, so
+ long replies read as page content rather than as a chat bubble.
+
+
+ 1:52 AM
+
+ ⧉
+
+
+`
+};
diff --git a/apps/www/src/content/docs/ai-elements/message/index.mdx b/apps/www/src/content/docs/ai-elements/message/index.mdx
index cfb89efb5..c8a96bf80 100644
--- a/apps/www/src/content/docs/ai-elements/message/index.mdx
+++ b/apps/www/src/content/docs/ai-elements/message/index.mdx
@@ -5,7 +5,7 @@ source: packages/raystack/components/message
tag: new
---
-import { preview, anatomyDemo, groupDemo, bubbleVariantsDemo } from "./demo.ts";
+import { preview, anatomyDemo, groupDemo, bubbleVariantsDemo, ghostDemo } from "./demo.ts";
@@ -72,11 +72,21 @@ appear once.
### Bubble variants
-`variant` picks the surface treatment (`solid` or `outline`) and `color` the
-palette (`neutral`, `accent` or `danger`).
+`variant` picks the surface treatment (`solid`, `outline` or `ghost`) and
+`color` the palette (`neutral`, `accent` or `danger`).
+### Ghost bubbles
+
+`variant="ghost"` removes the surface — no background, border or padding — and
+the bubble runs the full width of the message row instead of shrink-wrapping.
+It renders as plain body copy (Text at `size="small"`), which is the
+conventional treatment for assistant replies: the reader's own messages keep a
+bubble, the assistant's read as page content.
+
+
+
## Accessibility
- `Message.Actions` reveal on hover **and** on focus-within, and stay visible
diff --git a/apps/www/src/content/docs/ai-elements/message/props.ts b/apps/www/src/content/docs/ai-elements/message/props.ts
index b1f055b9d..0e51e63e4 100644
--- a/apps/www/src/content/docs/ai-elements/message/props.ts
+++ b/apps/www/src/content/docs/ai-elements/message/props.ts
@@ -13,10 +13,12 @@ export interface MessageProps {
export interface MessageBubbleProps {
/**
- * Visual style of the message surface.
+ * Visual style of the message surface. `"ghost"` drops the surface
+ * entirely — no background, border or padding — and renders the message as
+ * full-width body copy.
* @defaultValue "solid"
*/
- variant?: 'solid' | 'outline';
+ variant?: 'solid' | 'outline' | 'ghost';
/**
* Color of the message surface.
diff --git a/apps/www/src/content/docs/ai-elements/prompt-input/index.mdx b/apps/www/src/content/docs/ai-elements/prompt-input/index.mdx
index 432f924aa..ac6348a43 100644
--- a/apps/www/src/content/docs/ai-elements/prompt-input/index.mdx
+++ b/apps/www/src/content/docs/ai-elements/prompt-input/index.mdx
@@ -39,7 +39,13 @@ anything else.
### Root
-The `