引导页组件
六个用来「展示」智能体产品而不必「成为」它的部件:窗口框、进行中的任务、起点卡片、等待屏、交接,以及消息输入框。
属于
@aora-bot/surfaces,不在@aora-bot/react里:pnpm add @aora-bot/surfaces @aora-bot/react,并在 React 样式之后引入@aora-bot/surfaces/styles.css。
- 读需求
- 起草回复
- 核对数字
- 开 pull request
DemoDesktop
一个窗口框。引导页要展示软件而又不能是软件本身,而一张普通卡片读不出「应用」的感觉——窗口标题栏一个字不说,就能告诉人「这是别处正在发生的事情的一张画面」。框是纯展示的:包在宿主内容外面的 aria-hidden 外壳,内容保留自己的语义。toolbar 放标题栏下方的小外壳——页签、一个假 URL。
JobBubbles
进行中的任务,化作角色周围的文字。用气泡而不是列表,因为要表达的是「几件事同时进行」,而列表读起来像队列。每个气泡以各自的周期、负的延迟漂浮,永不同相——一圈标签同步起伏,看起来就像一个物体在旋转。环用真实角度排布,而不是旋转容器:容器一转,文字跟着转,翻过来的标签没法读。jobs 默认取本地化里的四条;radiusPx 设定环的半径。
SuggestionCards
三个起点,各带一张脸。角色由卡片 id 推导而非人为挑选,这正是这个组件通用的原因:宿主传自己的卡片,不必挑形状就得到三个能分辨的智能体;同一张卡永远长一个样,因为推导是哈希而不是洗牌。在卡片上设 shape 和 color 可以覆盖。
LandingWait
随时可以开始
给它第一个任务,它就会动起来。
批准窗口
超时后运行会自行继续。
什么都还没发生之前的那一屏。空白状态配一个等待中的角色,说的是「产品已经就绪,不是坏了」,这恰恰是空屏幕自己说不出来的一句话。不传 waitMs 时底盘计时已经等了多久;传了就变成截止时间,角色戴上 timer 表盘,底盘倒数。时钟放在底盘而不是表盘里,因为数字就是数字:外圈能表示时间在流逝,只有文字能说流逝了多少。since 让时钟从宿主已知的某个时刻起算。
HandOff
一个智能体把工作交给另一个。sending 和 receiving 在引擎里是配对的——一个向外倾,另一个接住——所以交接不需要一个在两人之间飞的物件也能读懂;身体已经描述了方向。用三拍而不是连续动画,因为这个过程有含义、有结尾:之前、之中、之后。loop 需要主动开启,给没人等它结束的营销页用。
Composer
一个有角色留意着它的消息输入框。三个状态,由输入框在做什么驱动,而不是靠定时器:聚焦时 listening,输入时 composing,停止输入 idleMs 之后回到 idle。输入框旁边的角色只有在对输入有反应时才值得存在,而这个反应要快到像是在关注,又要安静到不打扰书写。不传 value 时是非受控的,可以不带任何状态直接放进演示里。下方的 props 表由 ComposerProps 生成;sendButton 隐藏发送按钮,给自带发送控件的宿主用。
API
DemoDesktop
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<AwaitedReactNode> | null | — | Content of the window. |
className | string | — | — |
style | CSSProperties | — | — |
title | string | — | Text in the window's title bar. |
toolbar | string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<AwaitedReactNode> | null | — | Small chrome below the title bar — tabs, a toolbar, a fake URL. |
widthPx | number | 420 | — |
JobBubbles
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
className | string | — | — |
color | "black" | "brown" | "red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "violet" | "magenta" | "pink" | "gray" | — | — |
face | "cute" | "eyes" | "line" | — | — |
jobs | readonly string[] | — | Short task strings. Defaults to four from the locale. |
lineWeight | number | — | — |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
radiusPx | number | 96 | Radius of the ring the bubbles sit on. |
shape | "mochi" | "circle" | "blob" | "pebble" | "egg" | "bun" | "tofu" | "pill" | "hex" | "onigiri" | "gem" | "cloud" | "drop" | "leaf" | "flower" | "clover" | "sprout" | "mushroom" | "acorn" | "cactus" | "pear" | "apple" | "strawberry" | "cherry" | "star" | "heart" | "flame" | "bell" | "ghost" | "bot" | "crescent" | "boba" | "fish" | "octopus" | "cat" | "bunny" | "bear" | "chick" | "penguin" | "pig" | "fox" | "bob" | "topknot" | "spiky" | "curly" | "twinbuns" | — | — |
sizePx | number | — | — |
state | "idle" | "sleeping" | "drowsy" | "bored" | "waking" | "powering-down" | "listening" | "thinking" | "searching" | "reading" | "writing" | "working" | "loading" | "progress" | "radar" | "orbit" | "dictating" | "uploading" | "sending" | "receiving" | "messaging" | "waiting" | "spawning" | "composing" | "dragging" | "happy" | "excited" | "laughing" | "playful" | "proud" | "celebrate" | "bouncing" | "shy" | "sad" | "angry" | "confused" | "suspicious" | "surprised" | "scared" | "curious" | "humming" | "alerting" | "notifying" | "refusing" | 'working' | — |
style | CSSProperties | — | — |
SuggestionCards
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
cards | readonly SuggestionCard[] | — | Defaults to three examples from the locale. |
className | string | — | — |
face | "cute" | "eyes" | "line" | — | — |
lineWeight | number | — | — |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
onPick | ((card: SuggestionCard) => void) | — | — |
sizePx | number | — | — |
style | CSSProperties | — | — |
LandingWait
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
action | string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<AwaitedReactNode> | null | — | Buttons under the copy. |
body | string | — | — |
className | string | — | — |
color | "black" | "brown" | "red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "violet" | "magenta" | "pink" | "gray" | — | — |
face | "cute" | "eyes" | "line" | — | — |
lineWeight | number | — | — |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
shape | "mochi" | "circle" | "blob" | "pebble" | "egg" | "bun" | "tofu" | "pill" | "hex" | "onigiri" | "gem" | "cloud" | "drop" | "leaf" | "flower" | "clover" | "sprout" | "mushroom" | "acorn" | "cactus" | "pear" | "apple" | "strawberry" | "cherry" | "star" | "heart" | "flame" | "bell" | "ghost" | "bot" | "crescent" | "boba" | "fish" | "octopus" | "cat" | "bunny" | "bear" | "chick" | "penguin" | "pig" | "fox" | "bob" | "topknot" | "spiky" | "curly" | "twinbuns" | — | — |
since | number | — | Start the clock from a moment the host already knows about. |
sizePx | number | — | — |
style | CSSProperties | — | — |
title | string | — | — |
waitMs | number | — | A deadline. With one the character shows a countdown dial and the plate counts down; without one it shows how long it has been waiting. |
HandOff
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
from* | AgentLike | — | Who is passing the work on. |
to* | AgentLike | — | Who is picking it up. |
beatMs | number | 1400 | Length of each beat. The full cycle is three of them. |
className | string | — | — |
face | "cute" | "eyes" | "line" | — | — |
label | string | — | What is being handed over, shown between the two. |
lineWeight | number | — | — |
loop | boolean | false | Run the beats on a loop. A single pass by default. |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
sizePx | number | — | — |
style | CSSProperties | — | — |
Composer
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
className | string | — | — |
color | "black" | "brown" | "red" | "orange" | "yellow" | "green" | "cyan" | "blue" | "violet" | "magenta" | "pink" | "gray" | — | — |
face | "cute" | "eyes" | "line" | — | — |
idleMs | number | 1200 | How long after the last keystroke the character stops composing. |
lineWeight | number | — | — |
nose | "slant" | "angular" | "hook" | "long" | "curve" | "bow" | null | — | — |
onChange | ((value: string) => void) | — | — |
onSubmit | ((value: string) => void) | — | — |
placeholder | string | — | — |
sendButton | boolean | true | Show the send button. On by default. |
shape | "mochi" | "circle" | "blob" | "pebble" | "egg" | "bun" | "tofu" | "pill" | "hex" | "onigiri" | "gem" | "cloud" | "drop" | "leaf" | "flower" | "clover" | "sprout" | "mushroom" | "acorn" | "cactus" | "pear" | "apple" | "strawberry" | "cherry" | "star" | "heart" | "flame" | "bell" | "ghost" | "bot" | "crescent" | "boba" | "fish" | "octopus" | "cat" | "bunny" | "bear" | "chick" | "penguin" | "pig" | "fox" | "bob" | "topknot" | "spiky" | "curly" | "twinbuns" | — | — |
sizePx | number | — | — |
style | CSSProperties | — | — |
value | string | — | — |