@@ -0,0 +1,33 @@ + + + + + + + + + + + + + @@ -0,0 +1,10 @@ + + + + @@ -102,6 +102,7 @@ export function useMediaBotPagination(deviceType: Device, type:'image' | 'video' offset.current += answer.length } finally { fetchLock.current = false + setLoading(false) } } @@ -142,8 +143,6 @@ export function useMediaBotPagination(deviceType: Device, type:'image' | 'video' behavior: 'smooth', }) } - - setLoading(false) }, 500) } @@ -46,7 +46,7 @@ } .upload { - border: 1px dashed #a4aab5; + border: 4px dashed #A4AAB51A; width: 100%; padding: 32px 24px; background-color: #1d1d2180; @@ -198,10 +198,10 @@ } .btnMicIcon { - font-size: 20px; - width: 1em; - height: 1em; + width: 21px; + height: 21px; flex-shrink: 0; + color: #fff; } .btnStopSquare { @@ -270,8 +270,12 @@ justify-content: center; border-radius: 8px; height: 38px; - color: #8280FF; - border: 1px solid #8280ff; + color: #FFFFFF; + + &_recording { + color: #8280FF; + border: 1px solid #8280FF; + } } .micHint { @@ -283,5 +287,8 @@ .or { margin: 0; font-size: 14px; - color: #80808e; + padding: 4px 12px; + border-radius: 12px; + background-color: #8280FF1A; + color: #8280FF; } \ No newline at end of file @@ -1,4 +1,3 @@ -import Mic from '@mui/icons-material/Mic' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useSession } from 'next-auth/react' @@ -12,10 +11,12 @@ import { CommonButton } from '#/shared/ui/button' import { CommonInput } from '#/shared/ui/common-input' import { AudioPlayer } from '#/widgets/messages/ui/audio-player' +import AddVoiceMicIcon from '#/assets/svg/add-voice-mic.svg?react' + import styles from './add-voice-plate.module.scss' const MAX_BYTES = 10 * 1024 * 1024 -const FILE_ACCEPT = '.mp3,.wav,audio/mpeg,audio/wav' +const FILE_ACCEPT = '.mp3,.wav,.ogg,.weba' const PREVIEW_PLAYER_UID = 'add-voice-plate-preview' const DEFAULT_VOICE_TITLE = 'Аудиозапись 1' @@ -31,7 +32,11 @@ type AddVoicePlateProps = { function MicButtonLabel({ children, stopRecording }: { children: React.ReactNode; stopRecording?: boolean }) { return ( - {stopRecording ? : } + {stopRecording ? ( + + ) : ( + + )} {children} ) @@ -432,8 +437,8 @@ export function AddVoicePlate({ onSuccess }: AddVoicePlateProps) { void onMicStartStop(ev)} > @@ -483,8 +488,8 @@ export function AddVoicePlate({ onSuccess }: AddVoicePlateProps) { Загрузите примеры голоса
для клонирования

- аудиофайлы в формате .mp3 или{' '} - .wav, не более{' '} + аудиофайлы в формате .mp3,{' '} или {' '} + .wav не более{' '} 10MB каждый

e.stopPropagation()}> @@ -36,6 +36,24 @@ } } +/* мобилка: поповер слева от кнопки — стрелка справа кнопки, смотрит на неё */ +.deleteConfirmPaperMobile { + margin-bottom: 0; + margin-right: 6px; + + &::after { + bottom: auto; + left: auto; + right: -6px; + top: 50%; + transform: translateY(-50%); + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; + border-left: 6px solid #4b4b4b; + border-right: none; + } +} + .deleteConfirmContent { display: flex; align-items: center; @@ -1,5 +1,4 @@ import { Pause, PlayArrow } from '@mui/icons-material' -import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import { Box, IconButton, @@ -10,10 +9,13 @@ import { SelectChangeEvent, Stack, Typography, + useMediaQuery, } from '@mui/material' import type { MenuProps } from '@mui/material/Menu' import Image from 'next/image' -import { useCallback, useEffect, useRef, useState, type SVGProps } from 'react' +import { forwardRef, useCallback, useEffect, useRef, useState, type ComponentProps, type SVGProps } from 'react' + +import VoiceSelectArrowsSvg from '#/assets/svg/voice-select-arrows.svg?react' import { deleteMediaVoice, @@ -33,6 +35,13 @@ import styles from './voice-clone-select.module.scss' const VOICE_TITLE_DISPLAY_MAX = 15 +/** Иконка раскрытия селекта (Frame 48097616.svg), ref нужен для MUI Select IconComponent */ +const VoiceSelectArrowsIcon = forwardRef>( + function VoiceSelectArrowsIcon(props, ref) { + return + } +) + function formatVoiceSelectLabel(full: string): { text: string; tooltip?: string } { if (full.length <= VOICE_TITLE_DISPLAY_MAX) return { text: full } return { text: `${full.slice(0, VOICE_TITLE_DISPLAY_MAX)}…`, tooltip: full } @@ -41,7 +50,7 @@ function formatVoiceSelectLabel(full: string): { text: string; tooltip?: string function VoiceSelectLabel({ full }: { full: string }) { const { text, tooltip } = formatVoiceSelectLabel(full) const typo = ( - + {text} ) @@ -125,10 +134,11 @@ const selectSx = { borderRadius: '13px', backgroundColor: '#4B4B4B', border: '2px solid #40404E;', - color: '#A6A5A5', + color: '#FFFFFF', '.MuiOutlinedInput-root': { minHeight: VOICE_SELECT_ROW_HEIGHT, height: VOICE_SELECT_ROW_HEIGHT, + color: '#FFFFFF', }, '.MuiSelect-select': { display: 'flex', @@ -138,9 +148,13 @@ const selectSx = { paddingTop: 0, paddingBottom: 0, boxSizing: 'border-box', + color: '#FFFFFF', }, '.MuiSelect-icon': { - color: '#A6A5A5', + marginRight: 1, + color: '#FFFFFF', + width: 18, + height: 18, }, '&& fieldset': { border: '0px solid transparent', @@ -172,10 +186,10 @@ const VOICE_SELECT_MENU_PROPS: Partial = { boxShadow: '0px 0px 4px rgba(0, 0, 0, 0.04), 0px 4px 32px rgba(0, 0, 0, 0.16)', }, }, - MenuListProps: { + MenuListProps: { className: 'smallScroll', sx: { - color: '#A6A5A5', + color: '#FFFFFF', backgroundColor: '#151518', maxHeight: VOICE_SELECT_MENU_LIST_MAX_HEIGHT, overflowY: 'auto', @@ -210,6 +224,7 @@ export function VoiceCloneSelect({ onVoiceListChanged, onDeletedVoice, }: Props) { + const isMobile = useMediaQuery('(max-width: 768px)', { noSsr: true }) const { showMessage } = useShowDataStore() const all = [...voices, ...presets] const audioRef = useRef(null) @@ -453,13 +468,21 @@ export function VoiceCloneSelect({ open={Boolean(deleteConfirm)} anchorEl={deleteConfirm?.anchorEl ?? null} onClose={() => setDeleteConfirm(null)} - anchorOrigin={{ vertical: 'top', horizontal: 'center' }} - transformOrigin={{ vertical: 'bottom', horizontal: 'center' }} + anchorOrigin={ + isMobile + ? { vertical: 'center', horizontal: 'left' } + : { vertical: 'top', horizontal: 'center' } + } + transformOrigin={ + isMobile + ? { vertical: 'center', horizontal: 'right' } + : { vertical: 'bottom', horizontal: 'center' } + } disableAutoFocus disableEnforceFocus sx={{ zIndex: 1600 }} PaperProps={{ - className: styles.deleteConfirmPaper, + className: c(styles.deleteConfirmPaper, isMobile && styles.deleteConfirmPaperMobile), }} > {deleteConfirm ? ( @@ -486,14 +509,14 @@ export function VoiceCloneSelect({