Шапка карточки и диалоги
Шапку карточки, её плитку обзора и диалоги рисует приложение, а не ваша страница, — поэтому они выглядят как родные, работают, пока карточка усыплена, и видны на телефоне. Вы задаёте их содержимое.
Вызывайте их сколько угодно часто. Приложение ограничивает частоту этих обновлений (заголовок —
30 в минуту; статус, бейдж и обзор — по 120 в минуту; внимание — раз в 10 секунд), а SDK берёт это
на себя: для setTitle, setStatus, setBadge, setOverview и attention он отправляет по одному
вызову каждого метода за раз, схлопывает вызовы, сделанные тем временем, в один с последним
значением и повторяет последнее значение, если приложение ответило RATE_LIMITED. Из-за частоты они
никогда не бросают исключений — обновлять на каждом рендере нормально. Обновление, которое ничего не
меняет, ничего не стоит.
Заголовок
await card.setTitle('Checkout tests') // null clears itИмя в шапке, в сайдбаре и в карточке «Команда». Если пользователь переименовал карточку, побеждает
его имя; если нет ни того ни другого, показывается displayName из манифеста. До 120 символов;
управляющие символы, символы направления текста (bidi) и невидимые символы вырезаются.
Статус
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.
Бейдж
await card.setBadge(3) // a count
await card.setBadge('NEW', { tone: 'accent' }) // or a short text, ≤ 12 characters
await card.setBadge(null) // clearПлитка обзора
Когда пользователь отдаляет холст за порог обзора, каждая карточка вместо тела показывает плитку с главным фактом. Ваша показывает то, что вы задали здесь, — и продолжает показывать, пока карточка усыплена, и на телефоне. Приложение это запоминает.
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). Внутри своей страницы рисуйте любые иконки.
Внимание
await card.attention('needs-input', 'Pick a branch to deploy')
await card.attention('info') // a softer pulse
await card.attention('none') // clearneeds-input заставляет карточку пульсировать, как агент, который ждёт пользователя, и добавляет её
во входящие (Inbox). Не чаще раза в 10 секунд. В версии 1 нет ни звука, ни уведомлений ОС.
Что показано сейчас. Приложение сохраняет заголовок, статус, бейдж, обзор и внимание карточки
при перезагрузках, усыплении и обновлениях. card.context.chrome сообщает вашей странице, что там
сейчас, так что карточка, поднявшая внимание до перезагрузки, может снять устаревшую пульсацию:
const attention = card.context.chrome?.attention
if (attention && !stillNeedsTheUser()) await card.attention('none')
declare function stillNeedsTheUser(): booleanВ старых версиях приложения chrome нет — считайте это «неизвестно».
Изменение размера
const applied = await card.requestResize({ w: 640, h: 480 })
render(applied.w, applied.h)Ограничивается minSize/maxSize манифеста; возвращает размер, который реально применился. Шаг
попадает в историю отмены холста, как изменение размера руками. Не чаще раза в 500 мс. Пользователь
тоже всегда может поменять размер — слушайте card.lifecycle.onResized.
Открыть ссылку
const opened = await card.openLink('https://github.com/acme/app/issues/42')Приложение показывает полный адрес в своём диалоге на всё окно; ссылка откроется в браузере
пользователя только после Open link. Только https:// и только пока карточка видна (иначе
NOT_VISIBLE). Возвращает false, если пользователь отказался. Карточка не может ни перейти на
другую страницу сама, ни открыть окно.
Полететь к другой карточке
await card.focusCard(cardId)Переносит камеру к другой карточке того же воркспейса — для кнопок вида «покажи упавшего агента». Только пока ваша карточка видна, не чаще раза в 5 секунд.
Создать карточку
Нужно разрешение canvas.spawn.
// 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 в минуту.
Сведения о карточке
const info = await card.getInfo() // CardInstanceInfo, fresh from the app
render(info.instanceId, info.packageId, info.commit ?? 'dev folder', info.size)Тосты
await card.ui.toast('Copied', { tone: 'success', durationMs: 2000 })Небольшое уведомление внутри коробки карточки, до 280 символов, на 1–15 секунд, не чаще раза в 2 секунды.
Диалог подтверждения
alert, confirm и prompt в песочнице карточки ничего не делают. Попросите приложение:
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…).
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 — одна из иконок приложения.
Всё на этой странице, кроме диалогов и ссылок, работает, даже когда карточки нет на экране.
Диалогам, ссылкам, полёту камеры и запросам разрешений нужно, чтобы пользователь смотрел: иначе
они падают с NOT_VISIBLE.