Files
new-api/web/default/src/features/playground/components/input/playground-parameter-panel.tsx
T

266 lines
8.0 KiB
TypeScript
Vendored

/*
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 <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { SlidersHorizontalIcon } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { PromptInputButton } from '@/components/ai-elements/prompt-input'
import { Input } from '@/components/design-system/input'
import { StatusBadge } from '@/components/status-badge'
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover'
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet'
import { Slider } from '@/components/ui/slider'
import { Switch } from '@/components/ui/switch'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip'
import { useIsMobile } from '@/hooks/use-mobile'
import { cn } from '@/lib/utils'
import {
getParameterControlValueText,
normalizeParameterNumberValue,
PLAYGROUND_PARAMETER_CONTROLS,
PLAYGROUND_PARAMETER_PANEL_SCROLL_CLASS,
type PlaygroundParameterKey,
} from '../../lib/parameters/playground-parameters'
import type { ParameterEnabled, PlaygroundConfig } from '../../types'
type PlaygroundParameterPanelProps = {
config: PlaygroundConfig
disabled?: boolean
onConfigChange: <K extends keyof PlaygroundConfig>(
key: K,
value: PlaygroundConfig[K]
) => void
onParameterEnabledChange: (
key: PlaygroundParameterKey,
value: boolean
) => void
parameterEnabled: ParameterEnabled
}
type PlaygroundParameterContentProps = PlaygroundParameterPanelProps & {
compact?: boolean
}
function PlaygroundParameterContent({
compact = false,
config,
disabled,
onConfigChange,
onParameterEnabledChange,
parameterEnabled,
}: PlaygroundParameterContentProps) {
const { t } = useTranslation()
const updateParameterConfig = (
key: PlaygroundParameterKey,
value: number | null
) => {
if (key === 'seed') {
onConfigChange('seed', value)
return
}
onConfigChange(key, value ?? 0)
}
return (
<div
className={cn(
'grid gap-3',
PLAYGROUND_PARAMETER_PANEL_SCROLL_CLASS,
compact ? 'px-4 pb-4' : 'p-1'
)}
>
{PLAYGROUND_PARAMETER_CONTROLS.map((control) => {
const enabled = parameterEnabled[control.key]
const value = config[control.key]
const controlId = `playground-${control.key}`
return (
<div
className={cn(
'border-border/70 bg-background/60 grid gap-2 rounded-lg border p-3 transition-opacity',
(!enabled || disabled) && 'opacity-55'
)}
key={control.key}
>
<div className='flex items-start justify-between gap-3'>
<div className='min-w-0 space-y-1'>
<div className='flex min-w-0 items-center gap-2'>
<label
className='truncate text-sm leading-5 font-medium'
htmlFor={controlId}
>
{t(control.labelKey)}
</label>
<StatusBadge
appearance='outline'
className='max-w-24 shrink-0'
>
{t(getParameterControlValueText(control.key, value))}
</StatusBadge>
</div>
<p className='text-muted-foreground text-xs leading-4'>
{t(control.descriptionKey)}
</p>
</div>
<Switch
aria-label={t('Enable {{parameter}}', {
parameter: t(control.labelKey),
})}
checked={enabled}
disabled={disabled}
onCheckedChange={(checked) =>
onParameterEnabledChange(control.key, checked)
}
size='sm'
/>
</div>
{control.valueType === 'slider' ? (
<Slider
className='py-1.5'
disabled={disabled || !enabled}
id={controlId}
max={control.max}
min={control.min}
onValueChange={(nextValue) => {
const firstValue = Array.isArray(nextValue)
? nextValue[0]
: nextValue
updateParameterConfig(
control.key,
normalizeParameterNumberValue(control.key, firstValue)
)
}}
step={control.step}
value={[Number(value)]}
/>
) : (
<Input
disabled={disabled || !enabled}
id={controlId}
inputMode='numeric'
max={control.max}
min={control.min}
onChange={(event) => {
updateParameterConfig(
control.key,
normalizeParameterNumberValue(
control.key,
event.target.value
)
)
}}
step={control.step}
type='number'
value={value ?? ''}
/>
)}
</div>
)
})}
</div>
)
}
export function PlaygroundParameterPanel(props: PlaygroundParameterPanelProps) {
const { t } = useTranslation()
const isMobile = useIsMobile()
const activeCount = PLAYGROUND_PARAMETER_CONTROLS.filter(
(control) => props.parameterEnabled[control.key]
).length
const trigger = (
<PromptInputButton
aria-label={t('Parameters')}
className='text-muted-foreground hover:text-foreground hover:bg-muted/70 relative font-medium'
disabled={props.disabled}
variant='ghost'
>
<SlidersHorizontalIcon size={16} />
<span className='bg-primary text-primary-foreground absolute -top-1 -right-1 flex h-3.5 min-w-3.5 items-center justify-center rounded-full px-1 text-[9px] leading-none font-semibold'>
{activeCount}
</span>
</PromptInputButton>
)
if (isMobile) {
return (
<Sheet>
<Tooltip>
<TooltipTrigger render={<SheetTrigger render={trigger} />} />
<TooltipContent>
<p>{t('Parameters')}</p>
</TooltipContent>
</Tooltip>
<SheetContent
className='max-h-[85vh] overflow-hidden rounded-t-xl'
side='bottom'
>
<SheetHeader>
<SheetTitle>{t('Parameter settings')}</SheetTitle>
</SheetHeader>
<PlaygroundParameterContent {...props} compact />
</SheetContent>
</Sheet>
)
}
return (
<Popover>
<Tooltip>
<TooltipTrigger render={<PopoverTrigger render={trigger} />} />
<TooltipContent>
<p>{t('Parameters')}</p>
</TooltipContent>
</Tooltip>
<PopoverContent
align='start'
className='w-[22rem] max-w-[calc(100vw-2rem)] gap-3 p-3'
side='top'
sideOffset={8}
>
<div className='space-y-1 px-1'>
<div className='text-sm font-semibold'>{t('Parameter settings')}</div>
<div className='text-muted-foreground text-xs leading-4'>
{t('Only enabled parameters are sent with the request.')}
</div>
</div>
<PlaygroundParameterContent {...props} />
</PopoverContent>
</Popover>
)
}