Skip to content

Conversation

@ShobhitPatra
Copy link
Member

@ShobhitPatra ShobhitPatra commented Dec 13, 2025

Fixes an SSR crash caused by accessing browser globals in the WebSpeechRecognitionAdapter constructor.


Important

This pull request adds speech-to-text dictation support using WebSpeechRecognitionAdapter and ElevenLabsScribeAdapter, with new primitives for dictation control and updated documentation.

  • Behavior:
    • Adds WebSpeechRecognitionAdapter and ElevenLabsScribeAdapter for speech-to-text dictation.
    • Updates ComposerRuntime to handle speech recognition sessions with startListening() and stopListening() methods.
    • Introduces listening state in ComposerClientState to track dictation status.
  • Primitives:
    • Adds ComposerPrimitive.Dictate and ComposerPrimitive.StopDictation for starting and stopping dictation.
    • Adds ComposerPrimitive.ListeningTranscript to display interim transcripts.
  • Examples:
    • Adds examples/with-elevenlabs-scribe demonstrating integration with ElevenLabs Scribe for real-time dictation.
  • Documentation:
    • Updates README.md and adds Dictation.mdx guide for speech-to-text setup and usage.
    • Updates API reference in Composer.mdx to include new dictation primitives.

This description was created by Ellipsis for 29ecc9a. You can customize this summary. It will automatically update as commits are pushed.

@changeset-bot
Copy link

changeset-bot bot commented Dec 13, 2025

🦋 Changeset detected

Latest commit: 29ecc9a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@assistant-ui/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
"fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in text-balance rounded-md bg-primary px-3 py-1.5 text-primary-foreground text-xs data-[state=closed]:animate-out",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typographical issue: The class string contains origin-(--radix-tooltip-content-transform-origin). Typically in Tailwind for arbitrary values, it should be written as origin-[--radix-tooltip-content-transform-origin]. Please verify if this is intended.

Suggested change
"fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in text-balance rounded-md bg-primary px-3 py-1.5 text-primary-foreground text-xs data-[state=closed]:animate-out",
"fade-in-0 zoom-in-95 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-[--radix-tooltip-content-transform-origin] animate-in text-balance rounded-md bg-primary px-3 py-1.5 text-primary-foreground text-xs data-[state=closed]:animate-out",

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

50 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 52 files

Prompt for AI agents (all 4 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="examples/with-elevenlabs-scribe/app/page.tsx">

<violation number="1" location="examples/with-elevenlabs-scribe/app/page.tsx:11">
P2: Creating `new ElevenLabsScribeAdapter()` directly in the component body creates a new instance on every render. Consider memoizing the adapter to maintain referential stability.</violation>
</file>

<file name="examples/with-elevenlabs-scribe/components/assistant-ui/tool-fallback.tsx">

<violation number="1" location="examples/with-elevenlabs-scribe/components/assistant-ui/tool-fallback.tsx:56">
P2: Chevron icons are inverted. When collapsed, `ChevronDownIcon` should be shown (indicating &quot;click to expand&quot;), and when expanded, `ChevronUpIcon` should be shown (indicating &quot;click to collapse&quot;). Currently it&#39;s the opposite, which contradicts the `aria-label` and standard UX conventions.</violation>
</file>

<file name="apps/docs/content/docs/api-reference/primitives/Composer.mdx">

<violation number="1" location="apps/docs/content/docs/api-reference/primitives/Composer.mdx:310">
P2: Inconsistent component naming: Uses `Composer.If` instead of `ComposerPrimitive.If`. All other examples in this file use `ComposerPrimitive` consistently.</violation>
</file>

<file name="examples/with-elevenlabs-scribe/components/assistant-ui/markdown-text.tsx">

<violation number="1" location="examples/with-elevenlabs-scribe/components/assistant-ui/markdown-text.tsx:68">
P2: The `setTimeout` callback can fire after component unmount, causing a React state update warning. Consider using `useEffect` with cleanup to clear the timeout, or track the timeout ID and clear it on unmount.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

export default function Home() {
const runtime = useChatRuntime({
adapters: {
speechRecognition: new ElevenLabsScribeAdapter({
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Creating new ElevenLabsScribeAdapter() directly in the component body creates a new instance on every render. Consider memoizing the adapter to maintain referential stability.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/with-elevenlabs-scribe/app/page.tsx, line 11:

<comment>Creating `new ElevenLabsScribeAdapter()` directly in the component body creates a new instance on every render. Consider memoizing the adapter to maintain referential stability.</comment>

<file context>
@@ -0,0 +1,25 @@
+export default function Home() {
+  const runtime = useChatRuntime({
+    adapters: {
+      speechRecognition: new ElevenLabsScribeAdapter({
+        tokenEndpoint: &quot;/api/scribe-token&quot;,
+        languageCode: &quot;en&quot;, // Change to your preferred language
</file context>
Fix with Cubic

aria-label={isCollapsed ? "Show tool details" : "Hide tool details"}
aria-expanded={!isCollapsed}
>
{isCollapsed ? <ChevronUpIcon /> : <ChevronDownIcon />}
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Chevron icons are inverted. When collapsed, ChevronDownIcon should be shown (indicating "click to expand"), and when expanded, ChevronUpIcon should be shown (indicating "click to collapse"). Currently it's the opposite, which contradicts the aria-label and standard UX conventions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/with-elevenlabs-scribe/components/assistant-ui/tool-fallback.tsx, line 56:

<comment>Chevron icons are inverted. When collapsed, `ChevronDownIcon` should be shown (indicating &quot;click to expand&quot;), and when expanded, `ChevronUpIcon` should be shown (indicating &quot;click to collapse&quot;). Currently it&#39;s the opposite, which contradicts the `aria-label` and standard UX conventions.</comment>

<file context>
@@ -0,0 +1,97 @@
+          aria-label={isCollapsed ? &quot;Show tool details&quot; : &quot;Hide tool details&quot;}
+          aria-expanded={!isCollapsed}
+        &gt;
+          {isCollapsed ? &lt;ChevronUpIcon /&gt; : &lt;ChevronDownIcon /&gt;}
+        &lt;/Button&gt;
+      &lt;/div&gt;
</file context>
Fix with Cubic

/>

```tsx
<Composer.If editing>{/* rendered if message is being edited */}</Composer.If>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Inconsistent component naming: Uses Composer.If instead of ComposerPrimitive.If. All other examples in this file use ComposerPrimitive consistently.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/content/docs/api-reference/primitives/Composer.mdx, line 310:

<comment>Inconsistent component naming: Uses `Composer.If` instead of `ComposerPrimitive.If`. All other examples in this file use `ComposerPrimitive` consistently.</comment>

<file context>
@@ -213,3 +227,87 @@ This primitive renders a `&lt;button&gt;` element unless `asChild` is set.
+/&gt;
+
+```tsx
+&lt;Composer.If editing&gt;{/* rendered if message is being edited */}&lt;/Composer.If&gt;
+
+&lt;Composer.If listening&gt;{/* rendered if dictation is active */}&lt;/Composer.If&gt;
</file context>
Suggested change
<Composer.If editing>{/* rendered if message is being edited */}</Composer.If>
<ComposerPrimitive.If editing>{/* rendered if message is being edited */}</ComposerPrimitive.If>
Fix with Cubic

.writeText(value)
.then(() => {
setIsCopied(true);
setTimeout(() => setIsCopied(false), copiedDuration);
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The setTimeout callback can fire after component unmount, causing a React state update warning. Consider using useEffect with cleanup to clear the timeout, or track the timeout ID and clear it on unmount.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At examples/with-elevenlabs-scribe/components/assistant-ui/markdown-text.tsx, line 68:

<comment>The `setTimeout` callback can fire after component unmount, causing a React state update warning. Consider using `useEffect` with cleanup to clear the timeout, or track the timeout ID and clear it on unmount.</comment>

<file context>
@@ -0,0 +1,237 @@
+      .writeText(value)
+      .then(() =&gt; {
+        setIsCopied(true);
+        setTimeout(() =&gt; setIsCopied(false), copiedDuration);
+      })
+      .catch((error) =&gt; {
</file context>
Fix with Cubic

@promptless
Copy link
Contributor

promptless bot commented Dec 13, 2025

📝 Documentation updates detected!

Updated existing suggestion: Add speech recognition adapters to API reference overview

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants