diff --git a/packages/app/src/app/_layout.tsx b/packages/app/src/app/_layout.tsx
index 506cd5b4a..d4c88d3ba 100644
--- a/packages/app/src/app/_layout.tsx
+++ b/packages/app/src/app/_layout.tsx
@@ -218,6 +218,7 @@ export default function RootLayout() {
}}
>
+
diff --git a/packages/app/src/app/agents.tsx b/packages/app/src/app/agents.tsx
new file mode 100644
index 000000000..418226b3a
--- /dev/null
+++ b/packages/app/src/app/agents.tsx
@@ -0,0 +1,36 @@
+import { useMemo } from "react";
+import { View } from "react-native";
+import { StyleSheet } from "react-native-unistyles";
+import { BackHeader } from "@/components/headers/back-header";
+import { AgentList } from "@/components/agent-list";
+import { useAggregatedAgents } from "@/hooks/use-aggregated-agents";
+
+export default function AgentsScreen() {
+ const { agents, isRevalidating, refreshAll } = useAggregatedAgents();
+
+ const sortedAgents = useMemo(() => {
+ return [...agents].sort((a, b) => {
+ if (a.requiresAttention && !b.requiresAttention) return -1;
+ if (!a.requiresAttention && b.requiresAttention) return 1;
+ return 0;
+ });
+ }, [agents]);
+
+ return (
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create((theme) => ({
+ container: {
+ flex: 1,
+ backgroundColor: theme.colors.background,
+ },
+}));
diff --git a/packages/app/src/app/settings.tsx b/packages/app/src/app/settings.tsx
index 892cac23d..773f40311 100644
--- a/packages/app/src/app/settings.tsx
+++ b/packages/app/src/app/settings.tsx
@@ -49,226 +49,265 @@ const styles = StyleSheet.create((theme) => ({
flex: 1,
},
content: {
- padding: theme.spacing[6],
+ padding: theme.spacing[4],
+ paddingTop: theme.spacing[6],
},
section: {
- marginBottom: theme.spacing[8],
+ marginBottom: theme.spacing[6],
},
sectionTitle: {
- color: theme.colors.foreground,
- fontSize: theme.fontSize.lg,
+ color: theme.colors.mutedForeground,
+ fontSize: theme.fontSize.xs,
fontWeight: theme.fontWeight.semibold,
- marginBottom: theme.spacing[4],
+ letterSpacing: 0.6,
+ textTransform: "uppercase",
+ marginBottom: theme.spacing[3],
+ marginLeft: theme.spacing[1],
},
label: {
color: theme.colors.mutedForeground,
- fontSize: theme.fontSize.sm,
+ fontSize: theme.fontSize.xs,
+ fontWeight: theme.fontWeight.semibold,
+ letterSpacing: 0.4,
+ textTransform: "uppercase",
marginBottom: theme.spacing[2],
},
input: {
- backgroundColor: theme.colors.card,
+ backgroundColor: theme.colors.background,
color: theme.colors.foreground,
- padding: theme.spacing[4],
- borderRadius: theme.borderRadius.lg,
- marginBottom: theme.spacing[2],
+ padding: theme.spacing[3],
+ borderRadius: theme.borderRadius.md,
+ borderWidth: 1,
+ borderColor: theme.colors.border,
+ fontSize: theme.fontSize.base,
},
- settingCard: {
+ inputUrl: {
+ fontFamily: "monospace",
+ },
+ // Host card styles
+ hostCard: {
backgroundColor: theme.colors.card,
borderRadius: theme.borderRadius.lg,
- padding: theme.spacing[4],
+ borderWidth: 1,
+ borderColor: theme.colors.border,
marginBottom: theme.spacing[3],
+ overflow: "hidden",
},
- daemonCard: {
+ hostCardContent: {
+ padding: theme.spacing[4],
gap: theme.spacing[2],
},
- settingRow: {
- flexDirection: "row",
- justifyContent: "space-between",
- alignItems: "center",
- },
- daemonHeaderRow: {
+ hostHeaderRow: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
- settingContent: {
- flex: 1,
- },
- settingTitle: {
+ hostLabel: {
color: theme.colors.foreground,
fontSize: theme.fontSize.base,
- marginBottom: theme.spacing[1],
+ fontWeight: theme.fontWeight.semibold,
},
- settingDescription: {
+ hostUrl: {
color: theme.colors.mutedForeground,
fontSize: theme.fontSize.sm,
+ fontFamily: "monospace",
},
- connectionStatusBadge: {
+ hostError: {
+ color: theme.colors.palette.red[300],
+ fontSize: theme.fontSize.xs,
+ },
+ // Status pill
+ statusPill: {
flexDirection: "row",
alignItems: "center",
- gap: theme.spacing[1],
- },
- connectionStatusDot: {
- width: 8,
- height: 8,
+ gap: 6,
+ paddingHorizontal: theme.spacing[2],
+ paddingVertical: 4,
borderRadius: theme.borderRadius.full,
},
- connectionStatusText: {
+ statusDot: {
+ width: 6,
+ height: 6,
+ borderRadius: theme.borderRadius.full,
+ },
+ statusText: {
fontSize: theme.fontSize.xs,
fontWeight: theme.fontWeight.semibold,
},
- connectionErrorText: {
- color: theme.colors.destructive,
- fontSize: theme.fontSize.xs,
- marginTop: theme.spacing[1],
- },
- daemonActionsRow: {
+ // Host actions
+ hostActionsRow: {
flexDirection: "row",
- flexWrap: "wrap",
- gap: theme.spacing[2],
+ alignItems: "center",
+ borderTopWidth: 1,
+ borderTopColor: theme.colors.border,
+ paddingVertical: theme.spacing[2],
+ paddingHorizontal: theme.spacing[4],
+ gap: theme.spacing[1],
},
- daemonActionButton: {
+ hostActionButton: {
paddingVertical: theme.spacing[2],
paddingHorizontal: theme.spacing[3],
borderRadius: theme.borderRadius.md,
- borderWidth: theme.borderWidth[1],
- borderColor: theme.colors.border,
},
- daemonActionText: {
- color: theme.colors.foreground,
+ hostActionText: {
+ color: theme.colors.mutedForeground,
fontSize: theme.fontSize.xs,
fontWeight: theme.fontWeight.semibold,
},
- daemonActionDestructive: {
- borderColor: theme.colors.destructive,
+ hostActionPrimary: {
+ backgroundColor: theme.colors.palette.blue[500],
},
- daemonActionDestructiveText: {
- color: theme.colors.destructive,
+ hostActionPrimaryText: {
+ color: theme.colors.palette.white,
},
- daemonFormActionsRow: {
+ hostActionDestructiveText: {
+ color: theme.colors.palette.red[500],
+ },
+ hostActionDisabled: {
+ opacity: theme.opacity[50],
+ },
+ hostActionSeparator: {
+ width: 1,
+ height: 16,
+ backgroundColor: theme.colors.border,
+ marginHorizontal: theme.spacing[1],
+ },
+ testResultText: {
+ fontSize: theme.fontSize.xs,
+ },
+ // Add host button
+ addButton: {
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "center",
+ gap: theme.spacing[2],
+ paddingVertical: theme.spacing[3],
+ borderRadius: theme.borderRadius.lg,
+ borderWidth: 1,
+ borderColor: theme.colors.border,
+ borderStyle: "dashed",
+ },
+ addButtonText: {
+ color: theme.colors.mutedForeground,
+ fontSize: theme.fontSize.sm,
+ fontWeight: theme.fontWeight.semibold,
+ },
+ // Add/Edit form
+ formCard: {
+ backgroundColor: theme.colors.card,
+ borderRadius: theme.borderRadius.lg,
+ borderWidth: 1,
+ borderColor: theme.colors.border,
+ padding: theme.spacing[4],
+ marginBottom: theme.spacing[3],
+ gap: theme.spacing[4],
+ },
+ formTitle: {
+ color: theme.colors.foreground,
+ fontSize: theme.fontSize.base,
+ fontWeight: theme.fontWeight.semibold,
+ },
+ formField: {
+ gap: theme.spacing[2],
+ },
+ formActionsRow: {
flexDirection: "row",
justifyContent: "flex-end",
gap: theme.spacing[2],
- marginTop: theme.spacing[3],
},
- daemonActionPrimary: {
+ formButton: {
+ paddingVertical: theme.spacing[2],
+ paddingHorizontal: theme.spacing[4],
+ borderRadius: theme.borderRadius.md,
+ borderWidth: 1,
+ borderColor: theme.colors.border,
+ },
+ formButtonPrimary: {
backgroundColor: theme.colors.palette.blue[500],
borderColor: theme.colors.palette.blue[500],
},
- daemonActionPrimaryText: {
- color: theme.colors.palette.white,
- },
- daemonActionDisabled: {
- opacity: theme.opacity[50],
- },
- testResultSuccessText: {
- color: theme.colors.palette.green[400],
- fontSize: theme.fontSize.xs,
- },
- testResultErrorText: {
- color: theme.colors.palette.red[200],
- fontSize: theme.fontSize.xs,
- },
- testResultInfoText: {
- color: theme.colors.mutedForeground,
- fontSize: theme.fontSize.xs,
- },
- addButton: {
- paddingVertical: theme.spacing[3],
- borderRadius: theme.borderRadius.lg,
- borderWidth: theme.borderWidth[1],
- borderColor: theme.colors.border,
- alignItems: "center",
- },
- addButtonText: {
+ formButtonText: {
color: theme.colors.foreground,
+ fontSize: theme.fontSize.sm,
fontWeight: theme.fontWeight.semibold,
},
- themeCardDisabled: {
+ formButtonPrimaryText: {
+ color: theme.colors.palette.white,
+ },
+ // Audio settings card
+ audioCard: {
backgroundColor: theme.colors.card,
borderRadius: theme.borderRadius.lg,
- padding: theme.spacing[4],
- opacity: theme.opacity[50],
+ borderWidth: 1,
+ borderColor: theme.colors.border,
+ overflow: "hidden",
},
- themeHelpText: {
- color: theme.colors.mutedForeground,
- fontSize: theme.fontSize.sm,
- marginBottom: theme.spacing[3],
- },
- themeOption: {
+ audioRow: {
flexDirection: "row",
alignItems: "center",
- paddingVertical: theme.spacing[2],
+ justifyContent: "space-between",
+ paddingVertical: theme.spacing[4],
+ paddingHorizontal: theme.spacing[4],
},
- radioOuter: {
- width: 20,
- height: 20,
- borderRadius: theme.borderRadius.full,
- borderWidth: theme.borderWidth[2],
+ audioRowBorder: {
+ borderTopWidth: 1,
+ borderTopColor: theme.colors.border,
+ },
+ audioRowContent: {
+ flex: 1,
marginRight: theme.spacing[3],
- alignItems: "center",
- justifyContent: "center",
},
- radioOuterSelected: {
- borderColor: theme.colors.palette.blue[500],
+ audioRowTitle: {
+ color: theme.colors.foreground,
+ fontSize: theme.fontSize.base,
},
- radioOuterUnselected: {
- borderColor: theme.colors.border,
- },
- radioInner: {
- width: 12,
- height: 12,
- borderRadius: theme.borderRadius.full,
- backgroundColor: theme.colors.palette.blue[500],
- },
- themeOptionText: {
+ audioRowDescription: {
color: theme.colors.mutedForeground,
- fontSize: theme.fontSize.base,
- textTransform: "capitalize",
- },
- saveButton: {
- padding: theme.spacing[4],
- borderRadius: theme.borderRadius.lg,
- marginBottom: theme.spacing[3],
- backgroundColor: theme.colors.palette.blue[500],
- },
- saveButtonDisabled: {
- backgroundColor: theme.colors.palette.blue[900],
- opacity: theme.opacity[50],
- },
- saveButtonText: {
- color: theme.colors.palette.white,
- textAlign: "center",
- fontSize: theme.fontSize.base,
- fontWeight: theme.fontWeight.semibold,
- },
- resetButton: {
- padding: theme.spacing[4],
- borderRadius: theme.borderRadius.lg,
- borderWidth: theme.borderWidth[1],
- borderColor: theme.colors.destructive,
- },
- resetButtonText: {
- color: theme.colors.destructive,
- textAlign: "center",
- fontSize: theme.fontSize.base,
- fontWeight: theme.fontWeight.semibold,
+ fontSize: theme.fontSize.sm,
+ marginTop: 2,
},
+ // Footer
footer: {
- borderTopWidth: theme.borderWidth[1],
+ borderTopWidth: 1,
borderTopColor: theme.colors.border,
paddingTop: theme.spacing[6],
+ paddingBottom: theme.spacing[4],
+ alignItems: "center",
+ gap: theme.spacing[3],
+ },
+ footerAppInfo: {
+ alignItems: "center",
+ gap: theme.spacing[1],
},
footerText: {
color: theme.colors.mutedForeground,
fontSize: theme.fontSize.sm,
- textAlign: "center",
},
footerVersion: {
color: theme.colors.mutedForeground,
fontSize: theme.fontSize.xs,
+ },
+ resetButton: {
+ paddingVertical: theme.spacing[2],
+ paddingHorizontal: theme.spacing[3],
+ },
+ resetButtonText: {
+ color: theme.colors.palette.red[500],
+ fontSize: theme.fontSize.sm,
+ },
+ // Empty state
+ emptyCard: {
+ backgroundColor: theme.colors.card,
+ borderRadius: theme.borderRadius.lg,
+ borderWidth: 1,
+ borderColor: theme.colors.border,
+ padding: theme.spacing[4],
+ marginBottom: theme.spacing[3],
+ },
+ emptyText: {
+ color: theme.colors.mutedForeground,
+ fontSize: theme.fontSize.sm,
textAlign: "center",
- marginTop: theme.spacing[1],
},
}));
@@ -281,10 +320,6 @@ export default function SettingsScreen() {
const { settings, isLoading: settingsLoading, updateSettings, resetSettings } = useAppSettings();
const { daemons, isLoading: daemonLoading, addDaemon, updateDaemon, removeDaemon } = useDaemonRegistry();
const { connectionStates, updateConnectionStatus } = useDaemonConnections();
- const [useSpeaker, setUseSpeaker] = useState(settings.useSpeaker);
- const [keepScreenOn, setKeepScreenOn] = useState(settings.keepScreenOn);
- const [theme, setTheme] = useState<"dark" | "light" | "auto">(settings.theme);
- const [hasChanges, setHasChanges] = useState(false);
const [isDaemonFormVisible, setIsDaemonFormVisible] = useState(false);
const [daemonForm, setDaemonForm] = useState<{ id: string | null; label: string; wsUrl: string }>({ id: null, label: "", wsUrl: "" });
const [isSavingDaemon, setIsSavingDaemon] = useState(false);
@@ -484,21 +519,19 @@ export default function SettingsScreen() {
[testServerConnection, updateConnectionStatus, updateDaemonTestState]
);
- // Update local state when settings load
- useEffect(() => {
- setUseSpeaker(settings.useSpeaker);
- setKeepScreenOn(settings.keepScreenOn);
- setTheme(settings.theme);
- }, [settings]);
+ const handleToggleUseSpeaker = useCallback(
+ (value: boolean) => {
+ void updateSettings({ ...settings, useSpeaker: value });
+ },
+ [settings, updateSettings]
+ );
- // Track changes
- useEffect(() => {
- const changed =
- useSpeaker !== settings.useSpeaker ||
- keepScreenOn !== settings.keepScreenOn ||
- theme !== settings.theme;
- setHasChanges(changed);
- }, [useSpeaker, keepScreenOn, theme, settings]);
+ const handleToggleKeepScreenOn = useCallback(
+ (value: boolean) => {
+ void updateSettings({ ...settings, keepScreenOn: value });
+ },
+ [settings, updateSettings]
+ );
function validateServerUrl(url: string): boolean {
try {
@@ -509,32 +542,7 @@ export default function SettingsScreen() {
}
}
- async function handleSave() {
- try {
- await updateSettings({
- useSpeaker,
- keepScreenOn,
- theme,
- });
-
- Alert.alert(
- "Settings Saved",
- "Your settings have been saved successfully.",
- [
- {
- text: "OK",
- onPress: () => router.back(),
- },
- ]
- );
- } catch (error) {
- Alert.alert("Error", "Failed to save settings. Please try again.", [
- { text: "OK" },
- ]);
- }
- }
-
- async function handleReset() {
+ function handleReset() {
Alert.alert(
"Reset Settings",
"Are you sure you want to reset all settings to defaults?",
@@ -584,8 +592,8 @@ export default function SettingsScreen() {
Hosts
{daemons.length === 0 ? (
-
- No hosts configured.
+
+ No hosts configured
) : (
daemons.map((daemon) => {
@@ -613,46 +621,49 @@ export default function SettingsScreen() {
)}
{isDaemonFormVisible ? (
-
- {daemonForm.id ? "Edit Host" : "Add Host"}
- Label
- setDaemonForm((prev) => ({ ...prev, label: text }))}
- placeholder="My Host"
- placeholderTextColor={defaultTheme.colors.mutedForeground}
- />
-
- WebSocket URL
- setDaemonForm((prev) => ({ ...prev, wsUrl: text }))}
- placeholder="wss://example.com/ws"
- placeholderTextColor={defaultTheme.colors.mutedForeground}
- autoCapitalize="none"
- autoCorrect={false}
- keyboardType="url"
- />
-
-
- Cancel
+
+ {daemonForm.id ? "Edit Host" : "Add Host"}
+
+ Label
+ setDaemonForm((prev) => ({ ...prev, label: text }))}
+ placeholder="My Host"
+ placeholderTextColor={defaultTheme.colors.mutedForeground}
+ />
+
+
+ WebSocket URL
+ setDaemonForm((prev) => ({ ...prev, wsUrl: text }))}
+ placeholder="wss://example.com/ws"
+ placeholderTextColor={defaultTheme.colors.mutedForeground}
+ autoCapitalize="none"
+ autoCorrect={false}
+ keyboardType="url"
+ />
+
+
+
+ Cancel
-
- {isSavingDaemon ? "Saving..." : daemonForm.id ? "Save Host" : "Add Host"}
+
+ {isSavingDaemon ? "Saving..." : daemonForm.id ? "Save" : "Add"}
) : (
handleOpenDaemonForm()}>
- Add Host
+ + Add Host
)}
@@ -661,96 +672,48 @@ export default function SettingsScreen() {
Audio
-
-
-
- Use Speaker
-
+
+
+
+ Use Speaker
+
Play audio through speaker instead of earpiece
-
-
-
-
- Keep Screen On
-
+
+
+ Keep Screen On
+
Prevent screen from sleeping during voice sessions
- {/* Theme Settings */}
-
- Theme
-
-
-
- Theme selection (coming soon)
-
-
- {(["dark", "light", "auto"] as const).map((themeOption) => (
-
-
- {theme === themeOption && (
-
- )}
-
- {themeOption}
-
- ))}
-
-
-
- {/* Action Buttons */}
-
-
- Save Settings
-
-
-
- Reset to Defaults
-
-
-
- {/* App Info */}
+ {/* Footer */}
- Voice Assistant Mobile
- Version 1.0.0
+
+ Voice Dev Mobile
+ Version 1.0.0
+
+
+ Reset to defaults
+
@@ -921,67 +884,73 @@ function DaemonCard({
]);
}, [beginServerRestart, daemon.label, restartConfirmationMessage, restartServerFn]);
+ // Status pill background with 10% opacity
+ const statusPillBg =
+ statusTone === "success"
+ ? "rgba(74, 222, 128, 0.1)"
+ : statusTone === "warning"
+ ? "rgba(245, 158, 11, 0.1)"
+ : statusTone === "error"
+ ? "rgba(248, 113, 113, 0.1)"
+ : "rgba(161, 161, 170, 0.1)";
+
+ const testResultColor =
+ testState?.status === "success"
+ ? theme.colors.palette.green[400]
+ : testState?.status === "error"
+ ? theme.colors.palette.red[300]
+ : theme.colors.mutedForeground;
+
return (
-
-
- {daemon.label}
-
-
- {badgeText}
+
+
+
+ {daemon.label}
+
+
+ {badgeText}
+
+ {daemon.wsUrl}
+ {connectionError ? {connectionError} : null}
+ {testState && testState.status !== "idle" ? (
+
+ {testState.message ?? (testState.status === "success" ? "Reachable" : "Testing...")}
+
+ ) : null}
- {daemon.wsUrl}
- {connectionError ? {connectionError} : null}
- {testState && testState.status !== "idle" ? (
-
- {testState.message ?? (testState.status === "success" ? "Reachable" : "Testing...")}
-
- ) : null}
-
+
onTestConnection(daemon)}
disabled={isTesting}
>
-
+
{isTesting ? "Testing..." : "Test"}
+
{isRestarting ? (
-
+
) : (
- Restart
+ Restart
)}
- onEdit(daemon)}>
- Edit
+ onEdit(daemon)}>
+ Edit
- onRemove(daemon)}
- >
- Remove
+
+ onRemove(daemon)}>
+ Remove
diff --git a/packages/app/src/components/agent-list.tsx b/packages/app/src/components/agent-list.tsx
index 2ad5085e2..04cd621c5 100644
--- a/packages/app/src/components/agent-list.tsx
+++ b/packages/app/src/components/agent-list.tsx
@@ -1,6 +1,6 @@
import { View, Text, Pressable, Modal, RefreshControl, type ListRenderItem } from "react-native";
import { FlatList } from "react-native-gesture-handler";
-import { useCallback, useMemo, useState } from "react";
+import { useCallback, useState, type ReactElement } from "react";
import { router, usePathname } from "expo-router";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { formatTimeAgo } from "@/utils/time";
@@ -14,25 +14,21 @@ interface AgentListProps {
onRefresh?: () => void;
selectedAgentId?: string;
onAgentSelect?: () => void;
+ listFooterComponent?: ReactElement | null;
}
-export function AgentList({ agents, isRefreshing = false, onRefresh, selectedAgentId, onAgentSelect }: AgentListProps) {
+export function AgentList({
+ agents,
+ isRefreshing = false,
+ onRefresh,
+ selectedAgentId,
+ onAgentSelect,
+ listFooterComponent,
+}: AgentListProps) {
const { theme } = useUnistyles();
const pathname = usePathname();
const [actionAgent, setActionAgent] = useState(null);
- // Sort agents with requires attention at the top, limit to 15 for fast rendering
- const sortedAgents = useMemo(() => {
- return [...agents]
- .sort((a, b) => {
- // Requires attention first
- if (a.requiresAttention && !b.requiresAttention) return -1;
- if (!a.requiresAttention && b.requiresAttention) return 1;
- return 0;
- })
- .slice(0, 15);
- }, [agents]);
-
// Get the methods for the specific server
const methods = useSessionStore((state) =>
actionAgent?.serverId ? state.sessions[actionAgent.serverId]?.methods : undefined
@@ -148,7 +144,7 @@ export function AgentList({ agents, isRefreshing = false, onRefresh, selectedAge
return (
<>
{
+ return [...agents].sort((a, b) => {
+ if (a.requiresAttention && !b.requiresAttention) return -1;
+ if (!a.requiresAttention && b.requiresAttention) return 1;
+ return 0;
+ });
+ }, [agents]);
+
+ const limitedAgents = useMemo(
+ () => sortedAgents.slice(0, SIDEBAR_AGENT_LIMIT),
+ [sortedAgents]
+ );
+ const hasMore = agents.length > SIDEBAR_AGENT_LIMIT;
+
const handleClose = useCallback(() => {
close();
}, [close]);
@@ -74,6 +89,15 @@ export function SlidingSidebar({ selectedAgentId }: SlidingSidebarProps) {
close();
}, [close, translateX, backdropOpacity, windowWidth]);
+ const handleViewMore = useCallback(() => {
+ if (isMobile) {
+ translateX.value = -windowWidth;
+ backdropOpacity.value = 0;
+ }
+ close();
+ router.push("/agents");
+ }, [backdropOpacity, close, isMobile, translateX, windowWidth]);
+
// Close gesture (swipe left to close when sidebar is open)
const closeGesture = Gesture.Pan()
.enabled(isOpen)
@@ -121,6 +145,21 @@ export function SlidingSidebar({ selectedAgentId }: SlidingSidebarProps) {
pointerEvents: backdropOpacity.value > 0.01 ? "auto" : "none",
}));
+ const viewMoreButton = hasMore ? (
+
+ [
+ styles.newAgentButton,
+ styles.viewMoreButton,
+ hovered && styles.newAgentButtonHovered,
+ ]}
+ onPress={handleViewMore}
+ >
+ View More
+
+
+ ) : null;
+
// Render mobile sidebar
// On web, use "auto" instead of "box-none" because web's pointer-events: none blocks scroll
const overlayPointerEvents = Platform.OS === "web" ? "auto" : "box-none";
@@ -158,11 +197,12 @@ export function SlidingSidebar({ selectedAgentId }: SlidingSidebarProps) {
{/* Middle: scrollable agent list */}
{/* Footer */}
@@ -205,10 +245,11 @@ export function SlidingSidebar({ selectedAgentId }: SlidingSidebarProps) {
{/* Middle: scrollable agent list */}
{/* Footer: Settings button */}
@@ -277,6 +318,17 @@ const styles = StyleSheet.create((theme) => ({
fontWeight: theme.fontWeight.normal,
color: theme.colors.foreground,
},
+ viewMoreContainer: {
+ paddingTop: theme.spacing[2],
+ },
+ viewMoreButton: {
+ paddingVertical: theme.spacing[2],
+ },
+ viewMoreButtonText: {
+ fontSize: theme.fontSize.base,
+ fontWeight: theme.fontWeight.normal,
+ color: theme.colors.foreground,
+ },
sidebarFooter: {
paddingHorizontal: theme.spacing[4],
paddingTop: theme.spacing[2],