mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-07 06:52:28 +00:00
feat(web): 优化聚合 tool use 展示与聊天背景设置 (#619)
This commit is contained in:
@@ -51,6 +51,24 @@ vi.mock('@/hooks/useTerminalToolDisplayMode', () => ({
|
||||
],
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/useChatSurfaceColors', () => ({
|
||||
useChatSurfaceColors: () => ({
|
||||
toolGroupBackground: 'default',
|
||||
userMessageBackground: 'preset:soft-blue',
|
||||
setToolGroupBackground: vi.fn(),
|
||||
setUserMessageBackground: vi.fn(),
|
||||
}),
|
||||
getChatSurfaceColorPresetOptions: () => [
|
||||
{ value: 'default', labelKey: 'settings.chat.surfaceColor.default' },
|
||||
{ value: 'soft-blue', labelKey: 'settings.chat.surfaceColor.softBlue' },
|
||||
{ value: 'soft-green', labelKey: 'settings.chat.surfaceColor.softGreen' },
|
||||
{ value: 'soft-yellow', labelKey: 'settings.chat.surfaceColor.softYellow' },
|
||||
],
|
||||
getChatSurfaceColorPickerValue: () => '#7db7ff',
|
||||
toPresetChatSurfaceColorPreference: (value: string) => value === 'default' ? 'default' : `preset:${value}`,
|
||||
toCustomChatSurfaceColorPreference: (value: string) => `custom:${value}`,
|
||||
}))
|
||||
|
||||
// Mock useTheme hook
|
||||
vi.mock('@/hooks/useTheme', () => ({
|
||||
useAppearance: () => ({ appearance: 'system', setAppearance: vi.fn() }),
|
||||
@@ -172,6 +190,17 @@ describe('SettingsPage', () => {
|
||||
expect(screen.getAllByText('Compact (command only)').length).toBeGreaterThanOrEqual(1)
|
||||
})
|
||||
|
||||
it('renders grouped tool and user message background settings', () => {
|
||||
renderWithProviders(<SettingsPage />)
|
||||
expect(screen.getAllByText('Grouped Tool Use Background').length).toBeGreaterThanOrEqual(1)
|
||||
expect(screen.getAllByText('User Message Background').length).toBeGreaterThanOrEqual(1)
|
||||
expect(screen.getAllByText('Default color').length).toBeGreaterThanOrEqual(1)
|
||||
expect(screen.getAllByText('Soft blue').length).toBeGreaterThanOrEqual(1)
|
||||
expect(screen.getAllByText('Soft green').length).toBeGreaterThanOrEqual(1)
|
||||
expect(screen.getAllByText('Soft yellow').length).toBeGreaterThanOrEqual(1)
|
||||
expect(screen.getAllByLabelText('Custom color').length).toBeGreaterThanOrEqual(2)
|
||||
})
|
||||
|
||||
it('uses correct i18n keys for the Enter Key setting', () => {
|
||||
const spyT = renderWithSpyT(<SettingsPage />)
|
||||
const calledKeys = spyT.mock.calls.map((call) => call[0])
|
||||
@@ -180,5 +209,8 @@ describe('SettingsPage', () => {
|
||||
expect(calledKeys).toContain('settings.chat.enterBehavior.send')
|
||||
expect(calledKeys).toContain('settings.chat.terminalToolDisplay')
|
||||
expect(calledKeys).toContain('settings.chat.terminalToolDisplay.compact')
|
||||
expect(calledKeys).toContain('settings.chat.groupedToolBackground')
|
||||
expect(calledKeys).toContain('settings.chat.userMessageBackground')
|
||||
expect(calledKeys).toContain('settings.chat.surfaceColor.default')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,6 +6,15 @@ import { getFontScaleOptions, useFontScale, type FontScale } from '@/hooks/useFo
|
||||
import { getTerminalFontSizeOptions, useTerminalFontSize, type TerminalFontSize } from '@/hooks/useTerminalFontSize'
|
||||
import { getComposerEnterBehaviorOptions, useComposerEnterBehavior, type ComposerEnterBehavior } from '@/hooks/useComposerEnterBehavior'
|
||||
import { getTerminalToolDisplayModeOptions, useTerminalToolDisplayMode, type TerminalToolDisplayMode } from '@/hooks/useTerminalToolDisplayMode'
|
||||
import {
|
||||
getChatSurfaceColorPickerValue,
|
||||
getChatSurfaceColorPresetOptions,
|
||||
toCustomChatSurfaceColorPreference,
|
||||
toPresetChatSurfaceColorPreference,
|
||||
useChatSurfaceColors,
|
||||
type ChatSurfaceColorPreference,
|
||||
type ChatSurfaceColorPreset,
|
||||
} from '@/hooks/useChatSurfaceColors'
|
||||
import { useAppearance, getAppearanceOptions, type AppearancePreference } from '@/hooks/useTheme'
|
||||
import { PROTOCOL_VERSION } from '@hapi/protocol'
|
||||
|
||||
@@ -73,6 +82,63 @@ function ChevronDownIcon(props: { className?: string }) {
|
||||
)
|
||||
}
|
||||
|
||||
function ChatSurfaceColorControl(props: {
|
||||
label: string
|
||||
preference: ChatSurfaceColorPreference
|
||||
onPresetChange: (preset: ChatSurfaceColorPreset) => void
|
||||
onCustomChange: (value: string) => void
|
||||
t: (key: string) => string
|
||||
}) {
|
||||
const presetOptions = getChatSurfaceColorPresetOptions()
|
||||
const pickerValue = getChatSurfaceColorPickerValue(props.preference)
|
||||
const isCustomSelected = props.preference.startsWith('custom:')
|
||||
|
||||
return (
|
||||
<div className="border-t border-[var(--app-divider)] px-3 py-3">
|
||||
<div className="mb-2 text-[var(--app-fg)]">{props.label}</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{presetOptions.map((option) => {
|
||||
const selected = props.preference === toPresetChatSurfaceColorPreference(option.value)
|
||||
const swatchColor = getChatSurfaceColorPickerValue(toPresetChatSurfaceColorPreference(option.value))
|
||||
return (
|
||||
<button
|
||||
key={option.value}
|
||||
type="button"
|
||||
onClick={() => props.onPresetChange(option.value)}
|
||||
className={`inline-flex items-center gap-2 rounded-full border px-3 py-1.5 text-sm transition-colors ${
|
||||
selected
|
||||
? 'border-[var(--app-link)] bg-[var(--app-subtle-bg)] text-[var(--app-link)]'
|
||||
: 'border-[var(--app-border)] bg-[var(--app-bg)] text-[var(--app-fg)] hover:bg-[var(--app-subtle-bg)]'
|
||||
}`}
|
||||
>
|
||||
<span className="h-2.5 w-2.5 rounded-full opacity-80" style={{ backgroundColor: swatchColor }} />
|
||||
<span>{props.t(option.labelKey)}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-3 flex items-center justify-between gap-3">
|
||||
<span className="text-sm text-[var(--app-hint)]">{props.t('settings.chat.surfaceColor.custom')}</span>
|
||||
<label
|
||||
className={`inline-flex items-center rounded-xl border px-2 py-1 transition-colors ${
|
||||
isCustomSelected
|
||||
? 'border-[var(--app-link)] bg-[var(--app-subtle-bg)]'
|
||||
: 'border-[var(--app-border)] bg-[var(--app-bg)]'
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
aria-label={props.t('settings.chat.surfaceColor.custom')}
|
||||
type="color"
|
||||
value={pickerValue}
|
||||
onChange={(event) => props.onCustomChange(event.target.value)}
|
||||
className="h-8 w-11 cursor-pointer appearance-none border-0 bg-transparent p-0"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { t, locale, setLocale } = useTranslation()
|
||||
const goBack = useAppGoBack()
|
||||
@@ -94,6 +160,12 @@ export default function SettingsPage() {
|
||||
const { terminalFontSize, setTerminalFontSize } = useTerminalFontSize()
|
||||
const { composerEnterBehavior, setComposerEnterBehavior } = useComposerEnterBehavior()
|
||||
const { terminalToolDisplayMode, setTerminalToolDisplayMode } = useTerminalToolDisplayMode()
|
||||
const {
|
||||
toolGroupBackground,
|
||||
userMessageBackground,
|
||||
setToolGroupBackground,
|
||||
setUserMessageBackground,
|
||||
} = useChatSurfaceColors()
|
||||
const { appearance, setAppearance } = useAppearance()
|
||||
|
||||
// Voice language state - read from localStorage
|
||||
@@ -530,6 +602,20 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ChatSurfaceColorControl
|
||||
label={t('settings.chat.groupedToolBackground')}
|
||||
preference={toolGroupBackground}
|
||||
onPresetChange={(preset) => setToolGroupBackground(toPresetChatSurfaceColorPreference(preset))}
|
||||
onCustomChange={(value) => setToolGroupBackground(toCustomChatSurfaceColorPreference(value))}
|
||||
t={t}
|
||||
/>
|
||||
<ChatSurfaceColorControl
|
||||
label={t('settings.chat.userMessageBackground')}
|
||||
preference={userMessageBackground}
|
||||
onPresetChange={(preset) => setUserMessageBackground(toPresetChatSurfaceColorPreference(preset))}
|
||||
onCustomChange={(value) => setUserMessageBackground(toCustomChatSurfaceColorPreference(value))}
|
||||
t={t}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Voice Assistant section */}
|
||||
|
||||
Reference in New Issue
Block a user