fix(web): prevent plugin config form overflow

This commit is contained in:
Junyan Qin
2026-05-20 19:55:21 +08:00
parent aa8d53dde6
commit 49064ffc2d
6 changed files with 86 additions and 77 deletions

View File

@@ -126,13 +126,13 @@ function WebhookUrlField({
}; };
return ( return (
<FormItem> <FormItem className="min-w-0">
<FormLabel>{label}</FormLabel> <FormLabel className="break-words">{label}</FormLabel>
<div className="flex items-center gap-2"> <div className="flex min-w-0 items-center gap-2">
<Input <Input
value={url} value={url}
readOnly readOnly
className="flex-1 bg-muted" className="min-w-0 flex-1 bg-muted"
onClick={(e) => (e.target as HTMLInputElement).select()} onClick={(e) => (e.target as HTMLInputElement).select()}
/> />
<Button <Button
@@ -149,11 +149,11 @@ function WebhookUrlField({
</Button> </Button>
</div> </div>
{extraUrl && ( {extraUrl && (
<div className="flex items-center gap-2 mt-2"> <div className="mt-2 flex min-w-0 items-center gap-2">
<Input <Input
value={extraUrl} value={extraUrl}
readOnly readOnly
className="flex-1 bg-muted" className="min-w-0 flex-1 bg-muted"
onClick={(e) => (e.target as HTMLInputElement).select()} onClick={(e) => (e.target as HTMLInputElement).select()}
/> />
<Button <Button
@@ -171,12 +171,14 @@ function WebhookUrlField({
</div> </div>
)} )}
{description && ( {description && (
<p className="text-sm text-muted-foreground">{description}</p> <p className="text-sm break-words text-muted-foreground">
{description}
</p>
)} )}
{systemInfo.edition === 'community' && ( {systemInfo.edition === 'community' && (
<div className="flex items-start gap-2.5 rounded-md border border-border/60 bg-muted/40 px-3 py-2.5 mt-1 max-w-2xl"> <div className="mt-1 flex max-w-full min-w-0 items-start gap-2.5 rounded-md border border-border/60 bg-muted/40 px-3 py-2.5">
<Globe className="h-4 w-4 text-muted-foreground shrink-0 mt-0.5" /> <Globe className="h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
<p className="text-sm text-muted-foreground leading-relaxed"> <p className="text-sm leading-relaxed break-words text-muted-foreground">
{t('bots.webhookSaasHint')}{' '} {t('bots.webhookSaasHint')}{' '}
<a <a
href="https://space.langbot.app/cloud?utm_source=local_webui&utm_medium=webhook_alert&utm_campaign=saas_conversion" href="https://space.langbot.app/cloud?utm_source=local_webui&utm_medium=webhook_alert&utm_campaign=saas_conversion"
@@ -442,7 +444,7 @@ export default function DynamicFormComponent({
return ( return (
<Form {...form}> <Form {...form}>
<div className="space-y-4"> <div className="min-w-0 max-w-full space-y-4 overflow-x-hidden">
{itemConfigList.map((config) => { {itemConfigList.map((config) => {
if (config.show_if) { if (config.show_if) {
const dependValue = resolveShowIfValue( const dependValue = resolveShowIfValue(
@@ -582,20 +584,20 @@ export default function DynamicFormComponent({
control={form.control} control={form.control}
name={config.name as keyof FormValues} name={config.name as keyof FormValues}
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem className="min-w-0">
<div <div
className={cn( className={cn(
'flex flex-row items-center justify-between rounded-lg border p-4 max-w-2xl', 'flex w-full min-w-0 max-w-full flex-row items-center justify-between rounded-lg border p-4',
isFieldDisabled && 'pointer-events-none opacity-60', isFieldDisabled && 'pointer-events-none opacity-60',
)} )}
> >
<div className="space-y-0.5"> <div className="min-w-0 space-y-0.5">
<FormLabel className="text-base flex items-center gap-1.5"> <FormLabel className="flex min-w-0 items-center gap-1.5 text-base">
{extractI18nObject(config.label)} {extractI18nObject(config.label)}
{renderDisabledTooltipIcon()} {renderDisabledTooltipIcon()}
</FormLabel> </FormLabel>
{config.description && ( {config.description && (
<p className="text-sm text-muted-foreground"> <p className="text-sm break-words text-muted-foreground">
{extractI18nObject(config.description)} {extractI18nObject(config.description)}
</p> </p>
)} )}
@@ -621,9 +623,9 @@ export default function DynamicFormComponent({
control={form.control} control={form.control}
name={config.name as keyof FormValues} name={config.name as keyof FormValues}
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem className="min-w-0">
<FormLabel className="flex items-center gap-1.5"> <FormLabel className="flex min-w-0 items-center gap-1.5">
<span> <span className="min-w-0 break-words">
{extractI18nObject(config.label)}{' '} {extractI18nObject(config.label)}{' '}
{config.required && ( {config.required && (
<span className="text-red-500">*</span> <span className="text-red-500">*</span>
@@ -633,9 +635,10 @@ export default function DynamicFormComponent({
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<div <div
className={ className={cn(
isFieldDisabled ? 'pointer-events-none opacity-60' : '' 'min-w-0 max-w-full overflow-x-hidden',
} isFieldDisabled && 'pointer-events-none opacity-60',
)}
> >
<DynamicFormItemComponent <DynamicFormItemComponent
config={config} config={config}
@@ -645,7 +648,7 @@ export default function DynamicFormComponent({
</div> </div>
</FormControl> </FormControl>
{config.description && ( {config.description && (
<p className="text-sm text-muted-foreground"> <p className="text-sm break-words text-muted-foreground">
{extractI18nObject(config.description)} {extractI18nObject(config.description)}
</p> </p>
)} )}

View File

@@ -69,7 +69,6 @@ export default function DynamicFormItemComponent({
onFileUploaded, onFileUploaded,
}: { }: {
config: IDynamicFormItemSchema; config: IDynamicFormItemSchema;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
field: ControllerRenderProps<any, any>; field: ControllerRenderProps<any, any>;
onFileUploaded?: (fileKey: string) => void; onFileUploaded?: (fileKey: string) => void;
}) { }) {
@@ -251,7 +250,7 @@ export default function DynamicFormItemComponent({
return ( return (
<Input <Input
type="number" type="number"
className="max-w-xs" className="w-full max-w-xs"
{...field} {...field}
onChange={(e) => field.onChange(Number(e.target.value))} onChange={(e) => field.onChange(Number(e.target.value))}
/> />
@@ -260,8 +259,8 @@ export default function DynamicFormItemComponent({
case DynamicFormItemType.STRING: case DynamicFormItemType.STRING:
if (config.options && config.options.length > 0) { if (config.options && config.options.length > 0) {
return ( return (
<div className="flex items-center gap-1.5 max-w-md"> <div className="flex w-full max-w-md min-w-0 items-center gap-1.5">
<Input className="flex-1" {...field} /> <Input className="min-w-0 flex-1" {...field} />
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button
@@ -292,21 +291,26 @@ export default function DynamicFormItemComponent({
</div> </div>
); );
} }
return <Input className="max-w-md" {...field} />; return <Input className="w-full max-w-md" {...field} />;
case DynamicFormItemType.TEXT: case DynamicFormItemType.TEXT:
return <Textarea {...field} className="min-h-[120px] max-w-2xl" />; return (
<Textarea
{...field}
className="min-h-[120px] w-full max-w-full resize-y overflow-x-hidden break-all"
/>
);
case DynamicFormItemType.BOOLEAN: case DynamicFormItemType.BOOLEAN:
return <Switch checked={field.value} onCheckedChange={field.onChange} />; return <Switch checked={field.value} onCheckedChange={field.onChange} />;
case DynamicFormItemType.STRING_ARRAY: case DynamicFormItemType.STRING_ARRAY:
return ( return (
<div className="space-y-2 max-w-md"> <div className="w-full max-w-md min-w-0 space-y-2">
{field.value.map((item: string, index: number) => ( {field.value.map((item: string, index: number) => (
<div key={index} className="flex gap-1.5 items-center"> <div key={index} className="flex min-w-0 items-center gap-1.5">
<Input <Input
className="flex-1" className="min-w-0 flex-1"
value={item} value={item}
onChange={(e) => { onChange={(e) => {
const newValue = [...field.value]; const newValue = [...field.value];
@@ -347,7 +351,7 @@ export default function DynamicFormItemComponent({
case DynamicFormItemType.SELECT: case DynamicFormItemType.SELECT:
return ( return (
<Select value={field.value} onValueChange={field.onChange}> <Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger className="max-w-md bg-[#ffffff] dark:bg-[#2a2a2e]"> <SelectTrigger className="w-full max-w-md bg-[#ffffff] dark:bg-[#2a2a2e]">
<SelectValue placeholder={t('common.select')} /> <SelectValue placeholder={t('common.select')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -409,10 +413,10 @@ export default function DynamicFormItemComponent({
]; ];
return ( return (
<div className="max-w-md flex items-center gap-1.5"> <div className="flex w-full max-w-md min-w-0 items-center gap-1.5">
<div className="flex-1"> <div className="min-w-0 flex-1">
<Select value={field.value} onValueChange={field.onChange}> <Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger className="bg-[#ffffff] dark:bg-[#2a2a2e]"> <SelectTrigger className="min-w-0 bg-[#ffffff] dark:bg-[#2a2a2e]">
<SelectValue placeholder={t('models.selectModel')} /> <SelectValue placeholder={t('models.selectModel')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -577,9 +581,9 @@ export default function DynamicFormItemComponent({
); );
return ( return (
<div className="max-w-md"> <div className="w-full max-w-md min-w-0">
<Select value={field.value} onValueChange={field.onChange}> <Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger className="bg-[#ffffff] dark:bg-[#2a2a2e]"> <SelectTrigger className="min-w-0 bg-[#ffffff] dark:bg-[#2a2a2e]">
<SelectValue placeholder={t('knowledge.selectEmbeddingModel')} /> <SelectValue placeholder={t('knowledge.selectEmbeddingModel')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -612,12 +616,12 @@ export default function DynamicFormItemComponent({
); );
return ( return (
<div className="max-w-md"> <div className="w-full max-w-md min-w-0">
<Select <Select
value={field.value || '__none__'} value={field.value || '__none__'}
onValueChange={(v) => field.onChange(v === '__none__' ? '' : v)} onValueChange={(v) => field.onChange(v === '__none__' ? '' : v)}
> >
<SelectTrigger className="bg-[#ffffff] dark:bg-[#2a2a2e]"> <SelectTrigger className="min-w-0 bg-[#ffffff] dark:bg-[#2a2a2e]">
<SelectValue placeholder={t('models.rerank')} /> <SelectValue placeholder={t('models.rerank')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -713,7 +717,7 @@ export default function DynamicFormItemComponent({
placeholder: string, placeholder: string,
) => ( ) => (
<Select value={value} onValueChange={onChange}> <Select value={value} onValueChange={onChange}>
<SelectTrigger className="bg-[#ffffff] dark:bg-[#2a2a2e]"> <SelectTrigger className="min-w-0 bg-[#ffffff] dark:bg-[#2a2a2e]">
<SelectValue placeholder={placeholder} /> <SelectValue placeholder={placeholder} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -879,14 +883,14 @@ export default function DynamicFormItemComponent({
}; };
return ( return (
<div className="space-y-3"> <div className="w-full min-w-0 space-y-3">
{/* Primary model selector */} {/* Primary model selector */}
<div> <div>
<p className="text-xs text-muted-foreground mb-1"> <p className="text-xs text-muted-foreground mb-1">
{t('models.fallback.primary')} {t('models.fallback.primary')}
</p> </p>
<div className="flex items-center gap-1.5"> <div className="flex min-w-0 items-center gap-1.5">
<div className="flex-1"> <div className="min-w-0 flex-1">
{renderModelSelect( {renderModelSelect(
modelValue.primary, modelValue.primary,
(val) => updateValue({ primary: val }), (val) => updateValue({ primary: val }),
@@ -918,16 +922,16 @@ export default function DynamicFormItemComponent({
{/* Fallback models */} {/* Fallback models */}
{modelValue.fallbacks.length > 0 && ( {modelValue.fallbacks.length > 0 && (
<div className="space-y-2"> <div className="min-w-0 space-y-2">
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">
{t('models.fallback.fallbackList')} {t('models.fallback.fallbackList')}
</p> </p>
{modelValue.fallbacks.map((fbUuid: string, index: number) => ( {modelValue.fallbacks.map((fbUuid: string, index: number) => (
<div key={index} className="flex items-center gap-2"> <div key={index} className="flex min-w-0 items-center gap-2">
<span className="text-xs text-muted-foreground w-4 shrink-0"> <span className="text-xs text-muted-foreground w-4 shrink-0">
{index + 1}. {index + 1}.
</span> </span>
<div className="flex-1"> <div className="min-w-0 flex-1">
{renderModelSelect( {renderModelSelect(
fbUuid, fbUuid,
(val) => updateFallbackModel(index, val), (val) => updateFallbackModel(index, val),
@@ -1003,20 +1007,22 @@ export default function DynamicFormItemComponent({
return ( return (
<Select value={field.value} onValueChange={field.onChange}> <Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger className="bg-[#ffffff] dark:bg-[#2a2a2e]"> <SelectTrigger className="min-w-0 bg-[#ffffff] dark:bg-[#2a2a2e]">
{field.value && field.value !== '__none__' ? ( {field.value && field.value !== '__none__' ? (
(() => { (() => {
const selectedKb = knowledgeBases.find( const selectedKb = knowledgeBases.find(
(kb) => kb.uuid === field.value, (kb) => kb.uuid === field.value,
); );
return ( return (
<div className="flex items-center gap-2"> <div className="flex min-w-0 items-center gap-2">
{selectedKb?.emoji && ( {selectedKb?.emoji && (
<span className="text-sm shrink-0"> <span className="text-sm shrink-0">
{selectedKb.emoji} {selectedKb.emoji}
</span> </span>
)} )}
<span>{selectedKb?.name ?? field.value}</span> <span className="truncate">
{selectedKb?.name ?? field.value}
</span>
</div> </div>
); );
})() })()
@@ -1066,9 +1072,9 @@ export default function DynamicFormItemComponent({
return ( return (
<> <>
<div className="space-y-2"> <div className="min-w-0 space-y-2">
{field.value && field.value.length > 0 ? ( {field.value && field.value.length > 0 ? (
<div className="space-y-2"> <div className="min-w-0 space-y-2">
{field.value.map((kbId: string) => { {field.value.map((kbId: string) => {
const currentKb = knowledgeBases.find( const currentKb = knowledgeBases.find(
(base) => base.uuid === kbId, (base) => base.uuid === kbId,
@@ -1078,17 +1084,17 @@ export default function DynamicFormItemComponent({
return ( return (
<div <div
key={kbId} key={kbId}
className="flex items-center justify-between rounded-lg border p-3 hover:bg-accent" className="flex min-w-0 items-center justify-between rounded-lg border p-3 hover:bg-accent"
> >
<div className="flex items-center gap-2 flex-1"> <div className="flex min-w-0 flex-1 items-center gap-2">
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="font-medium flex items-center gap-2"> <div className="flex min-w-0 items-center gap-2 font-medium">
{currentKb.emoji && ( {currentKb.emoji && (
<span className="text-sm shrink-0"> <span className="text-sm shrink-0">
{currentKb.emoji} {currentKb.emoji}
</span> </span>
)} )}
{currentKb.name} <span className="truncate">{currentKb.name}</span>
{currentKb.knowledge_engine?.name && ( {currentKb.knowledge_engine?.name && (
<span className="text-xs px-2 py-0.5 rounded-full bg-purple-100 text-purple-700 dark:bg-purple-900 dark:text-purple-300"> <span className="text-xs px-2 py-0.5 rounded-full bg-purple-100 text-purple-700 dark:bg-purple-900 dark:text-purple-300">
{extractI18nObject( {extractI18nObject(
@@ -1098,7 +1104,7 @@ export default function DynamicFormItemComponent({
)} )}
</div> </div>
{currentKb.description && ( {currentKb.description && (
<div className="text-sm text-muted-foreground"> <div className="text-sm break-words text-muted-foreground">
{currentKb.description} {currentKb.description}
</div> </div>
)} )}
@@ -1221,7 +1227,7 @@ export default function DynamicFormItemComponent({
case DynamicFormItemType.BOT_SELECTOR: case DynamicFormItemType.BOT_SELECTOR:
return ( return (
<Select value={field.value} onValueChange={field.onChange}> <Select value={field.value} onValueChange={field.onChange}>
<SelectTrigger className="bg-[#ffffff] dark:bg-[#2a2a2e]"> <SelectTrigger className="min-w-0 bg-[#ffffff] dark:bg-[#2a2a2e]">
<SelectValue placeholder={t('bots.selectBot')} /> <SelectValue placeholder={t('bots.selectBot')} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -1239,9 +1245,9 @@ export default function DynamicFormItemComponent({
case DynamicFormItemType.TOOLS_SELECTOR: case DynamicFormItemType.TOOLS_SELECTOR:
return ( return (
<> <>
<div className="space-y-2"> <div className="min-w-0 space-y-2">
{field.value && field.value.length > 0 ? ( {field.value && field.value.length > 0 ? (
<div className="space-y-2"> <div className="min-w-0 space-y-2">
{field.value.map((toolName: string) => { {field.value.map((toolName: string) => {
const currentTool = tools.find( const currentTool = tools.find(
(tool) => tool.name === toolName, (tool) => tool.name === toolName,
@@ -1250,12 +1256,12 @@ export default function DynamicFormItemComponent({
return ( return (
<div <div
key={toolName} key={toolName}
className="flex items-center justify-between rounded-lg border p-3 hover:bg-accent" className="flex min-w-0 items-center justify-between rounded-lg border p-3 hover:bg-accent"
> >
<div className="flex items-center gap-2 flex-1"> <div className="flex min-w-0 flex-1 items-center gap-2">
<Wrench className="h-4 w-4 shrink-0 text-muted-foreground" /> <Wrench className="h-4 w-4 shrink-0 text-muted-foreground" />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="font-medium">{toolName}</div> <div className="truncate font-medium">{toolName}</div>
{currentTool?.human_desc && ( {currentTool?.human_desc && (
<div className="text-sm text-muted-foreground truncate"> <div className="text-sm text-muted-foreground truncate">
{currentTool.human_desc} {currentTool.human_desc}
@@ -1379,13 +1385,16 @@ export default function DynamicFormItemComponent({
? field.value ? field.value
: [{ role: 'system', content: '' }]; : [{ role: 'system', content: '' }];
return ( return (
<div className="space-y-2"> <div className="min-w-0 space-y-2">
{promptItems.map( {promptItems.map(
(item: { role: string; content: string }, index: number) => ( (item: { role: string; content: string }, index: number) => (
<div key={index} className="flex gap-2 items-center"> <div
key={index}
className="flex min-w-0 flex-col gap-2 sm:flex-row sm:items-center"
>
{/* 角色选择 */} {/* 角色选择 */}
{index === 0 ? ( {index === 0 ? (
<div className="w-[120px] px-3 py-2 border rounded bg-gray-50 dark:bg-[#2a292e] text-gray-500 dark:text-white dark:border-gray-600"> <div className="w-full shrink-0 rounded border bg-gray-50 px-3 py-2 text-gray-500 sm:w-[120px] dark:border-gray-600 dark:bg-[#2a292e] dark:text-white">
system system
</div> </div>
) : ( ) : (
@@ -1410,7 +1419,7 @@ export default function DynamicFormItemComponent({
)} )}
{/* 内容输入 */} {/* 内容输入 */}
<Textarea <Textarea
className="w-[300px]" className="min-h-20 w-full min-w-0 flex-1 resize-y overflow-x-hidden break-all sm:w-[300px]"
value={item.content} value={item.content}
onChange={(e) => { onChange={(e) => {
const newValue = [...(field.value ?? promptItems)]; const newValue = [...(field.value ?? promptItems)];
@@ -1428,7 +1437,6 @@ export default function DynamicFormItemComponent({
className="p-2 hover:bg-gray-100 rounded" className="p-2 hover:bg-gray-100 rounded"
onClick={() => { onClick={() => {
const newValue = (field.value ?? promptItems).filter( const newValue = (field.value ?? promptItems).filter(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(_: any, i: number) => i !== index, (_: any, i: number) => i !== index,
); );
field.onChange(newValue); field.onChange(newValue);

View File

@@ -208,7 +208,7 @@ export default function PluginDetailContent({ id }: { id: string }) {
</div> </div>
<div className="flex min-h-0 max-w-full flex-1 flex-col gap-6 overflow-y-auto md:flex-row md:overflow-hidden"> <div className="flex min-h-0 max-w-full flex-1 flex-col gap-6 overflow-y-auto md:flex-row md:overflow-hidden">
<div className="space-y-4 pb-6 md:min-h-0 md:w-[380px] md:flex-shrink-0 md:overflow-y-auto md:overflow-x-hidden xl:w-[420px]"> <div className="min-w-0 max-w-full space-y-4 pb-6 md:min-h-0 md:w-[380px] md:flex-shrink-0 md:overflow-y-auto md:overflow-x-hidden xl:w-[420px]">
<PluginForm <PluginForm
pluginAuthor={pluginAuthor} pluginAuthor={pluginAuthor}
pluginName={pluginName} pluginName={pluginName}

View File

@@ -42,7 +42,6 @@ export default function PluginForm({
setPluginConfig(res); setPluginConfig(res);
// 提取初始配置中的所有文件 key // 提取初始配置中的所有文件 key
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const extractFileKeys = (obj: any): string[] => { const extractFileKeys = (obj: any): string[] => {
const keys: string[] = []; const keys: string[] = [];
if (obj && typeof obj === 'object') { if (obj && typeof obj === 'object') {
@@ -77,7 +76,6 @@ export default function PluginForm({
); );
// 提取最终保存的配置中的所有文件 key // 提取最终保存的配置中的所有文件 key
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const extractFileKeys = (obj: any): string[] => { const extractFileKeys = (obj: any): string[] => {
const keys: string[] = []; const keys: string[] = [];
if (obj && typeof obj === 'object') { if (obj && typeof obj === 'object') {
@@ -143,13 +141,13 @@ export default function PluginForm({
} }
return ( return (
<div className="space-y-4"> <div className="min-w-0 max-w-full space-y-4">
<Card> <Card className="min-w-0 overflow-x-hidden">
<CardHeader> <CardHeader>
<CardTitle>{t('plugins.pluginConfig')}</CardTitle> <CardTitle>{t('plugins.pluginConfig')}</CardTitle>
<CardDescription>{t('plugins.saveConfig')}</CardDescription> <CardDescription>{t('plugins.saveConfig')}</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent className="min-w-0 overflow-x-hidden">
{pluginInfo.manifest.manifest.spec.config.length > 0 ? ( {pluginInfo.manifest.manifest.spec.config.length > 0 ? (
<DynamicFormComponent <DynamicFormComponent
itemConfigList={pluginInfo.manifest.manifest.spec.config} itemConfigList={pluginInfo.manifest.manifest.spec.config}

View File

@@ -78,7 +78,7 @@ function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
<FormItemContext.Provider value={{ id }}> <FormItemContext.Provider value={{ id }}>
<div <div
data-slot="form-item" data-slot="form-item"
className={cn('grid gap-2', className)} className={cn('grid min-w-0 gap-2', className)}
{...props} {...props}
/> />
</FormItemContext.Provider> </FormItemContext.Provider>
@@ -128,7 +128,7 @@ function FormDescription({ className, ...props }: React.ComponentProps<'p'>) {
<p <p
data-slot="form-description" data-slot="form-description"
id={formDescriptionId} id={formDescriptionId}
className={cn('text-muted-foreground text-sm', className)} className={cn('text-muted-foreground text-sm break-words', className)}
{...props} {...props}
/> />
); );
@@ -146,7 +146,7 @@ function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
<p <p
data-slot="form-message" data-slot="form-message"
id={formMessageId} id={formMessageId}
className={cn('text-destructive text-sm', className)} className={cn('text-destructive text-sm break-words', className)}
{...props} {...props}
> >
{body} {body}

View File

@@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
<textarea <textarea
data-slot="textarea" data-slot="textarea"
className={cn( className={cn(
'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm', 'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex min-h-16 w-full min-w-0 max-w-full resize-y overflow-x-hidden rounded-md border bg-transparent px-3 py-2 text-base break-words shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
className, className,
)} )}
{...props} {...props}