import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { ReactElement, ReactNode } from "react";
import {
View,
Text,
Pressable,
Modal,
TextInput,
ActivityIndicator,
useWindowDimensions,
ScrollView,
Platform,
} from "react-native";
import { StyleSheet, UnistylesRuntime } from "react-native-unistyles";
import {
BottomSheetModal,
BottomSheetScrollView,
BottomSheetBackdrop,
BottomSheetTextInput,
BottomSheetBackgroundProps,
} from "@gorhom/bottom-sheet";
import Animated from "react-native-reanimated";
import { ChevronDown, ChevronRight, Pencil, Check, X } from "lucide-react-native";
import { theme as defaultTheme } from "@/styles/theme";
import type {
AgentMode,
AgentModelDefinition,
AgentProvider,
} from "@server/server/agent/agent-sdk-types";
import type { AgentProviderDefinition } from "@server/server/agent/provider-manifest";
type DropdownTriggerRenderProps = {
label: string;
value: string;
placeholder: string;
onPress: () => void;
disabled?: boolean;
errorMessage?: string | null;
warningMessage?: string | null;
helperText?: string | null;
};
type DropdownTriggerRenderer = (props: DropdownTriggerRenderProps) => ReactNode;
interface DropdownFieldProps {
label: string;
value: string;
placeholder: string;
onPress: () => void;
disabled?: boolean;
errorMessage?: string | null;
warningMessage?: string | null;
helperText?: string | null;
renderTrigger?: DropdownTriggerRenderer;
}
export function DropdownField({
label,
value,
placeholder,
onPress,
disabled,
errorMessage,
warningMessage,
helperText,
renderTrigger,
}: DropdownFieldProps): ReactElement {
if (renderTrigger) {
return (
<>
{renderTrigger({
label,
value,
placeholder,
onPress,
disabled,
errorMessage,
warningMessage,
helperText,
})}
>
);
}
return (
{label}
{value || placeholder}
{errorMessage ? {errorMessage} : null}
{warningMessage ? {warningMessage} : null}
{!errorMessage && helperText ? (
{helperText}
) : null}
);
}
interface SelectFieldProps {
label: string;
value: string;
placeholder?: string;
onPress: () => void;
disabled?: boolean;
errorMessage?: string | null;
warningMessage?: string | null;
helperText?: string | null;
controlRef?: React.RefObject;
}
export function SelectField({
label,
value,
placeholder,
onPress,
disabled,
errorMessage,
warningMessage,
helperText,
controlRef,
}: SelectFieldProps): ReactElement {
return (
{label}
{value || placeholder || "Select..."}
{errorMessage ? {errorMessage} : null}
{warningMessage ? {warningMessage} : null}
{!errorMessage && !warningMessage && helperText ? (
{helperText}
) : null}
);
}
interface DropdownSheetProps {
title: string;
visible: boolean;
onClose: () => void;
children: ReactNode;
}
function DropdownSheetBackground({ style }: BottomSheetBackgroundProps) {
return (
);
}
export function DropdownSheet({
title,
visible,
onClose,
children,
}: DropdownSheetProps): ReactElement {
const bottomSheetRef = useRef(null);
const snapPoints = useMemo(() => ["60%", "90%"], []);
useEffect(() => {
if (visible) {
bottomSheetRef.current?.present();
} else {
bottomSheetRef.current?.dismiss();
}
}, [visible]);
const handleSheetChange = useCallback(
(index: number) => {
if (index === -1) {
onClose();
}
},
[onClose]
);
const renderBackdrop = useCallback(
(props: React.ComponentProps) => (
),
[]
);
return (
{title}
{children}
);
}
interface AdaptiveSelectProps {
title: string;
visible: boolean;
onClose: () => void;
children: ReactNode;
anchorRef: React.RefObject;
}
export function AdaptiveSelect({
title,
visible,
onClose,
children,
anchorRef,
}: AdaptiveSelectProps): ReactElement {
const isMobile =
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
const bottomSheetRef = useRef(null);
const snapPoints = useMemo(() => ["60%", "90%"], []);
const [dropdownPosition, setDropdownPosition] = useState({
top: 0,
left: 0,
width: 0,
});
useEffect(() => {
if (!visible || isMobile) {
return;
}
const anchor = anchorRef.current;
if (!anchor) {
return;
}
anchor.measureInWindow((x, y, width, height) => {
const verticalOffset = 4;
const horizontalMargin = 16;
const maxDropdownHeight = 400;
let top = y + height + verticalOffset;
const left = Math.max(horizontalMargin, Math.min(x, windowWidth - width - horizontalMargin));
if (top + maxDropdownHeight > windowHeight - horizontalMargin) {
top = y - maxDropdownHeight - verticalOffset;
if (top < horizontalMargin) {
top = y + height + verticalOffset;
}
}
setDropdownPosition({ top, left, width });
});
}, [visible, isMobile, anchorRef, windowWidth, windowHeight]);
useEffect(() => {
if (!isMobile) return;
if (visible) {
bottomSheetRef.current?.present();
} else {
bottomSheetRef.current?.dismiss();
}
}, [visible, isMobile]);
const handleSheetChange = useCallback(
(index: number) => {
if (index === -1) {
onClose();
}
},
[onClose]
);
const renderBackdrop = useCallback(
(props: React.ComponentProps) => (
),
[]
);
if (isMobile) {
return (
{title}
{children}
);
}
return (
{children}
);
}
interface ComboSelectOption {
id: string;
label: string;
description?: string;
}
interface ComboSelectProps {
label: string;
title: string;
value: string;
options: ComboSelectOption[];
placeholder?: string;
disabled?: boolean;
allowCustomValue?: boolean;
isLoading?: boolean;
onSelect: (id: string) => void;
}
export function ComboSelect({
label,
title,
value,
options,
placeholder,
disabled,
allowCustomValue = false,
isLoading,
onSelect,
}: ComboSelectProps): ReactElement {
const [isOpen, setIsOpen] = useState(false);
const anchorRef = useRef(null);
const [searchQuery, setSearchQuery] = useState("");
const selectedOption = options.find((opt) => opt.id === value);
const displayValue = selectedOption?.label ?? (value || "");
const handleOpen = useCallback(() => setIsOpen(true), []);
const handleClose = useCallback(() => {
setIsOpen(false);
setSearchQuery("");
}, []);
useEffect(() => {
if (isOpen) {
setSearchQuery("");
}
}, [isOpen]);
const normalizedSearch = searchQuery.trim().toLowerCase();
const filteredOptions = useMemo(() => {
if (!normalizedSearch) {
return options;
}
return options.filter(
(opt) =>
opt.label.toLowerCase().includes(normalizedSearch) ||
opt.id.toLowerCase().includes(normalizedSearch) ||
opt.description?.toLowerCase().includes(normalizedSearch)
);
}, [options, normalizedSearch]);
const hasMatches = filteredOptions.length > 0;
const sanitizedSearchValue = searchQuery.trim();
const showCustomOption =
allowCustomValue &&
sanitizedSearchValue.length > 0 &&
!options.some(
(opt) =>
opt.id.toLowerCase() === sanitizedSearchValue.toLowerCase() ||
opt.label.toLowerCase() === sanitizedSearchValue.toLowerCase()
);
const handleSelect = useCallback(
(id: string) => {
onSelect(id);
handleClose();
},
[handleClose, onSelect]
);
return (
<>
{Platform.OS === "web" ? (
) : (
)}
{showCustomOption ? (
handleSelect(sanitizedSearchValue)}
>
{`Use "${sanitizedSearchValue}"`}
) : null}
{hasMatches ? (
{filteredOptions.map((opt) => {
const isSelected = opt.id === value;
return (
handleSelect(opt.id)}
>
{opt.label}
{opt.description ? (
{opt.description}
) : null}
);
})}
) : !showCustomOption ? (
No options match your search.
) : null}
{isLoading ? (
) : null}
>
);
}
interface CompactSelectFieldProps {
label: string;
value: string;
placeholder?: string;
onPress: () => void;
disabled?: boolean;
isLoading?: boolean;
controlRef?: React.RefObject;
}
function CompactSelectField({
label,
value,
placeholder,
onPress,
disabled,
isLoading,
controlRef,
}: CompactSelectFieldProps): ReactElement {
return (
{label}
{isLoading ? (
) : (
{value || placeholder || "Select..."}
)}
);
}
interface AgentConfigRowProps {
providerDefinitions: AgentProviderDefinition[];
selectedProvider: AgentProvider;
onSelectProvider: (provider: AgentProvider) => void;
modeOptions: AgentMode[];
selectedMode: string;
onSelectMode: (modeId: string) => void;
models: AgentModelDefinition[];
selectedModel: string;
isModelLoading: boolean;
onSelectModel: (modelId: string) => void;
disabled?: boolean;
}
export function AgentConfigRow({
providerDefinitions,
selectedProvider,
onSelectProvider,
modeOptions,
selectedMode,
onSelectMode,
models,
selectedModel,
isModelLoading,
onSelectModel,
disabled,
}: AgentConfigRowProps): ReactElement {
const providerOptions: ComboSelectOption[] = useMemo(
() =>
providerDefinitions.map((def) => ({
id: def.id,
label: def.label,
description: def.description,
})),
[providerDefinitions]
);
const modeSelectOptions: ComboSelectOption[] = useMemo(() => {
if (modeOptions.length === 0) {
return [{ id: "", label: "Default", description: "Provider default mode" }];
}
return modeOptions.map((mode) => ({
id: mode.id,
label: mode.label,
description: mode.description,
}));
}, [modeOptions]);
const modelSelectOptions: ComboSelectOption[] = useMemo(() => {
const opts: ComboSelectOption[] = [
{ id: "", label: "Auto", description: "Provider default model" },
];
for (const model of models) {
opts.push({
id: model.id,
label: model.label,
description: model.description,
});
}
return opts;
}, [models]);
const effectiveSelectedMode = selectedMode || (modeOptions.length > 0 ? modeOptions[0]?.id : "");
return (
);
}
interface AssistantDropdownProps {
providerDefinitions: AgentProviderDefinition[];
selectedProvider: AgentProvider;
disabled: boolean;
onSelect: (provider: AgentProvider) => void;
}
export function AssistantDropdown({
providerDefinitions,
selectedProvider,
disabled,
onSelect,
}: AssistantDropdownProps): ReactElement {
const [isOpen, setIsOpen] = useState(false);
const anchorRef = useRef(null);
const selectedDefinition = providerDefinitions.find(
(definition) => definition.id === selectedProvider
);
const handleOpen = useCallback(() => setIsOpen(true), []);
const handleClose = useCallback(() => setIsOpen(false), []);
return (
<>
{providerDefinitions.map((definition) => {
const isSelected = definition.id === selectedProvider;
return (
{
onSelect(definition.id);
handleClose();
}}
>
{definition.label}
{definition.description ? (
{definition.description}
) : null}
);
})}
>
);
}
interface PermissionsDropdownProps {
modeOptions: AgentMode[];
selectedMode: string;
disabled: boolean;
onSelect: (modeId: string) => void;
}
export function PermissionsDropdown({
modeOptions,
selectedMode,
disabled,
onSelect,
}: PermissionsDropdownProps): ReactElement {
const [isOpen, setIsOpen] = useState(false);
const anchorRef = useRef(null);
const hasOptions = modeOptions.length > 0;
const selectedModeLabel = hasOptions
? modeOptions.find((mode) => mode.id === selectedMode)?.label ??
modeOptions[0]?.label ??
"Default"
: "Automatic";
const handleOpen = useCallback(() => {
if (hasOptions) {
setIsOpen(true);
}
}, [hasOptions]);
const handleClose = useCallback(() => setIsOpen(false), []);
return (
<>
{hasOptions ? (
{modeOptions.map((mode) => {
const isSelected = mode.id === selectedMode;
return (
{
onSelect(mode.id);
handleClose();
}}
>
{mode.label}
{mode.description ? (
{mode.description}
) : null}
);
})}
) : null}
>
);
}
interface ModelDropdownProps {
models: AgentModelDefinition[];
selectedModel: string;
isLoading: boolean;
error: string | null;
onSelect: (modelId: string) => void;
onClear: () => void;
onRefresh: () => void;
}
export function ModelDropdown({
models,
selectedModel,
isLoading,
error,
onSelect,
onClear,
onRefresh,
}: ModelDropdownProps): ReactElement {
const [isOpen, setIsOpen] = useState(false);
const anchorRef = useRef(null);
const selectedLabel = selectedModel
? models.find((model) => model.id === selectedModel)?.label ?? selectedModel
: "Automatic";
const placeholder = isLoading && models.length === 0 ? "Loading..." : "Automatic";
const helperText = error
? undefined
: isLoading
? "Fetching available models..."
: models.length === 0
? "This assistant did not expose selectable models."
: undefined;
const handleOpen = useCallback(() => setIsOpen(true), []);
const handleClose = useCallback(() => setIsOpen(false), []);
return (
<>
{
onClear();
handleClose();
}}
>
Automatic (provider default)
Let the assistant pick the recommended model.
{models.map((model) => {
const isSelected = model.id === selectedModel;
return (
{
onSelect(model.id);
handleClose();
}}
>
{model.label}
{model.description ? (
{model.description}
) : null}
);
})}
{
onRefresh();
}}
>
Refresh models
Request the latest catalog from the provider.
{isLoading ? (
) : null}
>
);
}
interface WorkingDirectoryDropdownProps {
workingDir: string;
errorMessage: string;
disabled: boolean;
suggestedPaths: string[];
onSelectPath: (value: string) => void;
}
export function WorkingDirectoryDropdown({
workingDir,
errorMessage,
disabled,
suggestedPaths,
onSelectPath,
}: WorkingDirectoryDropdownProps): ReactElement {
const [isOpen, setIsOpen] = useState(false);
const anchorRef = useRef(null);
const [searchQuery, setSearchQuery] = useState("");
const handleOpen = useCallback(() => setIsOpen(true), []);
const handleClose = useCallback(() => setIsOpen(false), []);
useEffect(() => {
if (isOpen) {
setSearchQuery("");
}
}, [isOpen]);
const normalizedSearch = searchQuery.trim().toLowerCase();
const filteredPaths = useMemo(() => {
if (!normalizedSearch) {
return suggestedPaths;
}
return suggestedPaths.filter((path) =>
path.toLowerCase().includes(normalizedSearch)
);
}, [suggestedPaths, normalizedSearch]);
const hasSuggestedPaths = suggestedPaths.length > 0;
const hasMatches = filteredPaths.length > 0;
const sanitizedSearchValue = searchQuery.trim();
const showCustomOption = sanitizedSearchValue.length > 0;
const handleSelect = useCallback(
(path: string) => {
onSelectPath(path);
handleClose();
},
[handleClose, onSelectPath]
);
return (
<>
{Platform.OS === "web" ? (
) : (
)}
{!hasSuggestedPaths && !showCustomOption ? (
We'll suggest directories from agents on this host once they exist.
) : null}
{showCustomOption ? (
handleSelect(sanitizedSearchValue)}
>
{`Use "${sanitizedSearchValue}"`}
Launch the agent in this directory
) : null}
{hasMatches ? (
{filteredPaths.map((path) => {
const isActive = path === workingDir;
return (
handleSelect(path)}
>
{path}
);
})}
) : hasSuggestedPaths ? (
No agent directories match your search.
) : null}
>
);
}
interface ToggleRowProps {
label: string;
description?: string;
value: boolean;
onToggle: (value: boolean) => void;
disabled?: boolean;
}
export function ToggleRow({
label,
description,
value,
onToggle,
disabled,
}: ToggleRowProps): ReactElement {
return (
{
if (!disabled) {
onToggle(!value);
}
}}
style={[styles.toggleRow, disabled && styles.toggleRowDisabled]}
>
{value ? : null}
{label}
{description ? (
{description}
) : null}
);
}
export interface GitOptionsSectionProps {
useWorktree: boolean;
onUseWorktreeChange: (value: boolean) => void;
worktreeSlug: string;
currentBranch: string | null;
baseBranch: string;
onBaseBranchChange: (value: string) => void;
branches: Array<{ name: string; isCurrent: boolean }>;
status: "idle" | "loading" | "ready" | "error";
repoError: string | null;
gitValidationError: string | null;
baseBranchError: string | null;
}
export function GitOptionsSection({
useWorktree,
onUseWorktreeChange,
worktreeSlug,
currentBranch,
baseBranch,
onBaseBranchChange,
branches,
status,
repoError,
gitValidationError,
baseBranchError,
}: GitOptionsSectionProps): ReactElement {
const isLoading = status === "loading";
const [isEditingBranch, setIsEditingBranch] = useState(false);
const [editedBranch, setEditedBranch] = useState(baseBranch);
const inputRef = useRef(null);
useEffect(() => {
setEditedBranch(baseBranch);
}, [baseBranch]);
useEffect(() => {
if (isEditingBranch) {
inputRef.current?.focus();
}
}, [isEditingBranch]);
const handleStartEdit = useCallback(() => {
setEditedBranch(baseBranch);
setIsEditingBranch(true);
}, [baseBranch]);
const handleConfirmEdit = useCallback(() => {
const trimmed = editedBranch.trim();
if (trimmed) {
onBaseBranchChange(trimmed);
}
setIsEditingBranch(false);
}, [editedBranch, onBaseBranchChange]);
const handleCancelEdit = useCallback(() => {
setEditedBranch(baseBranch);
setIsEditingBranch(false);
}, [baseBranch]);
const displayBranch = baseBranch || currentBranch || "HEAD";
return (
onUseWorktreeChange(!useWorktree)}
disabled={isLoading}
style={[styles.worktreeToggle, isLoading && styles.worktreeToggleDisabled]}
>
{useWorktree ? : null}
Create worktree
{isLoading
? "Inspecting repository…"
: useWorktree && worktreeSlug
? `Will create: ${worktreeSlug}`
: currentBranch
? `Run isolated from ${currentBranch}`
: "Run in an isolated directory"}
{useWorktree ? (
Base branch:
{isEditingBranch ? (
) : (
{displayBranch}
)}
) : null}
{baseBranchError ? (
{baseBranchError}
) : null}
{repoError ? (
{repoError}
) : null}
{gitValidationError ? (
{gitValidationError}
) : null}
);
}
const styles = StyleSheet.create((theme) => ({
formSection: {
gap: theme.spacing[3],
},
label: {
color: theme.colors.foreground,
fontSize: theme.fontSize.sm,
fontWeight: theme.fontWeight.semibold,
},
dropdownControl: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[3],
backgroundColor: theme.colors.surface0,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.lg,
paddingVertical: theme.spacing[3],
paddingHorizontal: theme.spacing[4],
},
dropdownControlDisabled: {
opacity: theme.opacity[50],
},
dropdownValue: {
flex: 1,
color: theme.colors.foreground,
fontSize: theme.fontSize.base,
},
dropdownPlaceholder: {
flex: 1,
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.base,
},
dropdownSearchInput: {
borderRadius: theme.borderRadius.md,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
backgroundColor: theme.colors.surface0,
paddingHorizontal: theme.spacing[3],
paddingVertical: theme.spacing[2],
color: theme.colors.foreground,
},
bottomSheetBackground: {
backgroundColor: theme.colors.surface2,
borderTopLeftRadius: theme.borderRadius["2xl"],
borderTopRightRadius: theme.borderRadius["2xl"],
},
bottomSheetHandle: {
backgroundColor: theme.colors.palette.zinc[600],
},
bottomSheetHeader: {
paddingHorizontal: theme.spacing[6],
paddingBottom: theme.spacing[2],
},
dropdownSheetOverlay: {
flex: 1,
justifyContent: "flex-end",
},
dropdownSheetBackdrop: {
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
backgroundColor: theme.colors.palette.gray[900],
opacity: 0.45,
},
dropdownSheetContainer: {
backgroundColor: theme.colors.surface2,
borderTopLeftRadius: theme.borderRadius["2xl"],
borderTopRightRadius: theme.borderRadius["2xl"],
paddingTop: theme.spacing[4],
paddingHorizontal: theme.spacing[6],
paddingBottom: theme.spacing[6] + theme.spacing[2],
maxHeight: 560,
width: "100%",
},
dropdownSheetHandle: {
width: 56,
height: 4,
borderRadius: theme.borderRadius.full,
backgroundColor: theme.colors.border,
alignSelf: "center",
marginBottom: theme.spacing[3],
},
dropdownSheetTitle: {
fontSize: theme.fontSize.lg,
fontWeight: theme.fontWeight.semibold,
color: theme.colors.foreground,
textAlign: "center",
},
dropdownSheetScrollContent: {
paddingBottom: theme.spacing[8],
paddingHorizontal: theme.spacing[1],
},
dropdownSheetList: {
marginTop: theme.spacing[3],
},
dropdownSheetOption: {
paddingVertical: theme.spacing[3],
paddingHorizontal: theme.spacing[4],
borderRadius: theme.borderRadius.lg,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
backgroundColor: theme.colors.surface0,
marginBottom: theme.spacing[2],
},
dropdownSheetOptionSelected: {
borderColor: theme.colors.palette.blue[400],
backgroundColor: "rgba(59, 130, 246, 0.18)",
},
dropdownSheetOptionLabel: {
color: theme.colors.foreground,
fontWeight: theme.fontWeight.semibold,
},
dropdownSheetOptionDescription: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.sm,
marginTop: theme.spacing[1],
},
dropdownSheetLoading: {
alignItems: "center",
paddingVertical: theme.spacing[4],
},
errorText: {
color: theme.colors.palette.red[500],
fontSize: theme.fontSize.sm,
},
warningText: {
color: theme.colors.palette.orange[500],
fontSize: theme.fontSize.sm,
},
helperText: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.sm,
},
selectorColumn: {
flex: 1,
gap: theme.spacing[3],
},
selectorColumnFull: {
width: "100%",
},
toggleRow: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[3],
paddingVertical: theme.spacing[2],
},
toggleRowDisabled: {
opacity: theme.opacity[50],
},
checkbox: {
width: 22,
height: 22,
borderRadius: theme.borderRadius.sm,
borderWidth: theme.borderWidth[2],
borderColor: theme.colors.border,
alignItems: "center",
justifyContent: "center",
},
checkboxChecked: {
borderColor: theme.colors.palette.blue[500],
backgroundColor: theme.colors.palette.blue[500],
},
checkboxDisabled: {
borderColor: theme.colors.border,
},
checkboxDot: {
width: 10,
height: 10,
borderRadius: theme.borderRadius.full,
backgroundColor: theme.colors.palette.white,
},
toggleTextContainer: {
flex: 1,
gap: theme.spacing[1],
},
toggleLabel: {
color: theme.colors.foreground,
fontSize: theme.fontSize.base,
fontWeight: theme.fontWeight.semibold,
},
input: {
backgroundColor: theme.colors.surface0,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.lg,
paddingVertical: theme.spacing[3],
paddingHorizontal: theme.spacing[4],
color: theme.colors.foreground,
fontSize: theme.fontSize.base,
},
dropdownLoading: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
},
selectFieldContainer: {
gap: theme.spacing[2],
},
selectFieldControl: {
flexDirection: "row",
alignItems: "center",
backgroundColor: theme.colors.surface0,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.lg,
paddingVertical: theme.spacing[3],
paddingHorizontal: theme.spacing[4],
},
selectFieldControlDisabled: {
opacity: theme.opacity[50],
},
selectFieldContent: {
flex: 1,
gap: theme.spacing[1],
},
selectFieldLabel: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.xs,
fontWeight: theme.fontWeight.medium,
textTransform: "uppercase",
letterSpacing: 0.5,
},
selectFieldValue: {
color: theme.colors.foreground,
fontSize: theme.fontSize.base,
},
selectFieldPlaceholder: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.base,
},
gitOptionsContainer: {
gap: theme.spacing[3],
},
worktreeToggle: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[3],
backgroundColor: theme.colors.surface0,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.lg,
paddingVertical: theme.spacing[3],
paddingHorizontal: theme.spacing[4],
},
worktreeToggleDisabled: {
opacity: theme.opacity[50],
},
worktreeToggleContent: {
flex: 1,
gap: theme.spacing[1],
},
worktreeToggleLabel: {
color: theme.colors.foreground,
fontSize: theme.fontSize.base,
fontWeight: theme.fontWeight.semibold,
},
worktreeToggleDescription: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.sm,
},
baseBranchRow: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
paddingHorizontal: theme.spacing[4],
},
baseBranchLabel: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.sm,
},
baseBranchValueRow: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
},
baseBranchValue: {
color: theme.colors.foreground,
fontSize: theme.fontSize.sm,
fontWeight: theme.fontWeight.medium,
},
baseBranchEditRow: {
flex: 1,
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
},
baseBranchInput: {
flex: 1,
backgroundColor: theme.colors.surface0,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.md,
paddingVertical: theme.spacing[1],
paddingHorizontal: theme.spacing[2],
color: theme.colors.foreground,
fontSize: theme.fontSize.sm,
},
baseBranchIconButton: {
padding: theme.spacing[1],
},
desktopDropdownOverlay: {
flex: 1,
},
desktopDropdownBackdrop: {
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0,
},
desktopDropdownContainer: {
backgroundColor: theme.colors.surface2,
borderRadius: theme.borderRadius.lg,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.15,
shadowRadius: 12,
elevation: 8,
maxHeight: 400,
},
desktopDropdownScroll: {
maxHeight: 400,
},
desktopDropdownScrollContent: {
padding: theme.spacing[2],
},
agentConfigRow: {
flexDirection: "row",
gap: theme.spacing[2],
},
agentConfigColumn: {
flex: 1,
},
compactSelectControl: {
backgroundColor: theme.colors.surface0,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.lg,
paddingVertical: theme.spacing[2],
paddingHorizontal: theme.spacing[3],
gap: theme.spacing[1],
},
compactSelectControlDisabled: {
opacity: theme.opacity[50],
},
compactSelectLabel: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.xs,
fontWeight: theme.fontWeight.medium,
textTransform: "uppercase",
letterSpacing: 0.5,
},
compactSelectValue: {
color: theme.colors.foreground,
fontSize: theme.fontSize.sm,
},
compactSelectPlaceholder: {
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.sm,
},
}));