style: show voice error message

main
jialin 1 year ago
parent 2ddeca8f9d
commit e622636686

@ -7,10 +7,12 @@ interface AlertInfoProps {
message: string;
rows?: number;
icon?: React.ReactNode;
ellipsis?: boolean;
style?: React.CSSProperties;
}
const AlertInfo: React.FC<AlertInfoProps> = (props) => {
const { message, type, rows = 1 } = props;
const { message, type, rows = 1, ellipsis, style } = props;
if (!message) {
return null;
}
@ -18,16 +20,21 @@ const AlertInfo: React.FC<AlertInfoProps> = (props) => {
return (
<Typography.Paragraph
type={type}
ellipsis={{
rows: rows,
tooltip: message
}}
ellipsis={
ellipsis !== undefined
? ellipsis
: {
rows: rows,
tooltip: message
}
}
style={{
textAlign: 'center',
padding: '2px 5px',
borderRadius: 'var(--border-radius-base)',
margin: 0,
backgroundColor: 'var(--ant-color-error-bg)'
backgroundColor: 'var(--ant-color-error-bg)',
...style
}}
>
<WarningOutlined className="m-r-8" />

@ -2,19 +2,54 @@
width: 100%;
display: flex;
background-color: var(--ant-color-fill-quaternary);
border-radius: 36px;
border-radius: 6px;
.player-ui {
padding: 5px 10px;
padding: 8px 16px;
flex: 1;
display: flex;
justify-content: flex-start;
align-items: center;
}
.controls {
display: flex;
justify-content: center;
align-items: center;
margin-inline: 10px;
}
.play-btn {
margin-inline: 30px;
height: 22px;
width: 22px;
overflow: hidden;
.ant-btn {
height: 22px;
width: 22px;
}
}
.backward,
.forward {
background: none !important;
}
.slider {
display: flex;
justify-content: center;
align-items: center;
.slider-inner {
flex: 1;
margin-inline: 10px;
}
}
.play-content {
display: flex;
justify-content: flex-start;
justify-content: center;
align-items: center;
flex: 1;
}
@ -43,7 +78,7 @@
}
.ant-slider-horizontal {
margin-block: 2px;
margin-block: 5px;
}
}

@ -1,5 +1,11 @@
import { formatTime } from '@/utils/index';
import { PauseCircleFilled, PlayCircleFilled } from '@ant-design/icons';
import {
FastBackwardOutlined,
FastForwardOutlined,
PauseCircleFilled,
PlayCircleFilled
} from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Slider, Tooltip } from 'antd';
import { round } from 'lodash';
import React, {
@ -8,8 +14,6 @@ import React, {
useEffect,
useImperativeHandle
} from 'react';
import CheckButtons from '../check-buttons';
import IconFont from '../icon-font';
import './index.less';
interface AudioPlayerProps {
@ -30,7 +34,14 @@ const speedOptions = [
{ label: '4x', value: 4 }
];
const speedConfig = {
min: 0.5,
max: 2,
step: 0.25
};
const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
const intl = useIntl();
const { autoplay = false, speed: defaultSpeed = 1 } = props;
const audioRef = React.useRef<HTMLAudioElement>(null);
const [audioState, setAudioState] = React.useState<{
@ -95,7 +106,10 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
setPlayOn(!playOn);
}, [playOn]);
const handleFormatVolume = (val: number) => {
const handleFormatVolume = (val?: number) => {
if (val === undefined) {
return `${round(volume * 100)}%`;
}
return `${round(val * 100)}%`;
};
@ -132,6 +146,28 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
});
}, []);
const handleReduceSpeed = () => {
setSpeed((pre) => {
if (pre - speedConfig.step < speedConfig.min) {
return speedConfig.min;
}
const next = pre - speedConfig.step;
audioRef.current!.playbackRate = next;
return next;
});
};
const handleAddSpeed = () => {
setSpeed((pre) => {
if (pre + speedConfig.step > speedConfig.max) {
return speedConfig.max;
}
const next = pre + speedConfig.step;
audioRef.current!.playbackRate = next;
return next;
});
};
useEffect(() => {
if (audioRef.current) {
initPlayerConfig();
@ -147,69 +183,115 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
return (
<div className="player-wrap" style={{ width: props.width || '100%' }}>
<div className="player-ui">
<span className="play-btn">
<Button
size="middle"
type="text"
onClick={handlePlay}
icon={
!playOn ? (
<PlayCircleFilled
style={{ fontSize: '22px' }}
></PlayCircleFilled>
) : (
<PauseCircleFilled
style={{ fontSize: '22px' }}
></PauseCircleFilled>
)
}
></Button>
</span>
<div className="play-content">
<span className="time current">
{' '}
{formatTime(audioState.currentTime)}
</span>
<div className="progress-bar">
<span className="file-name">{props.name}</span>
<div className="slider">
<Slider
tooltip={{ open: false }}
min={0}
max={audioState.duration}
value={audioState.currentTime}
onChange={handleCurrentChange}
/>
<span className="time current">
{' '}
{formatTime(audioState.currentTime)}
</span>
<div className="slider-inner">
<Slider
tooltip={{ open: false }}
min={0}
max={audioState.duration}
value={audioState.currentTime}
onChange={handleCurrentChange}
/>
</div>
<span className="time">{formatTime(audioState.duration)}</span>
</div>
<Tooltip
overlayInnerStyle={{
backgroundColor: 'var(--color-white-1)'
}}
arrow={false}
title={
<CheckButtons
options={speedOptions}
onChange={handleSeepdChange}
<div className="controls">
{/* <Tooltip
overlayInnerStyle={{
backgroundColor: 'var(--color-white-1)'
}}
arrow={false}
title={
<CheckButtons
options={speedOptions}
onChange={handleSeepdChange}
size="small"
></CheckButtons>
}
>
<span style={{ cursor: 'pointer' }}>
{speed ? `${speed}x` : '1x'}
</span>
</Tooltip> */}
<Tooltip
title={intl.formatMessage({
id: 'playground.audio.button.slow'
})}
>
<Button
type="text"
size="small"
></CheckButtons>
}
>
<span style={{ cursor: 'pointer' }}>
{speed ? `${speed}x` : '1x'}
className="backward"
disabled={
speed === speedConfig.min || speed < speedConfig.min
}
onClick={handleReduceSpeed}
>
<FastBackwardOutlined className="font-size-20" />
</Button>
</Tooltip>
<span className="play-btn">
<Button
size="middle"
type="text"
onClick={handlePlay}
disabled={!audioState?.duration}
icon={
!playOn ? (
<PlayCircleFilled
style={{ fontSize: '22px' }}
></PlayCircleFilled>
) : (
<PauseCircleFilled
style={{ fontSize: '22px' }}
></PauseCircleFilled>
)
}
></Button>
</span>
</Tooltip>
<Tooltip
title={intl.formatMessage({
id: 'playground.audio.button.fast'
})}
>
<Button
type="text"
size="small"
className="forward"
disabled={
speed === speedConfig.max || speed > speedConfig.max
}
onClick={handleAddSpeed}
>
<FastForwardOutlined className="font-size-20" />
</Button>
</Tooltip>
</div>
</div>
<span className="time">{formatTime(audioState.duration)}</span>
</div>
<span className="speaker">
{/* <span className="speaker">
<Button
size="middle"
type="text"
icon={
<IconFont
type="icon-SpeakerHigh"
style={{ fontSize: '22px' }}
></IconFont>
volume > 0 ? (
<IconFont
type="icon-SpeakerHigh"
style={{ fontSize: '22px' }}
></IconFont>
) : (
<IconFont
type="icon-speaker-slash"
style={{ fontSize: '22px' }}
></IconFont>
)
}
></Button>
{speakerOn && (
@ -225,7 +307,7 @@ const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
onChange={handleVolumeChange}
/>
)}
</span>
</span> */}
</div>
<audio
controls

@ -37,7 +37,7 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
autoBgColor
} = props;
const [color, setColor] = React.useState<string>('');
const [color, setColor] = React.useState({});
const imgWrapper = React.useRef<HTMLSpanElement>(null);
const thumImgWrapStyle = React.useMemo(() => {
@ -59,10 +59,17 @@ const SingleImage: React.FC<SingleImageProps> = (props) => {
return;
}
const color = palette?.Vibrant?.rgb;
const rgba = color
const mutedColor = palette?.Muted?.rgb;
const startColor = color
? `rgba(${color[0]}, ${color[1]}, ${color[2]},0.7)`
: '';
setColor(rgba);
const stopColor = mutedColor
? `rgba(${mutedColor[0]}, ${mutedColor[1]}, ${mutedColor[2]},0.5)`
: '';
setColor({
backgroundImage: `linear-gradient(135deg, ${startColor}, ${stopColor})`
});
});
}, []);

@ -1,8 +1,8 @@
import { createFromIconfontCN } from '@ant-design/icons';
import './iconfont/iconfont.js';
// import './iconfont/iconfont.js';
const IconFont = createFromIconfontCN({
scriptUrl: ''
scriptUrl: '//at.alicdn.com/t/c/font_4613488_oyisgn7lqa.js'
});
export default IconFont;

@ -18,13 +18,14 @@ interface AudioPlayerProps {
const AudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
const { autoplay, audioUrl, speed = 1, ...rest } = props;
const container = useRef<HTMLDivElement>(null);
const { createWavesurfer, play, pause, destroyWavesurfer } = useWavesurfer({
container,
autoplay: autoplay,
url: audioUrl,
audioRate: speed,
...rest
});
const { createWavesurfer, play, pause, destroyWavesurfer, wavesurfer } =
useWavesurfer({
container,
autoplay: autoplay,
url: audioUrl,
audioRate: speed,
...rest
});
useImperativeHandle(ref, () => {
return {

@ -61,6 +61,7 @@ const useWavesurfer = (options: Options) => {
createWavesurfer,
play,
pause,
wavesurfer,
destroyWavesurfer
};
};

@ -112,5 +112,9 @@ export default {
'playground.audio.button.play': 'Play',
'playground.audio.button.download': 'Download',
'playground.audio.button.stop': 'Stop',
'playground.image.prompt.random': 'Random Prompt'
'playground.image.prompt.random': 'Random Prompt',
'playground.audio.button.fast': 'Speed Up',
'playground.audio.button.slow': 'Slow Down',
'playgorund.audio.voice.error':
'Voices are unavailable. The model may still be initializing. Please refresh after a brief wait.'
};

@ -109,5 +109,9 @@ export default {
'playground.audio.button.play': '播放',
'playground.audio.button.download': '下载',
'playground.audio.button.stop': '停止',
'playground.image.prompt.random': '随机提示词'
'playground.image.prompt.random': '随机提示词',
'playground.audio.button.fast': '快进',
'playground.audio.button.slow': '慢放',
'playgorund.audio.voice.error':
'声音无法使用。该模型可能仍在初始化。请稍候后刷新。'
};

@ -12,7 +12,7 @@ import {
} from '@/utils/fetch-chunk-data';
import { FileImageOutlined, SwapOutlined } from '@ant-design/icons';
import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Form, Tooltip } from 'antd';
import { Button, Divider, Form, Tooltip } from 'antd';
import classNames from 'classnames';
import _ from 'lodash';
import 'overlayscrollbars/overlayscrollbars.css';
@ -530,9 +530,10 @@ const GroundImages: React.FC<MessageProps> = forwardRef((props, ref) => {
clearAll={handleClear}
tools={
<>
<span className="p-l-8 font-600">
<span className="font-600">
{intl.formatMessage({ id: 'playground.image.prompt' })}
</span>
<Divider type="vertical" />
<Tooltip
title={intl.formatMessage({
id: 'playground.image.prompt.random'

@ -177,7 +177,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
<Tag color={'geekblue'} bordered={false}>
{intl.formatMessage({ id: 'playground.rerank.rank' })}: {data.rank}
</Tag>
<Tag color={'cyan'} bordered={false}>
<Tag color={'gold'} bordered={false}>
{intl.formatMessage({ id: 'playground.rerank.score' })}:{' '}
{_.round(data.score, 2)}
</Tag>
@ -489,7 +489,16 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
</span>
}
>
<SendOutlined rotate={0} className="font-size-14" />
<Button
ghost
variant="filled"
color="default"
style={{
border: 'none'
}}
>
<SendOutlined rotate={0} className="font-size-14" />
</Button>
</Tooltip>
}
placeholder={intl.formatMessage({

@ -47,7 +47,10 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
{ uid: number; content: string }[]
>([]);
const [searchParams] = useSearchParams();
const selectModel = searchParams.get('model') || '';
const modelType = searchParams.get('type') || '';
const selectModel = searchParams.get('model')
? modelType === 'stt' && searchParams.get('model')
: '';
const [parameters, setParams] = useState<any>({});
const [show, setShow] = useState(false);
const [loading, setLoading] = useState(false);

@ -56,11 +56,15 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
const intl = useIntl();
const [searchParams] = useSearchParams();
const selectModel = searchParams.get('model') || '';
const modelType = searchParams.get('type') || '';
const selectModel = searchParams.get('model')
? modelType === 'tts' && searchParams.get('model')
: '';
const [parameters, setParams] = useState<any>({});
const [show, setShow] = useState(false);
const [loading, setLoading] = useState(false);
const [tokenResult, setTokenResult] = useState<any>();
const [tokenResult, setTokenResult] = useState<any>(null);
const [voiceError, setVoiceError] = useState<any>(null);
const [collapse, setCollapse] = useState(false);
const controllerRef = useRef<any>(null);
const scroller = useRef<any>(null);
@ -151,9 +155,6 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
}
};
const handleClear = () => {
if (!messageList.length) {
return;
}
setMessageId();
setMessageList([]);
setTokenResult(null);
@ -176,7 +177,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
});
console.log('res:', res);
if (res.error) {
setTokenResult({
setVoiceError({
error: true,
errorMessage:
res?.data?.error?.message ||
@ -205,7 +206,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
} catch (error: any) {
const res = error?.response?.data;
if (res.error) {
setTokenResult({
setVoiceError({
error: true,
errorMessage:
res?.error?.message || res?.data?.error || res.error?.detail || ''
@ -247,6 +248,26 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
});
}, [paramsConfig, intl, voiceList]);
const renderVoiceError = useMemo(() => {
if (!voiceError) return null;
return (
<>
{voiceError && (
<div style={{ height: 40 }}>
<AlertInfo
type="danger"
ellipsis={false}
style={{ textAlign: 'left' }}
message={intl.formatMessage({
id: 'playgorund.audio.voice.error'
})}
></AlertInfo>
</div>
)}
</>
);
}, [voiceError]);
useEffect(() => {
handleSelectModel(parameters.model);
}, [parameters.model, handleSelectModel]);
@ -324,7 +345,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
)}
<div className="ground-left-footer">
<MessageInput
actions={['check']}
actions={['check', 'clear']}
checkLabel={intl.formatMessage({
id: 'playground.toolbar.autoplay'
})}
@ -358,7 +379,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
params={parameters}
selectedModel={selectModel}
modelList={modelList}
extra={renderExtra}
extra={[renderExtra, renderVoiceError]}
/>
</div>
</div>

@ -3,7 +3,6 @@ import HighlightCode from '@/components/highlight-code';
import { BulbOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Modal } from 'antd';
import _ from 'lodash';
import React, { useEffect, useState } from 'react';
type ViewModalProps = {
@ -72,16 +71,11 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
...parameters,
...payload
};
_.keys(data).forEach((key: string) => {
if (data[key] === null) {
delete data[key];
}
});
const headers = {
'Content-type': 'application/json',
Authorization: `Bearer $\{YOUR_GPUSTACK_API_KEY}`
};
const code = `import requests\n\nurl="${BaseURL}"\n\nheaders = ${JSON.stringify(headers, null, 2)}\n\ndata=${JSON.stringify(data, null, 2)}\n\nresponse = requests.post(url, headers=headers, json=data)\n\nprint(response.${logcommand.python})`;
const code = `import requests\n\nurl="${BaseURL}"\n\nheaders = ${JSON.stringify(headers, null, 2)}\n\ndata=${JSON.stringify(data, null, 2).replace(/null/g, 'None')}\n\nresponse = requests.post(url, headers=headers, json=data)\n\nprint(response.${logcommand.python})`;
setCodeValue(code);
}
};

@ -146,5 +146,11 @@ export const promptList = [
'A cinematic shot of a baby racoon wearing an intricate italian priest robe.',
'Create portraits of characters in the style of classical oil painting, Capturing the essence of Chinese aristocracy in the Han Dynasty period. This portrait should feature a Chinese male character dressed in exquisite and gorgeous Chinese Han style clothing, with a dignified demeanor. The background should be a simple and elegant wallpaper pattern, with the face and upper body of the character as the focus. The lighting should be soft and diffuse, highlighting the characteristics of the character and the complex details of the clothing. The color palette should be rich and warm, with red, gold, and dark green.',
'photo of a young woman with long, wavy brown hair tied in a bun and glasses. She has a fair complexion and is wearing subtle makeup, emphasizing her eyes and lips. She is dressed in a black top. The background appears to be an urban setting with a building facade, and the sunlight casts a warm glow on her face.',
'A portrait of women with blue eyes.'
'A portrait of women with blue eyes.',
"A close-up image of a light brown and cream-colored Bengal kitten, lying on a cream-colored surface, with a person's hand gently touching the kitten's head, capturing the soft textures of the fur and the subtle patterns, emphasizing the natural light and shadow play on the animal's body.",
'A German Shepherd dog playfully carrying a stick, with a smaller German Shepherd puppy running alongside in a grassy field, showcasing a rich, natural color palette of greens, browns, and tans, and a dynamic action shot with a focus on vibrant texture and depth of field.',
"A young woman with long, dark, wavy hair, wearing a light olive green flowing dress, looks directly at the camera, standing near a window with a soft out-of-focus background of dark green curtains and light green. The image should be sharp, focused on the woman's face, and evoke a natural, soft beauty aesthetic with a slightly muted color palette.",
'A sleepy panda cub rests comfortably on a large, ornate tree branch, its black and white fur contrasting against the reddish-brown bark of the textured, sculpted tree trunk, surrounded by lush green foliage and branches. ',
'A full shot of two people jogging at sunset, featuring a vibrant, warm color palette shifting from twilight blues to peachy-orange tones, with visible sun rays and lens flares, conveying a sense of leisure and athleticism.',
'A close-up portrait of a golden retriever wearing black-framed glasses, exhibiting a rich golden-brown coat with a fluffy texture, and a neutral, light gray background.'
];

Loading…
Cancel
Save