/* eslint-disable react-refresh/only-export-components */ import { Braces, LayoutDashboard, LayoutGrid, LayoutPanelLeft, Server, Settings, Usb, User, TerminalSquare, Layers, // --- tmux-monitor --- Clock, Fingerprint, Hammer, ScrollText, Presentation, } from "lucide-react"; import { lazy, Suspense } from "react"; import { useTranslation } from "react-i18next"; import type { SerialHandle } from "@/features/serial/serial-types"; import type { Tab, TabType } from "@/types/ui-types"; import { hostToSSHHost } from "@/lib/host-to-ssh-host"; import { PluginViewPlaceholder } from "@/plugin-host/PluginViewPlaceholder"; import { getTabType, type TabShellCallbacks, type TabTypeDef, } from "./tab-registry"; import { markAdaptiveResourceUsed, runAdaptiveBackgroundTask, } from "@/lib/adaptive-resource-budget"; // Heavy tab surfaces — keep out of the AppShell critical path. const CollabRoomTab = lazy(() => import("@/features/collab/CollabRoomTab").then((m) => ({ default: m.CollabRoomTab, })), ); const LocalTerminal = lazy(() => import("@/features/local-terminal/LocalTerminal").then((m) => ({ default: m.LocalTerminal, })), ); const loadTmuxMonitor = () => import("@/features/tmux-monitor/TmuxMonitor").then((m) => ({ default: m.TmuxMonitor, })); const TmuxMonitor = lazy(loadTmuxMonitor); const DashboardTab = lazy(() => import("@/dashboard/DashboardTab").then((m) => ({ default: m.DashboardTab, })), ); const HomepageCanvas = lazy(() => import("@/features/homepage/HomepageCanvas").then((m) => ({ default: m.HomepageCanvas, })), ); const Serial = lazy(() => import("@/features/serial/Serial").then((m) => ({ default: m.Serial, })), ); // Rail panels promoted to full tabs. const TermixIdPanel = lazy(() => import("@/sidebar/TermixIdPanel").then((m) => ({ default: m.TermixIdPanel })), ); const SessionLogsPanel = lazy(() => import("@/sidebar/SessionLogsPanel").then((m) => ({ default: m.SessionLogsPanel, })), ); const MacrosPanel = lazy(() => import("@/sidebar/MacrosPanel").then((m) => ({ default: m.MacrosPanel })), ); const HistoryPanel = lazy(() => import("@/sidebar/HistoryPanel").then((m) => ({ default: m.HistoryPanel })), ); const SshToolsPanel = lazy(() => import("@/sidebar/SshToolsPanel").then((m) => ({ default: m.SshToolsPanel })), ); const tabSurfaceLoaders: Partial Promise>> = { tmux_monitor: loadTmuxMonitor, }; /** Download a likely next tab without starting a connection or mounting UI. */ export function preloadTabSurface(type: TabType): void { const loader = tabSurfaceLoaders[type] ?? getTabType(type)?.preload; if (loader) runAdaptiveBackgroundTask("module", `tab:${type}`, loader); } export function markTabSurfaceUsed(type: TabType): void { markAdaptiveResourceUsed("module", `tab:${type}`); } function EmptyState({ icon: Icon, messageKey, }: { icon: React.ElementType; messageKey: string; }) { const { t } = useTranslation(); return (
{t(messageKey)}
); } function TabChunkFallback() { return (
); } function withTabSuspense(node: React.ReactNode) { return }>{node}; } /** * Host frame for rail panels opened as tabs. Panels expect a full-height flex * column like the sidebar gives them. The max width keeps forms readable on a * wide monitor instead of stretching them edge to edge. */ function PanelTabFrame({ children }: { children: React.ReactNode }) { return (
{children}
); } export function tabIcon(type: TabType) { switch (type) { case "dashboard": return ; case "local-terminal": return ; case "host-manager": return ; case "user-profile": return ; case "admin-settings": return ; // --- tmux-monitor --- case "tmux_monitor": return ; case "serial": return ; case "homepage": return ; case "collab": return ; case "termix-id": return ; case "session-logs": return ; case "macros": return ; case "history": return ; case "ssh-tools": return ; case "split-screen": return ; default: { const Icon = getTabType(type)?.icon; return Icon ? : null; } } } /** * A plugin's tab: its registered component with the generic render props. * Unregistered types get the ownership placeholder in renderTabContent. */ function RegisteredTab({ def, tab, isVisible, isFocusedPane, shell, }: { def: TabTypeDef; tab: Tab; isVisible: boolean; isFocusedPane: boolean; shell: TabShellCallbacks; }) { const { host, label } = tab; if (def.requiresHost && !host) { return ( ); } const Component = def.component; const content = ( ) : undefined } label={label} isVisible={isVisible} isFocusedPane={isFocusedPane} handleRef={tab.terminalRef as React.Ref} shell={shell} /> ); return withTabSuspense( def.panelFrame ? {content} : content, ); } /** * Everything the promoted rail panels need from AppShell. Passed as one bag * rather than more positional params, which renderTabContent already has too * many of. */ export type PromotedPanelProps = { terminalTabs?: Tab[]; targetTerminalTabId?: string; storageMode?: "local" | "cloud"; }; export interface TabRenderContext { shell: TabShellCallbacks; isVisible?: boolean; isFocusedPane?: boolean; panelProps?: PromotedPanelProps; } export function renderTabContent(tab: Tab, context: TabRenderContext) { const { shell, isVisible = true, isFocusedPane = true, panelProps } = context; const { host } = tab; switch (tab.type) { case "dashboard": return withTabSuspense( shell.openSingletonTab(type)} onOpenTab={(host, type) => shell.openTab(host, type)} isVisible={isVisible} />, ); case "local-terminal": return withTabSuspense( , ); // --- tmux-monitor --- case "tmux_monitor": return withTabSuspense( , ); case "serial": if (!tab.serialConfig) return ; return withTabSuspense( } config={tab.serialConfig} isVisible={isVisible} instanceId={tab.instanceId} />, ); case "homepage": return withTabSuspense(); case "collab": return withTabSuspense( , ); case "termix-id": return withTabSuspense( , ); case "session-logs": return withTabSuspense( , ); case "split-screen": return null; case "macros": return withTabSuspense( , ); case "history": return withTabSuspense( , ); case "ssh-tools": return withTabSuspense( , ); case "host-manager": case "user-profile": case "admin-settings": return null; default: { const def = getTabType(tab.type); if (!def) { return ; } return ( ); } } }