# Шапка карточки и диалоги

> Шапка карточки — заголовок, статус, бейдж, — её плитка обзора, привлечение внимания, изменение размера, ссылки, полёт камеры и создание карточек; тосты, диалоги подтверждения и меню карточки.

Source: https://docs.neurosquad.ai/ru/card-sdk/api/card-ui

Шапку карточки, её плитку обзора и диалоги рисует **приложение**, а не ваша страница, — поэтому они
выглядят как родные, работают, пока карточка усыплена, и видны на телефоне. Вы задаёте их
содержимое.

**Вызывайте их сколько угодно часто.** Приложение ограничивает частоту этих обновлений (заголовок —
30 в минуту; статус, бейдж и обзор — по 120 в минуту; внимание — раз в 10 секунд), а SDK берёт это
на себя: для `setTitle`, `setStatus`, `setBadge`, `setOverview` и `attention` он отправляет по одному
вызову каждого метода за раз, схлопывает вызовы, сделанные тем временем, в один с последним
значением и повторяет последнее значение, если приложение ответило `RATE_LIMITED`. Из-за частоты они
никогда не бросают исключений — обновлять на каждом рендере нормально. Обновление, которое ничего не
меняет, ничего не стоит.

## Заголовок

```ts
await card.setTitle('Checkout tests') // null clears it
```

Имя в шапке, в сайдбаре и в карточке «Команда». Если пользователь переименовал карточку, побеждает
его имя; если нет ни того ни другого, показывается `displayName` из манифеста. До 120 символов;
управляющие символы, символы направления текста (bidi) и невидимые символы вырезаются.

## Статус

```ts
await card.setStatus('Running tests…', { busy: true })       // spinner
await card.setStatus('3 failed', { tone: 'danger' })
await card.setStatus(null)                                    // clear
```

Плашка в шапке, до 80 символов. Тона: `default`, `accent`, `success`, `warning`, `danger`.

## Бейдж

```ts
await card.setBadge(3)                          // a count
await card.setBadge('NEW', { tone: 'accent' })  // or a short text, ≤ 12 characters
await card.setBadge(null)                       // clear
```

## Плитка обзора

Когда пользователь отдаляет холст за порог обзора, каждая карточка вместо тела показывает плитку с
главным фактом. Ваша показывает то, что вы задали здесь, — и продолжает показывать, пока карточка
усыплена, и на [телефоне](https://docs.neurosquad.ai/ru/card-sdk/phone). Приложение это запоминает.

```ts
await card.setOverview({
  primary: '3 failing',        // big line, ≤ 160
  secondary: 'checkout, auth', // small line, ≤ 160
  progress: 0.8,               // 0..1, a progress bar; null hides it
  tone: 'danger',
  icon: 'bug-ant'
})
```

Держите её актуальной: на загруженном холсте большинство видит от вашей карточки только её.

**Иконки**, которые умеет рисовать приложение (heroicons, контурные): `academic-cap`, `arrow-path`,
`beaker`, `bell`, `bolt`, `book-open`, `bug-ant`, `calendar`, `camera`, `chart-bar`, `chart-pie`,
`chat-bubble-left-right`, `check-circle`, `clock`, `cloud`, `code-bracket`, `command-line`,
`cpu-chip`, `cube`, `currency-dollar`, `document-text`, `exclamation-triangle`, `film`, `fire`,
`flag`, `folder`, `globe-alt`, `heart`, `inbox`, `key`, `light-bulb`, `link`, `list-bullet`, `map`,
`megaphone`, `moon`, `musical-note`, `newspaper`, `paper-airplane`, `pause`, `photo`, `play`,
`puzzle-piece`, `rocket-launch`, `server`, `shield-check`, `signal`, `sparkles`, `star`, `stop`,
`sun`, `table-cells`, `tag`, `trash`, `trophy`, `users`, `wrench-screwdriver` (список —
`HOST_ICON_NAMES` в SDK). Внутри своей страницы рисуйте любые иконки.

## Внимание

```ts
await card.attention('needs-input', 'Pick a branch to deploy')
await card.attention('info')    // a softer pulse
await card.attention('none')    // clear
```

`needs-input` заставляет карточку пульсировать, как агент, который ждёт пользователя, и добавляет её
во **входящие** (Inbox). Не чаще раза в 10 секунд. В версии 1 нет ни звука, ни уведомлений ОС.

**Что показано сейчас.** Приложение сохраняет заголовок, статус, бейдж, обзор и внимание карточки
при перезагрузках, усыплении и обновлениях. `card.context.chrome` сообщает вашей странице, что там
сейчас, так что карточка, поднявшая внимание до перезагрузки, может снять устаревшую пульсацию:

```ts
const attention = card.context.chrome?.attention
if (attention && !stillNeedsTheUser()) await card.attention('none')

declare function stillNeedsTheUser(): boolean
```

В старых версиях приложения `chrome` нет — считайте это «неизвестно».

## Изменение размера

```ts
const applied = await card.requestResize({ w: 640, h: 480 })
render(applied.w, applied.h)
```

Ограничивается `minSize`/`maxSize` манифеста; возвращает размер, который реально применился. Шаг
попадает в историю отмены холста, как изменение размера руками. Не чаще раза в 500 мс. Пользователь
тоже всегда может поменять размер — слушайте [`card.lifecycle.onResized`](https://docs.neurosquad.ai/ru/card-sdk/api/environment#lifecycle).

## Открыть ссылку

```ts
const opened = await card.openLink('https://github.com/acme/app/issues/42')
```

Приложение показывает полный адрес в своём диалоге на всё окно; ссылка откроется в браузере
пользователя только после **Open link**. Только `https://` и только пока карточка видна (иначе
`NOT_VISIBLE`). Возвращает `false`, если пользователь отказался. Карточка не может ни перейти на
другую страницу сама, ни открыть окно.

## Полететь к другой карточке

```ts
await card.focusCard(cardId)
```

Переносит камеру к другой карточке того же воркспейса — для кнопок вида «покажи упавшего агента».
Только пока ваша карточка видна, не чаще раза в 5 секунд.

## Создать карточку

Нужно разрешение [`canvas.spawn`](https://docs.neurosquad.ai/ru/card-sdk/permissions#canvas-spawn).

```ts
// A note next to this card, connected with an arrow from this card.
const noteId = await card.spawn('note', { title: 'Test report' })
await card.ports.send(noteId, 'summary', '# Report\n\nAll green.')

// Another copy of this card, with data for its first start.
await card.spawn('self', { init: { suite: 'e2e' }, connect: false })
```

Виды: `self` (ещё одна карточка вашего пакета, которая при первом запуске получает `init` как
`card.spawnInit`), `note`, `todo`, `kanban`, `sticky`. Новая карточка встаёт рядом с вашей и
соединяется стрелкой от вашей карточки, если не указать `connect: false`. Не больше 4 на карточку и
4 в минуту.

## Сведения о карточке

```ts
const info = await card.getInfo() // CardInstanceInfo, fresh from the app
render(info.instanceId, info.packageId, info.commit ?? 'dev folder', info.size)
```

## Тосты

```ts
await card.ui.toast('Copied', { tone: 'success', durationMs: 2000 })
```

Небольшое уведомление внутри коробки карточки, до 280 символов, на 1–15 секунд, не чаще раза в 2
секунды.

## Диалог подтверждения

`alert`, `confirm` и `prompt` в песочнице карточки ничего не делают. Попросите приложение:

```ts
const ok = await card.ui.confirm({
  title: 'Delete all saved runs?',
  message: 'This removes 42 runs from this card. It cannot be undone.',
  confirmLabel: 'Delete',
  cancelLabel: 'Keep',
  tone: 'danger'
})
if (ok) await card.storage.clear()
```

Приложение показывает это **своим диалогом поверх всего окна**, а не внутри карточки: неизменный
заголовок о том, что ваша карточка просит подтверждения, ваши `title` и `message` как цитата, ваша
`confirmLabel` (её заменяет «Confirm» приложения, если она похожа на отмену, отказ или «нет»,
называет NeuroSquad или содержит управляющие символы) и собственная кнопка Cancel приложения. Только
пока карточка видна, не больше 10 в минуту. Диалоги от нескольких карточек выстраиваются в очередь.

## Меню карточки

Добавьте до 12 пунктов в меню **⋯** карточки, после собственных пунктов приложения (Settings…,
Reload, About…).

```ts
await card.ui.setMenu([
  { id: 'rerun', label: 'Run again', icon: 'arrow-path', onSelect: () => void rerun() },
  { id: 'export', label: 'Copy report', icon: 'document-text', onSelect: () => void copyReport() },
  { id: 'reset', label: 'Reset', tone: 'danger', disabled: true }
])

// Or handle every choice in one place:
card.ui.onMenu((id) => card.log.info('menu', id))

declare function rerun(): Promise<void>
declare function copyReport(): Promise<void>
```

Каждый вызов заменяет прежние пункты (не больше 30 вызовов в минуту). `onSelect` остаётся в вашей
карточке — в приложение он не отправляется.
Подписи до 60 символов; `icon` — одна из [иконок приложения](#overview).

> Всё на этой странице, кроме диалогов и ссылок, работает, даже когда карточки нет на экране.
> Диалогам, ссылкам, полёту камеры и запросам разрешений нужно, чтобы пользователь смотрел: иначе
> они падают с `NOT_VISIBLE`.
