feat: add image upload toggle with auto-disable after sending

Add a toggle switch to enable/disable image uploads in the playground,
with automatic disabling after message sending to prevent accidental
image inclusion in subsequent messages.

Changes:
- Add `imageEnabled` field to default configuration with false as default
- Enhance ImageUrlInput component with enable/disable toggle switch
- Update UI to show disabled state with opacity and color changes
- Modify message sending logic to only include images when enabled
- Implement auto-disable functionality after message is sent
- Update SettingsPanel to pass through new imageEnabled props
- Maintain backward compatibility with existing configurations

User Experience:
- Images are disabled by default for privacy and intentional usage
- Users must explicitly enable image uploads before adding URLs
- After sending a message with images, the feature auto-disables
- Clear visual feedback shows current enabled/disabled state
- Manual control allows users to re-enable when needed

This improves user control over multimodal conversations and prevents
unintentional image sharing in follow-up messages.
This commit is contained in:
Apple\Apple
2025-05-30 20:05:13 +08:00
parent 2abf2c464f
commit fbb189ecd7
4 changed files with 42 additions and 15 deletions

View File

@@ -3,15 +3,17 @@ import {
Input,
Typography,
Button,
Switch,
} from '@douyinfe/semi-ui';
import { IconFile } from '@douyinfe/semi-icons';
import {
FileText,
Plus,
X,
Image,
} from 'lucide-react';
const ImageUrlInput = ({ imageUrls, onImageUrlsChange }) => {
const ImageUrlInput = ({ imageUrls, imageEnabled, onImageUrlsChange, onImageEnabledChange }) => {
const handleAddImageUrl = () => {
const newUrls = [...imageUrls, ''];
onImageUrlsChange(newUrls);
@@ -32,7 +34,7 @@ const ImageUrlInput = ({ imageUrls, onImageUrlsChange }) => {
<div>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<FileText size={16} className="text-gray-500" />
<Image size={16} className={imageEnabled ? "text-blue-500" : "text-gray-400"} />
<Typography.Text strong className="text-sm">
图片地址
</Typography.Text>
@@ -40,18 +42,32 @@ const ImageUrlInput = ({ imageUrls, onImageUrlsChange }) => {
(多模态对话)
</Typography.Text>
</div>
<Button
icon={<Plus size={14} />}
size="small"
theme="solid"
type="primary"
onClick={handleAddImageUrl}
className="!rounded-full !w-4 !h-4 !p-0 !min-w-0"
disabled={imageUrls.length >= 5}
/>
<div className="flex items-center gap-2">
<Switch
checked={imageEnabled}
onChange={onImageEnabledChange}
checkedText="启用"
uncheckedText="停用"
size="small"
className="flex-shrink-0"
/>
<Button
icon={<Plus size={14} />}
size="small"
theme="solid"
type="primary"
onClick={handleAddImageUrl}
className="!rounded-full !w-4 !h-4 !p-0 !min-w-0"
disabled={!imageEnabled || imageUrls.length >= 5}
/>
</div>
</div>
{imageUrls.length === 0 ? (
{!imageEnabled ? (
<Typography.Text className="text-xs text-gray-500 mb-2 block">
图片发送已停用启用后可添加图片URL进行多模态对话
</Typography.Text>
) : imageUrls.length === 0 ? (
<Typography.Text className="text-xs text-gray-500 mb-2 block">
点击 + 按钮添加图片URL支持最多5张图片
</Typography.Text>
@@ -61,7 +77,7 @@ const ImageUrlInput = ({ imageUrls, onImageUrlsChange }) => {
</Typography.Text>
)}
<div className="space-y-2 max-h-32 overflow-y-auto">
<div className={`space-y-2 max-h-32 overflow-y-auto ${!imageEnabled ? 'opacity-50' : ''}`}>
{imageUrls.map((url, index) => (
<div key={index} className="flex items-center gap-2">
<div className="flex-1">
@@ -72,6 +88,7 @@ const ImageUrlInput = ({ imageUrls, onImageUrlsChange }) => {
className="!rounded-lg"
size="small"
prefix={<IconFile size='small' />}
disabled={!imageEnabled}
/>
</div>
<Button
@@ -81,6 +98,7 @@ const ImageUrlInput = ({ imageUrls, onImageUrlsChange }) => {
type="danger"
onClick={() => handleRemoveImageUrl(index)}
className="!rounded-full !w-6 !h-6 !p-0 !min-w-0 !text-red-500 hover:!bg-red-50 flex-shrink-0"
disabled={!imageEnabled}
/>
</div>
))}

View File

@@ -125,7 +125,9 @@ const SettingsPanel = ({
{/* 图片URL输入 */}
<ImageUrlInput
imageUrls={inputs.imageUrls}
imageEnabled={inputs.imageEnabled}
onImageUrlsChange={(urls) => onInputChange('imageUrls', urls)}
onImageEnabledChange={(enabled) => onInputChange('imageEnabled', enabled)}
/>
{/* 参数控制组件 */}

View File

@@ -12,6 +12,7 @@ const DEFAULT_CONFIG = {
seed: null,
stream: true,
imageUrls: [],
imageEnabled: false,
},
parameterEnabled: {
max_tokens: true,

View File

@@ -568,7 +568,7 @@ const Playground = () => {
let messageContent;
const validImageUrls = inputs.imageUrls.filter(url => url.trim() !== '');
if (validImageUrls.length > 0) {
if (inputs.imageEnabled && validImageUrls.length > 0) {
messageContent = [
{
type: 'text',
@@ -643,6 +643,12 @@ const Playground = () => {
handleNonStreamRequest(payload);
}
if (inputs.imageEnabled) {
setTimeout(() => {
handleInputChange('imageEnabled', false);
}, 100);
}
newMessage.push({
role: 'assistant',
content: '',
@@ -655,7 +661,7 @@ const Playground = () => {
return newMessage;
});
},
[getSystemMessage, inputs, setMessage, parameterEnabled],
[getSystemMessage, inputs, setMessage, parameterEnabled, handleInputChange],
);
const completeMessage = useCallback((status = 'complete') => {