Update files

This commit is contained in:
Mohamed Boudra
2026-02-07 00:07:04 +07:00
parent 8694696f45
commit f26d7f5552
5 changed files with 261 additions and 167 deletions
+129 -88
View File
@@ -416,7 +416,6 @@ export default function SettingsScreen() {
const [pendingRemoveHost, setPendingRemoveHost] = useState<HostProfile | null>(null); const [pendingRemoveHost, setPendingRemoveHost] = useState<HostProfile | null>(null);
const [isRemovingHost, setIsRemovingHost] = useState(false); const [isRemovingHost, setIsRemovingHost] = useState(false);
const [editingDaemon, setEditingDaemon] = useState<HostProfile | null>(null); const [editingDaemon, setEditingDaemon] = useState<HostProfile | null>(null);
const [editLabel, setEditLabel] = useState("");
const [isSavingEdit, setIsSavingEdit] = useState(false); const [isSavingEdit, setIsSavingEdit] = useState(false);
const isLoading = settingsLoading || daemonLoading; const isLoading = settingsLoading || daemonLoading;
const isMountedRef = useRef(true); const isMountedRef = useRef(true);
@@ -460,13 +459,11 @@ export default function SettingsScreen() {
const handleEditDaemon = useCallback((profile: HostProfile) => { const handleEditDaemon = useCallback((profile: HostProfile) => {
setEditingDaemon(profile); setEditingDaemon(profile);
setEditLabel(profile.label ?? "");
}, []); }, []);
const handleCloseEditDaemon = useCallback(() => { const handleCloseEditDaemon = useCallback(() => {
if (isSavingEdit) return; if (isSavingEdit) return;
setEditingDaemon(null); setEditingDaemon(null);
setEditLabel("");
}, [isSavingEdit]); }, [isSavingEdit]);
const closeAddConnectionFlow = useCallback(() => { const closeAddConnectionFlow = useCallback(() => {
@@ -510,17 +507,11 @@ export default function SettingsScreen() {
pendingEditReopenServerId, pendingEditReopenServerId,
]); ]);
useEffect(() => { const handleSaveEditDaemon = useCallback(async (nextLabelRaw: string) => {
if (!editingDaemon) return;
if (editingDaemonLive) return;
handleCloseEditDaemon();
}, [editingDaemon, editingDaemonLive, handleCloseEditDaemon]);
const handleSaveEditDaemon = useCallback(async () => {
if (!editingDaemon) return; if (!editingDaemon) return;
if (isSavingEdit) return; if (isSavingEdit) return;
const nextLabel = editLabel.trim(); const nextLabel = nextLabelRaw.trim();
if (!nextLabel) { if (!nextLabel) {
Alert.alert("Label required", "Enter a label for this host."); Alert.alert("Label required", "Enter a label for this host.");
return; return;
@@ -536,7 +527,7 @@ export default function SettingsScreen() {
} finally { } finally {
setIsSavingEdit(false); setIsSavingEdit(false);
} }
}, [editLabel, editingDaemon, handleCloseEditDaemon, isSavingEdit, updateHost]); }, [editingDaemon, handleCloseEditDaemon, isSavingEdit, updateHost]);
const handleRemoveConnection = useCallback( const handleRemoveConnection = useCallback(
async (serverId: string, connectionId: string) => { async (serverId: string, connectionId: string) => {
@@ -757,84 +748,14 @@ export default function SettingsScreen() {
</AdaptiveModalSheet> </AdaptiveModalSheet>
) : null} ) : null}
<AdaptiveModalSheet <EditHostModal
title="Edit host"
visible={Boolean(editingDaemon)} visible={Boolean(editingDaemon)}
host={editingDaemonLive ?? editingDaemon}
isSaving={isSavingEdit}
onClose={handleCloseEditDaemon} onClose={handleCloseEditDaemon}
testID="edit-host-modal" onSave={(label) => void handleSaveEditDaemon(label)}
> onRemoveConnection={handleRemoveConnection}
<View style={styles.formField}> />
<Text style={styles.label}>Label</Text>
<AdaptiveTextInput
style={styles.input}
value={editLabel}
onChangeText={setEditLabel}
placeholder="My Host"
placeholderTextColor={defaultTheme.colors.mutedForeground}
/>
</View>
{editingDaemonLive ? (
<View style={styles.formField}>
<Text style={styles.label}>Connections</Text>
<View style={{ gap: 8 }}>
{editingDaemonLive.connections.map((conn) => {
const title =
conn.type === "relay"
? `Relay (${conn.relayEndpoint})`
: `Direct (${conn.endpoint})`;
return (
<View
key={conn.id}
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
gap: 12,
paddingVertical: 10,
paddingHorizontal: 12,
borderRadius: 12,
borderWidth: 1,
borderColor: theme.colors.border,
backgroundColor: theme.colors.surface2,
}}
>
<Text style={{ color: theme.colors.foreground, fontSize: 12, flex: 1 }}>
{title}
</Text>
<Pressable
onPress={() => void handleRemoveConnection(editingDaemonLive.serverId, conn.id)}
>
<Text style={{ color: theme.colors.destructive, fontSize: 12, fontWeight: "500" }}>
Remove
</Text>
</Pressable>
</View>
);
})}
</View>
</View>
) : null}
<View style={styles.formActionsRow}>
<Button
variant="secondary"
size="sm"
onPress={handleCloseEditDaemon}
disabled={isSavingEdit}
>
Cancel
</Button>
<Button
variant="default"
size="sm"
onPress={() => void handleSaveEditDaemon()}
disabled={isSavingEdit}
>
{isSavingEdit ? "Saving..." : "Save"}
</Button>
</View>
</AdaptiveModalSheet>
{/* Appearance */} {/* Appearance */}
<View style={styles.section}> <View style={styles.section}>
@@ -895,6 +816,126 @@ export default function SettingsScreen() {
); );
} }
interface EditHostModalProps {
visible: boolean;
host: HostProfile | null;
isSaving: boolean;
onClose: () => void;
onSave: (label: string) => void;
onRemoveConnection: (serverId: string, connectionId: string) => Promise<void>;
}
function EditHostModal({
visible,
host,
isSaving,
onClose,
onSave,
onRemoveConnection,
}: EditHostModalProps) {
const [draftLabel, setDraftLabel] = useState("");
const activeServerIdRef = useRef<string | null>(null);
useEffect(() => {
if (!visible || !host) return;
if (activeServerIdRef.current !== host.serverId) {
setDraftLabel(host.label ?? "");
activeServerIdRef.current = host.serverId;
return;
}
if (!draftLabel.trim()) {
setDraftLabel(host.label ?? "");
}
}, [visible, host, draftLabel]);
useEffect(() => {
if (!visible) {
activeServerIdRef.current = null;
}
}, [visible]);
return (
<AdaptiveModalSheet
title="Edit host"
visible={visible}
onClose={onClose}
testID="edit-host-modal"
>
<View style={styles.formField}>
<Text style={styles.label}>Label</Text>
<AdaptiveTextInput
style={styles.input}
value={draftLabel}
onChangeText={setDraftLabel}
placeholder="My Host"
placeholderTextColor={defaultTheme.colors.mutedForeground}
/>
</View>
{host ? (
<View style={styles.formField}>
<Text style={styles.label}>Connections</Text>
<View style={{ gap: 8 }}>
{host.connections.map((conn) => {
const title =
conn.type === "relay"
? `Relay (${conn.relayEndpoint})`
: `Direct (${conn.endpoint})`;
return (
<View
key={conn.id}
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
gap: 12,
paddingVertical: 10,
paddingHorizontal: 12,
borderRadius: 12,
borderWidth: 1,
borderColor: defaultTheme.colors.border,
backgroundColor: defaultTheme.colors.surface2,
}}
>
<Text style={{ color: defaultTheme.colors.foreground, fontSize: 12, flex: 1 }}>
{title}
</Text>
<Pressable
onPress={() => void onRemoveConnection(host.serverId, conn.id)}
>
<Text style={{ color: defaultTheme.colors.destructive, fontSize: 12, fontWeight: "500" }}>
Remove
</Text>
</Pressable>
</View>
);
})}
</View>
</View>
) : null}
<View style={styles.formActionsRow}>
<Button
variant="secondary"
size="sm"
onPress={onClose}
disabled={isSaving}
>
Cancel
</Button>
<Button
variant="default"
size="sm"
onPress={() => onSave(draftLabel)}
disabled={isSaving}
>
{isSaving ? "Saving..." : "Save"}
</Button>
</View>
</AdaptiveModalSheet>
);
}
interface DaemonCardProps { interface DaemonCardProps {
daemon: HostProfile; daemon: HostProfile;
connectionStatus: ConnectionStatus; connectionStatus: ConnectionStatus;
@@ -163,6 +163,15 @@ export function SelectField({
[getWebKey, onPress, preventWebDefault] [getWebKey, onPress, preventWebDefault]
); );
const normalizedValue = (value ?? "").trim();
const normalizedPlaceholder = (placeholder ?? "").trim();
const hasConcreteValue =
normalizedValue.length > 0 &&
(normalizedPlaceholder.length === 0 || normalizedValue !== normalizedPlaceholder);
const displayText = hasConcreteValue
? normalizedValue
: (normalizedPlaceholder || "Select...");
return ( return (
<View style={styles.selectFieldContainer}> <View style={styles.selectFieldContainer}>
<Pressable <Pressable
@@ -423,6 +432,14 @@ export function FormSelectTrigger({
}, },
[getWebKey, onPress, preventWebDefault] [getWebKey, onPress, preventWebDefault]
); );
const normalizedValue = (value ?? "").trim();
const normalizedPlaceholder = (placeholder ?? "").trim();
const hasConcreteValue =
normalizedValue.length > 0 &&
(normalizedPlaceholder.length === 0 || normalizedValue !== normalizedPlaceholder);
const displayText = hasConcreteValue
? normalizedValue
: (normalizedPlaceholder || "Select...");
return ( return (
<Pressable <Pressable
@@ -451,11 +468,11 @@ export function FormSelectTrigger({
<ActivityIndicator size="small" color={defaultTheme.colors.foregroundMuted} /> <ActivityIndicator size="small" color={defaultTheme.colors.foregroundMuted} />
) : ( ) : (
<Text <Text
style={value ? styles.compactSelectValue : styles.compactSelectPlaceholder} style={hasConcreteValue ? styles.compactSelectValue : styles.compactSelectPlaceholder}
numberOfLines={1} numberOfLines={1}
ellipsizeMode={valueEllipsizeMode} ellipsizeMode={valueEllipsizeMode}
> >
{value || placeholder || "Select..."} {displayText}
</Text> </Text>
)} )}
</View> </View>
@@ -1426,7 +1443,10 @@ const styles = StyleSheet.create((theme) => ({
paddingVertical: theme.spacing[1], paddingVertical: theme.spacing[1],
}, },
agentConfigRow: { agentConfigRow: {
flexDirection: "row", flexDirection: {
xs: "column",
md: "row",
},
gap: theme.spacing[2], gap: theme.spacing[2],
}, },
agentConfigColumn: { agentConfigColumn: {
@@ -1206,7 +1206,10 @@ const stylesheet = StyleSheet.create((theme) => ({
}, },
list: { list: {
flex: 1, flex: 1,
paddingHorizontal: theme.spacing[2], paddingHorizontal: {
xs: theme.spacing[2],
md: theme.spacing[4],
},
}, },
streamItemWrapper: { streamItemWrapper: {
width: "100%", width: "100%",
+10 -25
View File
@@ -1050,24 +1050,18 @@ export function GitDiffPane({ serverId, agentId, cwd }: GitDiffPaneProps) {
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={gitActions.primary.label} accessibilityLabel={gitActions.primary.label}
> >
<View style={styles.splitButtonPrimaryInner}> {gitActions.primary.status === "pending" ? (
<View <ActivityIndicator
style={[ size="small"
styles.splitButtonContent, color={theme.colors.foreground}
gitActions.primary.status === "pending" && styles.splitButtonContentHidden, style={styles.splitButtonSpinnerOnly}
]} />
> ) : (
<View style={styles.splitButtonContent}>
{gitActions.primary.icon} {gitActions.primary.icon}
<Text style={styles.splitButtonText}>{getActionDisplayLabel(gitActions.primary)}</Text> <Text style={styles.splitButtonText}>{getActionDisplayLabel(gitActions.primary)}</Text>
</View> </View>
{gitActions.primary.status === "pending" ? ( )}
<ActivityIndicator
size="small"
color={theme.colors.foreground}
style={styles.splitButtonSpinnerOverlay}
/>
) : null}
</View>
</Pressable> </Pressable>
{gitActions.secondary.length > 0 ? ( {gitActions.secondary.length > 0 ? (
<DropdownMenu> <DropdownMenu>
@@ -1306,11 +1300,6 @@ const styles = StyleSheet.create((theme) => ({
justifyContent: "center", justifyContent: "center",
position: "relative", position: "relative",
}, },
splitButtonPrimaryInner: {
position: "relative",
alignItems: "center",
justifyContent: "center",
},
splitButtonPrimaryDisabled: { splitButtonPrimaryDisabled: {
opacity: 0.6, opacity: 0.6,
}, },
@@ -1326,11 +1315,7 @@ const styles = StyleSheet.create((theme) => ({
justifyContent: "center", justifyContent: "center",
gap: theme.spacing[2], gap: theme.spacing[2],
}, },
splitButtonContentHidden: { splitButtonSpinnerOnly: {
opacity: 0,
},
splitButtonSpinnerOverlay: {
position: "absolute",
transform: [{ scale: 0.8 }], transform: [{ scale: 0.8 }],
}, },
splitButtonCaret: { splitButtonCaret: {
@@ -30,6 +30,7 @@ import {
import { useDaemonConnections } from "@/contexts/daemon-connections-context"; import { useDaemonConnections } from "@/contexts/daemon-connections-context";
import { useDaemonRegistry } from "@/contexts/daemon-registry-context"; import { useDaemonRegistry } from "@/contexts/daemon-registry-context";
import { formatConnectionStatus } from "@/utils/daemons"; import { formatConnectionStatus } from "@/utils/daemons";
import { shortenPath } from "@/utils/shorten-path";
import { usePanelStore } from "@/stores/panel-store"; import { usePanelStore } from "@/stores/panel-store";
import { useSessionStore } from "@/stores/session-store"; import { useSessionStore } from "@/stores/session-store";
import { useCreateFlowStore } from "@/stores/create-flow-store"; import { useCreateFlowStore } from "@/stores/create-flow-store";
@@ -199,11 +200,25 @@ export function DraftAgentScreen({
isCreateFlow: true, isCreateFlow: true,
onlineServerIds, onlineServerIds,
}); });
const daemonLabelByServerId = useMemo(() => {
const map = new Map<string, string>();
for (const daemon of daemons) {
const label = daemon.label?.trim();
map.set(daemon.serverId, label && label.length > 0 ? label : daemon.serverId);
}
return map;
}, [daemons]);
const hostEntry = selectedServerId const hostEntry = selectedServerId
? connectionStates.get(selectedServerId) ? connectionStates.get(selectedServerId)
: undefined; : undefined;
const selectedDaemonLabel = selectedServerId
? daemonLabelByServerId.get(selectedServerId)
: null;
const hostLabel = const hostLabel =
hostEntry?.daemon.label ?? selectedServerId ?? "Select host"; selectedDaemonLabel ??
hostEntry?.daemon.label ??
selectedServerId ??
"Select host";
const isMobile = const isMobile =
UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm"; UnistylesRuntime.breakpoint === "xs" || UnistylesRuntime.breakpoint === "sm";
const isSidebarOpen = isMobile ? mobileView === "agent-list" : desktopAgentListOpen; const isSidebarOpen = isMobile ? mobileView === "agent-list" : desktopAgentListOpen;
@@ -561,9 +576,8 @@ export function DraftAgentScreen({
const handleSelectWorktreePath = useCallback( const handleSelectWorktreePath = useCallback(
(path: string) => { (path: string) => {
setSelectedWorktreePath(path); setSelectedWorktreePath(path);
setWorkingDirFromUser(path);
}, },
[setWorkingDirFromUser] []
); );
useEffect(() => { useEffect(() => {
@@ -591,35 +605,36 @@ export function DraftAgentScreen({
const selectedWorktreeLabel = const selectedWorktreeLabel =
worktreeOptions.find((option) => option.path === selectedWorktreePath)?.label ?? ""; worktreeOptions.find((option) => option.path === selectedWorktreePath)?.label ?? "";
const displayWorkingDir = shortenPath(workingDir);
const worktreeTriggerValue = const worktreeTriggerValue =
worktreeMode === "create" worktreeMode === "create"
? "Create new worktree" ? "Create new worktree"
: selectedWorktreeLabel || "Select worktree"; : selectedWorktreeLabel || "Select worktree";
const hostOptions = useMemo( const hostOptions = useMemo(
() => () =>
Array.from(connectionStates.values()).map(({ daemon, status }) => ({ daemons.map((daemon) => ({
id: daemon.serverId, id: daemon.serverId,
label: daemon.label?.trim() ? daemon.label : daemon.serverId, label: daemonLabelByServerId.get(daemon.serverId) ?? daemon.serverId,
description: formatConnectionStatus(status), description: formatConnectionStatus(
connectionStates.get(daemon.serverId)?.status ?? "idle"
),
})), })),
[connectionStates] [connectionStates, daemonLabelByServerId, daemons]
); );
const worktreeComboOptions = useMemo( const worktreeComboOptions = useMemo(
() => [ () => [
{
id: "__create_new__",
label: "Create new worktree",
description: "Create a new isolated worktree",
},
{ {
id: "__none__", id: "__none__",
label: "None", label: "None",
description: "Do not use a worktree", },
{
id: "__create_new__",
label: "Create new worktree",
}, },
...worktreeOptions.map((option) => ({ ...worktreeOptions.map((option) => ({
id: option.path, id: option.path,
label: option.label, label: option.label,
description: option.path, description: shortenPath(option.path),
})), })),
], ],
[worktreeOptions] [worktreeOptions]
@@ -922,12 +937,12 @@ export function DraftAgentScreen({
) : ( ) : (
<ScrollView style={styles.scrollView} contentContainerStyle={styles.configScrollContent}> <ScrollView style={styles.scrollView} contentContainerStyle={styles.configScrollContent}>
<View style={styles.configSection}> <View style={styles.configSection}>
<View style={styles.topSelectorRow}> <View style={isMobile ? styles.stackedSelectorGroup : styles.topSelectorRow}>
<FormSelectTrigger <FormSelectTrigger
controlRef={workingDirAnchorRef} controlRef={workingDirAnchorRef}
containerStyle={styles.topSelectorPrimary} containerStyle={isMobile ? styles.fullSelector : styles.topSelectorPrimary}
label="Working directory" label="Working directory"
value={workingDir} value={displayWorkingDir}
placeholder="/path/to/project" placeholder="/path/to/project"
onPress={() => setIsWorkingDirOpen(true)} onPress={() => setIsWorkingDirOpen(true)}
icon={<Folder size={16} color={theme.colors.foregroundMuted} />} icon={<Folder size={16} color={theme.colors.foregroundMuted} />}
@@ -936,7 +951,7 @@ export function DraftAgentScreen({
/> />
<FormSelectTrigger <FormSelectTrigger
controlRef={hostAnchorRef} controlRef={hostAnchorRef}
containerStyle={styles.topSelectorSecondary} containerStyle={isMobile ? styles.fullSelector : styles.topSelectorSecondary}
label="Host" label="Host"
value={hostLabel} value={hostLabel}
placeholder="Select host" placeholder="Select host"
@@ -952,36 +967,7 @@ export function DraftAgentScreen({
</Text> </Text>
</View> </View>
)} )}
{trimmedWorkingDir.length > 0 && !isNonGitDirectory ? ( {isMobile ? <View style={styles.formSeparator} /> : null}
<View style={styles.topSelectorRow}>
<FormSelectTrigger
controlRef={worktreeAnchorRef}
containerStyle={
worktreeMode === "create" ? styles.halfSelector : styles.topSelectorPrimary
}
label="Worktree"
value={worktreeTriggerValue}
placeholder="Select worktree"
onPress={() => setIsWorktreePickerOpen(true)}
icon={<GitBranch size={16} color={theme.colors.foregroundMuted} />}
showLabel={false}
valueEllipsizeMode="middle"
/>
{worktreeMode === "create" ? (
<FormSelectTrigger
controlRef={branchAnchorRef}
containerStyle={styles.halfSelector}
label="Base branch"
value={baseBranch}
placeholder="From branch"
onPress={() => setIsBranchOpen(true)}
disabled={repoInfoStatus === "loading"}
icon={<GitBranch size={16} color={theme.colors.foregroundMuted} />}
showLabel={false}
/>
) : null}
</View>
) : null}
<AgentConfigRow <AgentConfigRow
providerDefinitions={providerDefinitions} providerDefinitions={providerDefinitions}
selectedProvider={selectedProvider} selectedProvider={selectedProvider}
@@ -994,6 +980,43 @@ export function DraftAgentScreen({
isModelLoading={isModelLoading} isModelLoading={isModelLoading}
onSelectModel={setModelFromUser} onSelectModel={setModelFromUser}
/> />
{isMobile && trimmedWorkingDir.length > 0 && !isNonGitDirectory ? (
<View style={styles.formSeparator} />
) : null}
{trimmedWorkingDir.length > 0 && !isNonGitDirectory ? (
<View style={isMobile ? styles.stackedSelectorGroup : styles.topSelectorRow}>
<FormSelectTrigger
controlRef={worktreeAnchorRef}
containerStyle={
isMobile
? styles.fullSelector
: worktreeMode === "create"
? styles.halfSelector
: styles.topSelectorPrimary
}
label="Worktree"
value={worktreeTriggerValue}
placeholder="Select worktree"
onPress={() => setIsWorktreePickerOpen(true)}
icon={<GitBranch size={16} color={theme.colors.foregroundMuted} />}
showLabel={false}
valueEllipsizeMode="middle"
/>
{worktreeMode === "create" ? (
<FormSelectTrigger
controlRef={branchAnchorRef}
containerStyle={isMobile ? styles.fullSelector : styles.halfSelector}
label="Base branch"
value={baseBranch}
placeholder="From branch"
onPress={() => setIsBranchOpen(true)}
disabled={repoInfoStatus === "loading"}
icon={<GitBranch size={16} color={theme.colors.foregroundMuted} />}
showLabel={false}
/>
) : null}
</View>
) : null}
{baseBranchError ? <Text style={styles.errorInlineText}>{baseBranchError}</Text> : null} {baseBranchError ? <Text style={styles.errorInlineText}>{baseBranchError}</Text> : null}
{repoInfoError ? <Text style={styles.errorInlineText}>{repoInfoError}</Text> : null} {repoInfoError ? <Text style={styles.errorInlineText}>{repoInfoError}</Text> : null}
{gitBlockingError ? <Text style={styles.errorInlineText}>{gitBlockingError}</Text> : null} {gitBlockingError ? <Text style={styles.errorInlineText}>{gitBlockingError}</Text> : null}
@@ -1046,7 +1069,10 @@ export function DraftAgentScreen({
/> />
<Combobox <Combobox
options={agentWorkingDirSuggestions.map((path) => ({ id: path, label: path }))} options={agentWorkingDirSuggestions.map((path) => ({
id: path,
label: shortenPath(path),
}))}
value={workingDir} value={workingDir}
onSelect={setWorkingDirFromUser} onSelect={setWorkingDirFromUser}
searchPlaceholder="/path/to/project" searchPlaceholder="/path/to/project"
@@ -1136,7 +1162,10 @@ const styles = StyleSheet.create((theme) => ({
justifyContent: "flex-end", justifyContent: "flex-end",
}, },
configSection: { configSection: {
paddingHorizontal: theme.spacing[0], paddingHorizontal: {
xs: theme.spacing[4],
md: theme.spacing[0],
},
paddingTop: theme.spacing[3], paddingTop: theme.spacing[3],
paddingBottom: theme.spacing[4], paddingBottom: theme.spacing[4],
gap: theme.spacing[2], gap: theme.spacing[2],
@@ -1149,6 +1178,22 @@ const styles = StyleSheet.create((theme) => ({
alignItems: "center", alignItems: "center",
gap: theme.spacing[2], gap: theme.spacing[2],
}, },
stackedSelectorGroup: {
gap: theme.spacing[2],
},
fullSelector: {
width: "100%",
},
formSeparator: {
height: theme.borderWidth[1],
backgroundColor: {
xs: theme.colors.surface1,
sm: theme.colors.surface1,
md: theme.colors.border,
},
marginHorizontal: theme.spacing[1],
marginVertical: theme.spacing[2],
},
topSelectorPrimary: { topSelectorPrimary: {
flex: 7, flex: 7,
}, },