# @cloud-ru/ds-ai-tool > Базовые презентационные элементы для сборки AI-компонентов Tool и Tool Simple — иконки, статус, текст, key-value, дерево, бейджи и блок деталей. Docs: /snack-v2/components/ai-tool/ ## Установка ```sh pnpm add @cloud-ru/ds-ai-tool ``` ## Когда использовать - Рендер аргументов и результата вызова инструмента в чате AI-ассистента. - Пошаговое отображение статуса инструмента (pending → loading → success / error). - Сворачиваемое дерево JSON-подобных данных (объекты, массивы, ключ-значение). ### Когда не нужен - Готовый высокоуровневый компонент вызова инструмента: - используйте `Tool` / `Tool Simple` (собраны из этих элементов). - Обычный контент-блок без семантики инструмента: - используйте `@cloud-ru/ds-card` или типографику `@cloud-ru/ds-typography`. ## API ### AiTool | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `call` | `ReactNode` | — | no | Содержимое блока запроса. Блок рендерится только при переданном значении. | | `callLabel` | `ReactNode` | `Запрос` | no | Заголовок блока запроса. | | `children` | `string \| number \| boolean \| ReactElement> \| Iterable \| ReactPortal \| null \| undefined` | — | no | | | `className` | `string` | — | no | Доп. класс корня. | | `connector` | `boolean` | `false` | no | Линия-коннектор к следующему инструменту в таймлайне. Линия выходит на 8px ниже корня — рассчитана на вертикальный список с `gap: 8px`. | | `data-test-id` | `string` | `ai-tool` | no | | | `defaultOpened` | `boolean` | `false` | no | Начальное раскрытое состояние (uncontrolled). | | `duration` | `number` | — | no | Длительность выполнения в секундах. Форматируется компонентом в д/ч/м/с (ведущие нулевые единицы опускаются, секунды показываются всегда). | | `icon` | `act \| read \| reasoning \| search \| security \| wait` | — | yes | Тип инструмента — глиф `AiToolIcon` в заголовке. | | `name` | `ReactNode` | — | yes | Имя инструмента — моноширинная строка заголовка, обрезается ellipsis. | | `onToggle` | `((opened: boolean) => void)` | — | no | Переключение раскрытия. Получает новое значение `opened`. | | `opened` | `boolean` | — | no | Раскрытое состояние (controlled). Для uncontrolled-режима — `defaultOpened`. | | `result` | `ReactNode` | — | no | Содержимое блока ответа. Блок рендерится только при переданном значении. | | `resultLabel` | `ReactNode` | `Ответ` | no | Заголовок блока ответа. | | `state` | `error \| loading \| pending \| success` | `pending` | no | Состояние выполнения инструмента: `loading` — выполняется (синяя пульсирующая точка, заголовок основным цветом текста вместо приглушённого), `success` — завершён, `error` — завершён с ошибкой (блок ответа подсвечивается красным), `pending` — в очереди. | #### Related types - `AiToolIconType` = `act | read | reasoning | search | security | wait` - `AiToolStatusState` = `error | loading | pending | success` ### AiToolArray | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `ReactNode` | — | no | Вложенные элементы (при раскрытии). | | `className` | `string` | — | no | Доп. класс корня. | | `count` | `number` | — | no | Количество элементов — рендерится как `[ N ]` (или `[ N unit ]`). | | `data-test-id` | `string` | `ai-tool-array` | no | | | `error` | `boolean` | — | no | Состояние ошибки: имя и счётчик красные. По умолчанию наследуется от `AiToolDetails`. | | `mono` | `boolean` | — | no | Моноширинный режим имени и счётчика. По умолчанию наследуется от `AiToolDetails`. | | `name` | `ReactNode` | — | no | Имя узла (`Key[ArrayName]`). | | `onToggle` | `((opened: boolean) => void)` | — | no | Переключение раскрытия. Получает новое значение `opened`. | | `opened` | `boolean` | `false` | no | Раскрытое состояние. Источник истины — родитель. | | `unit` | `string` | — | no | Единица измерения после количества (например `шт.`). | ### AiToolBadge | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `as` | `ElementType` | — | no | Полиморфный тег корня (`'a'` для ссылки и т.д.). По умолчанию `'span'`. | | `badgeType` | `cloud-ru \| other` | — | no | Тип бейджа — определяет встроенную иконку (`cloud-ru` / `other`). Без него иконка не рендерится. | | `className` | `string` | — | no | Доп. класс корня. | | `data-test-id` | `string` | `ai-tool-badge` | no | | | `innerRef` | `any` | — | no | Ref на корневой элемент (вместо `forwardRef`). | | `label` | `ReactNode` | — | no | Текст бейджа (одна строка с ellipsis). | #### Related types - `AiToolBadgeType` = `cloud-ru | other` - `PolymorphicRef` (alias) ### AiToolDetails | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `ReactNode` | — | no | Контент блока деталей (текст, key-value, дерево). | | `className` | `string` | — | no | Доп. класс корня. | | `data-test-id` | `string` | `ai-tool-details` | no | | | `label` | `ReactNode` | — | no | Текст заголовка-лейбла. | | `onToggleSecret` | `((event: MouseEvent) => void)` | — | no | Клик по кнопке-«глаз» заголовка. | | `scroll` | `boolean` | `true` | no | Ограничить высоту контента и включить вертикальный скролл. По умолчанию `true`. | | `secretRevealed` | `boolean` | `false` | no | Секрет раскрыт. Источник истины — родитель. | | `showSecret` | `boolean` | `false` | no | Показать кнопку-«глаз» в заголовке для раскрытия секрета. | | `state` | `default \| error` | `default` | no | Состояние: `default` — нейтральный, `error` — красная рамка и лейбл. | #### Related types - `AiToolDetailsState` = `default | error` ### AiToolDetailsLabel | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `string \| number \| boolean \| ReactElement> \| Iterable \| ReactPortal \| null \| undefined` | — | no | | | `className` | `string` | — | no | Доп. класс корня. | | `data-test-id` | `string` | `ai-tool-details-label` | no | | | `label` | `ReactNode` | — | no | Текст лейбла (заголовок блока деталей). | | `onToggleSecret` | `((event: MouseEvent) => void)` | — | no | Клик по кнопке-«глаз». Не вызывается, если `showSecret` не задан. | | `secretRevealed` | `boolean` | `false` | no | Секрет раскрыт: глаз открыт (секреты видны). Зачёркнутый глаз — секреты скрыты. Источник истины — родитель. | | `showSecret` | `boolean` | `false` | no | Показать кнопку-«глаз» для раскрытия секретного значения. | | `state` | `default \| error` | `default` | no | Состояние: `default` — нейтральный, `error` — красный. | #### Related types - `AiToolDetailsState` = `default | error` ### AiToolIcon | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `string \| number \| boolean \| ReactElement> \| Iterable \| ReactPortal \| null \| undefined` | — | no | | | `className` | `string` | — | no | Доп. класс корня. | | `data-test-id` | `string` | `ai-tool-icon` | no | | | `variant` | `act \| read \| reasoning \| search \| security \| wait` | — | yes | Тип инструмента — определяет глиф (reasoning / search / read / act / security / wait). | #### Related types - `AiToolIconType` = `act | read | reasoning | search | security | wait` ### AiToolKeyValue | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `string \| number \| boolean \| ReactElement> \| Iterable \| ReactPortal \| null \| undefined` | — | no | | | `className` | `string` | — | no | Доп. класс корня. | | `data-test-id` | `string` | `ai-tool-key-value` | no | | | `error` | `boolean` | — | no | Состояние ошибки: ключ и значение красные. По умолчанию наследуется от `AiToolDetails`. | | `label` | `ReactNode` | — | no | Ключ (левая / верхняя часть пары). | | `mono` | `boolean` | — | no | Моноширинный режим ключа и значения. По умолчанию наследуется от `AiToolDetails`. | | `value` | `ReactNode` | — | no | Значение (правая / нижняя часть пары). | | `variant` | `column \| line` | `line` | no | Раскладка пары: `line` — ключ и значение в строку, `column` — стопкой. | #### Related types - `AiToolKeyValueType` = `column | line` ### AiToolObject | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `ReactNode` | — | no | Вложенное дерево (только для раскрытого `complex`). | | `className` | `string` | — | no | Доп. класс корня. | | `data-test-id` | `string` | `ai-tool-object` | no | | | `error` | `boolean` | — | no | Состояние ошибки: имя и значение красные. По умолчанию наследуется от `AiToolDetails`. | | `mono` | `boolean` | — | no | Моноширинный режим имени и значения. По умолчанию наследуется от `AiToolDetails`. | | `name` | `ReactNode` | — | no | Имя узла (`Key[ObjectName]`). | | `onToggle` | `((opened: boolean) => void)` | — | no | Переключение раскрытия (только для `complex`). Получает новое значение `opened`. | | `opened` | `boolean` | `false` | no | Раскрытое состояние (только для `complex`). Источник истины — родитель. | | `value` | `ReactNode` | — | no | Значение для типа `string` (инлайн рядом с именем). | | `variant` | `complex \| string` | `complex` | no | Тип узла: `complex` — сворачиваемое дерево, `string` — инлайн ключ-значение. | #### Related types - `AiToolObjectType` = `complex | string` ### AiToolSimple | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `ReactNode` | — | no | Контент раскрытия под описанием — например, ряд `AiToolBadge` с задействованными ресурсами. Выкладывается в строку с переносом. | | `className` | `string` | — | no | Доп. класс корня. | | `connector` | `boolean` | `false` | no | Линия-коннектор к следующему инструменту в таймлайне. Линия выходит на 8px ниже корня — рассчитана на вертикальный список с `gap: 8px`. | | `data-test-id` | `string` | `ai-tool-simple` | no | | | `defaultOpened` | `boolean` | `false` | no | Начальное раскрытое состояние (uncontrolled). | | `description` | `ReactNode` | — | no | Текстовое описание под заголовком в раскрытом состоянии. | | `icon` | `act \| read \| reasoning \| search \| security \| wait` | — | yes | Тип инструмента — глиф `AiToolIcon` слева от заголовка. | | `name` | `ReactNode` | — | yes | Имя инструмента — строка заголовка; в свёрнутом состоянии обрезается ellipsis. | | `onToggle` | `((opened: boolean) => void)` | — | no | Переключение раскрытия. Получает новое значение `opened`. | | `opened` | `boolean` | — | no | Раскрытое состояние (controlled). Для uncontrolled-режима — `defaultOpened`. | | `state` | `error \| loading \| pending \| success` | `pending` | no | Состояние выполнения. В `loading` тип инструмента ещё неизвестен, поэтому вместо иконки показывается пульсирующая точка `AiToolStatus`, а заголовок подсвечивается основным цветом текста. В остальных состояниях слева рендерится иконка типа (`icon`). | #### Related types - `AiToolIconType` = `act | read | reasoning | search | security | wait` - `AiToolStatusState` = `error | loading | pending | success` ### AiToolStatus | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `string \| number \| boolean \| ReactElement> \| Iterable \| ReactPortal \| null \| undefined` | — | no | | | `className` | `string` | — | no | Доп. класс корня. | | `data-test-id` | `string` | `ai-tool-status` | no | | | `state` | `error \| loading \| pending \| success` | — | yes | Состояние выполнения инструмента: success / error / loading / pending. | #### Related types - `AiToolStatusState` = `error | loading | pending | success` ### AiToolText | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `ReactNode` | — | no | Текст блока. | | `className` | `string` | — | no | Доп. класс корня. | | `data-test-id` | `string` | `ai-tool-text` | no | | | `error` | `boolean` | — | no | Состояние ошибки: текст красный. По умолчанию наследуется от `AiToolDetails`. | | `mono` | `boolean` | — | no | Моноширинный режим: шрифт mono/body вместо label. По умолчанию наследуется от `AiToolDetails`. | ## Примеры ### ArrayList ```tsx import { AiToolArray, AiToolObject } from '@cloud-ru/ds-ai-tool'; import { useState } from 'react'; export function ArrayList() { const [opened, setOpened] = useState(true); return (
); } ``` ### Badges ```tsx import { AI_TOOL_BADGE_TYPE, AiToolBadge } from '@cloud-ru/ds-ai-tool'; export function Badges() { return (
); } ``` ### DetailsCard ```tsx import { AiToolDetails, AiToolText } from '@cloud-ru/ds-ai-tool'; export function DetailsCard() { return ( {`{ "region": "ru-central1", "status": "ok" }`} ); } ``` ### DetailsLabelSecret ```tsx import { AiToolDetailsLabel } from '@cloud-ru/ds-ai-tool'; import { useState } from 'react'; export function DetailsLabelSecret() { const [revealed, setRevealed] = useState(false); return (
setRevealed(prev => !prev)} />
); } ``` ### IconSet ```tsx import { AI_TOOL_ICON_TYPE, AiToolIcon } from '@cloud-ru/ds-ai-tool'; export function IconSet() { return (
{Object.values(AI_TOOL_ICON_TYPE).map(variant => ( ))}
); } ``` ### KeyValuePair ```tsx import { AiToolKeyValue } from '@cloud-ru/ds-ai-tool'; export function KeyValuePair() { return (
); } ``` ### StatusRow ```tsx import { AI_TOOL_STATUS_STATE, AiToolStatus } from '@cloud-ru/ds-ai-tool'; export function StatusRow() { return (
); } ``` ### TextBlock ```tsx import { AiToolText } from '@cloud-ru/ds-ai-tool'; export function TextBlock() { return (
Обычный текст результата {`{ "status": "ok" }`} Ошибка выполнения инструмента {`{ "error": "timeout" }`}
); } ``` ### ToolCallTree ```tsx import { AiToolArray, AiToolKeyValue, AiToolObject } from '@cloud-ru/ds-ai-tool'; import { useState } from 'react'; export function ToolCallTree() { const [openRoot, setOpenRoot] = useState(true); const [openZones, setOpenZones] = useState(true); return ( ); } ``` ### ToolSimpleBadges ```tsx import { AI_TOOL_BADGE_TYPE, AI_TOOL_ICON_TYPE, AI_TOOL_STATUS_STATE, AiToolBadge, AiToolSimple } from '@cloud-ru/ds-ai-tool'; export function ToolSimpleBadges() { return (
); } ``` ### ToolTimeline ```tsx import { AI_TOOL_ICON_TYPE, AI_TOOL_STATUS_STATE, AiTool, AiToolKeyValue, AiToolText } from '@cloud-ru/ds-ai-tool'; export function ToolTimeline() { return (
{'{ "query": "instance status" }'}} result={ <> } /> {'{ "user_id": 42 }'}} />
); } ```