diff --git a/packages/app/src/components/agent-status-bar.tsx b/packages/app/src/components/agent-status-bar.tsx index f6ab12284..bd95960e0 100644 --- a/packages/app/src/components/agent-status-bar.tsx +++ b/packages/app/src/components/agent-status-bar.tsx @@ -456,10 +456,14 @@ function ControlledStatusBar({ [canSelectMode, disabled], ); + const sheetSelectStyle = useMemo( + () => [styles.sheetSelect, modelDisabled && styles.disabledSheetSelect], + [modelDisabled], + ); const renderSheetModelTrigger = useCallback( ({ selectedModelLabel }: { selectedModelLabel: string }) => ( @@ -470,7 +474,7 @@ function ControlledStatusBar({ ), - [ProviderIcon, modelDisabled, theme.colors.foregroundMuted, theme.iconSize.md], + [ProviderIcon, sheetSelectStyle, theme.colors.foregroundMuted, theme.iconSize.md], ); if (!hasAnyControl) { @@ -1353,6 +1357,15 @@ export function DraftAgentStatusBar({ selectedThinkingOptionId || mappedThinkingOptions[0]?.id || undefined; const hasSelectedProvider = selectedProvider !== null; + const modelOptions = useMemo( + () => + models.map((model) => ({ + id: model.id, + label: model.label, + })), + [models], + ); + const handleToggleFavorite = useCallback( (provider: string, modelId: string) => { void updatePreferences((current) => @@ -1400,11 +1413,6 @@ export function DraftAgentStatusBar({ ); } - const modelOptions: StatusOption[] = models.map((model) => ({ - id: model.id, - label: model.label, - })); - return ( + {content} {isEndOfAssistantTurn ? ( ) : null} - + ); }, [getGapBetween, renderStreamItemContent, agent.status, streamRenderStrategy], @@ -916,6 +917,15 @@ function PermissionRequestCard({ const isPlanRequest = request.kind === "plan"; const title = isPlanRequest ? "Plan" : (request.title ?? request.name ?? "Permission Required"); const description = request.description ?? ""; + const resolvedToolCallDetail = useMemo( + () => + request.detail ?? { + type: "unknown" as const, + input: request.input ?? null, + output: null, + }, + [request.detail, request.input], + ); const resolvedActions = useMemo((): AgentPermissionAction[] => { if (request.kind === "question") { return []; @@ -1121,13 +1131,7 @@ function PermissionRequestCard({ {!isPlanRequest ? ( ) : null} @@ -1317,3 +1321,16 @@ const permissionStyles = StyleSheet.create((theme) => ({ fontWeight: theme.fontWeight.normal, }, })); + +interface StreamItemWrapperProps { + gapBelow: number; + children: ReactNode; +} + +function StreamItemWrapper({ gapBelow, children }: StreamItemWrapperProps) { + const wrapperStyle = useMemo( + () => [stylesheet.streamItemWrapper, { marginBottom: gapBelow }], + [gapBelow], + ); + return {children}; +} diff --git a/packages/app/src/components/file-explorer-pane.tsx b/packages/app/src/components/file-explorer-pane.tsx index ab2663cd2..626400225 100644 --- a/packages/app/src/components/file-explorer-pane.tsx +++ b/packages/app/src/components/file-explorer-pane.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useRef } from "react"; +import { useCallback, useEffect, useMemo, useRef, type ReactNode } from "react"; import { useQuery } from "@tanstack/react-query"; import { ActivityIndicator, @@ -146,29 +146,21 @@ function TreeRowItem({ {depth > 0 && Array.from({ length: depth }, (_, i) => ( - + ))} - {isDirectory ? ( - loading ? ( - - ) : ( + {(() => { + if (!isDirectory) { + return ; + } + if (loading) return ; + return ( - ) - ) : ( - - )} + ); + })()} {entry.name} @@ -540,77 +532,84 @@ export function FileExplorerPane({ ); } - return ( - - {error ? ( - - {error} - - {showBackFromError ? ( - - Back - - ) : null} - - Retry + let paneContent: ReactNode; + if (error) { + paneContent = ( + + {error} + + {showBackFromError ? ( + + Back - + ) : null} + + Retry + - ) : showInitialLoading ? ( - - - Loading files… + + ); + } else if (showInitialLoading) { + paneContent = ( + + + Loading files… + + ); + } else if (treeRows.length === 0) { + paneContent = ( + + No files + + ); + } else { + paneContent = ( + + + + {currentSortLabel} + + + + + {isRefreshFetching ? ( + + ) : ( + + )} + + - ) : treeRows.length === 0 ? ( - - No files - - ) : ( - - - - {currentSortLabel} - - - - - {isRefreshFetching ? ( - - ) : ( - - )} - - - - - {scrollbar.overlay} - - )} - - ); + + {scrollbar.overlay} + + ); + } + + return {paneContent}; } function sortEntries(entries: ExplorerEntry[], sortOption: SortOption): ExplorerEntry[] { @@ -959,3 +958,21 @@ const styles = StyleSheet.create((theme) => ({ padding: theme.spacing[4], }, })); + +const TREE_PANE_CONTAINER_STYLE = [styles.treePane, styles.treePaneFill]; + +interface IndentGuideProps { + index: number; +} + +function IndentGuide({ index }: IndentGuideProps) { + const { theme } = useUnistyles(); + const guideStyle = useMemo( + () => [ + styles.indentGuide, + { left: theme.spacing[3] + index * INDENT_PER_LEVEL + 4 }, + ], + [index, theme.spacing], + ); + return ; +} diff --git a/packages/app/src/components/terminal-pane.tsx b/packages/app/src/components/terminal-pane.tsx index 0b7e6d5d3..f8da457a9 100644 --- a/packages/app/src/components/terminal-pane.tsx +++ b/packages/app/src/components/terminal-pane.tsx @@ -106,11 +106,13 @@ function ModifierButton({ modifier, active, onToggle }: ModifierButtonProps) { ], [active], ); + const textStyle = useMemo( + () => [styles.keyButtonText, active && styles.keyButtonTextActive], + [active], + ); return ( - - {MODIFIER_LABELS[modifier]} - + {MODIFIER_LABELS[modifier]} ); } @@ -628,16 +630,7 @@ export function TerminalPane({ ({ textAlign: "center", }, })); + +const TERMINAL_EMULATOR_DOM_PROPS = { + style: { flex: 1 }, + matchContents: false, + scrollEnabled: true, + nestedScrollEnabled: true, + overScrollMode: "never" as const, + bounces: false, + automaticallyAdjustContentInsets: false, + contentInsetAdjustmentBehavior: "never" as const, +}; diff --git a/packages/app/src/components/welcome-screen.tsx b/packages/app/src/components/welcome-screen.tsx index b57e41882..84f34344f 100644 --- a/packages/app/src/components/welcome-screen.tsx +++ b/packages/app/src/components/welcome-screen.tsx @@ -283,27 +283,9 @@ export function WelcomeScreen({ onHostAdded }: WelcomeScreenProps) { - {actions.map((action) => { - const Icon = action.icon; - return ( - - - - {action.label} - - - ); - })} + {actions.map((action) => ( + + ))}