Refactor spacing and layout in forms for improved UI consistency
This commit is contained in:
@@ -28,8 +28,8 @@ export function ParamInput<T extends FieldValues>({
|
|||||||
max,
|
max,
|
||||||
}: ParamInputProps<T>) {
|
}: ParamInputProps<T>) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1.5">
|
||||||
<Label htmlFor={name}>{label}</Label>
|
<Label htmlFor={name}>{label}</Label>
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { memo, useMemo } from 'react'
|
import { memo, useMemo } from 'react'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Shuffle } from 'lucide-react'
|
||||||
|
|
||||||
interface Preset {
|
interface Preset {
|
||||||
label: string
|
label: string
|
||||||
@@ -20,16 +21,33 @@ const PresetSelectorInner = <T extends Preset>({ presets, onSelect }: PresetSele
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => onSelect(preset)}
|
onClick={() => onSelect(preset)}
|
||||||
className="text-xs md:text-sm px-2.5 md:px-3 h-7 md:h-8"
|
className="text-xs md:text-sm px-2 h-6 md:h-7"
|
||||||
>
|
>
|
||||||
{preset.label}
|
{preset.label}
|
||||||
</Button>
|
</Button>
|
||||||
))
|
))
|
||||||
}, [presets, onSelect])
|
}, [presets, onSelect])
|
||||||
|
|
||||||
|
const handleRandomSelect = () => {
|
||||||
|
const randomIndex = Math.floor(Math.random() * presets.length)
|
||||||
|
onSelect(presets[randomIndex])
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-wrap gap-1.5 md:gap-2 mt-1.5 md:mt-2">
|
<div className="flex items-center gap-2 mt-1">
|
||||||
{presetButtons}
|
<div className="flex flex-wrap gap-1 flex-1">
|
||||||
|
{presetButtons}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleRandomSelect}
|
||||||
|
className="h-6 md:h-7 px-2 flex-shrink-0"
|
||||||
|
title="随机选择"
|
||||||
|
>
|
||||||
|
<Shuffle className="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
|
|||||||
}, [currentJob?.id, currentJob?.audio_url])
|
}, [currentJob?.id, currentJob?.audio_url])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4 md:space-y-6">
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-3">
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="language">语言</Label>
|
<Label htmlFor="language">语言</Label>
|
||||||
<Select
|
<Select
|
||||||
value={watch('language')}
|
value={watch('language')}
|
||||||
@@ -139,7 +139,7 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="speaker">发音人</Label>
|
<Label htmlFor="speaker">发音人</Label>
|
||||||
<Select
|
<Select
|
||||||
value={watch('speaker')}
|
value={watch('speaker')}
|
||||||
@@ -161,26 +161,24 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="text">合成文本</Label>
|
<Label htmlFor="text">合成文本</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
{...register('text')}
|
{...register('text')}
|
||||||
placeholder="输入要合成的文本..."
|
placeholder="输入要合成的文本..."
|
||||||
rows={2}
|
className="min-h-[40px] md:min-h-[60px]"
|
||||||
className="min-h-[60px] md:min-h-[96px]"
|
|
||||||
/>
|
/>
|
||||||
{errors.text && (
|
{errors.text && (
|
||||||
<p className="text-sm text-destructive">{errors.text.message}</p>
|
<p className="text-sm text-destructive">{errors.text.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="instruct">情绪指导(可选)</Label>
|
<Label htmlFor="instruct">情绪指导(可选)</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
{...register('instruct')}
|
{...register('instruct')}
|
||||||
placeholder="例如:温柔体贴,语速平缓,充满关怀"
|
placeholder="例如:温柔体贴,语速平缓,充满关怀"
|
||||||
rows={2}
|
className="min-h-[40px] md:min-h-[60px]"
|
||||||
className="min-h-[60px] md:min-h-[80px]"
|
|
||||||
/>
|
/>
|
||||||
<PresetSelector
|
<PresetSelector
|
||||||
presets={PRESET_INSTRUCTS}
|
presets={PRESET_INSTRUCTS}
|
||||||
@@ -198,12 +196,12 @@ const CustomVoiceForm = forwardRef<CustomVoiceFormHandle>((_props, ref) => {
|
|||||||
|
|
||||||
<Collapsible open={advancedOpen} onOpenChange={setAdvancedOpen}>
|
<Collapsible open={advancedOpen} onOpenChange={setAdvancedOpen}>
|
||||||
<CollapsibleTrigger asChild>
|
<CollapsibleTrigger asChild>
|
||||||
<Button type="button" variant="ghost" className="w-full">
|
<Button type="button" variant="ghost" className="w-full py-1.5">
|
||||||
高级选项
|
高级选项
|
||||||
<ChevronDown className="ml-2 h-4 w-4" />
|
<ChevronDown className="ml-2 h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent className="space-y-3 md:space-y-4 pt-3 md:pt-4">
|
<CollapsibleContent className="space-y-2 pt-2">
|
||||||
<ParamInput
|
<ParamInput
|
||||||
name="max_new_tokens"
|
name="max_new_tokens"
|
||||||
label={ADVANCED_PARAMS_INFO.max_new_tokens.label}
|
label={ADVANCED_PARAMS_INFO.max_new_tokens.label}
|
||||||
|
|||||||
@@ -101,14 +101,13 @@ function VoiceCloneForm() {
|
|||||||
}, [currentJob?.id, currentJob?.audio_url])
|
}, [currentJob?.id, currentJob?.audio_url])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4 md:space-y-6">
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-3">
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="ref_text">参考文稿(可选)</Label>
|
<Label htmlFor="ref_text">参考文稿(可选)</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
{...register('ref_text')}
|
{...register('ref_text')}
|
||||||
placeholder="参考音频对应的文本..."
|
placeholder="参考音频对应的文本..."
|
||||||
rows={2}
|
className="min-h-[40px] md:min-h-[60px]"
|
||||||
className="min-h-[60px] md:min-h-[80px]"
|
|
||||||
/>
|
/>
|
||||||
<PresetSelector
|
<PresetSelector
|
||||||
presets={PRESET_REF_TEXTS}
|
presets={PRESET_REF_TEXTS}
|
||||||
@@ -119,7 +118,7 @@ function VoiceCloneForm() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="ref_audio">参考音频</Label>
|
<Label htmlFor="ref_audio">参考音频</Label>
|
||||||
<Controller
|
<Controller
|
||||||
name="ref_audio"
|
name="ref_audio"
|
||||||
@@ -134,7 +133,7 @@ function VoiceCloneForm() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="language">语言(可选)</Label>
|
<Label htmlFor="language">语言(可选)</Label>
|
||||||
<Select
|
<Select
|
||||||
value={watch('language')}
|
value={watch('language')}
|
||||||
@@ -153,13 +152,12 @@ function VoiceCloneForm() {
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="text">合成文本</Label>
|
<Label htmlFor="text">合成文本</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
{...register('text')}
|
{...register('text')}
|
||||||
placeholder="输入要合成的文本..."
|
placeholder="输入要合成的文本..."
|
||||||
rows={2}
|
className="min-h-[40px] md:min-h-[60px]"
|
||||||
className="min-h-[60px] md:min-h-[96px]"
|
|
||||||
/>
|
/>
|
||||||
<PresetSelector
|
<PresetSelector
|
||||||
presets={PRESET_REF_TEXTS}
|
presets={PRESET_REF_TEXTS}
|
||||||
@@ -170,7 +168,7 @@ function VoiceCloneForm() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-3">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Controller
|
<Controller
|
||||||
name="x_vector_only_mode"
|
name="x_vector_only_mode"
|
||||||
@@ -208,12 +206,12 @@ function VoiceCloneForm() {
|
|||||||
|
|
||||||
<Collapsible open={advancedOpen} onOpenChange={setAdvancedOpen}>
|
<Collapsible open={advancedOpen} onOpenChange={setAdvancedOpen}>
|
||||||
<CollapsibleTrigger asChild>
|
<CollapsibleTrigger asChild>
|
||||||
<Button type="button" variant="ghost" className="w-full">
|
<Button type="button" variant="ghost" className="w-full py-1.5">
|
||||||
高级选项
|
高级选项
|
||||||
<ChevronDown className="ml-2 h-4 w-4" />
|
<ChevronDown className="ml-2 h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent className="space-y-3 md:space-y-4 pt-3 md:pt-4">
|
<CollapsibleContent className="space-y-2 pt-2">
|
||||||
<ParamInput
|
<ParamInput
|
||||||
name="max_new_tokens"
|
name="max_new_tokens"
|
||||||
label={ADVANCED_PARAMS_INFO.max_new_tokens.label}
|
label={ADVANCED_PARAMS_INFO.max_new_tokens.label}
|
||||||
|
|||||||
@@ -107,8 +107,8 @@ const VoiceDesignForm = forwardRef<VoiceDesignFormHandle>((_props, ref) => {
|
|||||||
}, [currentJob?.id, currentJob?.audio_url])
|
}, [currentJob?.id, currentJob?.audio_url])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4 md:space-y-6">
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-3">
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="language">语言</Label>
|
<Label htmlFor="language">语言</Label>
|
||||||
<Select
|
<Select
|
||||||
value={watch('language')}
|
value={watch('language')}
|
||||||
@@ -130,26 +130,24 @@ const VoiceDesignForm = forwardRef<VoiceDesignFormHandle>((_props, ref) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="text">合成文本</Label>
|
<Label htmlFor="text">合成文本</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
{...register('text')}
|
{...register('text')}
|
||||||
placeholder="输入要合成的文本..."
|
placeholder="输入要合成的文本..."
|
||||||
rows={2}
|
className="min-h-[40px] md:min-h-[60px]"
|
||||||
className="min-h-[60px] md:min-h-[96px]"
|
|
||||||
/>
|
/>
|
||||||
{errors.text && (
|
{errors.text && (
|
||||||
<p className="text-sm text-destructive">{errors.text.message}</p>
|
<p className="text-sm text-destructive">{errors.text.message}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-1.5 md:space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="instruct">音色描述</Label>
|
<Label htmlFor="instruct">音色描述</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
{...register('instruct')}
|
{...register('instruct')}
|
||||||
placeholder="例如:成熟男性,低沉磁性,充满权威感"
|
placeholder="例如:成熟男性,低沉磁性,充满权威感"
|
||||||
rows={2}
|
className="min-h-[40px] md:min-h-[60px]"
|
||||||
className="min-h-[60px] md:min-h-[80px]"
|
|
||||||
/>
|
/>
|
||||||
<PresetSelector
|
<PresetSelector
|
||||||
presets={PRESET_VOICE_DESIGNS}
|
presets={PRESET_VOICE_DESIGNS}
|
||||||
@@ -167,12 +165,12 @@ const VoiceDesignForm = forwardRef<VoiceDesignFormHandle>((_props, ref) => {
|
|||||||
|
|
||||||
<Collapsible open={advancedOpen} onOpenChange={setAdvancedOpen}>
|
<Collapsible open={advancedOpen} onOpenChange={setAdvancedOpen}>
|
||||||
<CollapsibleTrigger asChild>
|
<CollapsibleTrigger asChild>
|
||||||
<Button type="button" variant="ghost" className="w-full">
|
<Button type="button" variant="ghost" className="w-full py-1.5">
|
||||||
高级选项
|
高级选项
|
||||||
<ChevronDown className="ml-2 h-4 w-4" />
|
<ChevronDown className="ml-2 h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
<CollapsibleContent className="space-y-3 md:space-y-4 pt-3 md:pt-4">
|
<CollapsibleContent className="space-y-2 pt-2">
|
||||||
<ParamInput
|
<ParamInput
|
||||||
name="max_new_tokens"
|
name="max_new_tokens"
|
||||||
label={ADVANCED_PARAMS_INFO.max_new_tokens.label}
|
label={ADVANCED_PARAMS_INFO.max_new_tokens.label}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ function Home() {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent className="pt-4 md:pt-6 px-3 md:px-6">
|
<CardContent className="pt-0 px-3 md:px-6">
|
||||||
<Tabs value={currentTab}>
|
<Tabs value={currentTab}>
|
||||||
<TabsContent value="custom-voice" className="mt-0">
|
<TabsContent value="custom-voice" className="mt-0">
|
||||||
<Suspense fallback={<FormSkeleton />}>
|
<Suspense fallback={<FormSkeleton />}>
|
||||||
|
|||||||
Reference in New Issue
Block a user