AI Panel

Pro

The assistant sidepanel: a header, a scrollable conversation and a pinned composer in one column. Drop a Chat in the body and a Prompt Input in the footer. Pure layout, so it docks in a page or drops into a Drawer.

Koala Assistant
Always here to help
What changed in the latest release?
K
Three things landed: a faster build, dark-mode fixes, and the new Prompt Input. Want the full changelog?

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add ai-panel

Usage#

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

The panel fills its container (h-full) and owns the scroll: only the body scrolls, while the header and composer stay pinned. Give it a sized frame when docking it in a page.

Anatomy#

import {  AIPanel,  AIPanelHeader,  AIPanelHeading,  AIPanelTitle,  AIPanelDescription,  AIPanelActions,  AIPanelAction,  AIPanelExpandToggle,  AIPanelBody,  AIPanelFooter,} from "@/components/ui/ai-panel"<AIPanel>  <AIPanelHeader>    <AIPanelHeading>      <AIPanelTitle />      <AIPanelDescription />    </AIPanelHeading>    <AIPanelActions>      <AIPanelExpandToggle />      <AIPanelAction />    </AIPanelActions>  </AIPanelHeader>  <AIPanelBody />  <AIPanelFooter /></AIPanel>

Window modes (split & floating)#

The panel can manage its own window, Notion/Edge-style. Beyond the embedded inline default, it has two windowed views: split (a full-height rail pinned to the right edge, the split-screen assistant) and floating (a compact window hovering bottom-right). Drop an AIPanelExpandToggle in the header to flip between them; it swaps its own icon and label with the view. These modes are non-modal: the page stays interactive, so closing is the host's call (render the panel behind a launcher). The view is controllable via view / defaultView / onViewChange.

Your app content lives here. Open the assistant as a split rail, then pop it out to a floating window.

Inside an app frame (anchor)#

By default the split and floating windows pin to the browser window (fixed, sized in dvh). When the assistant lives inside an app frame instead (a tablet window, a device mock, a dashboard pane) pass anchor="container": the panel positions itself absolute against the nearest positioned ancestor and sizes the rail and the window in % of it, so it travels and scales with the frame. Give the frame relative overflow-hidden. The AIPanelExpandToggle morphs between the two exactly as it does on the viewport, and the docked rail draws only its inner border.

The example also hands the scroll to the thread: <AIPanelBody scrollable={false}> becomes a fixed column and the Conversation filling it scrolls, so a ConversationScrollButton can float over its bottom edge. Scroll up in the thread to see it.

Case files· #4821
Koala Assistant
Docked in the frame
Summarise case #4821 for the shift briefing.
K
A stolen sedan was recovered on Route 68 at 02:10. Two suspects fled on foot; one was detained near the gas station, the other is still outstanding.
Which charges apply to the detained suspect?
K
Grand theft auto and evading on foot. The search turned up no weapon, so armed charges don't apply. Add resisting only if the arrest report says so.
Draft the report intro.
K
At approximately 02:10, units responded to a stolen vehicle alert on Route 68. Upon arrival, two occupants exited the vehicle and fled north on foot…

As an overlay (Drawer)#

Because it's pure layout, the same panel drops straight into a DrawerContent for a slide-in assistant: the Drawer brings the trigger, scrim, focus trap, and animation. Hide the Drawer's own close (showClose=false) and put a close action in the panel header instead. An overlay is never a dead end: keep a minimize to window action that pops the panel out to a floating window (and back), so the assistant can ride along while the user keeps working.

Empty state#

With no messages yet, center a greeting and a few starters in the body. The body is a plain flex region, so a centered welcome composes with no extra parts. For genuine no-content moments (no history, no results, offline), reach for the canonical EmptyState instead: see below.

Koala Assistant
Always here to help

How can I help?

Ask about your workspace, draft content, or summarize a thread.

No content & error states#

Beyond the welcome state, an assistant hits real no-content moments: an empty history, a search that finds nothing, a dropped connection. Because the body is a plain flex region, drop the canonical EmptyState straight in. density="compact" fits the in-panel scale, and variant re-themes the media and action for an error (destructive) versus a neutral zero state (default).

Koala Assistant
Always here to help
No saved conversations

Chats you start with Koala show up here, so you can pick them back up later.

API reference#

AIPanel

The shell: a full-height flex column on the popover surface that owns the view mode. Forwards all native <div> props. Every part accepts className, merged last.

PropTypeDefaultDescription
view"inline" | "split" | "floating"-The view mode, controlled: fills its container, a full-height right rail, or a bottom-right window.
defaultView"inline" | "split" | "floating""inline"The initial view mode, uncontrolled.
onViewChange(view: "inline" | "split" | "floating") => void-Called when the view mode changes, e.g. from AIPanelExpandToggle.
anchor"viewport" | "container""viewport"What the split and floating windows are positioned against: the browser window (fixed), or the nearest positioned ancestor (absolute, sized in %) for a panel inside an app frame. Ignored by inline.
Data attributeDescription
data-view"inline", "split" or "floating": the current view mode.
data-anchor"viewport" or "container"; absent in the inline view.
CSS variableDescription
--surfaceSet to var(--popover), so a nested composer blends with the panel rather than the page.

AIPanelHeader

The bordered header row: leading media (an Avatar), the heading column and the actions. Inside a Drawer, also render a <DrawerTitle className="sr-only">. Forwards all native <div> props.

AIPanelHeading

The title and description column that grows between the media and the actions. Forwards all native <div> props.

AIPanelTitle

The panel title, truncated to one line. Forwards all native <div> props.

AIPanelDescription

A muted one-line subtitle under the title. Forwards all native <div> props.

AIPanelActions

The trailing action row of the header. Forwards all native <div> props.

AIPanelAction

A header action (new chat, history, close): a Button that takes every Button prop. Use asChild to wrap a DrawerClose.

PropTypeDefaultDescription
variantButtonProps["variant"]"ghost"Any Button variant.
size"sm" | "md" | "lg" | "xl""sm"The Button size.
iconOnlybooleantrueA square icon button; its aria-label doubles as the tooltip.

AIPanelExpandToggle

A prewired AIPanelAction that flips the panel between the split rail and the floating window, swapping its icon and label ("Float" or "Dock to side"). Takes every Button prop; must live inside an AIPanel.

AIPanelBody

The only scrolling region, with edge fades; drop a Conversation inside. Forwards all native <div> props.

PropTypeDefaultDescription
scrollablebooleantrueThe body scrolls its own overflow, padded with a scroll fade. Set false to hand the scroll to the child that fills it, e.g. a Conversation with min-h-0 flex-1, so a ConversationScrollButton can float over the thread.
Data attributeDescription
data-scrollable"false" when scrollable is off; absent otherwise.

AIPanelFooter

The pinned composer dock; drop a PromptInput inside. Forwards all native <div> props.

FAQ#

AIPanel is pure layout and fills its container with `h-full`, so to dock it just give it a sized frame in your page. For an overlay, drop the same panel into a `DrawerContent` and let the Drawer supply the trigger, scrim, focus trap, and slide animation.