Files
claudecodeui/src/components/quick-settings-panel/view/QuickSettingsPanelView.tsx
Haileyesus 2492eb1052 refactor(quick-settings): migrate panel to typed feature-based modules
Refactor QuickSettingsPanel from a single JSX component into a modular TypeScript feature structure while preserving behavior and translations.

Highlights:
- Replace legacy src/components/QuickSettingsPanel.jsx with a typed entrypoint (src/components/QuickSettingsPanel.tsx).
- Introduce src/components/quick-settings-panel/ with clear separation of concerns:
  - view/: panel shell, header, handle, section wrappers, toggle rows, and content sections.
  - hooks/: drag interactions and whisper mode persistence.
  - constants.ts and types.ts for shared config and strict local typing.
- Move drag logic into useQuickSettingsDrag with explicit touch/mouse handling, drag threshold detection, click suppression after drag, position clamping, and localStorage persistence.
- Keep user-visible behavior intact:
  - same open/close panel interactions.
  - same mobile/desktop drag behavior and persisted handle position.
  - same quick preference toggles and wiring to useUiPreferences.
  - same hidden whisper section behavior and localStorage/event updates.
- Improve readability and maintainability by extracting repetitive setting rows and section scaffolding into reusable components.
- Add focused comments around non-obvious behavior (drag click suppression, touch scroll lock, hidden whisper section intent).
- Keep files small and reviewable (all new/changed files are under 300 lines).

Validation:
- npm run typecheck
- npm run build
2026-03-05 13:11:38 +03:00

92 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,
}), [
preferences.autoExpandTools,
preferences.autoScrollToBottom,
preferences.sendByCtrlEnter,
preferences.showRawParameters,
preferences.showThinking,
]);
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 top-0 right-0 h-full w-64 bg-background border-l border-border shadow-xl transform transition-transform duration-150 ease-out z-40 ${isOpen ? 'translate-x-0' : 'translate-x-full'} ${isMobile ? 'h-screen' : ''}`}
>
<div className="h-full flex flex-col">
<QuickSettingsPanelHeader />
<QuickSettingsContent
isDarkMode={isDarkMode}
isMobile={isMobile}
preferences={quickSettingsPreferences}
onPreferenceChange={handlePreferenceChange}
/>
</div>
</div>
{isOpen && (
<div
className="fixed inset-0 bg-background/80 backdrop-blur-sm z-30 transition-opacity duration-150 ease-out"
onClick={() => setIsOpen(false)}
/>
)}
</>
);
}