import { configureTracking } from "@agent-native/core/client/analytics"; import { appPath } from "@agent-native/core/client/api-path"; import { AppProviders, createAgentNativeQueryClient, useDbSync, } from "@agent-native/core/client/hooks"; import { enterStyleEditing as coreEnterStyleEditing, enterTextEditing as coreEnterTextEditing, exitSelectionMode as coreExitSelectionMode, } from "@agent-native/core/client/host"; import { useT } from "@agent-native/core/client/i18n"; import { getLocaleInitScript } from "@agent-native/core/client/i18n"; import { CommandMenu, useCommandMenuShortcut, } from "@agent-native/core/client/navigation"; import { getThemeInitScript } from "@agent-native/core/client/ui"; import { IconHierarchy2, IconSun, IconMoon } from "@tabler/icons-react"; import { useQueryClient } from "@tanstack/react-query"; import { useTheme } from "next-themes"; import { useCallback, useEffect, useState } from "react"; import { Links, Meta, Outlet, Scripts, ScrollRestoration, useLocation, useNavigate, } from "react-router"; import type { LinksFunction } from "react-router"; import { Layout as AppLayout } from "@/components/layout/Layout"; import { AppToolkitProvider } from "@/components/ui/toolkit-provider"; import { DeckProvider } from "@/context/DeckContext"; import { useNavigationState } from "@/hooks/use-navigation-state"; import { TAB_ID } from "@/lib/tab-id"; import changelog from "../CHANGELOG.md?raw"; import { i18nCatalog } from "./i18n"; import stylesheet from "./global.css?url"; configureTracking({ getDefaultProps: (_name, properties) => ({ ...properties, app: "agent-native-slides", }), }); /** Routes that render without the app shell (sidebar + AgentSidebar) */ const BARE_ROUTES = new Set(["/slide"]); /** Route prefixes that render without the app shell */ const BARE_PREFIXES = ["/share/", "/p/"]; export const links: LinksFunction = () => [ { rel: "stylesheet", href: stylesheet }, ]; // Key forces DeckProvider remount when code changes (HMR) const DECK_KEY = 3; /** Track whether we (the app) put the user into selection mode via a slide click */ let weEnteredSelectionMode = false; /** Helper to send selection mode messages and track state */ export function enterSelectionMode( type: "agentNative.enterStyleEditing" | "agentNative.enterTextEditing", data: { selector: string }, ) { weEnteredSelectionMode = true; if (type === "agentNative.enterStyleEditing") { coreEnterStyleEditing(data.selector); } else { coreEnterTextEditing(data.selector); } } export function exitSelectionMode() { weEnteredSelectionMode = false; coreExitSelectionMode(); } function useExitSelectionOnOutsideClick() { useEffect(() => { const handler = (e: PointerEvent) => { if (!weEnteredSelectionMode) return; const target = e.target as HTMLElement; if ( target.closest(".slide-content") || target.closest(".slide-image-clickable") ) { return; } exitSelectionMode(); }; window.addEventListener("pointerdown", handler, { capture: true }); return () => window.removeEventListener("pointerdown", handler, { capture: true }); }, []); } const THEME_INIT_SCRIPT_SELECTOR = "script[data-agent-native-theme-init]"; const LOCALE_INIT_SCRIPT_SELECTOR = "script[data-agent-native-locale-init]"; function getHydrationStableThemeInitScript() { if (typeof document !== "undefined") { const existing = document.querySelector( THEME_INIT_SCRIPT_SELECTOR, ); if (existing?.innerHTML) return existing.innerHTML; } return getThemeInitScript("dark", true); } function getHydrationStableLocaleInitScript() { if (typeof document !== "undefined") { const existing = document.querySelector( LOCALE_INIT_SCRIPT_SELECTOR, ); if (existing?.innerHTML) return existing.innerHTML; } return getLocaleInitScript(); } const THEME_INIT_SCRIPT = getHydrationStableThemeInitScript(); const LOCALE_INIT_SCRIPT = getHydrationStableLocaleInitScript(); export function Layout({ children }: { children: React.ReactNode }) { return (