feat(ai-chat): role-selection cards as new-chat empty-state
Replace the new-chat <Select label="Agent role"> picker with colored role cards rendered as the empty-state of a brand-new chat (centered in the window), per docs/backlog/ai-chat-role-cards-empty-state.md. Clicking a card selects that identity; sending without a pick falls back to the Universal assistant; the cards disappear once the chat is non-empty. Purely client-side — the existing selectedAiRoleIdAtom + roleId request wiring (server role fixation on chat creation) is unchanged. - new RoleCards rendered through the existing emptyState prop chain (AiChatWindow -> ChatThread -> MessageList); MessageList already supported it. - Universal assistant card (gray, value null, default-selected) + one card per enabled role, color cycled from a 10-name Mantine palette via the pure roleCardColor() helper; theme-aware CSS vars (light/-light-color/-filled). - each card is an UnstyledButton with aria-pressed for a11y + testability. - tests: role-card-color (palette cycling, negative-safe) + role-cards.test.tsx (render, emoji/name, selection highlight, click -> onSelect). 9 tests green, client tsc clean. Verified live in-browser: cards (not a Select) show for a new chat; selecting Пират binds the chat to that role end-to-end (badge + pirate reply); no pick => Universal; cards vanish after the first message. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useRef } from "react";
|
||||
import { ReactNode, useMemo, useRef } from "react";
|
||||
import { generateId } from "ai";
|
||||
import { Alert, Box, Stack } from "@mantine/core";
|
||||
import { IconAlertTriangle } from "@tabler/icons-react";
|
||||
@@ -29,6 +29,9 @@ interface ChatThreadProps {
|
||||
* in the request body so the server persists it on chat creation; ignored by
|
||||
* the server for existing chats (the role is read from the chat row). */
|
||||
roleId?: string | null;
|
||||
/** Content shown when the transcript is empty (forwarded to MessageList).
|
||||
* Used by the new-chat window to render the colored role cards. */
|
||||
emptyState?: ReactNode;
|
||||
/** Called when a turn finishes; the parent refreshes the chat list and, for
|
||||
* a new chat, adopts the freshly created chat id. */
|
||||
onTurnFinished: () => void;
|
||||
@@ -66,6 +69,7 @@ export default function ChatThread({
|
||||
initialRows,
|
||||
openPage,
|
||||
roleId,
|
||||
emptyState,
|
||||
onTurnFinished,
|
||||
}: ChatThreadProps) {
|
||||
const { t } = useTranslation();
|
||||
@@ -161,7 +165,11 @@ export default function ChatThread({
|
||||
|
||||
return (
|
||||
<Box className={classes.panel}>
|
||||
<MessageList messages={messages} isStreaming={isStreaming} />
|
||||
<MessageList
|
||||
messages={messages}
|
||||
isStreaming={isStreaming}
|
||||
emptyState={emptyState}
|
||||
/>
|
||||
|
||||
{error && (
|
||||
<Alert
|
||||
|
||||
Reference in New Issue
Block a user