Feat: web app dark mode (#14732)

This commit is contained in:
KVOJJJin
2025-03-03 14:44:51 +08:00
committed by GitHub
parent e53052ab7a
commit d0d0bf570e
98 changed files with 3006 additions and 2496 deletions

View File

@@ -1,117 +0,0 @@
.audioPlayer {
display: flex;
flex-direction: row;
align-items: center;
background-color: var(--color-components-chat-input-audio-bg-alt);
border-radius: 10px;
padding: 8px;
min-width: 240px;
max-width: 420px;
max-height: 40px;
backdrop-filter: blur(5px);
border: 1px solid var(--color-components-panel-border-subtle);
box-shadow: 0 1px 2px var(--color-shadow-shadow-3);
gap: 8px;
}
.playButton {
display: inline-flex;
width: 16px;
height: 16px;
border-radius: 50%;
background-color: var(--color-components-button-primary-bg);
color: var(--color-components-chat-input-audio-bg-alt);
border: none;
cursor: pointer;
align-items: center;
justify-content: center;
transition: background-color 0.1s;
flex-shrink: 0;
}
.playButton:hover {
background-color: var(--color-components-button-primary-bg-hover);
}
.playButton:disabled {
background-color: var(--color-components-button-primary-bg-disabled);
}
.audioControls {
flex-grow: 1;
}
.progressBarContainer {
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.waveform {
position: relative;
display: flex;
cursor: pointer;
height: 24px;
width: 100%;
flex-grow: 1;
align-items: center;
justify-content: center;
}
.progressBar {
position: absolute;
top: 0;
left: 0;
opacity: 0.5;
border-radius: 2px;
flex: none;
order: 55;
flex-grow: 0;
height: 100%;
background-color: rgba(66, 133, 244, 0.3);
pointer-events: none;
}
.timeDisplay {
/* position: absolute; */
color: var(--color-text-accent-secondary);
font-size: 12px;
order: 0;
height: 100%;
width: 50px;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* .currentTime {
position: absolute;
bottom: calc(100% + 5px);
transform: translateX(-50%);
background-color: rgba(255,255,255,.8);
padding: 2px 4px;
border-radius:10px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.08);
} */
.duration {
padding: 2px 4px;
border-radius: 10px;
}
.source_unavailable {
border: none;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
position: absolute;
color: #bdbdbf;
}
.playButton svg path,
.playButton svg rect {
fill: currentColor;
}

View File

@@ -1,7 +1,13 @@
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { t } from 'i18next'
import styles from './AudioPlayer.module.css'
import {
RiPauseCircleFill,
RiPlayLargeFill,
} from '@remixicon/react'
import Toast from '@/app/components/base/toast'
import { useAppContext } from '@/context/app-context'
import { Theme } from '@/types/app'
import cn from '@/utils/classnames'
type AudioPlayerProps = {
src: string
@@ -18,6 +24,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({ src }) => {
const [hasStartedPlaying, setHasStartedPlaying] = useState(false)
const [hoverTime, setHoverTime] = useState(0)
const [isAudioAvailable, setIsAudioAvailable] = useState(true)
const { theme } = useAppContext()
useEffect(() => {
const audio = audioRef.current
@@ -230,11 +237,11 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({ src }) => {
let color
if (index * barWidth <= playedWidth)
color = '#296DFF'
color = theme === Theme.light ? '#296DFF' : '#84ABFF'
else if ((index * barWidth / width) * duration <= hoverTime)
color = 'rgba(21,90,239,.40)'
color = theme === Theme.light ? 'rgba(21,90,239,.40)' : 'rgba(200, 206, 218, 0.28)'
else
color = 'rgba(21,90,239,.20)'
color = theme === Theme.light ? 'rgba(21,90,239,.20)' : 'rgba(200, 206, 218, 0.14)'
const barHeight = value * height
const rectX = index * barWidth
@@ -253,7 +260,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({ src }) => {
ctx.fillRect(rectX, rectY, rectWidth, rectHeight)
}
})
}, [currentTime, duration, hoverTime, waveformData])
}, [currentTime, duration, hoverTime, theme, waveformData])
useEffect(() => {
drawWaveform()
@@ -279,40 +286,32 @@ const AudioPlayer: React.FC<AudioPlayerProps> = ({ src }) => {
}, [duration])
return (
<div className={styles.audioPlayer}>
<div className='flex items-end gap-2 h-9 min-w-[240px] max-w-[420px] p-2 bg-components-chat-input-audio-bg-alt backdrop-blur-sm rounded-[10px] border border-components-panel-border-subtle shadow-xs'>
<audio ref={audioRef} src={src} preload="auto"/>
<button className={styles.playButton} onClick={togglePlay} disabled={!isAudioAvailable}>
<button className='shrink-0 inline-flex items-center justify-center border-none text-text-accent hover:text-text-accent-secondary transition-all cursor-pointer disabled:text-components-button-primary-bg-disabled' onClick={togglePlay} disabled={!isAudioAvailable}>
{isPlaying
? (
<svg viewBox="0 0 24 24" width="16" height="16">
<rect x="7" y="6" width="3" height="12" rx="1.5" ry="1.5"/>
<rect x="15" y="6" width="3" height="12" rx="1.5" ry="1.5"/>
</svg>
<RiPauseCircleFill className='w-5 h-5' />
)
: (
<svg viewBox="0 0 24 24" width="16" height="16">
<path d="M8 5v14l11-7z" fill="currentColor"/>
</svg>
<RiPlayLargeFill className='w-5 h-5' />
)}
</button>
<div className={isAudioAvailable ? styles.audioControls : styles.audioControls_disabled} hidden={!isAudioAvailable}>
<div className={styles.progressBarContainer}>
<div className={cn(isAudioAvailable && 'grow')} hidden={!isAudioAvailable}>
<div className='h-8 flex items-center justify-center'>
<canvas
ref={canvasRef}
className={styles.waveform}
className='relative grow h-6 w-full flex items-center justify-center cursor-pointer'
onClick={handleCanvasInteraction}
onMouseMove={handleMouseMove}
onMouseDown={handleCanvasInteraction}
/>
{/* <div className={styles.currentTime} style={{ left: `${(currentTime / duration) * 81}%`, bottom: '29px' }}>
{formatTime(currentTime)}
</div> */}
<div className={styles.timeDisplay}>
<span className={styles.duration}>{formatTime(duration)}</span>
<div className='inline-flex items-center justify-center min-w-[50px] text-text-accent-secondary system-xs-medium'>
<span className='px-0.5 py-1 rounded-[10px]'>{formatTime(duration)}</span>
</div>
</div>
</div>
<div className={styles.source_unavailable} hidden={isAudioAvailable}>{t('common.operation.audioSourceUnavailable')}</div>
<div className='absolute top-0 left-0 w-full h-full flex items-center justify-center text-text-quaternary' hidden={isAudioAvailable}>{t('common.operation.audioSourceUnavailable')}</div>
</div>
)
}