Files
orca/mobile/src/session/SessionDockColumn.tsx
T
Kelvin Amoaba f87a746200 feat(mobile): light and dark themes with desktop's palettes (#34217)
* feat(mobile): light and dark themes with desktop's palettes

Settings → Appearance offers System, Light and Dark. The shell passes the
resolved scheme to over-the-air pages in its init frame.

Fixes #22184

* fix(mobile): draw your chat messages in desktop's user bubble

Desktop uses a faint surface with a hairline border in both themes; the inverted fill went black in light.

* test(mobile): re-measure the page script sweep after the shared theme hooks

Every route now imports the theme hooks while the entry does not, so they emit one shared script (96 for the tree). Re-swept every route prefix; the worst route step is now 15.

* test(mobile): read themed stylesheets in the text-input font-size census

The census only unwrapped StyleSheet.create({...}); inputs styled through themedStyleSheet(() => ({...})) came back unread.
2026-10-27 17:35:31 +00:00

105 lines
3.0 KiB
TypeScript

import { memo } from 'react'
import { View, StyleSheet } from 'react-native'
import { MobileSourceControlPanel } from '../source-control/MobileSourceControlPanel'
import { MobileFileExplorerPanel } from '../files/MobileFileExplorerPanel'
import { mobilePrSidebarStyles } from '../components/pr-sidebar/mobile-pr-sidebar-styles'
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
import { useMobileDockResize } from './use-mobile-dock-resize'
import type { ActivePanel } from './session-panel-host'
type Props = {
activePanel: Exclude<ActivePanel, null>
hostId: string
worktreeId: string
name: string
availableWidth: number
onRequestClose: () => void
onFileOpenStart?: () => void
onOpenedFileDiff?: (relativePath: string) => void
}
type DockPanelContentProps = Omit<Props, 'availableWidth'>
// Keep drag width state in this subtree; terminal refits follow frame onLayout and need no width prop.
export function SessionDockColumn({
activePanel,
hostId,
worktreeId,
name,
availableWidth,
onRequestClose,
onFileOpenStart,
onOpenedFileDiff
}: Props) {
useMobileColorScheme()
const { dockWidth, panHandlers } = useMobileDockResize(availableWidth)
return (
<View style={[mobilePrSidebarStyles.dockColumn, { width: dockWidth }]}>
{/* Dedicated drag handle over the dock's left border — a leaf overlay so the
inner ScrollView can't intercept the gesture on Android. */}
<View style={styles.resizeHandle} {...panHandlers} />
<DockPanelContent
activePanel={activePanel}
hostId={hostId}
worktreeId={worktreeId}
name={name}
onRequestClose={onRequestClose}
onFileOpenStart={onFileOpenStart}
onOpenedFileDiff={onOpenedFileDiff}
/>
</View>
)
}
// Width-independent panel props let memo skip content rerenders during resize.
const DockPanelContent = memo(function DockPanelContent({
activePanel,
hostId,
worktreeId,
name,
onRequestClose,
onFileOpenStart,
onOpenedFileDiff
}: DockPanelContentProps) {
// Reuse the source-control hub so dock icon switches retain commit draft, visited tabs and scroll.
if (activePanel === 'sourceControl' || activePanel === 'pr') {
return (
<MobileSourceControlPanel
hostId={hostId}
worktreeId={worktreeId}
name={name}
origin="session"
embedded
initialTab={activePanel === 'pr' ? 'pr' : 'changes'}
onRequestClose={onRequestClose}
onFileOpenStart={onFileOpenStart}
onOpenedFileDiff={onOpenedFileDiff}
/>
)
}
return (
<MobileFileExplorerPanel
hostId={hostId}
worktreeId={worktreeId}
name={name}
embedded
onRequestClose={onRequestClose}
/>
)
})
const RESIZE_EDGE_WIDTH = 24
const styles = StyleSheet.create({
// Elevate the absolute grab strip so Android scroll content cannot steal its drag.
resizeHandle: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
width: RESIZE_EDGE_WIDTH,
zIndex: 20,
elevation: 20
}
})