beta

Thinking Orb <mv-thinking-orb>

Orbe de assistente de IA em canvas: uma bolha brilhante que se deforma, gira e muda de material conforme o estado (idle, listening, thinking com satélites em órbita, speaking, success, error), com transições suaves entre os estados. Reage à voz: nível manual, MediaStream ou um microfone ativado pelo usuário.

CategoriaFeedback
TipoWeb Component (<mv-thinking-orb>)
Statusbeta
Keywordsai, assistant, orb, voice, microphone, audio, loader, agent, canvas, status

When to use

  • A voice or AI assistant needs a visual presence that reflects idle, listening, thinking and speaking states
  • The assistant should react to microphone input or an audio stream in real time
  • A single orb doubles as a mic toggle button for a voice interface

Avoid when

  • A lightweight inline thinking indicator is enough, such as in a chat message → use Pixel Loader instead
  • Several instances appear on one page or the target is low-end mobile; each orb runs its own canvas
  • A generic loading state with no assistant persona is needed → use Spinner instead

Instalação

node scripts/add.mjs thinking-orb --out ./src/marvelous

Agente de IA com o servidor MCP do Marvelous UI: install_components({ slugs: ["thinking-orb"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Arquivos copiados (dependências incluídas): tokens/tokens.css, core/base.css, core/canvas.js, core/dom.js, core/element.js, core/motion.js, components/thinking-orb/thinking-orb.js, components/thinking-orb/thinking-orb.css.

Uso

Marcação de referência, para usar como ponto de partida e personalizar com atributos, data-* e variáveis CSS:

<div style="display:grid;justify-items:center;gap:1.1rem;width:100%;max-width:560px">
  <mv-thinking-orb id="mv-thinking-orb-demo" state="idle" label="Assistant" mic style="--mv-thinking-orb-size:12rem"></mv-thinking-orb>
  <p id="mv-thinking-orb-demo-caption" style="margin:0;min-height:1.4em;font-size:.9rem;color:var(--mv-fg-muted)">Tap the orb to talk, or pick a state.</p>
  <div role="group" aria-label="Assistant state" style="display:flex;flex-wrap:wrap;justify-content:center;gap:.4rem">
    <button class="mv-button" data-size="sm" data-variant="outline" data-state="idle" aria-pressed="true">Idle</button>
    <button class="mv-button" data-size="sm" data-variant="outline" data-state="listening" aria-pressed="false">Listening</button>
    <button class="mv-button" data-size="sm" data-variant="outline" data-state="thinking" aria-pressed="false">Thinking</button>
    <button class="mv-button" data-size="sm" data-variant="outline" data-state="speaking" aria-pressed="false">Speaking</button>
    <button class="mv-button" data-size="sm" data-variant="outline" data-state="success" aria-pressed="false">Success</button>
    <button class="mv-button" data-size="sm" data-variant="outline" data-state="error" aria-pressed="false">Error</button>
  </div>
  <div style="display:flex;gap:1.5rem;align-items:center;margin-top:.4rem">
    <mv-thinking-orb state="thinking" label="Search" style="--mv-thinking-orb-size:3.5rem"></mv-thinking-orb>
    <mv-thinking-orb state="speaking" label="Reading" colors="oklch(0.78 0.16 160), oklch(0.72 0.14 200), oklch(0.85 0.12 110)" style="--mv-thinking-orb-size:3.5rem"></mv-thinking-orb>
    <mv-thinking-orb state="listening" label="Dictation" colors="oklch(0.72 0.19 30), oklch(0.8 0.15 70), oklch(0.66 0.2 350)" style="--mv-thinking-orb-size:3.5rem"></mv-thinking-orb>
  </div>
</div>
<style>
  [aria-label="Assistant state"] .mv-button[aria-pressed="true"] { background: var(--mv-accent-subtle); border-color: var(--mv-accent); color: var(--mv-accent-fg); }
</style>
<script type="module">
  const orb = document.getElementById("mv-thinking-orb-demo");
  const caption = document.getElementById("mv-thinking-orb-demo-caption");
  const texts = {
    idle: "Tap the orb to talk, or pick a state.",
    listening: "I’m listening…",
    thinking: "Looking up flights to Lisbon in October…",
    speaking: "Three nonstop flights on Oct 12, from $89.",
    success: "Booking confirmed.",
    error: "Couldn’t reach the server. Try again.",
  };
  const buttons = [...document.querySelectorAll("[data-state]")].filter((b) => b.closest("[role=group]"));
  const sync = () => {
    for (const b of buttons) b.setAttribute("aria-pressed", String(b.dataset.state === orb.state));
    caption.textContent = texts[orb.state] ?? "";
  };
  for (const b of buttons) b.addEventListener("click", () => { orb.state = b.dataset.state; sync(); });
  orb.addEventListener("mv-mic", (e) => {
    if (e.detail.error) caption.textContent = "Microphone unavailable (" + e.detail.error + ").";
    else sync();
  });
</script>

API

Attributes

NameTipoDefaultDescription
stateidle | listening | thinking | speaking | success | erroridleDisplayed state; parameters (size, warp, swirl, glow, satellites, hue) are smoothly interpolated.
labelstringAssistantName announced with the state (“Assistant: thinking”).
colorslist of CSS colors--mv-thinking-orb-1/2/3Three material colors (tokens accepted, follow the theme).
levelnumber 0-1Manual voice level (listening / speaking). With no level or mic, “speaking” simulates a voice.
micbooleanThe orb becomes a toggle button that opens the microphone on click (getUserMedia) and switches to listening.
speednumber1Overall animation speed.

Properties

NameTipoDescription
levelnumber | nullVoice level 0-1 (update every frame if needed, without an attribute).
streamMediaStream | nullExternal audio stream to visualize (not stopped by the orb).
listeningMicbooleanTrue while the orb’s microphone is open.

Methods

NameDescription
listenMic()Opens the microphone (call it from a user gesture); returns a Promise<MediaStream>.
stopMic()Stops the orb’s microphone.

Events

NameDescription
mv-micdetail: { active, stream?, error? }; microphone opened, closed or denied.

CSS variables

NameDefaultDescription
--mv-thinking-orb-size10remSize (the glow and satellites fit inside the box).
--mv-thinking-orb-1 / -2 / -3accent, sky blue, pinkDefault material colors.

Accessibility

Decorative canvas (aria-hidden); the state is exposed as visually hidden text in a polite live region (“Assistant: listening”). With mic: toggle button with a constant name and aria-pressed, visible focus. The microphone is never opened without a user gesture. Reduced motion: a representative still frame for each state. Pauses when off-screen.

Esta página foi traduzida com IA. Informar um problema de tradução