diff --git a/app/src/components/LayoutShell.tsx b/app/src/components/LayoutShell.tsx
index 4c007ec..5ad61f3 100644
--- a/app/src/components/LayoutShell.tsx
+++ b/app/src/components/LayoutShell.tsx
@@ -1,23 +1,36 @@
"use client";
+import { useEffect } from "react";
import { SidebarNav } from "./SidebarNav";
import { WatchlistSidebar } from "./WatchlistSidebar";
import { SymbolHeader } from "./SymbolHeader";
+import { CollapsibleProvider, useCollapsibleContext } from "@/lib/collapsible-context";
/**
- * Main layout shell for the redesigned terminal.
- * - Desktop (>1024px): Sidebar + Watchlist + Header + Content (3-column)
- * - Tablet (768-1024px): Compact watchlist bar + Header + Content (2-column, sidebar hidden, right column below center)
- * - Mobile (<768px): Header + Content (single column)
+ * Inner component that attaches the global Escape listener.
+ * Wrapped in CollapsibleProvider so all CollapsibleSections can register.
*/
-export function LayoutShell({ children }: { children: React.ReactNode }) {
+function LayoutShellInner({ children }: { children: React.ReactNode }) {
+ const { openSectionId, setOpenSectionId } = useCollapsibleContext();
+
+ // Global Escape listener: collapse the currently open section.
+ useEffect(() => {
+ const handler = (e: KeyboardEvent) => {
+ if (e.key === "Escape" && openSectionId) {
+ e.preventDefault();
+ // Collapse the currently open section by clearing the shared state.
+ // Individual CollapsibleSection Escape handlers also respond to this.
+ setOpenSectionId(null);
+ }
+ };
+ window.addEventListener("keydown", handler, true); // capture phase
+ return () => window.removeEventListener("keydown", handler, true);
+ }, [openSectionId, setOpenSectionId]);
+
return (
{/* Symbol Header — always visible */}
- {/* Tablet Compact Watchlist Bar — replaces sidebar on tablet */}
-
-
{/* Main Content Area */}
{/* Left Sidebar Navigation — hidden on tablet/mobile */}
@@ -26,88 +39,27 @@ export function LayoutShell({ children }: { children: React.ReactNode }) {
{/* Watchlist Sidebar — hidden on mobile */}
- {/* Content + Right Analytics Column */}
-
- {/* Center Content — full width on tablet/mobile, narrower on desktop */}
-
-
- {children}
-
-
-
- {/* Right Analytics Column — drops below center on tablet */}
-
-
+ {/* Content */}
+
+
+ {children}
+
+
);
}
/**
- * Compact horizontal watchlist bar for tablet (768-1024px).
- * Replaces the sidebar on tablet — shows 2-3 symbols with mini-overview.
- * Hidden on desktop and mobile.
+ * Main layout shell for the redesigned terminal.
+ * - Desktop (>1024px): Sidebar + Watchlist + Header + Content (3-column)
+ * - Tablet (768-1024px): Watchlist + Header + Content (2-column, sidebar hidden)
+ * - Mobile (<768px): Header + Content (single column)
*/
-function CompactWatchlistBar() {
+export function LayoutShell({ children }: { children: React.ReactNode }) {
return (
-
- {/* Placeholder watchlist items — will be replaced with real data */}
-
- AAPL
- +1.2%
-
-
- MSFT
- +0.8%
-
-
- NVDA
- -0.3%
-
-
- );
-}
-
-/**
- * Right analytics column containing Filings and Options DD panels.
- * Desktop: flex row with center + right columns side by side
- * Tablet: right column drops below center content (flex column)
- */
-function RightAnalyticsColumn() {
- return (
-
- {/* Collapsible Filings Panel */}
-
-
-
-
Loading filings...
-
-
-
- {/* Collapsible Options DD Panel */}
-
-
-
-
Loading options data...
-
-
-
- {/* Footer */}
-
-
+
+ {children}
+
);
}
diff --git a/app/src/lib/collapsible-context.tsx b/app/src/lib/collapsible-context.tsx
new file mode 100644
index 0000000..6c69269
--- /dev/null
+++ b/app/src/lib/collapsible-context.tsx
@@ -0,0 +1,32 @@
+"use client";
+import { createContext, useContext, useState, type ReactNode } from "react";
+
+/**
+ * Shared context for tracking which CollapsibleSection is currently open.
+ * Used by LayoutShell to implement Escape-to-close across all panels.
+ */
+
+interface CollapsibleContextValue {
+ /** The id of the currently open section, or null if all are collapsed */
+ openSectionId: string | null;
+ /** Set the open section. Pass null to close all. */
+ setOpenSectionId: (id: string | null) => void;
+}
+
+const CollapsibleContext = createContext({
+ openSectionId: null,
+ setOpenSectionId: () => {},
+});
+
+export function CollapsibleProvider({ children }: { children: ReactNode }) {
+ const [openSectionId, setOpenSectionId] = useState(null);
+ return (
+
+ {children}
+
+ );
+}
+
+export function useCollapsibleContext() {
+ return useContext(CollapsibleContext);
+}