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:
@@ -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();
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
Reference in New Issue
Block a user