"use client"
/**
* Every Leo surface the app shell mounts, and the rules for which one shows.
*
* Leo has grown three shells — the docked panel, the floating window, and the
* corner FAB — and the choice between them depends on the persisted `dock`
* preference, whether the window is minimised, the route, and whether the
* viewport can carry a draggable window at all.
*
* Docked panel participates in the workspace `ResizablePanelGroup` (pill
* `ResizableHandle`). Window / FAB / overlay flyout mount as siblings outside
* that group so they do not break panel layout.
*
* @see components/ask-leo-context.tsx — `open`, `dock`, `minimized`
* @see docs/ask-leo-pattern.md — view modes and their entry points
*/
import * as React from "react"
import { useLocation } from "react-router"
import { useAskLeo } from "@/components/ask-leo-context"
import { useLeoAmbience } from "@/components/leo-ambience-context"
import { LeoLauncherFab } from "@/components/leo-launcher-fab"
import { useSecondaryPanel } from "@exxatdesignux/ui/components/shell/secondary-panel"
import { useBorrowedPrimarySidebar } from "@/hooks/use-borrowed-primary-sidebar"
import { useIsMobile } from "@/hooks/use-mobile"
import { useSidebarReflowZoom } from "@/hooks/use-sidebar-reflow-zoom"
import { isProductsHomePath } from "@/lib/product-home"
// Imported eagerly, unlike the window and the ambience surfaces below. The rail
// used to be `React.lazy`, which bought nothing: `utility-bar-slot.tsx` imports
// `AskLeoToggle` from this same module, so the code is in the entry chunk
// regardless and the lazy boundary only added a suspend-and-retry round trip
// before the first open could commit. Measured on the library hub: first open
// 511ms with the boundary, 251ms without, and the first open now costs the same
// as every later one.
import { AskLeoSidebar } from "@/components/ask-leo-sidebar"
const AskLeoWindow = React.lazy(() =>
import("@/components/ask-leo-window").then(m => ({
default: m.AskLeoWindow,
})),
)
const LeoAmbienceWindow = React.lazy(() =>
import("@/components/leo-ambience-window").then(m => ({
default: m.LeoAmbienceWindow,
})),
)
/** Borrow the primary nav's width while the docked panel is up. */
function AskLeoPrimarySidebarCollapse({ panelOpen }: { panelOpen: boolean }) {
useBorrowedPrimarySidebar(panelOpen)
return null
}
/**
* One floating layer at a time on mobile / reflow: opening Ask Leo closes the
* secondary scope sheet. Desktop docked Leo collapses secondary to the icon
* strip in {@link SecondaryPanel} (keeps the rail mounted).
*/
function AskLeoSecondaryPanelCollapse({ panelOpen }: { panelOpen: boolean }) {
const { activePanel, closeSecondaryFlyout, secondaryFlyoutVisible } =
useSecondaryPanel()
const isMobile = useIsMobile()
const reflowZoom = useSidebarReflowZoom()
const navFlyout = isMobile || reflowZoom
const prevOpen = React.useRef(panelOpen)
React.useEffect(() => {
if (
panelOpen &&
!prevOpen.current &&
navFlyout &&
activePanel &&
secondaryFlyoutVisible
) {
closeSecondaryFlyout()
}
prevOpen.current = panelOpen
}, [
panelOpen,
activePanel,
navFlyout,
secondaryFlyoutVisible,
closeSecondaryFlyout,
])
return null
}
function useAskLeoShellMode() {
const { open, dock, minimized } = useAskLeo()
const isMobile = useIsMobile()
const reflowZoom = useSidebarReflowZoom()
const windowMode = dock === "window" && !isMobile && !reflowZoom
const dockedPanelOpen = open && !windowMode
const overlayFlyout = dockedPanelOpen && (isMobile || reflowZoom)
return { open, minimized, windowMode, dockedPanelOpen, overlayFlyout }
}
/**
* Docked Ask Leo rail — `ResizableHandle` + `ResizablePanel` for the workspace
* group. Overlay flyout mounts from {@link AskLeoFloatingSlot}.
*/
export function AskLeoDockedSlot() {
const { dockedPanelOpen, overlayFlyout } = useAskLeoShellMode()
if (!dockedPanelOpen || overlayFlyout) return null
return
}
/**
* Window / FAB / ambience / overlay flyout — mount **outside** `ResizablePanelGroup`.
*/
export function AskLeoFloatingSlot() {
const { pathname } = useLocation()
const { settingsOpen } = useLeoAmbience()
const { open, minimized, windowMode, dockedPanelOpen, overlayFlyout } =
useAskLeoShellMode()
const salesFraming = isProductsHomePath(pathname)
const showFab = salesFraming || (windowMode && minimized)
return (
<>
{dockedPanelOpen && overlayFlyout ? : null}
{open && windowMode ? (
) : null}
{settingsOpen ? (
) : null}
{showFab ? : null}
>
)
}
/** @deprecated Prefer {@link AskLeoDockedSlot} + {@link AskLeoFloatingSlot}. */
export function AskLeoShell() {
return (
<>
>
)
}