Commit 8af4bf2
Frontend UX alignment with Foundry Portal design (#22)
* Phase 1: Add TopBar, remove StartScreen, auto-connect on load
- Add TopBar component with agent name, Voice/Text segmented toggle,
New Thread button, and settings gear icon
- Add useUrlParams hook for ?mode=voice|text and ?lock=true params
- ?mode= locks input mode and hides toggle
- ?lock=true hides settings gear + toggle entirely
- Remove StartScreen — app auto-connects WebSocket on mount
- Remove SessionEnded screen — idle state shows agent details
- Settings panel now accessible from TopBar gear icon in all states
- Frontend builds and type-checks cleanly
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Phase 2: Voice mode polish — fix layout, remove dead components
- Fix ActiveSession height from 100vh to 100% (works with TopBar)
- Delete StartScreen.tsx and SessionEnded.tsx (no longer used)
- Session end now returns to idle state with orb in content area
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Phase 3: Text mode via Voice Live WebSocket
- Add ChatMessages component (scrollable bubbles, user right/assistant left)
- Add ChatInput component (text bar + send button, Enter to send)
- Add sendTextMessage to useVoiceSession hook (sends 'send_text' WS msg)
- Add send_text handler to Python voice_handler (conversation.item.create
+ response.create via Voice Live SDK)
- Wire send_text in Python app.py WebSocket message router
- TopBar Voice/Text toggle switches between VoiceOrb and Chat views
- Fix auto-connect race: gate on configLoaded flag (review feedback)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Phase 4: Agent details, starter messages, waves background
- Add AgentPrimaryDetails component (icon, name, description)
- Add StarterMessages component (clickable prompt cards)
- Add Waves component (animated SVG background for idle state)
- Replace basic idle VoiceOrb with rich agent details + prompts
- Waves animate behind idle content, pause when session is active
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix review findings: session leak, mic capture, transcript duplication
- Fix New Thread: call stopSession before resetSession to close WS/capture
- Fix text mode: skip mic capture when inputMode is 'text' via ref
- Fix text mode: add end-session button alongside ChatInput
- Fix ChatMessages: filter out non-final streaming duplicates
- Sync inputModeRef from App to useVoiceSession hook
Addresses all 3 high-severity findings from Codex 5.3 review.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Phase 5: Visual polish — update BuiltWithBadge to Microsoft Foundry
- Update badge text from 'Azure AI Foundry' to 'Microsoft Foundry'
matching the Foundry Portal's branding
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix final review: starter prompts, setInputModeRef stability
- Fix starter prompts: start session if idle before sending text,
queues message to fire after connection establishes
- Wrap setInputModeRef in useCallback to prevent useEffect re-runs
on every render (new function identity issue)
Addresses Opus 4.6 final review findings.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Playwright smoke tests for new frontend UX
9 tests covering: TopBar controls, Voice/Text toggle, URL params
(?lock=true, ?mode=text), settings panel, built-with badge, mode
switching. All pass against local Python backend.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Visual overhaul: match Foundry Portal design exactly
TopBar:
- Replace segmented toggle with ··· dropdown menu
- Menu has Settings, Terms of use, Privacy, Send feedback
- New chat button with text+icon label
Idle state:
- Re-introduce Start Session flow (agent heading + description +
solid purple orb + 'Lets talk' + Start session pill button)
- Gray waves background matching Foundry screenshot
Active voice orb:
- 3 concentric circles (244/182/138px ratio) with graduated opacity
- Pulse animation 1.8s for listening state
- Solid circle in idle (120px)
Theme:
- Default to light mode (was dark)
- Gray waves (was purple)
URL params:
- Added ?agent=, ?project= (pre-fill agent config)
- Added ?theme=light|dark (override theme)
- Added ?greeting=false (suppress proactive greeting)
Badge:
- Updated to 'Build & deploy AI agents with Microsoft Foundry →'
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix review findings: connecting overlap, locked mode, variable ordering, tests
- Fix text mode connecting overlap: add state !== 'connecting' guard
- Fix locked mode dead-end: don't disable Start when isLocked
- Fix URL params: only force agent mode when BOTH agent+project provided
- Fix variable ordering: move isActive/isIdle before handleNewThread
- Rewrite Playwright tests for new TopBar (··· menu, Settings from menu)
- All 8 Playwright tests pass
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fine-tune visual details to match Foundry Portal screenshots
- Theme: change from segmented buttons to dropdown in Settings
- TopBar: use bg-1 background (lighter), round ··· button, thinner icons (1.5 strokeWidth)
- BuiltWithBadge: two-row layout matching screenshot (line 1: small text, line 2: bold brand + arrow)
- Menu icons: 18px with 1.5 stroke for cleaner Fluent-like appearance
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Align font sizes to match Foundry Portal exactly
All sizes now use px values matching the Foundry CSS:
- Agent heading: 20px (was 1.6rem/26px)
- Description: 14px (was 0.95rem/15px)
- Let's talk: 20px (was 1.25rem/20px - already correct)
- Talk description: 14px, max-width 280px
- Start button: 14px (was 1rem/16px)
- TopBar agent name: 14px (was 0.95rem)
- New chat button: 14px, weight 400
- Menu items: 14px
- Badge: 12px/14px
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Align spacing and sizing to Foundry design tokens
Mapped to Fluent UI spacing tokens (spacingS=8, spacingL=16, spacingXL=20, spacingXXL=24):
- Idle content gap: 8px (was 12px) matching labelBlock gap
- Let's talk margin-top: 8px (was 16px) matching idleLabelSpacing
- Start button: margin-top 24px (spacingXXL), font 16px, padding 8px 24px
- Subtitle max-width: 250px (was 280px) matching subtitleText
- Title lineHeight: 20px (was 24px) matching lineHeightBase300
- Controls: 40px buttons (was 48px), 8px gap (was 16px), 16px padding
- Status text: 20px semibold (was 1.1rem/500) matching titleText
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Comprehensive UX alignment — fix all 33 gap analysis items
P0 fixes:
- VoiceOrb active circle ratios: 1.15x/1.52x/2.03x (138/182/244 from 120)
- ActiveSession orb always 120px base (was 240/160)
- Idle heading 28px (was 20px) for visual prominence
- Start button min-width 200px, padding 8px 16px
- Waves: 3 layers, --wave-color token, height 30%
P1 fixes:
- All secondary text: --fg-2 (was --fg-3) for colorNeutralForeground2
- Bubble borderRadius: 8px everywhere (was 12-16px)
- Transcript text: 12px (was 15px), chat text: 14px
- All rem values converted to px
- Settings title: 20px/600 (was 1.3rem/700)
- Settings labels: 14px/600/--fg-2 (was 0.85rem/500/--fg-3)
- All input fonts: 14px, padding 8px 12px
- CC label weight: 600 (was 700)
- Mic button: purple accent ring (--voice-primary border)
P2 fixes:
- TopBar dots button: 40px (was 32px)
- All SVG icons: 20px (was 16-18px)
- All borderRadius on inputs/banners: 8px (was 10px)
- Menu item gap: 8px, TopBar padding: 12px 20px
- Send feedback icon: chat bubble (was person-group)
P3 fixes:
- --wave-color CSS token added (gray for light, subtle for dark)
- Badge aria-label selector fixed
- Optional badge: 12px (was 0.7rem)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix layout: heading at top, orb centered — match reference screenshot
- Separate heading+description (top of content) from orb+labels+button
(vertically centered) matching the Foundry Portal layout
- Start button: auto-width (removed minWidth 200px), 14px font
- Heading: 24px (was 28px), positioned at top not centered
- TopBar: New chat grayed (--fg-3), dots button thinner border
- Fix connecting state style reference
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Replace all icons with official Fluent UI 20px paths, lighten waves
- TopBar: all 6 icons replaced with Fluent UI system icons
(ChatAdd, MoreHorizontal, Settings, TextAlignLeft, Shield, PersonFeedback)
- All SVGs now use fill='currentColor' (no stroke) matching Foundry
- Waves: reduced opacity to 0.04/0.025 for subtler appearance
- Wave color lightened (light: #e0e0e0, dark: rgba(255,255,255,0.05))
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Remove temp icon extraction script
* Fix font, text colors, and wave visibility to match reference
Fonts:
- Add 'Segoe UI Variable' as primary font (Foundry Portal uses this)
- Set base font-size 14px and line-height 20px on body
Light theme colors:
- bg-1: #fafafa (was #f5f5f5) — slightly warmer white
- fg-2: #707070 (was #616161) — lighter secondary text
- fg-3: #a0a0a0 (was #8a8a8a) — lighter tertiary text
- wave-color: #d1cdc7 — warm taupe gray matching reference waves
Waves:
- Opacity 0.50/0.35/0.25 (was 0.04/0.025/0.04) — now clearly visible
like the reference screenshot's layered gray waves
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Canvas waves, fix font colors and sizes to match Foundry exactly
- Waves: rewritten from SVG to Canvas rendering matching Foundry code
(3 layers, hsla colors, requestAnimationFrame, ResizeObserver)
- Font colors: fg-2=#424242, fg-3=#616161 (matching colorNeutralForeground)
- Agent heading: 20px (was 24px) matching Foundry titleText
- Background: bg-1=#f5f5f5 (Fluent colorNeutralBackground3Selected)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Match Foundry VoiceOrb, controls, and session-ended exactly
VoiceOrb — adapted from Foundry AudioPulse pattern:
- Idle: 120px solid purple circle, no rings
- Connected: two pulse rings (228px@0.2, 190px@0.3) behind core
- Speaking: pulseMove animation (1.5s, scale 1.06)
- Listening: pulseListening animation (1.8s, scale 1.06)
- Sizes match Foundry (MIN_PULSE_SIZE=190, SCALE_FACTOR=1.2)
SessionControls — adapted from Foundry actionBar:
- Icon buttons: 40px, transparent bg, circular
- Mic: branded border (--voice-primary), accent ring
- Muted mic: filled bg, accent stroke
- End session: text pill button (not X icon)
SessionEndedView — new component:
- Shows sessionID at top
- Filtered final transcript entries
- New chat button to restart
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: align all frontend styles to Foundry design tokens
- App.tsx: start button fontSize 14→16px, lineHeight 20→22px
- TopBar.tsx: bar padding 20→16px, right gap 4→8px, --fg-3→--fg-2, rem→px
- SettingsPanel.tsx: all --fg-3→--fg-2 (6 inline + 2 style objects), rem→px
- TranscriptOverlay.tsx: bubble padding 12px 16px→8px 12px, role 0.75rem→12px, --fg-3→--fg-2
- ErrorBanner.tsx: close button 1rem→14px
- BuiltWithBadge.tsx: logo 1.4rem→22px, line1 --fg-3→--fg-2
- AgentPrimaryDetails.tsx: 3rem→48px, 1.4rem→20px, 0.95rem→14px, --fg-3→--fg-2
- StarterMessages.tsx: 0.9rem→14px
Zero rem values remain in components. Zero --fg-3 usage in components.
tsc --noEmit and vite build pass clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix session-ended overlap, CC icon, purple color
- Fix isIdle: exclude 'ended' state so SessionEndedView renders alone
(was showing idle Start screen on top of ended transcript)
- CC button: replace text 'CC' with Fluent ClosedCaption20Regular icon
- Purple color: #7B5EA7 (was #7c5cbf) — matches Foundry colorBrandBackground
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat: adopt Fluent UI components across Voice Live frontend
- Wrap app in FluentProvider with dynamic theme switching (light/dark)
- Replace CSS theme vars with Fluent token aliases (with fallbacks)
- App.tsx: Replace start button with Fluent Button
- TopBar.tsx: Replace hand-built dropdown with Fluent Menu/MenuItem
- SessionControls.tsx: Replace all buttons with Fluent Button components
- SessionEndedView.tsx: Replace new chat button with Fluent Button
- ActiveSession.tsx: Replace status text with Fluent Text component
- ChatInput.tsx: Replace send button with Fluent Button + SendRegular icon
- ErrorBanner.tsx: Replace close button with Fluent Button + DismissRegular
- SettingsPanel.tsx: Replace all form controls with Fluent components
(Select, Input, Textarea, Slider, Checkbox, Label, Text, Button)
- Remove all manual menu state management (useState, useRef, useEffect)
- Remove ~340 lines of hand-written style constants
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Adopt Fluent UI components + fix review findings
Phase 1: Foundation
- Add @fluentui/react-components dependency
- Wrap app in FluentProvider with dynamic webLightTheme/webDarkTheme
- Map all CSS vars to Fluent token aliases with fallbacks
Phase 2: Component swaps
- TopBar: Fluent Menu/MenuItem/Button (removed 100 lines of manual dropdown)
- SessionControls: all buttons → Fluent Button with icons
- App.tsx: Start session → Fluent Button appearance=primary shape=circular
- SessionEndedView: New chat → Fluent Button
- ActiveSession: status → Fluent Text
- ChatInput: send → Fluent Button with SendRegular icon
- ErrorBanner: close → Fluent Button with DismissRegular
Phase 4: SettingsPanel
- All form controls → Fluent Select/Input/Textarea/Slider/Checkbox/Label
Review fixes:
- Scope button hover/active CSS to non-Fluent buttons only
- Remove !important from focus styles, scope to non-Fluent controls
- Fix system theme detection: track via React state for FluentProvider re-render
- Update Playwright tests for Fluent component selectors
8/8 Playwright tests pass. TypeScript + build clean.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix: use custom purple brand theme instead of default blue
- Created theme.ts with purple BrandVariants ramp (base #7B5EA7)
- App.tsx: use voiceLiveLightTheme/voiceLiveDarkTheme instead of webLightTheme/webDarkTheme
- main.tsx: remove duplicate FluentProvider wrapper (App.tsx handles it)
- Now --colorBrandBackground resolves to purple, not Fluent default blue
- Start session button, orb, and all brand-colored elements are purple
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(frontend): align UX with Foundry Portal reference implementation
- CSS Grid layout matching agentPreview.module.css (items 1, 10, 11)
- Background tokens using colorNeutralBackground3Selected (item 6)
- Global CSS cleanup: removed conflicting non-Fluent rules (item 13)
- Waves: dark/light theme support, fade-in animation (item 5)
- VoiceOrb: 3-ring circleStack system (244/182/138px) (item 2)
- SessionControls: 200px branded pill end button, brand mic (item 3)
- TopBar: removed border, h1 ellipsis, disable New chat in session (item 4)
- Idle state: proper spacing, start button sizing (item 7)
- SessionEndedView: branded pill New chat button (item 8)
- TranscriptOverlay: 420px max-width, 12px bubbles (item 12)
- BuiltWithBadge: grid-positioned with container query responsive (item 10)
Reference: ADO msdata/Vienna/azure-ai-foundry main branch
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): match reference code exactly for controls, orb, and active session
SessionControls:
- CC: always transparent bg + neutralForeground2 (no brand-filled active state)
- Mic: neutralBackground1 bg + brand border (not transparent)
- Muted: neutralBackground3 + neutralForeground1 (not red/brand colors)
- End: 20% width, min-200px, neutral bg + brand text + thin border
VoiceOrb:
- Active core: 160px with opacity 0.33 (was 120px)
- Idle core: 120px solid (was wrongly 0.33 opacity)
- stackTalking: reduces ring opacities (was scale animation)
- captionsActiveHidden: hides circle when CC active
ActiveSession:
- Passes captionsActive to VoiceOrb for proper hiding
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): match reference icons exactly
TopBar menu:
- Settings: Settings24Regular (was SettingsRegular)
- Privacy: ShieldRegular (was ShieldCheckmarkRegular)
- Terms/Privacy: Open16Regular external-link suffix (was ↗ text)
SessionControls (action bar):
- CC: ClosedCaption24Regular / ClosedCaptionOff24Regular (was custom SVG)
- Mic: Mic24Regular / MicOff24Regular (was MicRegular/MicOffRegular)
- End: Dismiss24Regular icon-only (was 200px text pill)
- Icon color: colorNeutralForeground3 lighter at rest (was Fg2 darker)
AgentPrimaryDetails:
- Removed robot emoji icon (not in reference)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): settings panel fields stacked vertically with full width
fieldStyle now uses flex-direction:column so Label headers appear
above their Textarea/Input controls, matching dropdown layout width.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): session ended — add session ID box, remove New chat button and emoticons
- Session ID displayed in bordered monospace box matching reference
- Removed purple 'New chat' button (use header button instead)
- Removed 👤/🤖 emoticons from transcript entries
- Centered layout
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): always show session ID box (with N/A fallback)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): center agent details, expand CC transcript area, session ID box outline
- Agent name + description: added alignItems:center to flex container
- CC transcript area: removed justifyContent:center from container so
transcript gets full available height, reduced gap to 12px
- Session ID box: use colorNeutralStroke1 (darker) for visible outline
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): color audit fixes — Fg2 icons, hover/active states, brand-blue token
SessionControls:
- Icon color: --colorNeutralForeground2 (#424242) matching reference line 472
- Hover: transitions to --colorNeutralForeground1 matching reference line 526
- Active: scale(0.92) matching reference line 530
ChatMessages:
- User bubble bg: --colorBrandBackground (was undefined --brand-blue)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(backend): forward service session ID from SESSION_CREATED event
The VoiceLive service sends a SESSION_CREATED event with a
ResponseSession object containing an 'id' field. We now capture
this in _handle_event and include it as 'session_id' in the
'session_started' message sent to the frontend.
Previously the frontend received no session_id, so the ended
screen always showed 'N/A'.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(backends): forward service session ID in all backends (JS, C#, Java)
All backends now handle SESSION_CREATED to capture the service
session ID, and include it as 'session_id' in the 'session_started'
WebSocket message sent to the frontend.
Pattern (same in all 4 backends):
1. SESSION_CREATED → store event.session.id
2. SESSION_UPDATED → fallback capture if not already set
3. session_started message → include session_id field
Falls back to client_id if service doesn't provide one.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(frontend): text mode improvements + audio playback toggle + agent mode guard
Text mode:
- Moved text chat + controls inside CSS Grid content area (same
position as voice mode, no longer below page footer)
- Added speaker toggle button (Speaker224Regular / SpeakerOff24Regular)
with same circle design as mic button in voice mode
- Audio playback defaults to OFF, user can toggle on
- Dismiss X button for ending session (same as voice mode)
Audio playback:
- Added GainNode-based mute/unmute to useAudioPlayback hook
- Exposed isPlaybackMuted + togglePlaybackMute through useVoiceSession
Agent mode guard:
- Agent mode toggle disabled in Settings when no agent is configured
on the server (agentName and project both empty)
- Matches main branch behavior
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): text mode — wider controls, barge-in on send, skip audio when muted
- Text input bar uses full chat width (removed justify-content:center)
- sendTextMessage calls stopPlayback() for barge-in (interrupts audio)
- playAudio skips decode+worklet entirely when muted (saves resources)
- Uses isMutedRef for synchronous check in playAudio callback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: add URL query parameters, text mode, and session ID documentation
- New 'Frontend URL Query Parameters' section documenting all 6 params
(?mode, ?lock, ?agent, ?project, ?theme, ?greeting)
- New 'Text Mode' section explaining Voice Live text modality (not
Foundry Agent Service API), audio playback toggle, barge-in
- Updated architecture diagram showing voice + text paths
- Added send_text to WebSocket protocol table
- Updated session_started to mention session_id
- Added agent mode guard and audio playback UX guards
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): voice mode audio no longer muted, text mode mute resets on new chat
Root cause: useAudioPlayback had isPlaybackMuted=true by default and
playAudio skipped ALL audio when muted — affecting both voice and text.
Fixes:
- Gain node starts at 1 (full volume) — voice mode always plays
- Mute check moved to useVoiceSession: only skips audio in text mode
- resetPlaybackMute() called on session reset (fixes stale icon)
- Removed early-return from playAudio (was killing voice mode)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(csharp): use _clientId instead of _config.ClientId for session ID fallback
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(frontend): Foundry-aligned dark theme with custom background overrides
theme.ts:
- Dark theme uses Foundry Portal's HSL step system for backgrounds
(bg1=12%, bg2=10%, bg3=8%, bg4=6%, bg5=3%, bg6=0%)
- Brand foreground overrides for dark mode contrast (step 110/120/140)
- Light theme uses step 70 for colorBrandBackground (darker, per Foundry)
App.css:
- Dark mode fallbacks now use Foundry-exact values:
--bg-1: #1c1c1c, --bg-2: #1f1f1f, --bg-3: #1a1a1a
--fg-1: #ffffff, --fg-2: #d6d6d6
--border: rgba(255,255,255,0.10)
- Fixes settings panel white background in dark mode
- Fixes all borders/overlays appearing as dark-on-dark
Reference: ADO msdata/Vienna/azure-ai-foundry packages/core/contexts/theme/src/themes.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* feat(frontend): switch to exact Foundry brand ramp + Aptos font
Brand ramp: exact values from Foundry Portal themes.ts
Base: #8251EE (was #7B5EA7)
Step 70 (brandBg): #643FB2 (was #6B3AB3)
All 16 steps now match Foundry exactly
Font: Aptos, sans-serif (was Segoe UI Variable)
Matches Foundry Portal's fontFamilyTokens
Monospace: Aptos Mono (was system default)
Reference: ADO msdata/Vienna/azure-ai-foundry
packages/core/contexts/theme/src/themes.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): CC transcript uses full height — skip orb in CC mode
When closed captions are active, the orb area (with hidden circle)
was still consuming space via padding and flexShrink:0. Now the
CC layout skips the orb entirely — transcript gets flex:1 full
height, status text shown inline with minimal padding, controls
at bottom. Normal voice mode layout unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(frontend): text mode audio toggle works during active session
Root cause: playAudio wrapper used isPlaybackMuted state in closure,
which was stale after WebSocket onmessage handler was bound at session
start. Toggling unmute created a new playAudio function but the WS
handler kept the old reference with isPlaybackMuted=true.
Fix: use isMutedRef.current (ref, not state) for the mute check.
Refs are stable across renders and always reflect current value.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add MCP config for Copilot coding agent
Configures Azure MCP (docs, best practices) and Foundry Storybook MCP
for the GitHub Copilot coding agent.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix MCP config: add required type field
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix MCP config: add required tools arrays
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Switch CodeQL to advanced setup with autobuild for C# (#23)
Default setup used build-mode 'none' for C# which resulted in low
analysis quality (call target resolution 83% vs 85% threshold).
Switching to autobuild resolves NuGet dependencies properly.
Other languages (JS/TS, Python, Java) remain on build-mode 'none'.
Upgraded to security-extended query suite.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Dependabot config for automated dependency updates (#24)
* Switch CodeQL to advanced setup with autobuild for C#
Default setup used build-mode 'none' for C# which resulted in low
analysis quality (call target resolution 83% vs 85% threshold).
Switching to autobuild resolves NuGet dependencies properly.
Other languages (JS/TS, Python, Java) remain on build-mode 'none'.
Upgraded to security-extended query suite.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add Dependabot config for all package directories
Covers 28 directories across 4 ecosystems:
- pip (5 Python dirs)
- npm (10 JS/TS dirs including @azure/ai-voicelive)
- nuget (6 C# dirs)
- maven (2 Java dirs)
Weekly schedule, voicelive-repo-maintainers as reviewers,
language-specific labels for filtering.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add send_text support to JS/C#/Java backends and fix useEffect deps
Critical: Text mode chat (send_text WebSocket message) was only handled by the
Python backend. JavaScript, C#, and Java backends silently ignored text messages,
making text mode non-functional on 3 of 4 backends.
Changes per backend:
- JavaScript: Add sendText() using session.sendEvent with
ConversationItemCreate + ResponseCreate
- C#: Add SendTextAsync() using session.SendCommandAsync with raw JSON
for conversation.item.create + response.create
- Java: Add sendText() using typed UserMessageItem with
session.addItem + session.startResponse
Also fix React useEffect exhaustive-deps warning in App.tsx by adding
all referenced values (agent, project, greetingDisabled, updateSettings)
to the dependency array.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add E2E test suite and fix Java SessionUpdate cast
Add test_local_all_backends.py — comprehensive local E2E test suite that:
- Starts each backend (Python/JS/C#/Java) sequentially on port 8000
- Tests WebSocket voice mode (send audio, verify response)
- Tests WebSocket text mode (send_text, verify response) — validates the PR fix
- Runs Playwright browser tests with video recording
- Screenshots saved per backend per test phase
Fix pre-existing Java compile error: SessionUpdate.getSession() doesn't exist
on the base class. Cast to SessionUpdateSessionCreated/SessionUpdateSessionUpdated
subclasses which have the getSession() method.
Test results: 11/12 passed across all 4 backends.
Only failure: Java voice WS test (pre-existing Spring WebSocket close frame issue).
All 4 backends pass the critical send_text test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Java/Spring WebSocket close frame and message buffer issues
Three fixes for the Java backend WebSocket handling:
1. WebSocketConfig: Add ServletServerContainerFactoryBean to disable
idle timeout (0L) and increase message buffer sizes (512KB). Voice
Live sessions stream audio continuously and need large buffers.
2. VoiceLiveWebSocketHandler.afterConnectionEstablished: Set per-session
text/binary message size limits to 512KB for incoming messages.
3. VoiceLiveWebSocketHandler.handleStopSession: Send proper WebSocket
close frame (CloseStatus.NORMAL) after the session_stopped response,
ensuring clean close handshake with RFC 6455-compliant clients.
4. VoiceLiveWebSocketHandler.handleTransportError: Close WebSocket with
SERVER_ERROR status on transport errors instead of leaving it dangling.
5. VoiceLiveWebSocketHandler.sendJson: Move isOpen() check inside the
synchronized block and downgrade IOException to debug level (expected
during teardown when VoiceLiveHandler reactor threads race with close).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix Java interim text delimiter and move SDK calls off WebSocket thread
Two fixes from Codex 5.3 review:
1. VoiceLiveHandler: Split interim_static_texts on newline instead of
pipe character, matching Python/JS/C# backends and frontend behavior.
2. VoiceLiveWebSocketHandler: Dispatch sendAudio/sendText/interrupt to
a dedicated executor thread pool instead of blocking the WebSocket
I/O thread with .block() calls. Prevents stalling other clients
under network latency. Executor is shut down in shutdownAll().
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add tests/results/ to .gitignore
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix CodeQL log injection findings (3 of 3)
Sanitize user-controlled values before logging to prevent log injection:
- Python app.py: Strip newlines/tabs from client_id (from URL path)
- Python voice_handler.py: Strip newlines from service session_id
- Java VoiceLiveWebSocketHandler: Strip control characters from clientId
extracted from the WebSocket URL path
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Strengthen Java clientId sanitization for CodeQL
Use allowlist pattern [a-zA-Z0-9\-_.] instead of blocklist stripping.
Removes all characters except alphanumeric, dash, underscore, and dot
to fully prevent log injection from user-controlled WebSocket URL paths.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>1 parent c53fdf9 commit 8af4bf2
39 files changed
Lines changed: 2599 additions & 980 deletions
File tree
- voice-live-universal-assistant
- csharp
- frontend
- src
- components
- hooks
- javascript
- java/src/main/java/com/azure/voicelive/sample
- python
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
11 | 14 | | |
12 | 15 | | |
13 | 16 | | |
| |||
199 | 202 | | |
200 | 203 | | |
201 | 204 | | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
202 | 255 | | |
203 | 256 | | |
204 | 257 | | |
| |||
377 | 430 | | |
378 | 431 | | |
379 | 432 | | |
| 433 | + | |
380 | 434 | | |
381 | | - | |
| 435 | + | |
382 | 436 | | |
383 | 437 | | |
384 | 438 | | |
| |||
402 | 456 | | |
403 | 457 | | |
404 | 458 | | |
| 459 | + | |
| 460 | + | |
405 | 461 | | |
406 | 462 | | |
407 | 463 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
275 | 275 | | |
276 | 276 | | |
277 | 277 | | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
278 | 285 | | |
279 | 286 | | |
280 | 287 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
77 | 78 | | |
78 | 79 | | |
79 | 80 | | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
80 | 111 | | |
81 | 112 | | |
82 | 113 | | |
| |||
386 | 417 | | |
387 | 418 | | |
388 | 419 | | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
389 | 427 | | |
390 | | - | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
391 | 435 | | |
392 | 436 | | |
393 | 437 | | |
| 438 | + | |
394 | 439 | | |
395 | 440 | | |
396 | 441 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
| 17 | + | |
16 | 18 | | |
17 | 19 | | |
18 | 20 | | |
| |||
0 commit comments