- Accordion
- Alert
- Alert Dialog
- Autocomplete
- Auth Surface
- Avatar
- Badge
- Browse Catalog Dialog
- Button
- Card
- Checkbox
- Checkbox Group
- Collapsible
- Combobox
- Command
- Connector Setup Dialog
- Cookie Banner
- Dialog
- Directory Card
- Directory Detail
- Directory Skeleton
- DrawerНовое
- Token Parts Input
- Empty
- Field
- Fieldset
- File Preview Modal
- File Preview Skeleton
- Form
- Frame
- Group
- Icon
- Input
- Input Group
- Kbd
- Label
- Legal Shell
- Menu
- Mermaid Diagram
- Mind Map Diagram
- Not Found Screen
- Onboarding Frame
- Popover
- PDF Thumbnail
- Personalization Landing
- Preview Card
- Pricing Page
- Progress
- Radio Group
- Ring Spinner
- Scroll Area
- Select
- Separator
- Settings Page
- Settings Skills
- Settings Connectors
- Settings Capabilities
- Settings Usage
- Settings Account
- Settings Billing
- Sheet
- Sidebar
- Skeleton
- Skill Create Dialog
- Slider
- Spinner
- Stat
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Tooltip
- Компоненты AI
- Chat Conversation
- Chat Message
- Chat Response
- Chat Suggestion
- Chat Prompt Input
- Slash Highlighted Textarea
- Chat Search Dialog
- Chat Skill Doc
- Chat Connector Detail
- Chat Attachments
- Chat File Card
- Chat Token Chip
- Chat Code Block
- Chat Image
- Chat Inline Citation
- Chat Sources
- Chat Web Search
- Chat Research
- Chat Source
- Chat Actions
- Chat Context
- Chat Loader
- Chat Compaction
- Chat Timeline
- Chat Snippet
- Chat Terminal
- Chat Stack Trace
- Chat Test Results
- Chat File Tree
- Chat Environment Variables
- Chat Audio Player
- Chat Transcription
- Chat Speech Input
- Chat Mic Selector
- Chat Voice Selector
- Chat Agent
- Chat Persona
- Chat Connection
- Chat Connector Suggestion
- Chat Queue
- Chat Checkpoint
- Chat Confirmation
- Chat Artifact
- Chat JSX Preview
- Chat Schema Display
- Chat Package Info
- Chat Commit
- Chat Plan
- Chat Open In Chat
- Chat Sandbox
- Chat Model Selector
- Chat Canvas
- Chat Node
- Chat Edge
Chat Confirmation
Встроенное подтверждение вызова инструмента — карточка разрешения в стиле Claude плюс устаревший составной API с состояниями pending / approved / rejected.
"use client";
import { useState } from "react";
import {
type ChatConfirmationState,
ToolPermissionCard,
} from "@/components/ui/chat-confirmation";
const ARGS_PREVIEW = JSON.stringify(
{
query: "токены дизайн-системы",
board: "Oracul DS — Основы",
limit: 20,
},
null,
2,
);
export default function Particle() {
const [state, setState] = useState<ChatConfirmationState>("pending");
return (
<div className="flex w-full max-w-md flex-col gap-3">
<ToolPermissionCard
allowOnceLabel="Разрешить один раз"
alwaysAllowLabel="Всегда разрешать"
approvedLabel="Разрешено"
argsPreview={ARGS_PREVIEW}
connectorLabel="Miro"
connectorPrefix="из"
denyLabel="Отклонить"
iconUrl="https://www.google.com/s2/favicons?sz=64&domain=miro.com"
moreOptionsLabel="Ещё"
onAllowOnce={() => setState("approved")}
onAlwaysAllow={() => setState("approved")}
onDeny={() => setState("rejected")}
reason="Вы отклонили этот вызов инструмента."
state={state}
titlePrefix="Хочет использовать"
toolLabel="Context Explore"
/>
{state !== "pending" ? (
<button
className="self-start text-muted-foreground text-xs underline"
onClick={() => setState("pending")}
type="button"
>
Сбросить
</button>
) : null}
</div>
);
}
Установка
pnpm dlx shadcn@latest add @oracul/chat-confirmation
ToolPermissionCard
Сворачиваемая встроенная карточка разрешения, повторяющая запрос Claude «хочет использовать <Tool> из <Connector>». Единый управляемый компонент — на вход простые данные, на выход колбэки. В состоянии pending он показывает раскрываемый заголовок, опциональную плашку с аргументами, составную кнопку «Всегда разрешать», меню «Разрешить один раз» с шевроном и кнопку «Отклонить». После разрешения он сворачивается в компактную строку успеха или причины.
Горячие клавиши повторяют Claude: Enter разрешает всегда, ⌘/Ctrl+Enter разрешает один раз, а Esc отклоняет. Сочетания активны только в состоянии pending при закрытом меню и никогда не срабатывают при наборе текста в поле ввода или форме.
"use client";
import { useState } from "react";
import {
type ChatConfirmationState,
ToolPermissionCard,
} from "@/components/ui/chat-confirmation";
const [state, setState] = useState<ChatConfirmationState>("pending");
<ToolPermissionCard
argsPreview={JSON.stringify({ query: "design tokens", limit: 20 }, null, 2)}
connectorLabel="Miro"
iconUrl="https://www.google.com/s2/favicons?sz=64&domain=miro.com"
onAllowOnce={() => setState("approved")}
onAlwaysAllow={() => setState("approved")}
onDeny={() => setState("rejected")}
reason="You denied this tool call."
state={state}
toolLabel="Context Explore"
/>;Состояния
"use client";
import { ToolPermissionCard } from "@/components/ui/chat-confirmation";
const noop = () => {};
export default function Particle() {
return (
<div className="flex w-full max-w-md flex-col gap-3">
{/* Pending — generic Plug icon, no connector, non-expandable (no args). */}
<ToolPermissionCard
onAllowOnce={noop}
onAlwaysAllow={noop}
onDeny={noop}
state="pending"
toolLabel="Показать файлы"
/>
{/* Approved — compact resolved success row. */}
<ToolPermissionCard
connectorLabel="GitHub"
iconUrl="https://www.google.com/s2/favicons?sz=64&domain=github.com"
onAllowOnce={noop}
onAlwaysAllow={noop}
onDeny={noop}
state="approved"
toolLabel="Создать задачу"
/>
{/* Rejected — compact resolved row with a custom reason. */}
<ToolPermissionCard
connectorLabel="Linear"
iconUrl="https://www.google.com/s2/favicons?sz=64&domain=linear.app"
onAllowOnce={noop}
onAlwaysAllow={noop}
onDeny={noop}
reason="Выходит за рамки этой задачи."
state="rejected"
toolLabel="Удалить проект"
/>
</div>
);
}
Свойства ToolPermissionCard
| Свойство | Тип | Описание |
|---|---|---|
toolLabel | string | Обязательное. Выделенное имя инструмента. |
connectorLabel | string | null | Выделенное имя коннектора. При nullish-значении суффикс «из …» опускается. |
iconUrl | string | null | Фавиконка коннектора. По умолчанию — обобщённый значок штекера. |
argsPreview | string | null | Отформатированный ввод инструмента. Когда значение истинно, заголовок становится триггером раскрытия и рендерит моноширинную плашку с аргументами. |
state | ChatConfirmationState | "pending" | "approved" | "rejected". |
reason | string | Показывается в состоянии rejected. По умолчанию "Denied". |
titlePrefix | React.ReactNode | Текст перед именем инструмента. По умолчанию "Wants to use". |
connectorPrefix | React.ReactNode | Текст перед именем коннектора. По умолчанию "from". |
alwaysAllowLabel / allowOnceLabel / denyLabel / approvedLabel / moreOptionsLabel | React.ReactNode / string | Переопределения текста действий и строки результата. |
onAlwaysAllow / onAllowOnce / onDeny | () => void | Колбэки действий. |
Составной API (устаревший)
Исходная составная карточка сохранена для обратной совместимости. В состоянии ожидания она рендерит запрос и действия «Подтвердить»/«Отклонить», а затем уведомление об успехе или отклонении.
rm -rf node_modules?Это удалит node_modules в корне проекта.
"use client";
import { useState } from "react";
import {
ChatConfirmation,
ChatConfirmationAction,
ChatConfirmationActions,
type ChatConfirmationApproval,
ChatConfirmationApproved,
ChatConfirmationPending,
ChatConfirmationReason,
ChatConfirmationRejected,
ChatConfirmationTitle,
} from "@/components/ui/chat-confirmation";
export default function Particle() {
const [approval, setApproval] = useState<ChatConfirmationApproval>({});
return (
<div className="flex w-full max-w-md flex-col gap-3 p-4">
<ChatConfirmation approval={approval}>
<ChatConfirmationTitle>
Разрешить агенту выполнить <code>rm -rf node_modules</code>?
</ChatConfirmationTitle>
<ChatConfirmationPending>
<p className="text-muted-foreground text-xs">
Это удалит <code>node_modules</code> в корне проекта.
</p>
<ChatConfirmationActions>
<ChatConfirmationAction
onClick={() =>
setApproval({
approved: false,
reason: "Пользователь отменил очистку.",
})
}
variant="ghost"
>
Отклонить
</ChatConfirmationAction>
<ChatConfirmationAction
onClick={() => setApproval({ approved: true })}
>
Разрешить
</ChatConfirmationAction>
</ChatConfirmationActions>
</ChatConfirmationPending>
<ChatConfirmationApproved>
<p className="text-success text-xs">
Разрешено · агент выполняет команду.
</p>
</ChatConfirmationApproved>
<ChatConfirmationRejected>
<p className="text-destructive text-xs">Действие отклонено.</p>
<ChatConfirmationReason />
</ChatConfirmationRejected>
</ChatConfirmation>
{approval.approved !== undefined ? (
<button
className="self-start text-muted-foreground text-xs underline"
onClick={() => setApproval({})}
type="button"
>
Сбросить
</button>
) : null}
</div>
);
}
"use client";
import { useState } from "react";
import {
type ChatConfirmationApproval,
ChatConfirmation,
ChatConfirmationAction,
ChatConfirmationActions,
ChatConfirmationApproved,
ChatConfirmationPending,
ChatConfirmationReason,
ChatConfirmationRejected,
ChatConfirmationTitle,
} from "@/components/ui/chat-confirmation";
const [approval, setApproval] = useState<ChatConfirmationApproval>({});
<ChatConfirmation approval={approval}>
<ChatConfirmationTitle>
Allow agent to run <code>rm -rf node_modules</code>?
</ChatConfirmationTitle>
<ChatConfirmationPending>
<ChatConfirmationActions>
<ChatConfirmationAction
onClick={() => setApproval({ approved: false, reason: "Skipped" })}
variant="ghost"
>
Reject
</ChatConfirmationAction>
<ChatConfirmationAction onClick={() => setApproval({ approved: true })}>
Approve
</ChatConfirmationAction>
</ChatConfirmationActions>
</ChatConfirmationPending>
<ChatConfirmationApproved>
<p className="text-success text-xs">Action approved — running…</p>
</ChatConfirmationApproved>
<ChatConfirmationRejected>
<p className="text-destructive text-xs">Action rejected.</p>
<ChatConfirmationReason />
</ChatConfirmationRejected>
</ChatConfirmation>;Составной API
| Компонент | Поведение |
|---|---|
ChatConfirmation | Корень. Принимает approval: { approved?, reason? } либо явное state="pending" | "approved" | "rejected". |
ChatConfirmationTitle | Заголовок с иконкой ⚠ / ✓ / ✗ в зависимости от состояния. |
ChatConfirmationPending | Рендерит дочерние элементы только в состоянии pending. |
ChatConfirmationApproved / ChatConfirmationRejected | То же — для разрешённых состояний. |
ChatConfirmationActions / ChatConfirmationAction | Кнопки подтверждения / отклонения. Видны только в состоянии pending. |
ChatConfirmationReason | Рендерит approval.reason, если он задан. |