Skip to content

fix: resolve meeting buttons visibility issue in light mode#86

Merged
NotYuSheng merged 19 commits intomainfrom
fix/meeting-buttons-light-mode-visibility
Aug 7, 2025
Merged

fix: resolve meeting buttons visibility issue in light mode#86
NotYuSheng merged 19 commits intomainfrom
fix/meeting-buttons-light-mode-visibility

Conversation

@NotYuSheng
Copy link
Copy Markdown
Owner

Summary

This PR resolves visibility issues with meeting buttons in light mode by fixing CSS styling conflicts that were preventing proper display of meeting button text and states.


Changes Made

  • Fix CSS specificity issues affecting meeting button visibility
  • Resolve styling conflicts between base button styles and themed styles
  • Ensure proper text contrast and visibility in light mode
  • Maintain button functionality while improving visual clarity

Context / Rationale

Meeting buttons were experiencing visibility issues in light mode due to CSS styling conflicts. This affected user experience by:

  1. Making meeting buttons difficult or impossible to read
  2. Causing inconsistent visual behavior between light and dark modes
  3. Reducing overall usability of the meetings interface

This fix ensures consistent, accessible button styling across all theme modes.


Related Docs or References

  • UI accessibility fix for theme consistency
  • Visual design improvement for better user experience

General Checklist

  • I have tested these changes locally
  • I have updated relevant documentation or added comments where needed
  • I have linked relevant issues and tagged reviewers
  • I have followed coding conventions and naming standards

NotYuSheng and others added 19 commits August 6, 2025 09:07
- Add CSS shadow variables for light and dark modes
- Apply shadows to header card, main cards, and buttons
- Add subtle hover animations with lift effect on buttons
- Style transcript entries with shadows and rounded corners
- Enhance visual hierarchy and modern design aesthetics

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add subtle opacity reduction (50%) for disabled buttons
- Include not-allowed cursor for better UX indication
- Prevent hover animations on disabled state
- Clean, minimal approach without color changes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Slow down particle movement speed from 6 to 2 for subtler motion
- Add organizational color variety to particles using brand palette
- Keep original white connecting lines with full opacity unchanged
- Particles now use: #2998D5, #265289, #75797C, #bba88e, #c42030, #FFFFFF

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed sun icon visibility in light mode (☀️)
- Corrected CSS logic for checked/unchecked states
- Light mode: sun icon on left with blue gradient
- Dark mode: moon icon on right with gray gradient
- Smooth transitions between states with proper icon positioning

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace flat colors with subtle linear gradients for visual depth
- Add gradient backgrounds to app container, cards, and components
- Enhance button styling with gradient backgrounds and hover effects
- Improve transcript entries with gradient backgrounds
- Maintain organizational color palette while adding modern visual appeal
- Keep accessibility and contrast standards intact

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add professional styling to model selector dropdown
- Implement proper focus and hover states with organizational colors
- Use solid backgrounds for better cross-browser compatibility
- Improve responsive layout with better spacing and alignment
- Add consistent typography using Barlow font family
- Support both light and dark theme modes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace plain #bba88e borders with subtle gradient border effects
- Use CSS pseudo-elements for sophisticated gradient border technique
- Fix rectangular shadow issue by moving shadows to pseudo-elements
- Apply proper border-radius calculations for clean rounded corners
- Add theme-aware gradient variations for light and dark modes
- Improve visual depth and premium appearance of all cards

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove ugly red color (#c42030) from particle palette
- Increase particle opacity from 1.0 to 0.9 for much sharper appearance
- Make connecting lines more prominent (opacity 0.8, width 2.5px)
- Use darker shade of #bba88e (#8a7c6b) for connecting lines
- Reduce app background opacity to 50-60% so particles show through
- Improve particle backgrounds with more contrasting gradients
- Clean color palette: #2998D5, #265289, #75797C, #bba88e, #FFFFFF

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Convert recording, upload, and transcription buttons to discrete red design
- Use #F4393B for discrete buttons with icon-only interface and tooltips
- Implement prominent transcription button (#D32F2F) when file is uploaded
- Add "Process Audio" text to transcription button for better UX clarity
- Fix CSS specificity issues with \!important to override gradient backgrounds
- Ensure consistent button sizing (2.5rem width) across all states
- Support both light and dark mode themes with proper red color scheme

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace confusing MicOff icon with Square icon for stop recording
- Fix recording timer vertical alignment with action buttons
- Move recording indicator inside button group for better layout
- Add smooth hover effects to transcript/summary tabs
- Implement tab-like appearance with rounded top corners
- Add subtle blue background and border hints on tab hover
- Improve overall controls container alignment and spacing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Implement pause/resume recording functionality with MediaRecorder API
- Add professional HTML5 audio player with timeline scrubbing and volume control
- Enable manual control over when to process recorded audio (no auto-processing)
- Add discard recording option for better user control
- Improve button state management for different recording phases:
  * Default: [Mic] [Upload] [Process Audio]
  * Recording: [Pause] [Stop] [Process Audio]
  * Preview: [Trash] [Upload] [Process Audio] + Audio Player
- Add visual feedback for paused recording state (orange dot, timer shows "(Paused)")
- Style audio player to match app theme with custom controls
- Handle proper cleanup of audio URLs to prevent memory leaks

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Refactored Dockerfile to use multi-stage build with nginx
- Added nginx.conf with production-ready configuration including gzip, security headers, and API proxying
- Updated docker-compose.yml port mapping from 3000:3000 to 80:80
- Removed unnecessary volume mounts for frontend container
- This addresses HTTPS requirements for getUserMedia API and improves production deployment

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove invalid 'must-revalidate' value from gzip_proxied directive
- Change frontend port mapping from 80:80 to 3000:80
- Comment out GPU deployment configuration

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add audio player that displays when file is uploaded
- Allow users to preview uploaded audio before processing
- Add discard functionality for uploaded files
- Integrate same audio controls as recording feature
- Auto-clear uploaded file after processing
- Update button states to handle upload workflow

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Replace emoji trash icon with Trash2 React icon component
- Change button style from btn-danger to btn-discrete for consistency
- Add tooltip for better user experience
- Use btn-icon class for consistent icon styling
- Match visual style of other action buttons (mic, upload)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Automatically select meeting after successful audio processing
- Set selectedMeetingId in both uploadFile and processAudio functions
- Ensures transcript appears immediately without manual selection
- Improves user experience by eliminating extra click step

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add pollJobStatus function to handle asynchronous job processing
- Replace synchronous processing assumption with proper status checking
- Handle backend responses that return 200 but indicate errors
- Add user-friendly error messages for failed processing
- Implement timeout protection (60 seconds max polling)
- Support both immediate transcript and job polling workflows
- Auto-select processed meeting when job completes successfully

Fixes issue where loading bar didn't properly wait for job completion
and backend errors weren't properly detected despite 200 HTTP status.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add separate location block for /jobs/ to handle sub-paths
- Fix routing for endpoints like /jobs/{uuid}/status and /jobs/{uuid}/transcript
- Maintain existing /jobs endpoint for job creation
- Ensure proper proxy_pass URLs for both patterns
- Add CORS handling for all job-related endpoints

Previously only /jobs was routed to backend, causing 404s for
status polling and other job management endpoints.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add \!important declarations to meeting button styles (.btn-past-1 through .btn-past-4)
- Override both background-color and background properties to prevent base .btn interference
- Force white text color with \!important for proper contrast
- Update hover states with consistent darker shades
- Apply fixes to both light and dark mode variants

Previously meeting buttons were invisible in light mode due to CSS
inheritance conflicts between base .btn styles and specific button colors.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @NotYuSheng, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

I've addressed the reported visibility issues with meeting buttons in light mode by implementing a significant overhaul of the application's CSS styling, introducing modern gradients, shadows, and new button types for improved clarity and user experience. Beyond the immediate UI fix, I've also enhanced the audio recording and processing capabilities, allowing for more flexible interaction with audio inputs. Crucially, I've undertaken a major architectural change by migrating the frontend deployment to a multi-stage Docker build served by Nginx, which will improve performance and maintainability. These changes collectively aim to deliver a more robust, visually consistent, and user-friendly application.

Highlights

  • Enhanced UI Styling and Button Visibility: I've implemented a comprehensive fix for the meeting buttons' visibility in light mode by overhauling the CSS styling. This includes introducing new gradient backgrounds, subtle box shadows, and distinct styles for various button states (e.g., discrete, prominent, disabled). These changes ensure proper text contrast and visual clarity across both light and dark themes, significantly improving the user experience.
  • Improved Audio Recording and Processing Workflow: I've refactored the audio recording and processing workflow. This now includes the ability to pause and resume recordings, discard recorded audio, and preview both recorded and uploaded audio files directly within the application. The backend communication for processing audio has also been improved to poll for job status, providing a more robust and responsive user experience during transcription.
  • Frontend Deployment Architecture Shift to Nginx/Docker Multi-Stage Build: I've made a significant architectural change to the frontend deployment by migrating from a direct Node.js server to a multi-stage Docker build with Nginx. This involves a new Dockerfile that builds the React app and then serves it using Nginx, along with a new nginx.conf for static file serving, API proxying to the backend, and security configurations. The docker-compose.yml has been updated to reflect these changes, including new port mappings and removal of old volume configurations.
  • Dependency Updates and Theme Toggle Enhancement: I've updated the react-router-dom dependency to version 7.7.1 and removed extraneous license fields from various dependencies in package-lock.json. Additionally, I've updated the particles-js background gradients and introduced a new, visually appealing theme toggle switch for dark mode.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant improvements by containerizing the frontend with a multi-stage Dockerfile and Nginx, enhancing the recording functionality with pause/resume capabilities, and implementing a more robust audio processing flow with job status polling. The UI has also been updated with new styles and a theme toggle. My review focuses on improving the new Nginx configuration for better maintainability and security, refactoring the CSS to avoid !important, and suggesting enhancements to the JavaScript code for better error handling, code reuse, and API interaction.

Comment thread frontend/nginx.conf
Comment on lines +40 to +78
# Proxy API requests to backend
location /jobs/ {
proxy_pass http://backend:8000/jobs/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# Handle preflight requests for CORS
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
}

# Also handle exact /jobs endpoint (without trailing slash)
location /jobs {
proxy_pass http://backend:8000/jobs;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# Handle preflight requests for CORS
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The logic for proxying requests to /jobs/ and /jobs is duplicated. This can be simplified into a single location block using a regular expression to improve maintainability and reduce code duplication.

    location ~ ^/jobs { # Matches both /jobs and /jobs/*
        proxy_pass http://backend:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # Handle preflight requests for CORS
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH';
            add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        }
    }

Comment thread frontend/nginx.conf

# Handle preflight requests for CORS
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Using Access-Control-Allow-Origin '*' is insecure and should be avoided in production environments. It's better to restrict it to the specific domain of your frontend application to prevent Cross-Site Request Forgery (CSRF) and other potential security vulnerabilities.

            add_header 'Access-Control-Allow-Origin' 'http://your-frontend-domain.com';

Comment on lines +179 to +190
.btn.btn-discrete {
background: var(--btn-discrete) !important;
color: #fff;
padding: 0.5rem;
width: 2.5rem;
justify-content: center;
flex-shrink: 0;
}

.btn.btn-discrete:hover {
background: var(--btn-discrete-hover) !important;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The use of !important should be avoided as it makes CSS difficult to maintain and debug. The selector .btn.btn-discrete already has higher specificity than the base .btn selector, so !important is likely unnecessary here. The same applies to the :hover state.

Suggested change
.btn.btn-discrete {
background: var(--btn-discrete) !important;
color: #fff;
padding: 0.5rem;
width: 2.5rem;
justify-content: center;
flex-shrink: 0;
}
.btn.btn-discrete:hover {
background: var(--btn-discrete-hover) !important;
}
.btn.btn-discrete {
background: var(--btn-discrete);
color: #fff;
padding: 0.5rem;
width: 2.5rem;
justify-content: center;
flex-shrink: 0;
}
.btn.btn-discrete:hover {
background: var(--btn-discrete-hover);
}

Comment on lines 693 to 697
.btn-past-1 {
background-color: #2998d5;
color: #fff;
background-color: #2998d5 !important;
background: #2998d5 !important;
color: #fff !important;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The use of !important should be avoided as it makes CSS difficult to maintain and debug. Given the class structure, these overrides can likely be achieved by increasing selector specificity if needed (e.g., .btn.btn-past-1), though it might not even be necessary due to source order. Also, the background-color property is redundant when the background shorthand property is already being used.

.btn-past-1 {
  background: #2998d5;
  color: #fff;
}

const transcriptData = await transcriptResponse.json();

if (transcriptData.full_transcript) {
const parsed = JSON.parse(transcriptData.full_transcript || "[]");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The API returns the transcript as a stringified JSON, which requires parsing on the client-side. It would be more idiomatic for the API to return a JSON object directly. This would simplify the frontend code and align with common API design practices.

Suggested change
const parsed = JSON.parse(transcriptData.full_transcript || "[]");
const parsed = transcriptData.full_transcript; // No parsing needed if API returns an object

Comment on lines +420 to 500
const uploadFile = async () => {
if (!selectedFile) return;
speakerColorMap.current = {};
setLoading(true);
const formData = new FormData();
formData.append("file", selectedFile);

try {
const formData = new FormData();
formData.append("file", selectedFile);

fetch(`${API_BASE_URL}/jobs`, {
method: "POST",
body: formData,
})
.then((result) => result.json())
.then((data) => {
setTranscript(
Array.isArray(data.transcript)
? processTranscriptWithSpeakerIds(data.transcript)
: [],
);
const response = await fetch(`${API_BASE_URL}/jobs`, {
method: "POST",
body: formData,
});

const data = await response.json();

// Check if the response indicates success and has expected data
if (data.error || (!data.uuid && !data.transcript)) {
throw new Error(data.error || 'Invalid response from server');
}

// If we get a transcript immediately, use it
if (data.transcript && Array.isArray(data.transcript)) {
setTranscript(processTranscriptWithSpeakerIds(data.transcript));
setSelectedMeetingId(data.uuid);
fetchSummary(data.uuid);
fetchMeetingList();
setLoading(false);
})
.catch((err) => {
console.error("Failed to fetch transcription.", err);
setLoading(false);
});
} else if (data.uuid) {
// Otherwise, poll for status
await pollJobStatus(data.uuid);
} else {
throw new Error('No transcript or job ID returned');
}

setSelectedFile(null);
} catch (err) {
console.error("Failed to process uploaded file:", err);
alert(`Failed to process file: ${err.message}`);
} finally {
setLoading(false);
}
};

const processAudio = async (audioBlob) => {
setIsProcessing(true);
const formData = new FormData();
formData.append("file", audioBlob);

try {
const formData = new FormData();
formData.append("file", audioBlob);

fetch(`${API_BASE_URL}/jobs`, {
method: "POST",
body: formData,
})
.then((result) => result.json())
.then((data) => {
setTranscript(
Array.isArray(data.transcript)
? processTranscriptWithSpeakerIds(data.transcript)
: [],
);
const response = await fetch(`${API_BASE_URL}/jobs`, {
method: "POST",
body: formData,
});

const data = await response.json();

// Check if the response indicates success and has expected data
if (data.error || (!data.uuid && !data.transcript)) {
throw new Error(data.error || 'Invalid response from server');
}

// If we get a transcript immediately, use it
if (data.transcript && Array.isArray(data.transcript)) {
setTranscript(processTranscriptWithSpeakerIds(data.transcript));
setSelectedMeetingId(data.uuid);
fetchSummary(data.uuid);
fetchMeetingList();
setIsProcessing(false);
})
.catch((err) => {
console.error("Failed to fetch transcription -", err);
setIsProcessing(false);
});
} else if (data.uuid) {
// Otherwise, poll for status
await pollJobStatus(data.uuid);
} else {
throw new Error('No transcript or job ID returned');
}
} catch (err) {
console.error("Failed to process recorded audio:", err);
alert(`Failed to process recording: ${err.message}`);
} finally {
setIsProcessing(false);
}
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The uploadFile and processAudio functions share a lot of common logic for making the API request and handling the response, including the polling mechanism. This duplicated code could be extracted into a single, reusable function that accepts the audio data (either a File or a Blob) as an argument. This would improve maintainability and reduce redundancy.

}
} catch (err) {
console.error("Failed to process recorded audio:", err);
alert(`Failed to process recording: ${err.message}`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using alert() for error handling provides a poor user experience as it is a blocking and disruptive dialog. Consider implementing a more user-friendly notification system, such as toast notifications, to display errors without interrupting the user's workflow.

@NotYuSheng NotYuSheng merged commit 9656fcf into main Aug 7, 2025
1 check failed
@NotYuSheng NotYuSheng deleted the fix/meeting-buttons-light-mode-visibility branch August 7, 2025 02:42
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.

1 participant