Skip to content

A part of your own is a component that reads the panel’s state and acts on it. Build it on what @super-assist/react exports, then render it where the part it replaces went.

useAssisted gives the panel’s open state and the open page’s name:

import { useAssisted } from "@super-assist/react";
function PageBadge() {
const { page } = useAssisted();
if (!page) return null;
return <span className="rounded bg-muted px-1.5 text-xs">{page}</span>;
}

It also returns isOpen, open(message?) and close().

useAuiState selects from the runtime’s state and re-renders when the selection changes. useAui returns the client that acts on it:

import { useAui, useAuiState } from "@super-assist/react";
function StartOver() {
const aui = useAui();
const running = useAuiState((s) => s.thread.isRunning);
return (
<button disabled={running} onClick={() => aui.threads.switchToNewThread()}>
Start over
</button>
);
}

s.thread holds the open thread: messages, isRunning. s.threads holds the list: threadIds, threadItems, mainThreadId. aui.threads switches, starts and deletes threads. useAuiEvent subscribes to the runtime’s events.

The primitives are unstyled components with the panel’s behavior: ComposerPrimitive, ThreadPrimitive, MessagePrimitive, ActionBarPrimitive, BranchPickerPrimitive, AttachmentPrimitive, ErrorPrimitive, SuggestionPrimitive, ThreadListPrimitive and ThreadListItemPrimitive. Style them with your own classes. A composer of your own:

import { ComposerPrimitive } from "@super-assist/react";
function PlainComposer() {
return (
<ComposerPrimitive.Root className="flex gap-2 border-t p-2">
<ComposerPrimitive.Input className="flex-1 bg-transparent" placeholder="Ask the shop…" />
<ComposerPrimitive.Send className="rounded bg-primary px-3 text-primary-foreground">
Send
</ComposerPrimitive.Send>
</ComposerPrimitive.Root>
);
}

A part you replace takes its behavior with it. PlainComposer has no slash commands, which belong to AssistedPanel.Input.

Render the parts around it as before. Your components sit beside them:

<AssistedPanel.Floating>
<AssistedPanel.Main>
<AssistedPanel.Header>
<AssistedPanel.Icon />
<AssistedPanel.Title />
<PageBadge />
<StartOver />
<AssistedPanel.Close />
</AssistedPanel.Header>
<AssistedPanel.Conversation />
<PlainComposer />
</AssistedPanel.Main>
</AssistedPanel.Floating>

Inside a root, your component sits inside the panel’s scoped styles and the runtime’s context. Outside one, under AssistedProvider, useAssisted, useAui and useAuiState still work.

Parts end where a single message begins. To change how a message looks, pass a component to Messages’ components, built from MessagePrimitive:

function Mine() {
return (
<MessagePrimitive.Root className="rounded-lg border-s-2 border-teal-600 bg-teal-50 px-3 py-2 text-sm">
<MessagePrimitive.Parts />
</MessagePrimitive.Root>
);
}
<AssistedPanel.Conversation>
<AssistedPanel.Messages components={{ AssistantMessage: Mine }} />
</AssistedPanel.Conversation>;

The slots are listed on Conversation. To change how a tool’s call shows, see Swap a card.

The pieces the parts are built from are exported too, for a part that keeps their look: ThreadList, ModelSelector, EmptyState with EmptyStateGreeting, EmptyStateSuggestions and EmptyStateSuggestion, ConversationSearch, ThreadSearch, StoppedRun, SettingsPanel, ContextBreakdown, QuotaBanner, LauncherBubble, Suggestions, TooltipIconButton and ComposerTriggerPopover. They take their data as props. The react reference lists them.