存储与设置
存储
卡片页面没有 localStorage、indexedDB 或 Cookie——它的沙箱没有可以保存它们的源(origin)。card.storage 就是替代品:
由应用以原子方式保存在磁盘上的 JSON 值,在重新加载、暂停和重启之后依然存在。不需要任何权限。
// This card on the canvas (instance scope).
const draft = await card.storage.get('draft', '') // string, '' when missing
await card.storage.set('draft', `${draft}\nmore`)
await card.storage.set('runs', [{ at: Date.now(), failed: 3 }])
const runs = await card.storage.get<{ at: number; failed: number }[]>('runs') // undefined when missing
await card.storage.delete('draft')
const keys = await card.storage.keys('run:') // keys starting with a prefix
const { bytes, quota } = await card.storage.usage()
render(runs?.length, keys, bytes, quota)
// Shared by every copy of this card, on every canvas (package scope).
await card.storage.package.set('lastSync', Date.now())
card.storage.onChange(({ key, byInstance }) => {
if (key === 'lastSync') render(`updated by card ${byInstance}`)
})| 方法 | 结果 |
|---|---|
get(key) | 值,或 undefined。 |
get(key, fallback) | 值,或 fallback。结果的类型与 fallback 一致。 |
set(key, value) | 保存任意 JSON 值。 |
delete(key) | |
keys(prefix?) | string[] |
clear() | 删除该作用域下的所有键。 |
usage() | { bytes, quota, keys } |
onChange(handler) | 这张卡片的另一份副本写入了包级键:{ scope, key, byInstance }。 |
card.storage.package 上有同样的方法。
限制: 键 ≤ 256 个字符,值 ≤ 1 MB,≤ 10 000 个键;每张卡片 5 MB,每个卡片包 20 MB。超出时以 QUOTA_EXCEEDED 失败。
写入会在大约四分之一秒内落盘,应用退出时会全部刷写。
它会怎样: 实例存储随卡片一起删除。包级存储在卸载后依然保留,除非用户勾选 Also delete its saved data and secrets。
存储在更新后保留——请让新版本仍能读懂你存下的数据结构(如果以后可能改动,就存一个 version 字段)。
在被卸载前保存:隐藏一段时间的卡片会被暂停,它的页面会被丢弃。用
card.lifecycle.onSuspend 把仍在内存中的内容写下来。
设置
在清单的 settings 中声明字段;应用负责绘制表单(从卡片的 ⋯ → Settings… 打开,
或在你调用 card.settings.open() 时打开)、校验并保存这些值。你的卡片读取它们:
const command = card.settings.value<string>('command') ?? 'npm test'
const all = card.settings.values // defaults filled in, secrets never included
const hasKey = card.settings.hasSecret('apiKey') // true when the user saved one
card.settings.onChange((snapshot) => {
render(snapshot.values['command'], snapshot.secrets['apiKey'])
})
// Change your own non-secret settings (a toggle in your UI, say). Validated like the form.
await card.settings.set({ compact: true })
// Open the form on the card (only while it is visible).
if (!hasKey) await card.settings.open()
render(command, all)| 成员 | 说明 |
|---|---|
values | 当前值,已填入默认值。实时更新。 |
secrets | Record<key, boolean>:哪些密钥设置已填写。 |
value(key) | 单个值,类型由你指定。 |
hasSecret(key) | 某个密钥设置是否已填写。 |
get() | 从应用获取最新的 SettingsSnapshot。 |
set(values) | 修改非密钥设置;卡片的每份副本都会收到 settings.changed。无效值会以 INVALID_PARAMS 失败。每分钟最多 60 次。 |
open() | 在卡片上打开设置表单。卡片不在屏幕上时为 NOT_VISIBLE。 |
onChange(handler) | 用户保存了表单,或 set 改动了某些值。 |
作用域。 带有 "scope": "package" 的字段对你卡片的所有副本只有一个值;其余字段按卡片各自保存。
密钥。 secret 字段只能在应用自己的对话框中输入——在设置表单中,它那一行只显示“已设置”或“未设置”,并会打开那个覆盖整个窗口的对话框——并由应用加密保存。
卡片永远读不到它——无论通过 values 还是任何方法。要使用它,把 {{secret:<key>}} 放进网络请求头;
应用会在请求发出时填入,且只针对你的卡片被授权的互联网主机(永远不会针对 localhost)。
不要在卡片里自己做一个“粘贴你的 API 密钥”的输入框:那样一来泄露密钥的责任就在你身上,而且用户已被告知 NeuroSquad 从不在卡片里索要密钥。