diff --git a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx index 16a5b850..53a2b81a 100644 --- a/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx +++ b/packages/ui/src/components/sections/openchamber/OpenChamberPage.tsx @@ -83,7 +83,7 @@ export const OpenChamberPage: React.FC = ({ section }) => // Visual section: Theme Mode, Font Size, Spacing, Corner Radius, Input Bar Offset (mobile) const VisualSectionContent: React.FC = () => { - return ; + return ; }; // Chat section: Default Tool Output, Diff layout, Show reasoning traces, Queue mode diff --git a/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx b/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx index 9849c3e1..c07a9275 100644 --- a/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx +++ b/packages/ui/src/components/sections/openchamber/OpenChamberVisualSettings.tsx @@ -8,6 +8,7 @@ import { useMessageQueueStore } from '@/stores/messageQueueStore'; import { cn, getModifierLabel } from '@/lib/utils'; import { ButtonSmall } from '@/components/ui/button-small'; import { NumberInput } from '@/components/ui/number-input'; +import { Switch } from '@/components/ui/switch'; import { isVSCodeRuntime } from '@/lib/desktop'; import { useDeviceInfo } from '@/lib/device'; import { @@ -73,7 +74,7 @@ const DIFF_VIEW_MODE_OPTIONS: Option<'single' | 'stacked'>[] = [ }, ]; -export type VisibleSetting = 'theme' | 'fontSize' | 'spacing' | 'cornerRadius' | 'inputBarOffset' | 'toolOutput' | 'diffLayout' | 'dotfiles' | 'reasoning' | 'queueMode' | 'textJustificationActivity'; +export type VisibleSetting = 'theme' | 'fontSize' | 'spacing' | 'cornerRadius' | 'inputBarOffset' | 'toolOutput' | 'diffLayout' | 'dotfiles' | 'reasoning' | 'queueMode' | 'textJustificationActivity' | 'terminalQuickKeys'; interface OpenChamberVisualSettingsProps { /** Which settings to show. If undefined, shows all. */ @@ -101,6 +102,8 @@ export const OpenChamberVisualSettings: React.FC const setDiffLayoutPreference = useUIStore(state => state.setDiffLayoutPreference); const diffViewMode = useUIStore(state => state.diffViewMode); const setDiffViewMode = useUIStore(state => state.setDiffViewMode); + const showTerminalQuickKeysOnDesktop = useUIStore(state => state.showTerminalQuickKeysOnDesktop); + const setShowTerminalQuickKeysOnDesktop = useUIStore(state => state.setShowTerminalQuickKeysOnDesktop); const queueModeEnabled = useMessageQueueStore(state => state.queueModeEnabled); const setQueueMode = useMessageQueueStore(state => state.setQueueMode); const { @@ -252,6 +255,25 @@ export const OpenChamberVisualSettings: React.FC )} + {shouldShow('terminalQuickKeys') && !isMobile && ( +
+
+

+ Show terminal optional key bar +

+

+ Esc, Ctrl, arrows, Enter. +

+
+ + +
+ )} + {shouldShow('cornerRadius') && (
diff --git a/packages/ui/src/components/terminal/TerminalViewport.tsx b/packages/ui/src/components/terminal/TerminalViewport.tsx index 0a949afe..4f83ace4 100644 --- a/packages/ui/src/components/terminal/TerminalViewport.tsx +++ b/packages/ui/src/components/terminal/TerminalViewport.tsx @@ -824,6 +824,12 @@ const TerminalViewport = React.forwardRef { + if (enableTouchScroll) { + const touch = event.touches?.[0]; + focusHiddenInput(touch?.clientX, touch?.clientY); + } + }} onClick={(event) => { if (enableTouchScroll) { focusHiddenInput(event.clientX, event.clientY); @@ -867,13 +873,37 @@ const TerminalViewport = React.forwardRef { + const nativeEvent = event.nativeEvent as unknown as InputEvent | undefined; + const inputType = nativeEvent?.inputType ?? ''; + const data = typeof nativeEvent?.data === 'string' ? nativeEvent.data : ''; + + // Android Chrome sometimes never commits text into the DOM value + // for invisible inputs. Use beforeinput as the primary path. + if (inputType === 'insertText' && data) { + event.preventDefault(); + inputHandlerRef.current(data); + return; + } + + if (inputType === 'insertLineBreak') { + event.preventDefault(); + inputHandlerRef.current('\r'); + return; + } + + if (inputType === 'deleteContentBackward') { + event.preventDefault(); + inputHandlerRef.current('\x7f'); + } + }} onInput={(event) => { const raw = String(event.currentTarget.value || ''); if (!raw) { return; } - // iOS often inserts `\n` for Enter; the PTY expects CR. + // Some mobile keyboards (iOS esp.) insert `\n` for Enter; PTY expects CR. const value = raw.replace(/\r\n|\r|\n/g, '\r'); inputHandlerRef.current(value); event.currentTarget.value = ''; diff --git a/packages/ui/src/components/views/TerminalView.tsx b/packages/ui/src/components/views/TerminalView.tsx index afb6174d..03d2556e 100644 --- a/packages/ui/src/components/views/TerminalView.tsx +++ b/packages/ui/src/components/views/TerminalView.tsx @@ -79,6 +79,8 @@ export const TerminalView: React.FC = () => { const { currentTheme } = useThemeSystem(); const { monoFont } = useFontPreferences(); const { isMobile, hasTouchInput } = useDeviceInfo(); + const showTerminalQuickKeysOnDesktop = useUIStore((state) => state.showTerminalQuickKeysOnDesktop); + const showQuickKeys = isMobile || showTerminalQuickKeysOnDesktop; const { currentSessionId, newSessionDraft } = useSessionStore(); const hasActiveContext = currentSessionId !== null || newSessionDraft?.open === true; @@ -138,10 +140,10 @@ export const TerminalView: React.FC = () => { }, [effectiveDirectory]); React.useEffect(() => { - if (!isMobile && activeModifier !== null) { + if (!showQuickKeys && activeModifier !== null) { setActiveModifier(null); } - }, [isMobile, activeModifier, setActiveModifier]); + }, [showQuickKeys, activeModifier, setActiveModifier]); React.useEffect(() => { if (!terminalSessionId && activeModifier !== null) { @@ -492,7 +494,7 @@ export const TerminalView: React.FC = () => { ); React.useEffect(() => { - if (!isMobile || !activeModifier || !terminalSessionId) { + if (!showQuickKeys || !activeModifier || !terminalSessionId) { return; } @@ -565,7 +567,7 @@ export const TerminalView: React.FC = () => { activeModifier, handleMobileKeyPress, handleViewportInput, - isMobile, + showQuickKeys, setActiveModifier, terminalSessionId, ]); @@ -693,7 +695,7 @@ export const TerminalView: React.FC = () => {
- {isMobile ? ( + {showQuickKeys ? (