refactor: unify layout widths and font sizes

- Add MAX_CONTENT_WIDTH (820px) constant for consistent chat/form widths
- Bump theme font sizes (base now 16px) for better readability
- Apply max width to stream view, input area, and new agent form
- Position new agent form at bottom of scroll area
- Remove working directory helper text
- Add cmd+b sidebar toggle when textarea is focused
This commit is contained in:
Mohamed Boudra
2026-01-09 19:11:35 +07:00
parent 2d03e86bcb
commit 584b0413a5
8 changed files with 33 additions and 23 deletions
+5
View File
@@ -26,6 +26,7 @@ import { useDaemonConnections } from "@/contexts/daemon-connections-context";
import { formatConnectionStatus } from "@/utils/daemons"; import { formatConnectionStatus } from "@/utils/daemons";
import { useSessionStore } from "@/stores/session-store"; import { useSessionStore } from "@/stores/session-store";
import { generateMessageId } from "@/types/stream"; import { generateMessageId } from "@/types/stream";
import { MAX_CONTENT_WIDTH } from "@/constants/layout";
import type { import type {
AgentProvider, AgentProvider,
AgentSessionConfig, AgentSessionConfig,
@@ -843,12 +844,16 @@ const styles = StyleSheet.create((theme) => ({
}, },
configScrollContent: { configScrollContent: {
flexGrow: 1, flexGrow: 1,
justifyContent: "flex-end",
}, },
configSection: { configSection: {
paddingHorizontal: theme.spacing[4], paddingHorizontal: theme.spacing[4],
paddingTop: theme.spacing[3], paddingTop: theme.spacing[3],
paddingBottom: theme.spacing[4], paddingBottom: theme.spacing[4],
gap: theme.spacing[2], gap: theme.spacing[2],
maxWidth: MAX_CONTENT_WIDTH,
alignSelf: "center",
width: "100%",
}, },
configRow: { configRow: {
flexDirection: "row", flexDirection: "row",
@@ -619,11 +619,6 @@ export function WorkingDirectoryDropdown({
onPress={handleOpen} onPress={handleOpen}
disabled={disabled} disabled={disabled}
errorMessage={errorMessage || undefined} errorMessage={errorMessage || undefined}
helperText={
hasSuggestedPaths
? "Search directories from existing agents or paste a new path."
: "No agent directories yet - search to add one."
}
controlRef={anchorRef} controlRef={anchorRef}
/> />
<AdaptiveSelect <AdaptiveSelect
@@ -16,7 +16,7 @@ import Animated, {
import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller"; import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useRealtime } from "@/contexts/realtime-context"; import { useRealtime } from "@/contexts/realtime-context";
import { FOOTER_HEIGHT } from "@/constants/layout"; import { FOOTER_HEIGHT, MAX_CONTENT_WIDTH } from "@/constants/layout";
import { generateMessageId } from "@/types/stream"; import { generateMessageId } from "@/types/stream";
import { AgentStatusBar } from "./agent-status-bar"; import { AgentStatusBar } from "./agent-status-bar";
import { RealtimeControls } from "./realtime-controls"; import { RealtimeControls } from "./realtime-controls";
@@ -52,7 +52,6 @@ interface AgentInputAreaProps {
} }
const EMPTY_ARRAY: readonly QueuedMessage[] = []; const EMPTY_ARRAY: readonly QueuedMessage[] = [];
const MAX_INPUT_WIDTH = 960;
// Android currently crashes inside ViewGroup.dispatchDraw when running Reanimated // Android currently crashes inside ViewGroup.dispatchDraw when running Reanimated
// entering/exiting animations (see react-native-reanimated#8422), so guard them. // entering/exiting animations (see react-native-reanimated#8422), so guard them.
const SHOULD_DISABLE_ENTRY_EXIT_ANIMATIONS = Platform.OS === "android"; const SHOULD_DISABLE_ENTRY_EXIT_ANIMATIONS = Platform.OS === "android";
@@ -525,7 +524,7 @@ const styles = StyleSheet.create(((theme: Theme) => ({
}, },
inputAreaContent: { inputAreaContent: {
width: "100%", width: "100%",
maxWidth: MAX_INPUT_WIDTH, maxWidth: MAX_CONTENT_WIDTH,
gap: theme.spacing[3], gap: theme.spacing[3],
}, },
realtimeButton: { realtimeButton: {
@@ -43,8 +43,7 @@ import {
} from "@/utils/tool-call-parsers"; } from "@/utils/tool-call-parsers";
import { ToolCallSheetProvider } from "./tool-call-sheet"; import { ToolCallSheetProvider } from "./tool-call-sheet";
import { createMarkdownStyles } from "@/styles/markdown-styles"; import { createMarkdownStyles } from "@/styles/markdown-styles";
import { MAX_CONTENT_WIDTH } from "@/constants/layout";
const MAX_CHAT_WIDTH = 960;
type PermissionResolvedMessage = Extract< type PermissionResolvedMessage = Extract<
SessionOutboundMessage, SessionOutboundMessage,
{ type: "agent_permission_resolved" } { type: "agent_permission_resolved" }
@@ -1024,7 +1023,7 @@ const stylesheet = StyleSheet.create((theme) => ({
}, },
contentWrapper: { contentWrapper: {
width: "100%", width: "100%",
maxWidth: MAX_CHAT_WIDTH, maxWidth: MAX_CONTENT_WIDTH,
alignSelf: "center", alignSelf: "center",
}, },
list: { list: {
@@ -1033,7 +1032,7 @@ const stylesheet = StyleSheet.create((theme) => ({
}, },
streamItemWrapper: { streamItemWrapper: {
width: "100%", width: "100%",
maxWidth: MAX_CHAT_WIDTH, maxWidth: MAX_CONTENT_WIDTH,
alignSelf: "center", alignSelf: "center",
}, },
emptyState: { emptyState: {
@@ -1096,7 +1095,7 @@ const stylesheet = StyleSheet.create((theme) => ({
}, },
scrollToBottomInner: { scrollToBottomInner: {
width: "100%", width: "100%",
maxWidth: MAX_CHAT_WIDTH, maxWidth: MAX_CONTENT_WIDTH,
alignSelf: "center", alignSelf: "center",
alignItems: "center", alignItems: "center",
}, },
@@ -28,6 +28,7 @@ import { useDictation } from "@/hooks/use-dictation";
import { DictationOverlay } from "./dictation-controls"; import { DictationOverlay } from "./dictation-controls";
import type { UseWebSocketReturn } from "@/hooks/use-websocket"; import type { UseWebSocketReturn } from "@/hooks/use-websocket";
import type { SessionContextValue } from "@/contexts/session-context"; import type { SessionContextValue } from "@/contexts/session-context";
import { useSidebarStore } from "@/stores/sidebar-store";
export interface ImageAttachment { export interface ImageAttachment {
uri: string; uri: string;
@@ -113,6 +114,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
ref ref
) { ) {
const { theme } = useUnistyles(); const { theme } = useUnistyles();
const toggleSidebar = useSidebarStore((state) => state.toggle);
const [inputHeight, setInputHeight] = useState(MIN_INPUT_HEIGHT); const [inputHeight, setInputHeight] = useState(MIN_INPUT_HEIGHT);
const textInputRef = useRef< const textInputRef = useRef<
TextInput | (TextInput & { getNativeRef?: () => unknown }) | null TextInput | (TextInput & { getNativeRef?: () => unknown }) | null
@@ -383,6 +385,13 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
if (!shouldHandleDesktopSubmit) return; if (!shouldHandleDesktopSubmit) return;
const { shiftKey, metaKey, ctrlKey } = event.nativeEvent; const { shiftKey, metaKey, ctrlKey } = event.nativeEvent;
// Cmd+B or Ctrl+B: toggle sidebar
if ((metaKey || ctrlKey) && event.nativeEvent.key === "b") {
event.preventDefault();
toggleSidebar();
return;
}
// Cmd+D or Ctrl+D: start dictation or submit if already dictating // Cmd+D or Ctrl+D: start dictation or submit if already dictating
if ((metaKey || ctrlKey) && event.nativeEvent.key === "d") { if ((metaKey || ctrlKey) && event.nativeEvent.key === "d") {
event.preventDefault(); event.preventDefault();
+2 -2
View File
@@ -56,8 +56,8 @@ const userMessageStylesheet = StyleSheet.create((theme) => ({
}, },
text: { text: {
color: theme.colors.foreground, color: theme.colors.foreground,
fontSize: theme.fontSize.lg, fontSize: theme.fontSize.base,
lineHeight: 24, lineHeight: 22,
}, },
bubblePressed: { bubblePressed: {
opacity: 0.85, opacity: 0.85,
+3
View File
@@ -4,3 +4,6 @@ export const FOOTER_HEIGHT = 75;
// Used by both agent header (ScreenHeader) and explorer sidebar header // Used by both agent header (ScreenHeader) and explorer sidebar header
// This ensures both headers have the same visual height // This ensures both headers have the same visual height
export const HEADER_INNER_HEIGHT = 48; export const HEADER_INNER_HEIGHT = 48;
// Max width for chat content (stream view, input area, new agent form)
export const MAX_CONTENT_WIDTH = 820;
+8 -8
View File
@@ -167,14 +167,14 @@ const commonTheme = {
}, },
fontSize: { fontSize: {
xs: 10, xs: 12,
sm: 12, sm: 14,
base: 14, base: 16,
lg: 16, lg: 18,
xl: 18, xl: 20,
"2xl": 20, "2xl": 22,
"3xl": 24, "3xl": 26,
"4xl": 32, "4xl": 34,
}, },
fontWeight: { fontWeight: {