refactor(agent-screen): extract state machine for agent loading flow

This commit is contained in:
Mohamed Boudra
2026-02-19 21:17:51 +07:00
parent bff1a5accc
commit 603e95de09
4 changed files with 519 additions and 187 deletions
@@ -290,7 +290,8 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
onError: handleDictationError,
canStart: canStartDictation,
canConfirm: canConfirmDictation,
autoStopWhenHidden: { isVisible: isScreenFocused },
autoStopWhenHidden:
Platform.OS === 'web' ? undefined : { isVisible: isScreenFocused },
enableDuration: true,
})
@@ -0,0 +1,247 @@
import { describe, expect, it } from "vitest";
import type { Agent } from "@/contexts/session-context";
import {
deriveAgentScreenViewState,
type AgentScreenMachineInput,
type AgentScreenMachineMemory,
} from "./use-agent-screen-state-machine";
function createAgent(id: string): Agent {
const now = new Date("2026-02-19T00:00:00.000Z");
return {
serverId: "server-1",
id,
provider: "claude",
status: "running",
createdAt: now,
updatedAt: now,
lastUserMessageAt: now,
lastActivityAt: now,
capabilities: {
supportsStreaming: true,
supportsSessionPersistence: true,
supportsDynamicModes: true,
supportsMcpServers: true,
supportsReasoningStream: true,
supportsToolInvocations: true,
},
currentModeId: null,
availableModes: [],
pendingPermissions: [],
persistence: null,
runtimeInfo: {
provider: "claude",
sessionId: "session-1",
model: null,
modeId: null,
},
title: "Agent",
cwd: "/repo",
model: null,
labels: {},
};
}
function createBaseInput(): AgentScreenMachineInput {
return {
agent: null,
placeholderAgent: null,
missingAgentState: { kind: "idle" },
isConnected: true,
isArchivingCurrentAgent: false,
isHistorySyncing: false,
needsAuthoritativeSync: false,
shouldUseOptimisticStream: false,
};
}
describe("deriveAgentScreenViewState", () => {
it("returns boot loading before first interactive paint", () => {
const memory: AgentScreenMachineMemory = {
hasRenderedReady: false,
lastReadyAgent: null,
};
const input = createBaseInput();
const result = deriveAgentScreenViewState({ input, memory });
expect(result.state.tag).toBe("boot");
if (result.state.tag !== "boot") {
throw new Error("expected boot state");
}
expect(result.state.reason).toBe("loading");
expect(result.state.source).toBe("none");
});
it("stays ready after first paint even if agent is temporarily missing", () => {
const memory: AgentScreenMachineMemory = {
hasRenderedReady: true,
lastReadyAgent: createAgent("agent-1"),
};
const input = createBaseInput();
const result = deriveAgentScreenViewState({ input, memory });
expect(result.state.tag).toBe("ready");
if (result.state.tag !== "ready") {
throw new Error("expected ready state");
}
expect(result.state.source).toBe("stale");
expect(result.state.syncStatus).toBe("idle");
expect(result.state.agent.id).toBe("agent-1");
});
it("shows reconnecting sync status without blocking after first paint", () => {
const memory: AgentScreenMachineMemory = {
hasRenderedReady: true,
lastReadyAgent: createAgent("agent-1"),
};
const input: AgentScreenMachineInput = {
...createBaseInput(),
isConnected: false,
};
const result = deriveAgentScreenViewState({ input, memory });
expect(result.state.tag).toBe("ready");
if (result.state.tag !== "ready") {
throw new Error("expected ready state");
}
expect(result.state.syncStatus).toBe("reconnecting");
});
it("shows non-blocking catching-up state after first paint", () => {
const memory: AgentScreenMachineMemory = {
hasRenderedReady: true,
lastReadyAgent: createAgent("agent-1"),
};
const input: AgentScreenMachineInput = {
...createBaseInput(),
needsAuthoritativeSync: true,
};
const result = deriveAgentScreenViewState({ input, memory });
expect(result.state.tag).toBe("ready");
if (result.state.tag !== "ready") {
throw new Error("expected ready state");
}
expect(result.state.syncStatus).toBe("catching_up");
});
it("keeps ready with sync_error when refresh fails after first paint", () => {
const memory: AgentScreenMachineMemory = {
hasRenderedReady: true,
lastReadyAgent: createAgent("agent-1"),
};
const input: AgentScreenMachineInput = {
...createBaseInput(),
missingAgentState: { kind: "error", message: "network timeout" },
};
const result = deriveAgentScreenViewState({ input, memory });
expect(result.state.tag).toBe("ready");
if (result.state.tag !== "ready") {
throw new Error("expected ready state");
}
expect(result.state.source).toBe("stale");
expect(result.state.syncStatus).toBe("sync_error");
expect(result.state.agent.id).toBe("agent-1");
});
it("returns blocking error before first paint when refresh fails", () => {
const memory: AgentScreenMachineMemory = {
hasRenderedReady: false,
lastReadyAgent: null,
};
const input: AgentScreenMachineInput = {
...createBaseInput(),
missingAgentState: { kind: "error", message: "network timeout" },
};
const result = deriveAgentScreenViewState({ input, memory });
expect(result.state.tag).toBe("error");
if (result.state.tag !== "error") {
throw new Error("expected error state");
}
expect(result.state.message).toContain("network timeout");
});
it("returns not_found when resolver confirms missing agent", () => {
const memory: AgentScreenMachineMemory = {
hasRenderedReady: true,
lastReadyAgent: createAgent("agent-1"),
};
const input: AgentScreenMachineInput = {
...createBaseInput(),
missingAgentState: { kind: "not_found", message: "agent missing" },
};
const result = deriveAgentScreenViewState({ input, memory });
expect(result.state.tag).toBe("not_found");
if (result.state.tag !== "not_found") {
throw new Error("expected not_found state");
}
expect(result.state.message).toContain("missing");
});
it("promotes optimistic source while placeholder is used", () => {
const memory: AgentScreenMachineMemory = {
hasRenderedReady: false,
lastReadyAgent: null,
};
const input: AgentScreenMachineInput = {
...createBaseInput(),
placeholderAgent: createAgent("draft-agent"),
shouldUseOptimisticStream: true,
};
const result = deriveAgentScreenViewState({ input, memory });
expect(result.state.tag).toBe("ready");
if (result.state.tag !== "ready") {
throw new Error("expected ready state");
}
expect(result.state.source).toBe("optimistic");
});
it("keeps optimistic flow non-blocking while transitioning to authoritative stream", () => {
const initialMemory: AgentScreenMachineMemory = {
hasRenderedReady: false,
lastReadyAgent: null,
};
const optimisticInput: AgentScreenMachineInput = {
...createBaseInput(),
placeholderAgent: createAgent("draft-agent"),
shouldUseOptimisticStream: true,
};
const optimistic = deriveAgentScreenViewState({
input: optimisticInput,
memory: initialMemory,
});
expect(optimistic.state.tag).toBe("ready");
if (optimistic.state.tag !== "ready") {
throw new Error("expected optimistic ready state");
}
expect(optimistic.state.source).toBe("optimistic");
const handoffInput: AgentScreenMachineInput = {
...createBaseInput(),
};
const handoff = deriveAgentScreenViewState({
input: handoffInput,
memory: optimistic.memory,
});
expect(handoff.state.tag).toBe("ready");
if (handoff.state.tag !== "ready") {
throw new Error("expected handoff ready state");
}
expect(handoff.state.source).toBe("stale");
expect(handoff.state.agent.id).toBe("draft-agent");
});
});
@@ -0,0 +1,159 @@
import { useRef } from "react";
import type { Agent } from "@/contexts/session-context";
export type AgentScreenMissingState =
| { kind: "idle" }
| { kind: "resolving" }
| { kind: "not_found"; message: string }
| { kind: "error"; message: string };
export interface AgentScreenMachineInput {
agent: Agent | null;
placeholderAgent: Agent | null;
missingAgentState: AgentScreenMissingState;
isConnected: boolean;
isArchivingCurrentAgent: boolean;
isHistorySyncing: boolean;
needsAuthoritativeSync: boolean;
shouldUseOptimisticStream: boolean;
}
export interface AgentScreenMachineMemory {
hasRenderedReady: boolean;
lastReadyAgent: Agent | null;
}
export type AgentScreenSyncStatus =
| "idle"
| "catching_up"
| "reconnecting"
| "sync_error";
export type AgentScreenViewState =
| {
tag: "boot";
reason: "loading" | "resolving";
source: "none";
}
| {
tag: "not_found";
message: string;
}
| {
tag: "error";
message: string;
}
| {
tag: "ready";
agent: Agent;
source: "authoritative" | "optimistic" | "stale";
syncStatus: AgentScreenSyncStatus;
isArchiving: boolean;
};
export function deriveAgentScreenViewState({
input,
memory,
}: {
input: AgentScreenMachineInput;
memory: AgentScreenMachineMemory;
}): { state: AgentScreenViewState; memory: AgentScreenMachineMemory } {
const nextMemory: AgentScreenMachineMemory = {
hasRenderedReady: memory.hasRenderedReady,
lastReadyAgent: memory.lastReadyAgent,
};
const candidateAgent = input.agent ?? input.placeholderAgent;
if (candidateAgent) {
nextMemory.hasRenderedReady = true;
nextMemory.lastReadyAgent = candidateAgent;
}
if (input.missingAgentState.kind === "not_found") {
return {
state: {
tag: "not_found",
message: input.missingAgentState.message,
},
memory: nextMemory,
};
}
if (input.missingAgentState.kind === "error" && !nextMemory.hasRenderedReady) {
return {
state: {
tag: "error",
message: input.missingAgentState.message,
},
memory: nextMemory,
};
}
const displayAgent =
candidateAgent ?? (nextMemory.hasRenderedReady ? nextMemory.lastReadyAgent : null);
if (!displayAgent) {
return {
state: {
tag: "boot",
reason: input.missingAgentState.kind === "resolving" ? "resolving" : "loading",
source: "none",
},
memory: nextMemory,
};
}
const source: "authoritative" | "optimistic" | "stale" = input.agent
? "authoritative"
: input.shouldUseOptimisticStream
? "optimistic"
: "stale";
let syncStatus: AgentScreenSyncStatus = "idle";
if (!input.isConnected) {
syncStatus = "reconnecting";
} else if (input.missingAgentState.kind === "error") {
syncStatus = "sync_error";
} else if (input.needsAuthoritativeSync || input.isHistorySyncing) {
syncStatus = "catching_up";
}
return {
state: {
tag: "ready",
agent: displayAgent,
source,
syncStatus,
isArchiving: input.isArchivingCurrentAgent,
},
memory: nextMemory,
};
}
export function useAgentScreenStateMachine({
routeKey,
input,
}: {
routeKey: string;
input: AgentScreenMachineInput;
}): AgentScreenViewState {
const routeKeyRef = useRef(routeKey);
const memoryRef = useRef<AgentScreenMachineMemory>({
hasRenderedReady: false,
lastReadyAgent: null,
});
if (routeKeyRef.current !== routeKey) {
routeKeyRef.current = routeKey;
memoryRef.current = {
hasRenderedReady: false,
lastReadyAgent: null,
};
}
const result = deriveAgentScreenViewState({
input,
memory: memoryRef.current,
});
memoryRef.current = result.memory;
return result.state;
}
@@ -63,6 +63,10 @@ import {
} from "@/hooks/use-checkout-status-query";
import { useAgentInitialization } from "@/hooks/use-agent-initialization";
import { useArchiveAgent } from "@/hooks/use-archive-agent";
import {
useAgentScreenStateMachine,
type AgentScreenMissingState,
} from "@/hooks/use-agent-screen-state-machine";
import { useToast } from "@/contexts/toast-context";
import { getInitDeferred, getInitKey } from "@/utils/agent-initialization";
import {
@@ -85,8 +89,6 @@ import type { ExplorerCheckoutContext } from "@/stores/panel-store";
const DROPDOWN_WIDTH = 220;
const EMPTY_STREAM_ITEMS: StreamItem[] = [];
const RECONNECT_NOTICE_DELAY_MS = 10_000;
const CONNECTED_NOTICE_DURATION_MS = 2_500;
const IS_DEV = Boolean((globalThis as { __DEV__?: boolean }).__DEV__);
function logAgentExplorer(event: string, details: Record<string, unknown>): void {
@@ -199,12 +201,6 @@ type AgentScreenContentProps = {
connectionStatus: ConnectionStatus;
};
type MissingAgentState =
| { kind: "idle" }
| { kind: "resolving" }
| { kind: "not_found"; message: string }
| { kind: "error"; message: string };
function toErrorMessage(error: unknown): string {
if (error instanceof Error) {
return error.message;
@@ -479,14 +475,10 @@ function AgentScreenContent({
(state) => state.sessions[serverId]?.focusedAgentId ?? null
);
const { ensureAgentIsInitialized, refreshAgent } = useAgentInitialization(serverId);
const [missingAgentState, setMissingAgentState] = useState<MissingAgentState>({
const [missingAgentState, setMissingAgentState] = useState<AgentScreenMissingState>({
kind: "idle",
});
const [showReconnectNotice, setShowReconnectNotice] = useState(false);
const [dismissedReconnectNotice, setDismissedReconnectNotice] = useState(false);
const [showConnectedNotice, setShowConnectedNotice] = useState(false);
const reconnectNoticeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const connectedNoticeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const reconnectToastArmedRef = useRef(false);
const initAttemptTokenRef = useRef(0);
const setFocusedAgentId = useCallback(
(agentId: string | null) => {
@@ -511,58 +503,43 @@ function AgentScreenContent({
};
});
const agentModel = extractAgentModel(agent);
const modelDisplayValue = agentModel ?? "Unknown";
useEffect(() => {
if (reconnectNoticeTimeoutRef.current) {
clearTimeout(reconnectNoticeTimeoutRef.current);
reconnectNoticeTimeoutRef.current = null;
}
if (connectionStatus === "online") {
if (showReconnectNotice || dismissedReconnectNotice) {
setShowConnectedNotice(true);
}
setShowReconnectNotice(false);
setDismissedReconnectNotice(false);
reconnectToastArmedRef.current = false;
return;
}
setShowConnectedNotice(false);
if (!showReconnectNotice && !dismissedReconnectNotice) {
reconnectNoticeTimeoutRef.current = setTimeout(() => {
setShowReconnectNotice(true);
}, RECONNECT_NOTICE_DELAY_MS);
if (connectionStatus === "idle") {
return;
}
return () => {
if (reconnectNoticeTimeoutRef.current) {
clearTimeout(reconnectNoticeTimeoutRef.current);
reconnectNoticeTimeoutRef.current = null;
}
};
}, [connectionStatus, dismissedReconnectNotice, showReconnectNotice]);
if (!reconnectToastArmedRef.current) {
reconnectToastArmedRef.current = true;
toast.show("Reconnecting...", {
durationMs: 2200,
testID: "agent-reconnecting-toast",
});
}
}, [connectionStatus, toast]);
useEffect(() => {
if (!showConnectedNotice) {
if (connectedNoticeTimeoutRef.current) {
clearTimeout(connectedNoticeTimeoutRef.current);
connectedNoticeTimeoutRef.current = null;
}
if (missingAgentState.kind !== "error") {
return;
}
connectedNoticeTimeoutRef.current = setTimeout(() => {
setShowConnectedNotice(false);
connectedNoticeTimeoutRef.current = null;
}, CONNECTED_NOTICE_DURATION_MS);
return () => {
if (connectedNoticeTimeoutRef.current) {
clearTimeout(connectedNoticeTimeoutRef.current);
connectedNoticeTimeoutRef.current = null;
toast.error("Failed to refresh agent. Retrying in background.");
}, [missingAgentState.kind, toast]);
useFocusEffect(
useCallback(() => {
if (!resolvedAgentId || !isConnected) {
return;
}
};
}, [showConnectedNotice]);
ensureAgentIsInitialized(resolvedAgentId).catch((error) => {
console.warn("[AgentScreen] focus sync failed", {
agentId: resolvedAgentId,
error,
});
});
}, [ensureAgentIsInitialized, isConnected, resolvedAgentId])
);
const isGitCheckout = activeExplorerCheckout?.isGit ?? false;
const isArchivingCurrentAgent = Boolean(
@@ -646,7 +623,6 @@ function AgentScreenContent({
}, [optimisticStreamItems, streamItems]);
const shouldUseOptimisticStream = isPendingCreateForRoute && optimisticStreamItems.length > 0;
const shouldBlockForHistorySync = !shouldUseOptimisticStream && (needsAuthoritativeSync || isHistorySyncing);
const placeholderAgent: Agent | null = useMemo(() => {
if (!shouldUseOptimisticStream || !resolvedAgentId) {
@@ -687,9 +663,26 @@ function AgentScreenContent({
};
}, [resolvedAgentId, serverId, shouldUseOptimisticStream]);
const effectiveAgent = agent ?? placeholderAgent;
const providerLabel = (effectiveAgent?.provider ?? "Provider").replace(/^\w/, (m) =>
m.toUpperCase()
const viewState = useAgentScreenStateMachine({
routeKey: `${serverId}:${resolvedAgentId ?? ""}`,
input: {
agent: agent ?? null,
placeholderAgent,
missingAgentState,
isConnected,
isArchivingCurrentAgent,
isHistorySyncing,
needsAuthoritativeSync,
shouldUseOptimisticStream,
},
});
const effectiveAgent = viewState.tag === "ready" ? viewState.agent : null;
const agentModel = extractAgentModel(effectiveAgent ?? agent);
const modelDisplayValue = agentModel ?? "Unknown";
const providerLabel = (effectiveAgent?.provider ?? "Provider").replace(
/^\w/,
(m) => m.toUpperCase()
);
const providerSessionId =
effectiveAgent?.runtimeInfo?.sessionId ?? effectiveAgent?.persistence?.sessionId ?? null;
@@ -931,34 +924,44 @@ function AgentScreenContent({
[theme.colors.primary, toast]
);
if (!effectiveAgent) {
if (missingAgentState.kind === "not_found") {
return (
<View style={styles.container} testID="agent-not-found">
<MenuHeader title="Agent" />
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Agent not found</Text>
</View>
</View>
);
}
if (missingAgentState.kind === "error") {
return (
<View style={styles.container} testID="agent-load-error">
<MenuHeader title="Agent" />
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Failed to load agent</Text>
<Text style={styles.statusText}>{missingAgentState.message}</Text>
</View>
</View>
);
}
const syncBannerMessage =
viewState.tag !== "ready"
? null
: viewState.syncStatus === "catching_up"
? "Refreshing agent history..."
: viewState.syncStatus === "sync_error"
? "Refresh delayed. Retrying in background."
: null;
if (viewState.tag === "not_found") {
return (
<View style={styles.container} testID="agent-not-found">
<MenuHeader title="Agent" />
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Agent not found</Text>
</View>
</View>
);
}
if (viewState.tag === "error") {
return (
<View style={styles.container} testID="agent-load-error">
<MenuHeader title="Agent" />
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Failed to load agent</Text>
<Text style={styles.statusText}>{viewState.message}</Text>
</View>
</View>
);
}
if (viewState.tag === "boot" || !effectiveAgent) {
return (
<View style={styles.container} testID="agent-loading">
<MenuHeader title="Agent" />
<View style={styles.errorContainer}>
<ActivityIndicator size="small" color={theme.colors.foregroundMuted} />
<ActivityIndicator size="large" color={theme.colors.foregroundMuted} />
</View>
</View>
);
@@ -968,7 +971,7 @@ function AgentScreenContent({
<View style={styles.outerContainer}>
<FileDropZone
onFilesDropped={handleFilesDropped}
disabled={isInitializing || shouldBlockForHistorySync || isArchivingCurrentAgent}
disabled={isArchivingCurrentAgent}
>
<View style={styles.container}>
{/* Header */}
@@ -1149,7 +1152,7 @@ function AgentScreenContent({
<DropdownMenuItem
leading={<RotateCcw size={theme.iconSize.md} color={theme.colors.foreground} />}
disabled={isInitializing || shouldBlockForHistorySync}
disabled={isInitializing}
trailing={
isInitializing ? (
<ActivityIndicator
@@ -1169,75 +1172,31 @@ function AgentScreenContent({
}
/>
{(showReconnectNotice || showConnectedNotice) && (
<View
style={[
styles.connectionNotice,
showReconnectNotice
? styles.connectionNoticeReconnecting
: styles.connectionNoticeConnected,
]}
>
{showConnectedNotice ? (
<CheckCircle2
size={theme.iconSize.sm}
color={theme.colors.palette.green[600]}
/>
) : null}
<Text
style={[
styles.connectionNoticeText,
showReconnectNotice
? styles.connectionNoticeTextReconnecting
: styles.connectionNoticeTextConnected,
]}
>
{showReconnectNotice ? "Reconnecting..." : "Connected"}
</Text>
{showReconnectNotice ? (
<Pressable
onPress={() => {
setShowReconnectNotice(false);
setDismissedReconnectNotice(true);
}}
style={styles.connectionNoticeDismiss}
accessibilityRole="button"
accessibilityLabel="Dismiss reconnecting notice"
>
<Text style={styles.connectionNoticeDismissText}>Dismiss</Text>
</Pressable>
) : null}
{syncBannerMessage ? (
<View style={styles.syncBanner} testID="agent-sync-banner">
<Text style={styles.syncBannerText}>{syncBannerMessage}</Text>
</View>
)}
) : null}
{/* Content Area with Keyboard Animation */}
<View style={styles.contentContainer}>
{shouldBlockForHistorySync ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={theme.colors.primary} />
</View>
) : (
<ReanimatedAnimated.View
style={[styles.content, animatedKeyboardStyle]}
>
<AgentStreamView
agentId={effectiveAgent.id}
serverId={serverId}
agent={effectiveAgent}
streamItems={
shouldUseOptimisticStream ? mergedStreamItems : streamItems
}
pendingPermissions={pendingPermissions}
/>
</ReanimatedAnimated.View>
)}
<ReanimatedAnimated.View
style={[styles.content, animatedKeyboardStyle]}
>
<AgentStreamView
agentId={effectiveAgent.id}
serverId={serverId}
agent={effectiveAgent}
streamItems={
shouldUseOptimisticStream ? mergedStreamItems : streamItems
}
pendingPermissions={pendingPermissions}
/>
</ReanimatedAnimated.View>
</View>
{/* Agent Input Area */}
{agent &&
resolvedAgentId &&
!shouldBlockForHistorySync &&
!isArchivingCurrentAgent && (
{resolvedAgentId && !isArchivingCurrentAgent && (
<AgentInputArea
agentId={resolvedAgentId}
serverId={serverId}
@@ -1377,58 +1336,24 @@ const styles = StyleSheet.create((theme) => ({
flex: 1,
overflow: "hidden",
},
connectionNotice: {
syncBanner: {
marginHorizontal: theme.spacing[4],
marginTop: theme.spacing[1],
marginBottom: theme.spacing[2],
minHeight: 32,
borderRadius: theme.borderRadius.full,
paddingVertical: theme.spacing[2],
paddingHorizontal: theme.spacing[3],
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[2],
},
connectionNoticeReconnecting: {
backgroundColor: `${theme.colors.palette.yellow[400]}22`,
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.palette.yellow[400],
borderColor: theme.colors.border,
backgroundColor: theme.colors.surface1,
},
connectionNoticeConnected: {
backgroundColor: theme.colors.palette.green[100],
borderWidth: theme.borderWidth[1],
borderColor: theme.colors.palette.green[400],
},
connectionNoticeText: {
syncBannerText: {
fontSize: theme.fontSize.sm,
fontWeight: theme.fontWeight.medium,
},
connectionNoticeTextReconnecting: {
color: theme.colors.foreground,
},
connectionNoticeTextConnected: {
color: theme.colors.palette.green[800],
},
connectionNoticeDismiss: {
marginLeft: "auto",
paddingVertical: theme.spacing[1],
paddingHorizontal: theme.spacing[2],
borderRadius: theme.borderRadius.full,
backgroundColor: theme.colors.surface0,
},
connectionNoticeDismissText: {
fontSize: theme.fontSize.xs,
fontWeight: theme.fontWeight.medium,
color: theme.colors.foregroundMuted,
},
content: {
flex: 1,
},
loadingContainer: {
flex: 1,
alignItems: "center",
justifyContent: "center",
gap: 16,
},
archivingOverlay: {
position: "absolute",
top: 0,