refactor(playground): extract input control state

- move submit, stop, and selector state derivation into a pure helper.

- keep input controls focused on rendering model selectors and action buttons.
This commit is contained in:
QuentinHsu
2026-05-30 10:00:54 +08:00
parent f87af88ca5
commit 80ee5244d9
3 changed files with 67 additions and 6 deletions
@@ -20,6 +20,7 @@ import { SendIcon, SquareIcon } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { PromptInputButton } from '@/components/ai-elements/prompt-input'
import { ModelGroupSelector } from '@/components/model-group-selector'
import { getInputControlState } from '../lib'
import type { GroupOption, ModelOption } from '../types'
type PlaygroundInputControlsProps = {
@@ -50,9 +51,16 @@ export function PlaygroundInputControls({
text,
}: PlaygroundInputControlsProps) {
const { t } = useTranslation()
const isModelSelectDisabled =
disabled || isModelLoading || models.length === 0
const isGroupSelectDisabled = disabled || groups.length === 0
const { canSubmit, isSelectorDisabled, shouldShowStop } =
getInputControlState({
disabled,
groups,
hasStopHandler: Boolean(onStop),
isGenerating,
isModelLoading,
models,
text,
})
return (
<div className='flex items-center gap-1.5 md:gap-2'>
@@ -63,10 +71,10 @@ export function PlaygroundInputControls({
selectedGroup={groupValue}
groups={groups}
onGroupChange={onGroupChange}
disabled={isModelSelectDisabled || isGroupSelectDisabled}
disabled={isSelectorDisabled}
/>
{isGenerating && onStop ? (
{shouldShowStop ? (
<PromptInputButton
className='text-foreground font-medium'
onClick={onStop}
@@ -79,7 +87,7 @@ export function PlaygroundInputControls({
) : (
<PromptInputButton
className='text-foreground font-medium'
disabled={disabled || !text.trim()}
disabled={!canSubmit}
type='submit'
variant='secondary'
>
+1
View File
@@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
export * from './message-utils'
export * from './input-control-utils'
export * from './message-action-utils'
export * from './message-reasoning-utils'
export * from './message-streaming-utils'
@@ -0,0 +1,52 @@
/*
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 type { GroupOption, ModelOption } from '../types'
type InputControlStateOptions = {
disabled?: boolean
groups: GroupOption[]
hasStopHandler: boolean
isGenerating?: boolean
isModelLoading?: boolean
models: ModelOption[]
text: string
}
type InputControlState = {
canSubmit: boolean
isSelectorDisabled: boolean
shouldShowStop: boolean
}
export function getInputControlState({
disabled,
groups,
hasStopHandler,
isGenerating,
isModelLoading,
models,
text,
}: InputControlStateOptions): InputControlState {
return {
canSubmit: !disabled && text.trim().length > 0,
isSelectorDisabled:
disabled || isModelLoading || models.length === 0 || groups.length === 0,
shouldShowStop: Boolean(isGenerating && hasStopHandler),
}
}