思考光球
Libraries.dev 的独立组件,请不要替换为 JGuang UI/Base UI 的同名 API。
安装
npm install thinking-orbs@0.3.2
安装包后使用它自己的导出组件,支持 React 18+。Canvas/WebGL 组件需要浏览器环境;提供静态降级,并尊重减少动画偏好。声音预览使用模拟信号,不会自动申请麦克风。
使用与验证
在左侧选择组件,在预览中操作它的状态;使用“查看代码”检查真实源码。安装后执行类型检查和构建,再验证键盘焦点、手机布局与深浅色主题。
来源
https://github.com/Jakubantalik/Libraries.dev/tree/b7f44b8744f4e09ff088d824719636a513110e2a MIT 许可证;不包括付费内容。下方提供上游英文参考内容,中文使用指南已适配;上游详细参考暂保留英文。
适用场景
为编程或聊天智能体展示紧凑的视觉状态。
关键原生参数
| 参数 | 说明 |
|---|---|
state | 九种原生状态值:working、searching、solving、listening、connecting、weaving、composing、breathing、shaping。 |
size | 可用 20、32、64;20 和 64 为调校后的形态,32 为插值。API 不接受任意数值尺寸。 |
paused | 冻结当前帧。speed、dots、dotSize 分别调节速度、密度和点大小。 |
theme | auto 观察祖先主题属性/类,light/dark 固定配色。 |
color | 可选墨色,需要保证与透明画布背后背景有足够对比。 |
交互与状态检查
检查每种状态、三种尺寸、暂停/继续与深浅色。为动画补充邻近状态文字,不只依赖画布传达信息。
业务接入边界
listening 不采集音频,searching 不调用搜索服务。frame/opts/gravity 是原生高级扩展口,不是通用状态机协议。
示例文件与宿主配置
从源码面板复制 examples 下的组件文件和 shared.tsx,保留它们的相对位置;不是只复制原生上游实现。先安装固定版本 npm 包,在浏览器组件中使用它自己的导出。示例不需要 Tailwind 或新的主题 Provider,采用宿主 html 的语言和主题,并提供减少动画与图形不可用降级。原生实现参考仅供阅读,通过 npm 导入,不要求手工复制内部引擎。示例场景名称不是新增的原生组件属性。下方上游详细参考保留英文,不宣称全文中文化。
可运行用法
以下代码使用资源原生 API;不是把其他设计系统的同名组件替换进来。
"use client";
import { useState } from "react";
import { ThinkingOrb, type OrbState, type OrbSize } from "thinking-orbs";
import { DemoFrame, GraphicsGuard, control, demoTheme, t } from "./shared";
export default function Example({ variant = "Default" }: { variant?: string }) {
const states: OrbState[] = ["working", "searching", "solving", "listening", "connecting", "weaving", "composing", "breathing", "shaping"];
const [state, setState] = useState<OrbState>(states.find(s => s === variant.toLowerCase()) ?? "working"), [size, setSize] = useState<OrbSize>(64), [paused, setPaused] = useState(variant === "Paused");
return <DemoFrame status={`state=${state}, size=${size}, paused=${paused}`} controls={<>
<label>{t("State", "状态")} <select style={control} aria-label={t("Orb state", "光球状态")} value={state} onChange={e => setState(e.target.value as OrbState)}>{states.map(s => <option key={s}>{s}</option>)}</select></label>
<label>{t("Size", "尺寸")} <select style={control} aria-label={t("Orb size", "光球尺寸")} value={size} onChange={e => setSize(Number(e.target.value) as OrbSize)}>{[20, 32, 64].map(s => <option key={s}>{s}</option>)}</select></label>
<button type="button" style={control} aria-pressed={paused} onClick={() => setPaused(!paused)}>{paused ? t("Resume", "继续动画") : t("Pause", "暂停动画")}</button>
</>}><GraphicsGuard><ThinkingOrb state={state} size={size} theme={demoTheme()} paused={paused} aria-label={t(`Agent visual state: ${state}`, `智能体视觉状态:${state}`)} /></GraphicsGuard></DemoFrame>;
}
可用状态与示例
工作中、搜索中、求解中、聆听中、连接中、编织中、创作中、呼吸、形状变化、暂停动画
组件中的状态与数据为演示用途,不包含后端接口。接入实际项目时请检查源码中的公开参数,再连接业务数据。
thinking-orbs
Dotted thought-orb loading indicators for AI & agent UIs. Nine hand-tuned animated states, each shipped at two purpose-tuned sizes, rendered on a plain 2D canvas — no WebGL, no filters, works identically in Chrome, Safari and Firefox.
Live demo · Repository · Report an issue
Install
npm install thinking-orbs
Quick start
import { ThinkingOrb } from 'thinking-orbs';
function Status() {
return <ThinkingOrb state="searching" size={64} />;
}
States
Nine verbs an agent can be doing, each a distinct animation:
<ThinkingOrb state="working" /> {/* particles on tilted orbits */}
<ThinkingOrb state="searching" /> {/* a scan meridian sweeps a dotted globe */}
<ThinkingOrb state="solving" /> {/* bands scramble, then click back solved */}
<ThinkingOrb state="listening" /> {/* a waveform rolls through the rings */}
<ThinkingOrb state="connecting" /> {/* a constellation wires itself */}
<ThinkingOrb state="weaving" /> {/* three strands plait around the sphere */}
<ThinkingOrb state="composing" /> {/* an undulating multi-band sash */}
<ThinkingOrb state="breathing" /> {/* a ring slowly morphing */}
<ThinkingOrb state="shaping" /> {/* dotted outline: circle → triangle → square */}
Sizes
Two tuned presets — separate designs, not a scale factor. 64 for chat-avatar scale, 20 for inline-text scale. Each carries its own dot count, dot size and speed tuning:
<ThinkingOrb state="working" size={64} />
<ThinkingOrb state="working" size={20} />
Theme
Strictly monochrome — light ink for dark backgrounds, dark ink for light backgrounds — with the mode picked automatically from the host project:
<ThinkingOrb theme="auto" /> {/* default — detects from the project */}
<ThinkingOrb theme="dark" /> {/* pin: light dots for dark backgrounds */}
<ThinkingOrb theme="light" /> {/* pin: dark dots for light backgrounds */}
auto resolves in three layers and updates live when any of them change:
- an ancestor
data-theme="dark|light"attribute ordark/lightclass (the Tailwind / shadcn convention), watched viaMutationObserver; - otherwise
prefers-color-scheme, subscribed for live OS theme switches; - SSR-safe — the canvas paints only on the client, after the theme has resolved.
Other props
<ThinkingOrb
state="solving"
size={20}
speed={1.5} // multiplier on the preset's baked speed
paused={false} // freeze on the current frame
aria-label="Analysing repository…" // overrides the per-state default
/>
All other <canvas> props (className, style, data-*, …) pass through.
Accessibility & performance
role="img"with a sensible per-statearia-labelout of the box.prefers-reduced-motion: reducerenders a static representative frame — no animation — and still follows the live theme.- Every instance pauses automatically when scrolled offscreen (
IntersectionObserver) or when the tab is hidden, and resumes in phase — all instances share one clock. - Plain 2D canvas arcs only: no
ctx.filter, no SVG filters, no WebGL — the same pixels everywhere, cheap on low-end devices. Device-pixel-ratio capped at 2.
License
MIT © Jakub Antalik
源码参数参考
以下实现签名和类型从本地固定版本源码生成。保留可选属性、原生继承类型、联合分支和 never 限制;不会将其他设计系统的 API 混入。forwardRef 实现中的 ref 属于 React 引用,不是新增业务属性。此区域是原始类型参考,不是上游说明的全文翻译。
ThinkingOrb({
state = 'working',
size = 64,
theme = 'auto',
speed = 1,
paused = false,
color,
dots = 1,
dotSize = 1,
opts: optsOverride,
frame: customFrame,
gravity,
style,
'aria-label': ariaLabel,
...rest
}: ThinkingOrbProps)
export type OrbState =
| 'working'
| 'searching'
| 'solving'
| 'listening'
| 'connecting'
| 'weaving'
| 'composing'
| 'breathing'
| 'shaping';
export type OrbSize = 64 | 32 | 20;
export type OrbTheme = 'auto' | 'dark' | 'light';
export interface ThinkingOrbProps extends Omit<CanvasHTMLAttributes<HTMLCanvasElement>, 'style'> {
/** Which animation to show. @default 'working' */
state?: OrbState;
/** Tuned size preset — 64 or 20 CSS px. @default 64 */
size?: OrbSize;
/** Theme mode; `auto` detects from the host project. @default 'auto' */
theme?: OrbTheme;
/**
* Animation speed multiplier on top of the preset's baked speed.
* @default 1
*/
speed?: number;
/** Freeze the animation on the current frame. @default false */
paused?: boolean;
/**
* Optional ink tint — any `#rgb`, `#rrggbb` or `rgb()` color. The orb's
* depth-shading ramp is preserved on the tint (fading toward black on
* dark substrates, toward white on light ones), so colored orbs read
* with the same 3D language as the grayscale default. Omit for the
* stock grayscale ink.
*/
color?: string;
/**
* Density multiplier for the mode's dot/strand/node counts, applied with
* the same paired-count scaler the size presets use so the mode keeps
* its balance. `1` (default) is the tuned look; `0.5` halves density,
* `2` doubles it. Clamped to a 0.1 floor.
*/
dots?: number;
/**
* Radius multiplier for every dot, applied with the same scaler the size
* presets use so near/far falloff keeps its proportion. `1` (default) is
* the tuned mark; `0.7` reads finer, `1.5` bolder.
*/
dotSize?: number;
/**
* Advanced: raw draw options merged over the resolved preset, last. These
* are the engine's own knobs, so they reach past the tuned surface — the
* orbit paths' `ghostA` and `particles` (working), the globe's `scanMul`
* and `dimBase` (searching), the constellation's `thr`, `signals`, `lineW`
* and `spread` (connecting), the plait's `turns` (weaving), the sash's
* `wobMul`, `bandMul` and `spin` (composing, breathing), and the outline's
* `spread` and `shape` (shaping — `shape` 0 / 1 / 2 holds the circle,
* triangle or square instead of cycling). Unknown keys are ignored; a key
* the current state does not read does nothing.
*/
opts?: ModeOpts;
/**
* Escape hatch below `opts`: replace the state's geometry outright. The
* function gets `(size, t, opts)` and returns the frame to paint — the
* same contract every built-in mode implements (see `MODE_FRAMES` and the
* helpers exported alongside it). The painter, theme, tint, pause and
* offscreen handling all stay the library's.
*/
frame?: ModeFrame;
/**
* Gravity: the orb pulls at the pointer. As the pointer nears, a raster
* of the platform's cursor is drawn in its place with its body warped
* toward the orb — the tip stays put, nothing rotates or scales. Needs the
* platform's real pointer raster (`gravity.sprite`, or
* `setGravitySprite`) or it stays off; off automatically under reduced
* motion, forced colours, coarse pointers, zoom, and over anything but
* the plain arrow. `true` takes the defaults. See gravity.ts.
*/
gravity?: boolean | GravityOptions;
style?: CSSProperties;
}
源码中的解构默认值
只列出直接写在参数或 props 解构中的默认表达式。条件表达式、运行时主题和其他计算值请结合源码;这里不是全部行为的默认值清单。
// ThinkingOrb
state = 'working'
// ThinkingOrb
size = 64
// ThinkingOrb
theme = 'auto'
// ThinkingOrb
speed = 1
// ThinkingOrb
paused = false
// ThinkingOrb
dots = 1
// ThinkingOrb
dotSize = 1
原生与外部类型
以下外部类型仍由 React 或资源自身依赖定义,没有手工展开为虚构属性。实际可用类型以安装版本的 TypeScript 检查为准。
Omit<CanvasHTMLAttributes<HTMLCanvasElement>, 'style'>
CanvasHTMLAttributes<HTMLCanvasElement>
HTMLCanvasElement
ModeOpts
ModeFrame
GravityOptions
CSSProperties