Skip to Content
卡片 SDKAPI 参考卡片外框与对话框

卡片外框与对话框

卡片的标题栏、概览图块和对话框都由应用绘制,而不是你的页面——因此它们看起来是原生的,在卡片暂停时依然有效,也会显示在手机上。你只负责设置内容。

想调用多少次都行。 应用会对这些更新限流(标题每分钟 30 次;状态、徽章和概览各每分钟 120 次;提醒每 10 秒一次), 而 SDK 会替你消化这些限制:对于 setTitle、setStatus、setBadge、setOverview 和 attention,每个方法同一时间只发送一个调用, 期间发起的调用会合并成一个、只保留最新的值,应用返回 RATE_LIMITED 时还会重试最新的值。它们永远不会因为限流而抛出异常—— 每次渲染都更新也没问题。没有改变任何内容的更新不产生任何开销。

标题

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

显示在标题栏、侧边栏和小队(Squad)卡片中的名称。如果用户给卡片改过名,以用户起的名字为准;两者都没有时显示清单中的 displayName。 ≤ 120 个字符;控制字符、双向文本字符和不可见字符会被去除。

状态

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,outline 风格):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(SDK 中的 HOST_ICON_NAMES 列表)。 在你自己的页面里,想画什么图标都可以。

提醒

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 告诉你的页面当前保存着什么,所以在重新加载前发出过提醒的卡片可以清除过时的闪烁:

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(你的卡片包的另一张卡片,首次启动时会以 card.spawnInit 收到 init)、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(如果它读起来像取消/否/拒绝、提到了 NeuroSquad 或包含控制字符,就会被替换为应用的“Confirm”),以及应用自己的 Cancel。只能在卡片可见时调用,每分钟最多 10 次。 多张卡片的对话框会排队。

在卡片的 ⋯ 菜单中,于应用自带的菜单项(Settings…、Reload、About…)之后添加最多 12 项。

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 失败。