Skip to content

Speaker recognition web UI fixes#142

Merged
AnkushMalaker merged 5 commits intoSimpleOpenSoftware:mainfrom
RKRitik:fix/speaker-recog-ui
Oct 27, 2025
Merged

Speaker recognition web UI fixes#142
AnkushMalaker merged 5 commits intoSimpleOpenSoftware:mainfrom
RKRitik:fix/speaker-recog-ui

Conversation

@RKRitik
Copy link
Contributor

@RKRitik RKRitik commented Oct 25, 2025

Fix web ui to use the tailwind theme properly (major change)
Added theme switcher
Fixed setup script to handle port for HTTP and HTTPS in nginx
Fix a null check when speaker details modal

image image

Summary by CodeRabbit

  • New Features

    • Added dark mode support with a theme switcher for toggling between light and dark themes.
  • Chores

    • Implemented dynamic configuration for web UI port and server IP during setup.
    • Refined UI styling consistency across the application with updated typography and color tokens.
    • Enhanced dark mode compatibility throughout all interface components.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 25, 2025

Walkthrough

The PR introduces dynamic web UI port configuration during setup based on HTTPS mode (5175 for HTTPS, 5174 for HTTP) and refactors nginx configuration generation with SERVER_IP substitution. Additionally, it adds comprehensive dark mode theming support across the web UI with new theme context, CSS variables, and updated component styling.

Changes

Cohort / File(s) Summary
Setup & Configuration
extras/speaker-recognition/nginx.conf.template, extras/speaker-recognition/setup.sh
nginx template now uses WEB_UI_PORT_PLACEHOLDER for dynamic port substitution. setup.sh refactored to dynamically set WEB_UI_PORT based on HTTPS mode, gate SSL generation by mode, prompt for SERVER_IP, and generate nginx.conf with substituted values.
Theme Infrastructure
extras/speaker-recognition/webui/tailwind.config.js, extras/speaker-recognition/webui/src/styles/components.css, extras/speaker-recognition/webui/src/App.tsx
Tailwind dark mode enabled via class; components.css adds CSS custom properties and dark mode utilities for cards, text, headings, and form elements; App.tsx wraps Router with ThemeProvider for context-based theming.
Theme Toggle Component
extras/speaker-recognition/webui/src/components/ThemeSwitcher.tsx
New component that consumes ThemeContext to toggle between dark/light themes; renders Sun/Moon icon buttons with dark-mode styling.
Layout & Navigation
extras/speaker-recognition/webui/src/components/layout/Layout.tsx
Added dark mode support with dark:bg classes, card-style header, ThemeSwitcher integration, updated navigation active states and footer styling.
Component Styling Updates
extras/speaker-recognition/webui/src/components/AudioRecordingControls.tsx, ConnectionStatus.tsx, FileUploader.tsx, ProcessingModeSelector.tsx, SpeakerResultsDisplay.tsx, UserSelector.tsx
Color and typography class updates replacing gray tokens with semantic design tokens (text-primary, text-muted, text-secondary) and adding dark mode variants throughout.
Page Component Styling
extras/speaker-recognition/webui/src/pages/Annotation.tsx, AudioViewer.tsx, Enrollment.tsx, InferLive.tsx, InferLiveSimplified.tsx, Inference.tsx, Speakers.tsx
Extensive dark mode styling updates across all pages: replaced gray-based classes with semantic color tokens, added dark: variants to backgrounds/borders/text, updated card and heading class usage, added dark-mode aware status indicators and badges.

Sequence Diagram

sequenceDiagram
    participant User
    participant ThemeContext
    participant Components
    participant DOM

    User->>ThemeSwitcher: Click toggle button
    ThemeSwitcher->>ThemeContext: Call toggleTheme()
    ThemeContext->>DOM: Update data-theme attribute
    DOM->>Components: Apply dark: CSS classes
    Components->>User: Render with dark/light colors
Loading
sequenceDiagram
    participant Setup Script
    participant User Input
    participant SSL Generation
    participant Nginx Config
    participant Output

    Setup Script->>User Input: Prompt for HTTPS mode
    alt HTTPS Mode
        Setup Script->>User Input: Prompt for SERVER_IP
        Setup Script->>SSL Generation: Generate certificates
        SSL Generation->>Setup Script: Certificates ready
    else HTTP Mode
        Setup Script->>Setup Script: Skip SSL
    end
    Setup Script->>Nginx Config: Generate with SERVER_IP and WEB_UI_PORT_PLACEHOLDER
    Nginx Config->>Setup Script: Substitute values
    Setup Script->>Output: Display configuration summary
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

  • setup.sh logic changes: The refactored SSL/HTTPS flow with conditional port assignment and nginx configuration generation requires careful verification of environment variable handling and script execution order.
  • Extensive styling repetition: While most component changes follow a consistent pattern of replacing gray tokens with semantic design tokens and adding dark mode variants, the breadth across 13+ component/page files requires systematic validation.
  • Theme infrastructure integration: Verify that ThemeProvider wrapping, CSS custom properties, Tailwind dark mode class configuration, and ThemeSwitcher functionality work cohesively across all pages.

Areas requiring extra attention:

  • Nginx template substitution logic in setup.sh (SERVER_IP and WEB_UI_PORT_PLACEHOLDER replacement)
  • HTTPS vs. HTTP port assignment correctness (5175 vs. 5174)
  • Dark mode CSS variable definitions and fallback values in components.css
  • Theme persistence and context propagation through App.tsx to all nested routes

Possibly related PRs

  • Fix/init system clean #131: Modifies speaker-recognition setup script and nginx templating for HTTPS/environment variable generation with similar configuration goals.
  • Memory config from UI #90: Updates extras/speaker-recognition/nginx.conf.template to add WebSocket proxy configuration, overlapping file modifications.

Poem

🐰 Dark mode hops and setup ports align,
With theme switches bright and config divine,
Dynamic IPs dance through the night,
As rabbits toggle dark and light!
✨🌙☀️

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title "Speaker recognition web UI fixes" accurately references the web UI domain, but the term "fixes" is vague and doesn't capture the actual scope of changes. The changeset primarily comprises theming implementation (introducing ThemeProvider, ThemeSwitcher component, dark mode support) and extensive styling refactors to use new design tokens across multiple components and pages. While the PR description mentions some specific fixes (port handling in setup script, null check in speaker details modal), these are secondary to the major theming work. The word "fixes" is a non-descriptive term that fails to convey the substantial feature additions and refactoring that constitute most of the changeset. Consider revising the title to more accurately reflect the primary changes, such as "Add dark mode theme support to speaker recognition web UI" or "Implement theming system with dark mode support in speaker recognition UI". This would better communicate to reviewers that the PR involves feature implementation and comprehensive styling refactors, not merely bug fixes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@RKRitik
Copy link
Contributor Author

RKRitik commented Oct 25, 2025

@CodeRabbit review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 25, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
extras/speaker-recognition/webui/src/pages/Annotation.tsx (1)

195-210: Critical bug fix: Missing file property added.

The addition of file: audioData.file at Line 201 fixes a critical issue where the processed audio object was missing the original file reference needed by the speaker identification service.

🧹 Nitpick comments (2)
extras/speaker-recognition/webui/src/components/SpeakerResultsDisplay.tsx (1)

265-269: Consider extending theming to the entire component.

The empty state now uses semantic theme tokens (text-primary, heading-sm, text-muted), which is good. However, the rest of the component (lines 107-332) still uses hardcoded Tailwind colors like text-gray-500, text-blue-600, bg-gray-50, etc. This creates inconsistent theme support—the empty state will adapt to dark mode, but segments, stats, and other UI elements won't.

If complete dark mode support is desired, consider applying semantic tokens throughout the component.

extras/speaker-recognition/webui/src/components/ThemeSwitcher.tsx (1)

10-10: Remove unnecessary empty className attributes.

The empty className="" attributes on the Moon and Sun icons serve no purpose and can be safely removed for cleaner code.

Apply this diff:

-        {isDark ? <Moon className="" /> : <Sun className="" />}
+        {isDark ? <Moon /> : <Sun />}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c117cb0 and fd521e4.

📒 Files selected for processing (20)
  • extras/speaker-recognition/nginx.conf.template (1 hunks)
  • extras/speaker-recognition/setup.sh (3 hunks)
  • extras/speaker-recognition/webui/src/App.tsx (1 hunks)
  • extras/speaker-recognition/webui/src/components/AudioRecordingControls.tsx (3 hunks)
  • extras/speaker-recognition/webui/src/components/ConnectionStatus.tsx (1 hunks)
  • extras/speaker-recognition/webui/src/components/FileUploader.tsx (3 hunks)
  • extras/speaker-recognition/webui/src/components/ProcessingModeSelector.tsx (21 hunks)
  • extras/speaker-recognition/webui/src/components/SpeakerResultsDisplay.tsx (1 hunks)
  • extras/speaker-recognition/webui/src/components/ThemeSwitcher.tsx (1 hunks)
  • extras/speaker-recognition/webui/src/components/UserSelector.tsx (4 hunks)
  • extras/speaker-recognition/webui/src/components/layout/Layout.tsx (4 hunks)
  • extras/speaker-recognition/webui/src/pages/Annotation.tsx (27 hunks)
  • extras/speaker-recognition/webui/src/pages/AudioViewer.tsx (6 hunks)
  • extras/speaker-recognition/webui/src/pages/Enrollment.tsx (11 hunks)
  • extras/speaker-recognition/webui/src/pages/InferLive.tsx (7 hunks)
  • extras/speaker-recognition/webui/src/pages/InferLiveSimplified.tsx (8 hunks)
  • extras/speaker-recognition/webui/src/pages/Inference.tsx (9 hunks)
  • extras/speaker-recognition/webui/src/pages/Speakers.tsx (20 hunks)
  • extras/speaker-recognition/webui/src/styles/components.css (1 hunks)
  • extras/speaker-recognition/webui/tailwind.config.js (1 hunks)
🔇 Additional comments (24)
extras/speaker-recognition/webui/tailwind.config.js (1)

3-3: LGTM! Correct dark mode configuration.

The darkMode: 'class' configuration enables class-based dark mode toggling, which aligns perfectly with the ThemeProvider and ThemeSwitcher implementation throughout the PR.

extras/speaker-recognition/webui/src/components/ConnectionStatus.tsx (1)

105-105: LGTM! Appropriate dark mode color adjustment.

The lighter gray shades with dark mode variants improve visibility in both themes.

extras/speaker-recognition/webui/src/components/FileUploader.tsx (1)

130-163: LGTM! Consistent use of design tokens.

The refactor to semantic tokens (text-primary, text-muted, text-primary-disabled) improves consistency and maintainability across the theme system.

extras/speaker-recognition/webui/src/components/ProcessingModeSelector.tsx (1)

114-510: LGTM! Comprehensive design token adoption.

The component has been systematically updated to use semantic design tokens (input-label, text-muted, text-primary, text-secondary, heading-sm) throughout, ensuring consistent theming without altering functionality.

extras/speaker-recognition/webui/src/components/UserSelector.tsx (1)

40-126: LGTM! Consistent token-based styling.

All interactive elements (dropdown, forms, buttons) now use semantic design tokens, ensuring proper dark mode support and maintainability.

extras/speaker-recognition/webui/src/styles/components.css (1)

5-92: LGTM! Solid design system foundation.

The CSS establishes a comprehensive token-based design system with:

  • Root-level theme variables for light/dark modes
  • Semantic utility classes (card, text-primary/secondary/muted, heading-, input-, btn-*)
  • Consistent dark mode variants throughout

This provides excellent maintainability and ensures theme consistency across the application.

extras/speaker-recognition/webui/src/pages/Speakers.tsx (1)

256-610: LGTM! Comprehensive dark mode styling.

The page has been thoroughly updated with dark mode variants across all UI elements (tables, cards, modals, buttons) using semantic design tokens consistently.

extras/speaker-recognition/nginx.conf.template (1)

48-50: LGTM! Dynamic port configuration enabled.

The placeholder substitution allows the setup script to configure the web UI port dynamically based on HTTP/HTTPS mode, improving deployment flexibility.

extras/speaker-recognition/webui/src/App.tsx (1)

12-32: LGTM! Theme provider integration is correct.

ThemeProvider correctly wraps the Router to provide theming context globally. The React Router future flags ensure compatibility with upcoming v7 features.

extras/speaker-recognition/webui/src/components/AudioRecordingControls.tsx (1)

132-221: LGTM! Theming updates are consistent.

The component successfully adopts the new design tokens for dark mode support while preserving all existing functionality.

extras/speaker-recognition/webui/src/components/layout/Layout.tsx (1)

6-89: LGTM! Layout successfully integrates theme switcher and dark mode.

The layout properly integrates the ThemeSwitcher component and applies dark mode styling consistently across header, navigation, and footer elements.

extras/speaker-recognition/webui/src/pages/AudioViewer.tsx (1)

185-295: LGTM! Comprehensive dark mode support added.

AudioViewer consistently applies dark mode theming across all UI elements while maintaining existing functionality.

extras/speaker-recognition/webui/src/pages/InferLive.tsx (1)

163-295: LGTM! InferLive theming updates are complete and consistent.

All UI elements properly support dark mode while preserving the real-time transcription and speaker identification functionality.

extras/speaker-recognition/webui/src/pages/Enrollment.tsx (1)

458-727: LGTM! Enrollment page fully supports dark mode.

The quality indicators, recording controls, file uploads, and all UI elements properly support dark mode theming. The enrollment logic remains unchanged and functional.

extras/speaker-recognition/setup.sh (3)

144-156: LGTM! Clean port configuration logic.

The dynamic port assignment based on HTTPS mode is well-implemented. The separation of concerns (determine ports → update .env) makes the flow easy to follow.


208-208: LGTM! Dynamic port in user message.

Correctly displays the dynamically determined port value, ensuring users see the accurate Web Interface URL.


179-188: No issues found. The verification confirms that nginx.conf.template contains both expected placeholders (TAILSCALE_IP at lines 55 and 144, WEB_UI_PORT_PLACEHOLDER at line 49), and the sed substitution logic in setup.sh correctly targets them.

extras/speaker-recognition/webui/src/pages/InferLiveSimplified.tsx (3)

207-214: LGTM! Well-implemented dark mode for connection status.

The icon color choices follow proper dark mode conventions, using lighter shades on dark backgrounds for good visibility.


329-331: LGTM! Button states properly handle dark mode.

Both start and stop button states include appropriate hover transitions for dark mode.


344-393: LGTM! Comprehensive dark mode support for transcripts.

The transcript display has thorough dark mode styling with proper semantic colors for badges, borders, and text states. The visual hierarchy is maintained across both themes.

extras/speaker-recognition/webui/src/pages/Inference.tsx (4)

89-92: LGTM! Consistent design token usage.

The header and user prompt sections properly use the semantic design tokens, maintaining consistency with other pages in the PR.

Also applies to: 100-103


167-181: LGTM! Audio quality display with proper semantic colors.

The quality level indicators use appropriate semantic colors (green for excellent, red for poor) with proper dark mode variants. The visual feedback is clear in both themes.


209-214: LGTM! Processing progress with dark mode support.

The progress indicator maintains proper contrast and visibility in both light and dark modes.


226-276: LGTM! Comprehensive dark mode for results history.

The results history section has thorough styling with proper status badge colors, hover states, and visual hierarchy maintained across both themes. The interactive elements (selection, hover, export) are well-defined.

Comment on lines +613 to +621
<p className="text-primary">{new Date(selectedSpeaker.created_at).toLocaleString()}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-500">Last Updated</label>
<p className="text-gray-900 dark:text-gray-100">{new Date(selectedSpeaker.updated_at).toLocaleString()}</p>
<label className="block text-sm font-medium text-muted">Last Updated</label>
<p className="text-primary">{new Date(selectedSpeaker.updated_at).toLocaleString()}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-500">Last Enrollment</label>
<p className="text-gray-900 dark:text-gray-100">{new Date(selectedSpeaker.last_enrollment).toLocaleString()}</p>
<label className="block text-sm font-medium text-muted">Last Enrollment</label>
<p className="text-primary">{new Date(selectedSpeaker.last_enrollment).toLocaleString()}</p>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add null safety for optional date fields in the modal.

Lines 613, 617, and 621 call new Date() on potentially undefined values (created_at, updated_at, last_enrollment). If these fields are undefined, this will result in new Date(undefined) which creates an Invalid Date.

Apply this diff to add proper null checks:

                <div>
                  <label className="block text-sm font-medium text-muted">Created</label>
-                  <p className="text-primary">{new Date(selectedSpeaker.created_at).toLocaleString()}</p>
+                  <p className="text-primary">{selectedSpeaker.created_at ? new Date(selectedSpeaker.created_at).toLocaleString() : 'N/A'}</p>
                </div>
                <div>
                  <label className="block text-sm font-medium text-muted">Last Updated</label>
-                  <p className="text-primary">{new Date(selectedSpeaker.updated_at).toLocaleString()}</p>
+                  <p className="text-primary">{selectedSpeaker.updated_at ? new Date(selectedSpeaker.updated_at).toLocaleString() : 'N/A'}</p>
                </div>
                <div>
                  <label className="block text-sm font-medium text-muted">Last Enrollment</label>
-                  <p className="text-primary">{new Date(selectedSpeaker.last_enrollment).toLocaleString()}</p>
+                  <p className="text-primary">{selectedSpeaker.last_enrollment ? new Date(selectedSpeaker.last_enrollment).toLocaleString() : 'N/A'}</p>
                </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<p className="text-primary">{new Date(selectedSpeaker.created_at).toLocaleString()}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-500">Last Updated</label>
<p className="text-gray-900 dark:text-gray-100">{new Date(selectedSpeaker.updated_at).toLocaleString()}</p>
<label className="block text-sm font-medium text-muted">Last Updated</label>
<p className="text-primary">{new Date(selectedSpeaker.updated_at).toLocaleString()}</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-500">Last Enrollment</label>
<p className="text-gray-900 dark:text-gray-100">{new Date(selectedSpeaker.last_enrollment).toLocaleString()}</p>
<label className="block text-sm font-medium text-muted">Last Enrollment</label>
<p className="text-primary">{new Date(selectedSpeaker.last_enrollment).toLocaleString()}</p>
<p className="text-primary">{selectedSpeaker.created_at ? new Date(selectedSpeaker.created_at).toLocaleString() : 'N/A'}</p>
</div>
<div>
<label className="block text-sm font-medium text-muted">Last Updated</label>
<p className="text-primary">{selectedSpeaker.updated_at ? new Date(selectedSpeaker.updated_at).toLocaleString() : 'N/A'}</p>
</div>
<div>
<label className="block text-sm font-medium text-muted">Last Enrollment</label>
<p className="text-primary">{selectedSpeaker.last_enrollment ? new Date(selectedSpeaker.last_enrollment).toLocaleString() : 'N/A'}</p>
🤖 Prompt for AI Agents
In extras/speaker-recognition/webui/src/pages/Speakers.tsx around lines 613 to
621, the modal renders dates by calling new Date(...) on possibly undefined
fields (created_at, updated_at, last_enrollment); update each date render to
guard against undefined by using optional chaining and a conditional fallback
(e.g. selectedSpeaker?.created_at ? new
Date(selectedSpeaker.created_at).toLocaleString() : '—') so Invalid Date is
never shown and undefined values render a clear placeholder.

@AnkushMalaker
Copy link
Collaborator

Thanks @RKRitik
setup.sh was actually deadcode so we need to call init.py
I took this opportunity to clean those
RKRitik#2

@AnkushMalaker AnkushMalaker merged commit fd521e4 into SimpleOpenSoftware:main Oct 27, 2025
3 checks passed
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