mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-27 06:05:54 +08:00
while dictating, the main send button stops recording, transcribes, and sends in one tap, matching the codex-style flow. the mic button still stops and drops the transcript into the input box to edit before sending. voice recording state is lifted into the composer so both buttons share it, and the send button is enabled (not grayed) while recording. also fix a pre-existing type error: the quick-settings preferences map was missing voiceEnabled.
93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
import { useCallback, useMemo, useState } from 'react';
|
|
import type { MouseEvent as ReactMouseEvent } from 'react';
|
|
import { useDeviceSettings } from '../../../hooks/useDeviceSettings';
|
|
import { useUiPreferences } from '../../../hooks/useUiPreferences';
|
|
import { useTheme } from '../../../contexts/ThemeContext';
|
|
import { useQuickSettingsDrag } from '../hooks/useQuickSettingsDrag';
|
|
import type { PreferenceToggleKey, QuickSettingsPreferences } from '../types';
|
|
import QuickSettingsContent from './QuickSettingsContent';
|
|
import QuickSettingsHandle from './QuickSettingsHandle';
|
|
import QuickSettingsPanelHeader from './QuickSettingsPanelHeader';
|
|
|
|
export default function QuickSettingsPanelView() {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const { isMobile } = useDeviceSettings({ trackPWA: false });
|
|
const { isDarkMode } = useTheme();
|
|
const { preferences, setPreference } = useUiPreferences();
|
|
const {
|
|
isDragging,
|
|
handleStyle,
|
|
startDrag,
|
|
consumeSuppressedClick,
|
|
} = useQuickSettingsDrag({ isMobile });
|
|
|
|
const quickSettingsPreferences = useMemo<QuickSettingsPreferences>(() => ({
|
|
autoExpandTools: preferences.autoExpandTools,
|
|
showRawParameters: preferences.showRawParameters,
|
|
showThinking: preferences.showThinking,
|
|
autoScrollToBottom: preferences.autoScrollToBottom,
|
|
sendByCtrlEnter: preferences.sendByCtrlEnter,
|
|
voiceEnabled: preferences.voiceEnabled,
|
|
}), [
|
|
preferences.autoExpandTools,
|
|
preferences.autoScrollToBottom,
|
|
preferences.sendByCtrlEnter,
|
|
preferences.showRawParameters,
|
|
preferences.showThinking,
|
|
preferences.voiceEnabled,
|
|
]);
|
|
|
|
const handlePreferenceChange = useCallback(
|
|
(key: PreferenceToggleKey, value: boolean) => {
|
|
setPreference(key, value);
|
|
},
|
|
[setPreference],
|
|
);
|
|
|
|
const handleToggleFromHandle = useCallback(
|
|
(event: ReactMouseEvent<HTMLButtonElement>) => {
|
|
// A drag releases a click event as well; this guard prevents accidental toggles.
|
|
if (consumeSuppressedClick()) {
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
|
|
setIsOpen((previous) => !previous);
|
|
},
|
|
[consumeSuppressedClick],
|
|
);
|
|
|
|
return (
|
|
<>
|
|
<QuickSettingsHandle
|
|
isOpen={isOpen}
|
|
isDragging={isDragging}
|
|
style={handleStyle}
|
|
onClick={handleToggleFromHandle}
|
|
onMouseDown={startDrag}
|
|
onTouchStart={startDrag}
|
|
/>
|
|
|
|
<div
|
|
className={`fixed right-0 top-0 z-40 h-full w-64 transform border-l border-border bg-background shadow-xl transition-transform duration-150 ease-out ${isOpen ? 'translate-x-0' : 'translate-x-full'} ${isMobile ? 'h-screen' : ''}`}
|
|
>
|
|
<div className="flex h-full flex-col">
|
|
<QuickSettingsPanelHeader />
|
|
<QuickSettingsContent
|
|
isDarkMode={isDarkMode}
|
|
preferences={quickSettingsPreferences}
|
|
onPreferenceChange={handlePreferenceChange}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{isOpen && (
|
|
<div
|
|
className="fixed inset-0 z-30 bg-background/80 backdrop-blur-sm transition-opacity duration-150 ease-out"
|
|
onClick={() => setIsOpen(false)}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|