/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
import { useState } from 'react'
import {
PaperclipIcon,
FileIcon,
ImageIcon,
ScreenShareIcon,
CameraIcon,
GlobeIcon,
SendIcon,
SquareIcon,
BarChartIcon,
BoxIcon,
NotepadTextIcon,
CodeSquareIcon,
GraduationCapIcon,
} from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { toast } from 'sonner'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import {
PromptInput,
PromptInputButton,
PromptInputFooter,
PromptInputTextarea,
PromptInputTools,
type PromptInputMessage,
} from '@/components/ai-elements/prompt-input'
import { Suggestion, Suggestions } from '@/components/ai-elements/suggestion'
import { ModelGroupSelector } from '@/components/model-group-selector'
import type { ModelOption, GroupOption } from '../types'
interface PlaygroundInputProps {
onSubmit: (text: string) => void
onStop?: () => void
disabled?: boolean
isGenerating?: boolean
models: ModelOption[]
modelValue: string
onModelChange: (value: string) => void
isModelLoading?: boolean
groups: GroupOption[]
groupValue: string
onGroupChange: (value: string) => void
}
const suggestions = [
{ icon: BarChartIcon, text: 'Analyze data', color: '#76d0eb' },
{ icon: BoxIcon, text: 'Surprise me', color: '#76d0eb' },
{ icon: NotepadTextIcon, text: 'Summarize text', color: '#ea8444' },
{ icon: CodeSquareIcon, text: 'Code', color: '#6c71ff' },
{ icon: GraduationCapIcon, text: 'Get advice', color: '#76d0eb' },
{ icon: null, text: 'More' },
]
export function PlaygroundInput({
onSubmit,
onStop,
disabled,
isGenerating,
models,
modelValue,
onModelChange,
isModelLoading = false,
groups,
groupValue,
onGroupChange,
}: PlaygroundInputProps) {
const { t } = useTranslation()
const [text, setText] = useState('')
const isModelSelectDisabled =
disabled || isModelLoading || models.length === 0
const isGroupSelectDisabled = disabled || groups.length === 0
const handleSubmit = (message: PromptInputMessage) => {
if (!message.text?.trim() || disabled) return
onSubmit(message.text)
setText('')
}
const handleFileAction = (action: string) => {
toast.info(t('Feature in development'), {
description: action,
})
}
const handleSuggestionClick = (suggestion: string) => {
onSubmit(suggestion)
}
return (
setText(event.target.value)}
placeholder={t('Ask anything')}
value={text}
/>
}
>
{t('Attach')}
{t('Attach')}
handleFileAction('upload-file')}
>
{t('Upload file')}
handleFileAction('upload-photo')}
>
{t('Upload photo')}
handleFileAction('take-screenshot')}
>
{t('Take screenshot')}
handleFileAction('take-photo')}
>
{t('Take photo')}
toast.info(t('Search feature in development'))}
variant='outline'
>
{t('Search')}
{t('Search')}
{isGenerating && onStop ? (
{t('Stop')}
{t('Stop')}
) : (
{t('Send')}
{t('Send')}
)}
{suggestions.map(({ icon: Icon, text, color }) => (
handleSuggestionClick(text)}
suggestion={text}
>
{Icon && }
{text}
))}
)
}