@datarecce/ui 1.40.1 → 1.40.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AuthModal-CboQ6ZoC.js +7 -0
- package/dist/AuthModal-CboQ6ZoC.js.map +1 -0
- package/dist/advanced.js +1 -1
- package/dist/components-run.js +1 -1
- package/dist/components.js +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +1 -1
- package/dist/{index-DVBYg7un.d.ts → index-BOLbDTy8.d.ts} +2 -2
- package/dist/{index-DVBYg7un.d.ts.map → index-BOLbDTy8.d.ts.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/primitives.js +1 -1
- package/dist/result.js +1 -1
- package/dist/{src-Bupu_4Ew.js → src-DPMlORPa.js} +2 -2
- package/dist/{src-Bupu_4Ew.js.map → src-DPMlORPa.js.map} +1 -1
- package/package.json +1 -1
- package/dist/AuthModal-DfjL0GZU.js +0 -7
- package/dist/AuthModal-DfjL0GZU.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"src-Bupu_4Ew.js","names":["ThemeProvider","BaseActionControl","BaseColumnLevelLineageControl","getIconForChangeStatus","GraphEdgeBase","Tooltip","RowCountDiffTag","Popover","TimelineEvent","RowCountDiffTag","getIconForChangeStatus","GraphEdge","getIconForChangeStatus","LineageViewContextMenu","useLineageViewContextMenu","useCopyToClipboard","EXPLORE_ACTION","EXPLORE_SOURCE","useMultiNodesActionBase","useBaseDialog","EXPLORE_ACTION","BaseLineageViewContextMenu","baseUseLineageViewContextMenu","BaseNodeSqlView","Tooltip","ResourceTypeTag","BaseSandboxView","RunResultPane","ResourceTypeTagBase","EXPLORE_ACTION","EXPLORE_SOURCE","BaseNodeView","BaseSetupConnectionBanner","LineageViewTopBarCore","useMultiNodesAction","useValueDiffAlertDialog","LineageViewTopBar","SetupConnectionBanner","NodeView","useTheme","Tooltip","formatTimestamp","saveAs","Dialog","MuiTabs","DisplayModeToggle","RecceVersionBadge","TopBar","NavBar","RunResultPane","LineageView","getIconForChangeStatus","LineageDiffView","CheckTimeline","CheckEmptyStateUI","CheckEmptyState","CheckList","CheckDetail","ResourceTypeTag"],"sources":["../src/providers/contexts/RoutingContext.tsx","../src/providers/RecceProvider.tsx","../src/components/lineage/controls/ActionControl.tsx","../src/components/lineage/controls/ColumnLevelLineageControl.tsx","../src/components/lineage/ActionControlOss.tsx","../src/components/lineage/ColumnLevelLineageControlOss.tsx","../src/components/lineage/GraphColumnNodeOss.tsx","../src/components/lineage/edges/GraphEdge.tsx","../src/components/lineage/GraphEdgeOss.tsx","../src/components/lineage/nodes/ActionTag.tsx","../src/components/check/timeline/TimelineEventOss.tsx","../src/components/check/timeline/CheckTimelineOss.tsx","../src/components/ui/SquareIcon.tsx","../src/components/query/QueryForm.tsx","../src/components/query/SetupConnectionGuide.tsx","../src/components/query/QueryPageOss.tsx","../src/components/lineage/GraphNodeOss.tsx","../src/components/lineage/config/nodeTypes.ts","../src/components/lineage/contextmenu/LineageViewContextMenu.tsx","../src/components/lineage/hooks/useLineageCopyToClipboard.ts","../src/components/lineage/hooks/useNavToCheck.ts","../src/components/lineage/hooks/useResizeObserver.ts","../src/components/lineage/hooks/useTrackLineageRender.ts","../src/hooks/useMultiNodesActionOss.ts","../src/hooks/useValueDiffAlertDialogOss.tsx","../src/components/notifications/LineageViewNotification.tsx","../src/components/lineage/LineageViewContextMenuOss.tsx","../src/components/lineage/lineage.ts","../src/components/schema/SchemaView.tsx","../src/components/lineage/NodeSqlView.tsx","../src/components/lineage/NodeSqlViewOss.tsx","../src/components/lineage/tags/tagStyles.ts","../src/components/lineage/tags/ResourceTypeTag.tsx","../src/components/lineage/NodeTag.tsx","../src/components/lineage/NodeView.tsx","../src/components/lineage/SandboxView.tsx","../src/components/lineage/SandboxViewOss.tsx","../src/components/lineage/NodeViewOss.tsx","../src/components/lineage/patchLineageDiffFromCll.ts","../src/components/lineage/SetupConnectionBanner.tsx","../src/components/lineage/SetupConnectionBannerOss.tsx","../src/components/lineage/SingleEnvironmentQueryView.tsx","../src/components/lineage/states/LineageViewStates.tsx","../src/components/shared/HistoryToggle.tsx","../src/components/lineage/topbar/LineageViewTopBar.tsx","../src/components/lineage/topbar/LineageViewTopBarOss.tsx","../src/components/lineage/LineageViewOss.tsx","../src/components/lineage/LineagePageOss.tsx","../src/components/app/AvatarDropdown.tsx","../src/components/app/DisplayModeToggleOss.tsx","../src/components/app/EnvInfo.tsx","../src/components/app/Filename.tsx","../src/components/app/PythonDeprecationModal.tsx","../src/components/app/StateExporter.tsx","../src/components/app/StateSharing.tsx","../src/components/app/StateSynchronizer.tsx","../src/components/app/NavBarOss.tsx","../src/components/timeout/IdleTimeoutBadge.tsx","../src/components/app/RecceVersionBadgeOss.tsx","../src/components/app/TopBarOss.tsx","../src/components/app/MainLayout.tsx","../src/components/app/SetupConnectionPopover.tsx","../src/components/app/StateImporter.tsx","../src/components/check/LineageDiffViewOss.tsx","../src/components/check/SchemaDiffView.tsx","../src/components/check/CheckDetailOss.tsx","../src/components/check/CheckEmptyStateOss.tsx","../src/components/check/CheckListOss.tsx","../src/components/check/CheckPageContentOss.tsx","../src/components/check/CheckPageLoadingOss.tsx","../src/components/summary/types.ts","../src/components/summary/utils.ts","../src/components/summary/ChangeSummary.tsx","../src/components/summary/SchemaSummary.tsx","../src/components/views/ChecksView.tsx","../src/index.ts"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n} from \"react\";\n\n/**\n * Navigation options for route changes\n */\nexport interface NavigateOptions {\n /** Replace current history entry instead of pushing */\n replace?: boolean;\n /** Scroll to top after navigation (default: true) */\n scroll?: boolean;\n}\n\n/**\n * Configuration for the routing provider\n */\nexport interface RoutingConfig {\n /** Base path prefix for all routes */\n basePath?: string;\n\n /**\n * Current pathname (provided by consumer's router)\n * If not provided, defaults to empty string\n */\n pathname?: string;\n\n /**\n * Navigation handler (provided by consumer's router)\n * Called when components need to navigate programmatically\n *\n * @example\n * ```tsx\n * // Next.js App Router\n * const router = useRouter();\n * <RecceProvider\n * routing={{\n * pathname: usePathname(),\n * onNavigate: (path, options) => {\n * options?.replace ? router.replace(path) : router.push(path);\n * }\n * }}\n * />\n * ```\n */\n onNavigate?: (path: string, options?: NavigateOptions) => void;\n}\n\n/**\n * Routing context value available to consumers\n */\nexport interface RoutingContextValue {\n /** Base path prefix */\n basePath: string;\n /** Build a full path with base path prefix */\n buildPath: (path: string) => string;\n /** Current pathname (if provided by consumer) */\n pathname: string;\n /** Navigate to a path */\n navigate: (path: string, options?: NavigateOptions) => void;\n}\n\nconst RoutingContext = createContext<RoutingContextValue | null>(null);\nRoutingContext.displayName = \"RecceRoutingContext\";\n\n/**\n * Hook to access routing context\n *\n * @returns Routing context with path utilities and navigation\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { pathname, navigate, buildPath } = useRouting();\n *\n * return (\n * <button onClick={() => navigate(buildPath('/checks'))}>\n * Go to Checks\n * </button>\n * );\n * }\n * ```\n */\nexport function useRouting(): RoutingContextValue {\n const context = useContext(RoutingContext);\n if (!context) {\n // Return sensible defaults if not within provider\n return {\n basePath: \"\",\n buildPath: (path: string) => path,\n pathname: \"\",\n navigate: (path: string) => {\n // Fallback: use window.location for navigation\n if (typeof window !== \"undefined\") {\n window.location.href = path;\n }\n },\n };\n }\n return context;\n}\n\n/**\n * Hook compatible with useAppLocation API\n *\n * Returns a tuple of [pathname, setLocation] for easy migration from\n * the OSS useAppLocation hook.\n *\n * @returns [pathname, navigate] tuple\n *\n * @example\n * ```tsx\n * const [location, setLocation] = useAppLocation();\n * setLocation('/checks?id=123');\n * setLocation('/checks', { replace: true });\n * ```\n */\nexport function useAppLocation(): [\n string,\n (path: string, options?: NavigateOptions) => void,\n] {\n const { pathname, navigate } = useRouting();\n return [pathname, navigate];\n}\n\ninterface RoutingProviderProps {\n children: ReactNode;\n config?: RoutingConfig;\n}\n\nexport function RoutingProvider({ children, config }: RoutingProviderProps) {\n const basePath = config?.basePath ?? \"\";\n const pathname = config?.pathname ?? \"\";\n const onNavigate = config?.onNavigate;\n\n const buildPath = useCallback(\n (path: string) => {\n if (!basePath) return path;\n return `${basePath}${path.startsWith(\"/\") ? path : `/${path}`}`;\n },\n [basePath],\n );\n\n const navigate = useCallback(\n (path: string, options?: NavigateOptions) => {\n if (onNavigate) {\n onNavigate(path, options);\n } else if (typeof window !== \"undefined\") {\n // Fallback: use window.location\n if (options?.replace) {\n window.history.replaceState(null, \"\", path);\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n } else {\n window.location.href = path;\n }\n }\n },\n [onNavigate],\n );\n\n const value = useMemo<RoutingContextValue>(\n () => ({\n basePath,\n buildPath,\n pathname,\n navigate,\n }),\n [basePath, buildPath, pathname, navigate],\n );\n\n return (\n <RoutingContext.Provider value={value}>{children}</RoutingContext.Provider>\n );\n}\n","\"use client\";\n\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport type { AxiosInstance } from \"axios\";\nimport { type ReactNode, useMemo } from \"react\";\n\nimport type { RecceActionProviderProps } from \"../contexts/action\";\nimport { RecceActionProvider } from \"../contexts/action\";\nimport { IdleTimeoutProvider } from \"../contexts/idle\";\nimport { RecceInstanceInfoProvider } from \"../contexts/instance\";\nimport type { LineageGraphProviderProps } from \"../contexts/lineage\";\nimport { LineageGraphProvider } from \"../contexts/lineage\";\nimport { ApiProvider } from \"./contexts/ApiContext\";\nimport type { Check, CheckProviderProps } from \"./contexts/CheckContext\";\nimport { CheckProvider } from \"./contexts/CheckContext\";\nimport type { QueryProviderProps, QueryResult } from \"./contexts/QueryContext\";\nimport { QueryProvider } from \"./contexts/QueryContext\";\nimport { RoutingProvider } from \"./contexts/RoutingContext\";\nimport { ThemeProvider } from \"./contexts/ThemeContext\";\n\n/**\n * Theme mode selection for RecceProvider.\n */\ntype ThemeMode = \"light\" | \"dark\" | \"system\";\n\n/**\n * Props for {@link RecceProvider}.\n */\ninterface RecceProviderProps {\n children: ReactNode;\n\n /**\n * API configuration - simple config OR custom client.\n */\n api:\n | {\n baseUrl: string;\n headers?: Record<string, string>;\n timeout?: number;\n }\n | {\n client: AxiosInstance;\n };\n\n /**\n * Theme mode.\n *\n * @defaultValue \"system\"\n */\n theme?: ThemeMode;\n\n /**\n * Routing configuration\n *\n * @example\n * ```tsx\n * // With Next.js App Router\n * const router = useRouter();\n * const pathname = usePathname();\n *\n * <RecceProvider\n * routing={{\n * basePath: '/app',\n * pathname,\n * onNavigate: (path, options) => {\n * options?.replace ? router.replace(path) : router.push(path);\n * }\n * }}\n * />\n * ```\n */\n routing?: {\n basePath?: string;\n pathname?: string;\n onNavigate?: (\n path: string,\n options?: { replace?: boolean; scroll?: boolean },\n ) => void;\n };\n\n /**\n * TanStack Query client configuration.\n */\n queryClient?: {\n staleTime?: number;\n gcTime?: number;\n };\n\n /**\n * Run action configuration\n * Props for RecceActionProvider - manages run execution and result display\n */\n runActions?: {\n /** Handler called when a run action is requested */\n onRunAction?: RecceActionProviderProps[\"onRunAction\"];\n /** Handler called when a run result should be shown */\n onShowRunId?: RecceActionProviderProps[\"onShowRunId\"];\n /** Initial run ID to display */\n initialRunId?: string;\n /** Initial state for history panel */\n initialHistoryOpen?: boolean;\n };\n\n /**\n * Lineage graph configuration\n * Props for LineageGraphProvider - manages lineage visualization\n */\n lineage?: {\n /** The processed lineage graph data */\n lineageGraph?: LineageGraphProviderProps[\"lineageGraph\"];\n /** Environment information (git, dbt, sqlmesh metadata) */\n envInfo?: LineageGraphProviderProps[\"envInfo\"];\n /** Whether in review mode (read-only checks) */\n reviewMode?: boolean;\n /** Whether in cloud mode (recce cloud) */\n cloudMode?: boolean;\n /** Whether in file mode (loading from file) */\n fileMode?: boolean;\n /** The state file name if in file mode */\n fileName?: string;\n /** Whether this is the demo site */\n isDemoSite?: boolean;\n /** Whether running in GitHub Codespace */\n isCodespace?: boolean;\n /** Loading state */\n isLoading?: boolean;\n /** Error message if loading failed */\n error?: string;\n /** Supported task types from server */\n supportTasks?: Record<string, boolean>;\n /** Callback to refetch the lineage graph */\n onRefetchLineageGraph?: () => void;\n /** Pre-aggregated run results by model */\n runsAggregated?: LineageGraphProviderProps[\"runsAggregated\"];\n /** Callback to refetch aggregated runs */\n onRefetchRunsAggregated?: () => void;\n };\n\n /**\n * Check management configuration\n * Props for CheckProvider - manages check CRUD operations\n */\n checks?: {\n /** List of checks */\n checks?: Check[];\n /** Loading state */\n isLoading?: boolean;\n /** Error message */\n error?: string;\n /** Currently selected check ID */\n selectedCheckId?: string;\n /** Handler for check selection */\n onSelectCheck?: CheckProviderProps[\"onSelectCheck\"];\n /** Handler for check creation */\n onCreateCheck?: CheckProviderProps[\"onCreateCheck\"];\n /** Handler for check updates */\n onUpdateCheck?: CheckProviderProps[\"onUpdateCheck\"];\n /** Handler for check deletion */\n onDeleteCheck?: CheckProviderProps[\"onDeleteCheck\"];\n /** Handler for check reordering */\n onReorderChecks?: CheckProviderProps[\"onReorderChecks\"];\n /** Callback to refetch checks */\n refetchChecks?: () => void;\n };\n\n /**\n * Query editor configuration\n * Props for QueryProvider - manages SQL query execution\n */\n query?: {\n /** Current SQL query */\n sql?: string;\n /** Whether query is executing */\n isExecuting?: boolean;\n /** Error message */\n error?: string;\n /** Query result for base environment */\n baseResult?: QueryResult;\n /** Query result for current environment */\n currentResult?: QueryResult;\n /** Handler for SQL changes */\n onSqlChange?: QueryProviderProps[\"onSqlChange\"];\n /** Handler for query execution */\n onExecute?: QueryProviderProps[\"onExecute\"];\n /** Handler for query cancellation */\n onCancel?: QueryProviderProps[\"onCancel\"];\n };\n\n /**\n * Feature flags - control which contexts are enabled\n * Set to false to skip a context entirely (reduces overhead if unused)\n */\n features?: {\n /** Enable RecceInstanceInfoProvider (instance info and feature toggles) */\n enableInstance?: boolean;\n /** Enable IdleTimeoutProvider (session timeout and keep-alive) */\n enableIdleTimeout?: boolean;\n /** Enable LineageGraphProvider (lineage visualization) */\n enableLineage?: boolean;\n /** Enable RecceActionProvider (run execution) */\n enableRunActions?: boolean;\n /** Enable CheckProvider (check management) */\n enableChecks?: boolean;\n /** Enable QueryProvider (SQL query editor) */\n enableQuery?: boolean;\n };\n}\n\n// Create a default query client\nconst createDefaultQueryClient = (\n options?: RecceProviderProps[\"queryClient\"],\n) =>\n new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: options?.staleTime ?? 1000 * 60, // 1 minute\n gcTime: options?.gcTime ?? 1000 * 60 * 5, // 5 minutes\n retry: 1,\n refetchOnWindowFocus: false,\n },\n },\n });\n\n/**\n * RecceProvider - The single entry point for @datarecce/ui\n *\n * This provider orchestrates ALL Recce contexts in the correct order.\n * It follows a props-driven design - pass your data and callbacks, no internal fetching.\n *\n * **Architecture:**\n * - Foundation layer: QueryClient → API → Theme → Routing\n * - Data layer: Instance → Idle → Lineage → RunActions\n * - UI layer: Checks → Query\n *\n * **Feature Flags:**\n * Use `features` prop to disable unused contexts and reduce overhead.\n *\n * @example\n * ```tsx\n * // Minimal setup (only API + Theme)\n * <RecceProvider api={{ baseUrl: \"/api\" }}>\n * <MyApp />\n * </RecceProvider>\n *\n * // Full setup with all features\n * <RecceProvider\n * api={{ baseUrl: \"/api\" }}\n * theme=\"dark\"\n * lineage={{\n * lineageGraph: data.lineageGraph,\n * envInfo: data.envInfo,\n * onRefetchLineageGraph: refetch,\n * }}\n * runActions={{\n * onRunAction: handleRunAction,\n * onShowRunId: handleShowRun,\n * }}\n * checks={{\n * checks: checksData,\n * onCreateCheck: createCheck,\n * onUpdateCheck: updateCheck,\n * }}\n * features={{\n * enableLineage: true,\n * enableRunActions: true,\n * enableChecks: true,\n * }}\n * >\n * <MyApp />\n * </RecceProvider>\n * ```\n */\nexport function RecceProvider({\n children,\n api,\n theme = \"system\",\n routing,\n queryClient: queryClientConfig,\n runActions,\n lineage,\n checks,\n query,\n features = {},\n}: RecceProviderProps) {\n // Extract primitive values to stabilize dependency and prevent unnecessary QueryClient recreation\n const staleTime = queryClientConfig?.staleTime;\n const gcTime = queryClientConfig?.gcTime;\n\n const queryClient = useMemo(\n () =>\n createDefaultQueryClient({\n staleTime,\n gcTime,\n }),\n [staleTime, gcTime],\n );\n\n // Default all features to true unless explicitly disabled\n const {\n enableInstance = true,\n enableIdleTimeout = true,\n enableLineage = true,\n enableRunActions = true,\n enableChecks = true,\n enableQuery = true,\n } = features;\n\n // Foundation layer: QueryClient → API → Theme → Routing\n let tree = children;\n\n // Wrap with Routing (innermost of foundation layer)\n tree = <RoutingProvider config={routing}>{tree}</RoutingProvider>;\n\n // Wrap with Theme\n tree = <ThemeProvider defaultMode={theme}>{tree}</ThemeProvider>;\n\n // Wrap with API\n tree = <ApiProvider config={api}>{tree}</ApiProvider>;\n\n // Wrap with QueryClient (outermost of foundation layer)\n tree = <QueryClientProvider client={queryClient}>{tree}</QueryClientProvider>;\n\n // Data layer: Instance → Idle → Lineage → RunActions\n // These are wrapped in reverse order (bottom-up)\n\n // UI layer: Query (innermost)\n if (enableQuery) {\n tree = (\n <QueryProvider\n sql={query?.sql}\n isExecuting={query?.isExecuting}\n error={query?.error}\n baseResult={query?.baseResult}\n currentResult={query?.currentResult}\n onSqlChange={query?.onSqlChange}\n onExecute={query?.onExecute}\n onCancel={query?.onCancel}\n >\n {tree}\n </QueryProvider>\n );\n }\n\n // UI layer: Checks\n if (enableChecks) {\n tree = (\n <CheckProvider\n checks={checks?.checks}\n isLoading={checks?.isLoading}\n error={checks?.error}\n selectedCheckId={checks?.selectedCheckId}\n onSelectCheck={checks?.onSelectCheck}\n onCreateCheck={checks?.onCreateCheck}\n onUpdateCheck={checks?.onUpdateCheck}\n onDeleteCheck={checks?.onDeleteCheck}\n onReorderChecks={checks?.onReorderChecks}\n refetchChecks={checks?.refetchChecks}\n >\n {tree}\n </CheckProvider>\n );\n }\n\n // Data layer: RunActions\n if (enableRunActions) {\n tree = (\n <RecceActionProvider\n onRunAction={runActions?.onRunAction}\n onShowRunId={runActions?.onShowRunId}\n initialRunId={runActions?.initialRunId}\n initialHistoryOpen={runActions?.initialHistoryOpen}\n >\n {tree}\n </RecceActionProvider>\n );\n }\n\n // Data layer: Lineage\n if (enableLineage) {\n tree = (\n <LineageGraphProvider\n lineageGraph={lineage?.lineageGraph}\n envInfo={lineage?.envInfo}\n reviewMode={lineage?.reviewMode}\n cloudMode={lineage?.cloudMode}\n fileMode={lineage?.fileMode}\n fileName={lineage?.fileName}\n isDemoSite={lineage?.isDemoSite}\n isCodespace={lineage?.isCodespace}\n isLoading={lineage?.isLoading}\n error={lineage?.error}\n supportTasks={lineage?.supportTasks}\n onRefetchLineageGraph={lineage?.onRefetchLineageGraph}\n runsAggregated={lineage?.runsAggregated}\n onRefetchRunsAggregated={lineage?.onRefetchRunsAggregated}\n >\n {tree}\n </LineageGraphProvider>\n );\n }\n\n // Data layer: Idle (depends on Instance for idle timeout config)\n if (enableIdleTimeout) {\n tree = <IdleTimeoutProvider>{tree}</IdleTimeoutProvider>;\n }\n\n // Data layer: Instance (outermost, provides feature toggles for all others)\n if (enableInstance) {\n tree = <RecceInstanceInfoProvider>{tree}</RecceInstanceInfoProvider>;\n }\n\n return tree;\n}\n\nexport type { RecceProviderProps };\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Divider from \"@mui/material/Divider\";\nimport Stack from \"@mui/material/Stack\";\nimport type { ActionState } from \"../../../contexts/lineage/types\";\n\n/**\n * Props for the ActionControl component.\n */\nexport interface ActionControlProps {\n /**\n * The current action state containing progress, status, and mode information\n */\n actionState: ActionState;\n\n /**\n * Callback invoked when the user clicks the Cancel button\n */\n onCancel: () => void;\n\n /**\n * Callback invoked when the user clicks the Close button (after action completes)\n */\n onClose: () => void;\n}\n\n/**\n * ActionControl Component\n *\n * Displays progress information and control buttons for batch operations\n * on lineage graph nodes. Shows different UI based on action status:\n * - Running/Canceling: Shows progress with Cancel button\n * - Completed/Canceled: Shows progress with Close button\n *\n * This is a props-based component that doesn't use context directly.\n * For context-aware usage, wrap this component with context consumers.\n *\n * @example Basic usage\n * ```tsx\n * import { ActionControl } from '@datarecce/ui/components/lineage';\n *\n * function MyComponent() {\n * const [actionState, setActionState] = useState<ActionState>({\n * mode: 'per_node',\n * status: 'running',\n * completed: 5,\n * total: 10,\n * actions: {},\n * });\n *\n * return (\n * <ActionControl\n * actionState={actionState}\n * onCancel={() => setActionState(prev => ({ ...prev, status: 'canceling' }))}\n * onClose={() => console.log('closed')}\n * />\n * );\n * }\n * ```\n *\n * @example With context wrapper (OSS pattern)\n * ```tsx\n * import { ActionControl as BaseActionControl } from '@datarecce/ui/components/lineage';\n * import { useLineageViewContext } from './LineageViewContext';\n *\n * function ActionControl({ onClose }: { onClose: () => void }) {\n * const { cancel, actionState } = useLineageViewContext();\n * return <BaseActionControl actionState={actionState} onCancel={cancel} onClose={onClose} />;\n * }\n * ```\n */\nexport function ActionControl({\n actionState,\n onCancel,\n onClose,\n}: ActionControlProps) {\n /**\n * Calculate the progress message based on action mode and status\n */\n const getProgressMessage = () => {\n if (actionState.mode === \"per_node\") {\n return `${actionState.completed} / ${actionState.total}`;\n }\n // multi_nodes mode\n if (actionState.currentRun?.progress?.percentage) {\n return `${actionState.currentRun.progress.percentage * 100}%`;\n }\n if (actionState.status === \"completed\") {\n return \"100%\";\n }\n return \"0%\";\n };\n\n const isActionInProgress =\n actionState.status === \"running\" || actionState.status === \"canceling\";\n\n return (\n <Box sx={{ bgcolor: \"background.paper\", borderRadius: 1, boxShadow: 6 }}>\n <Stack\n direction=\"row\"\n divider={<Divider orientation=\"vertical\" flexItem />}\n spacing={2}\n sx={{ p: \"5px 15px\", mt: 2 }}\n >\n <Box sx={{ fontSize: \"10pt\" }}>\n Progress: {getProgressMessage()}{\" \"}\n {actionState.status === \"canceled\" ? \" (canceled)\" : \"\"}\n </Box>\n\n {isActionInProgress ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={onCancel}\n disabled={actionState.status === \"canceling\"}\n >\n {actionState.status === \"canceling\" ? \"Canceling\" : \"Cancel\"}\n </Button>\n ) : (\n <Stack direction=\"row\">\n <Button size=\"small\" variant=\"outlined\" onClick={onClose}>\n Close\n </Button>\n </Stack>\n )}\n </Stack>\n </Box>\n );\n}\n","\"use client\";\n\n/**\n * @file ColumnLevelLineageControl.tsx\n * @description Control panel for Column-Level Lineage (CLL) and Impact Radius features.\n *\n * This component provides:\n * - Impact Radius button to analyze downstream impact of changes\n * - Mode message panel showing current CLL context\n * - Loading and error states for CLL operations\n * - Reset button to exit CLL mode\n *\n * @example\n * ```tsx\n * <ColumnLevelLineageControl\n * action={cllMutation}\n * interactive={true}\n * viewOptions={viewOptions}\n * lineageGraph={lineageGraph}\n * singleEnvMode={false}\n * onShowCll={(params) => showColumnLevelLineage(params)}\n * onResetCll={() => resetColumnLevelLineage()}\n * onCenterNode={(nodeId) => centerNode(nodeId)}\n * />\n * ```\n */\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Link from \"@mui/material/Link\";\nimport MuiPopover from \"@mui/material/Popover\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport type { UseMutationResult } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport { FaRegDotCircle } from \"react-icons/fa\";\nimport { PiInfo, PiX } from \"react-icons/pi\";\nimport type { CllInput, ColumnLineageData } from \"../../../api/cll\";\nimport type { LineageDiffViewOptions } from \"../../../api/lineagecheck\";\nimport type { LineageGraph } from \"../../../contexts/lineage/types\";\nimport { useIsDark } from \"../../../hooks/useIsDark\";\n\n/**\n * Props for the ColumnLevelLineageControl component.\n */\nexport interface ColumnLevelLineageControlProps {\n /**\n * Mutation result for CLL operations.\n * Used to track loading, error, and success states.\n */\n action: UseMutationResult<ColumnLineageData, Error, CllInput>;\n\n /**\n * Whether the view is interactive (allows user actions).\n * When false, buttons are disabled.\n */\n interactive: boolean;\n\n /**\n * Current view options including column_level_lineage settings.\n */\n viewOptions: LineageDiffViewOptions;\n\n /**\n * The lineage graph data containing nodes and catalog metadata.\n * Used to determine node names and catalog availability.\n */\n lineageGraph?: LineageGraph;\n\n /**\n * Whether in single environment mode.\n * When true, the Impact Radius button is hidden.\n */\n singleEnvMode?: boolean;\n\n /**\n * Whether change analysis (Impact Radius) is available.\n * When false, the Impact Radius button is disabled with a tooltip.\n */\n changeAnalysisAvailable?: boolean;\n\n /**\n * Callback to show column-level lineage.\n * Called with CLL parameters when Impact Radius button is clicked.\n */\n onShowCll: (params?: CllInput) => Promise<void>;\n\n /**\n * Callback to reset column-level lineage view.\n */\n onResetCll: () => Promise<void>;\n\n /**\n * Callback to center the view on a specific node.\n */\n onCenterNode: (nodeId: string) => void;\n\n /**\n * Callback to set change analysis mode on/off.\n */\n setChangeAnalysisMode?: (active: boolean) => void;\n}\n\n/**\n * Internal component to display mode-specific messages.\n * Shows what CLL context is currently active.\n */\nconst ModeMessage = ({\n lineageGraph,\n cllInput,\n onCenterNode,\n}: {\n lineageGraph?: LineageGraph;\n cllInput?: CllInput;\n onCenterNode: (nodeId: string) => void;\n}) => {\n const isDark = useIsDark();\n\n const codeBlockSx = {\n cursor: \"pointer\",\n fontFamily: \"monospace\",\n bgcolor: isDark ? \"grey.700\" : \"grey.100\",\n px: 0.5,\n borderRadius: 0.5,\n };\n\n if (!lineageGraph) {\n return <></>;\n }\n\n if (!cllInput) {\n return <>Default View</>;\n }\n\n if (cllInput.node_id === undefined) {\n return (\n <Typography component=\"span\">\n Impact Radius for All Changed Models\n </Typography>\n );\n }\n\n const nodeName =\n cllInput.node_id in lineageGraph.nodes\n ? lineageGraph.nodes[cllInput.node_id].data.name\n : cllInput.node_id;\n\n if (!cllInput.column) {\n const nodeId = cllInput.node_id;\n\n return (\n <>\n <Typography component=\"span\" sx={{ mr: \"5px\" }}>\n Impact Radius for\n </Typography>\n <Box\n component=\"code\"\n onClick={() => {\n onCenterNode(nodeId);\n }}\n sx={codeBlockSx}\n >\n {nodeName}\n </Box>\n </>\n );\n }\n const nodeId = `${cllInput.node_id}_${cllInput.column}`;\n return (\n <>\n <Typography component=\"span\" sx={{ mr: \"5px\" }}>\n Column Lineage for{\" \"}\n </Typography>\n <Box\n component=\"code\"\n onClick={() => {\n onCenterNode(nodeId);\n }}\n sx={codeBlockSx}\n >\n {nodeName}.{cllInput.column}\n </Box>\n </>\n );\n};\n\n/**\n * Control panel for Column-Level Lineage (CLL) and Impact Radius features.\n *\n * Features:\n * - **Impact Radius Button**: Triggers analysis of downstream impact for changed models\n * - **Mode Message Panel**: Shows current CLL context (node, column, or all changes)\n * - **Loading State**: Displays spinner during CLL operations\n * - **Error State**: Shows error indicator with tooltip for failure details\n * - **Reset Button**: Allows exiting CLL mode to return to default view\n *\n * The component is designed for dependency injection, receiving all callbacks\n * and state as props to enable reuse across different contexts.\n */\nexport const ColumnLevelLineageControl = ({\n action,\n interactive,\n viewOptions,\n lineageGraph,\n singleEnvMode = false,\n changeAnalysisAvailable = true,\n onShowCll,\n onResetCll,\n onCenterNode,\n setChangeAnalysisMode,\n}: ColumnLevelLineageControlProps) => {\n const cllInput = viewOptions.column_level_lineage;\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n\n return (\n <Stack direction=\"row\" spacing=\"5px\">\n {!singleEnvMode && (\n <Box sx={{ borderRadius: 1, boxShadow: 3 }}>\n <MuiTooltip\n enterDelay={50}\n title={\n !changeAnalysisAvailable\n ? \"Requires warehouse connection\"\n : noCatalogCurrent\n ? \"Please provide catalog.json to enable Impact Radius\"\n : \"\"\n }\n placement=\"top\"\n >\n <span>\n <Button\n size=\"small\"\n variant=\"outlined\"\n color=\"neutral\"\n sx={{\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n bgcolor: \"background.paper\",\n }}\n disabled={\n !interactive || noCatalogCurrent || !changeAnalysisAvailable\n }\n startIcon={<FaRegDotCircle />}\n onClick={() => {\n setChangeAnalysisMode?.(true);\n void onShowCll({\n no_upstream: true,\n change_analysis: true,\n });\n }}\n >\n Impact Radius\n </Button>\n </span>\n </MuiTooltip>\n </Box>\n )}\n {cllInput && (\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{\n borderRadius: 1,\n boxShadow: 3,\n border: \"1px solid\",\n borderColor: \"divider\",\n bgcolor: \"background.paper\",\n fontSize: \"0.8rem\",\n p: \"0 0.625rem\",\n }}\n >\n <ModeMessage\n lineageGraph={lineageGraph}\n cllInput={cllInput}\n onCenterNode={onCenterNode}\n />\n {action.isError && (\n <MuiTooltip\n title={`Error: ${action.error.message}`}\n placement=\"bottom\"\n >\n <Typography\n component=\"span\"\n sx={{\n color: \"error.main\",\n ml: \"2px\",\n display: \"inline-flex\",\n alignItems: \"center\",\n }}\n >\n <Box\n component={PiInfo}\n sx={{ color: \"error.main\", fontSize: \"14px\" }}\n />\n </Typography>\n </MuiTooltip>\n )}\n\n {action.isPending ? (\n <CircularProgress size={12} sx={{ ml: \"2px\" }} />\n ) : (\n <IconButton\n size=\"small\"\n sx={{ ml: \"2px\" }}\n aria-label=\"Reset Column Level Lineage\"\n onClick={() => {\n void onResetCll();\n }}\n >\n <PiX size=\"10px\" />\n </IconButton>\n )}\n </Stack>\n )}\n </Stack>\n );\n};\n","\"use client\";\n\n/**\n * @file ActionControlOss.tsx\n * @description OSS wrapper around @datarecce/ui ActionControl\n */\n\nimport { useLineageViewContextSafe } from \"../../contexts\";\nimport { ActionControl as BaseActionControl } from \"./controls\";\n\n/**\n * Props for the ActionControl wrapper component.\n */\nexport interface ActionControlOssProps {\n /**\n * Callback invoked when the user clicks the Close button (after action completes)\n */\n onClose: () => void;\n}\n\n/**\n * ActionControl Component (Wrapper)\n *\n * Wraps the @datarecce/ui ActionControl component with context from\n * LineageViewContext.\n */\nexport function ActionControlOss({ onClose }: ActionControlOssProps) {\n const { cancel, actionState } = useLineageViewContextSafe();\n\n return (\n <BaseActionControl\n actionState={actionState}\n onCancel={cancel}\n onClose={onClose}\n />\n );\n}\n","\"use client\";\n\n/**\n * @file ColumnLevelLineageControlOss.tsx\n * @description OSS wrapper around @datarecce/ui ColumnLevelLineageControl\n *\n * This wrapper:\n * 1. Connects the @datarecce/ui component to specific contexts\n * 2. Provides LineageViewContext callbacks as props\n * 3. Fetches server flags for single_env_onboarding\n */\n\nimport type { UseMutationResult } from \"@tanstack/react-query\";\nimport type { CllInput, ColumnLineageData } from \"../../api\";\nimport {\n useLineageGraphContext,\n useLineageViewContextSafe,\n useRecceServerFlag,\n} from \"../../contexts\";\nimport { ColumnLevelLineageControl as BaseColumnLevelLineageControl } from \"./controls\";\n\n/**\n * OSS wrapper for ColumnLevelLineageControl.\n *\n * Connects the @datarecce/ui component to:\n * - LineageViewContext for CLL operations\n * - LineageGraphContext for graph data\n * - Server flags for environment mode\n */\nexport const ColumnLevelLineageControlOss = ({\n action,\n}: {\n action: UseMutationResult<ColumnLineageData, Error, CllInput>;\n}) => {\n const {\n showColumnLevelLineage,\n resetColumnLevelLineage,\n interactive,\n viewOptions,\n centerNode,\n setChangeAnalysisMode,\n } = useLineageViewContextSafe();\n const { data: flagData } = useRecceServerFlag();\n const singleEnv = flagData?.single_env_onboarding ?? false;\n const { lineageGraph, isActionAvailable } = useLineageGraphContext();\n\n return (\n <BaseColumnLevelLineageControl\n action={action}\n interactive={interactive}\n viewOptions={viewOptions}\n lineageGraph={lineageGraph}\n singleEnvMode={singleEnv}\n changeAnalysisAvailable={isActionAvailable(\"change_analysis\")}\n onShowCll={showColumnLevelLineage}\n onResetCll={() => resetColumnLevelLineage()}\n onCenterNode={centerNode}\n setChangeAnalysisMode={setChangeAnalysisMode}\n />\n );\n};\n","\"use client\";\n\n/**\n * @file GraphColumnNodeOss.tsx\n * @description OSS wrapper for UI package LineageColumnNode component\n *\n * This component wraps the @datarecce/ui LineageColumnNode with OSS-specific\n * context integration. It extracts state from LineageViewContext and passes\n * it as props to the presentation component.\n *\n * Migration: Phase 3 of lineage component migration plan\n */\n\nimport type { NodeProps } from \"@xyflow/react\";\nimport { useStore } from \"@xyflow/react\";\nimport { type MouseEvent, memo } from \"react\";\nimport type { LineageGraphColumnNode } from \"../..\";\nimport { useLineageViewContextSafe } from \"../../contexts\";\nimport { useThemeColors } from \"../../hooks\";\nimport { LineageColumnNode, type LineageColumnNodeData } from \"./columns\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport type GraphColumnNodeProps = NodeProps<LineageGraphColumnNode>;\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * GraphColumnNode - OSS wrapper for UI package LineageColumnNode\n *\n * This component integrates LineageViewContext with the pure presentation\n * LineageColumnNode from @datarecce/ui.\n */\nfunction GraphColumnNodeComponent(nodeProps: GraphColumnNodeProps) {\n const { id: columnNodeId, data } = nodeProps;\n const { id: nodeId } = data.node;\n const { column, type, transformationType, changeStatus } = data;\n\n // Get zoom level for content visibility\n const showContent = useStore((s) => s.transform[2] > 0.3);\n\n // Get theme colors\n const { isDark } = useThemeColors();\n\n // Get context values\n const {\n viewOptions,\n showContextMenu,\n isNodeHighlighted,\n isNodeShowingChangeAnalysis,\n } = useLineageViewContextSafe();\n\n // Computed state\n const selectedNode = viewOptions.column_level_lineage?.node_id;\n const selectedColumn = viewOptions.column_level_lineage?.column;\n const isFocused = column === selectedColumn && nodeId === selectedNode;\n const isHighlighted = isNodeHighlighted(columnNodeId);\n const isShowingChangeAnalysis = isNodeShowingChangeAnalysis(nodeId);\n\n // Build LineageColumnNodeData\n const columnData: LineageColumnNodeData = {\n column,\n type,\n nodeId,\n transformationType:\n transformationType as LineageColumnNodeData[\"transformationType\"],\n changeStatus: changeStatus as LineageColumnNodeData[\"changeStatus\"],\n isHighlighted,\n isFocused,\n };\n\n // Callbacks\n const handleContextMenu = (event: MouseEvent, _columnId: string) => {\n showContextMenu(event, nodeProps as unknown as LineageGraphColumnNode);\n };\n\n return (\n <LineageColumnNode\n id={columnNodeId}\n data={columnData}\n showContent={showContent}\n showChangeAnalysis={isShowingChangeAnalysis}\n isDark={isDark}\n onContextMenu={handleContextMenu}\n />\n );\n}\n\nexport const GraphColumnNode = memo(GraphColumnNodeComponent);\nGraphColumnNode.displayName = \"GraphColumnNode\";\n","\"use client\";\n\n/**\n * @file GraphEdge.tsx\n * @description Graph edge component with dependency injection for highlighting\n *\n * This component renders a bezier edge in a React Flow graph with support for:\n * - Change status styling (added, removed edges)\n * - Highlighting via dependency injection\n * - Style customization\n *\n * The component uses dependency injection for context-dependent behavior,\n * allowing the OSS wrapper to inject its own highlighting logic.\n */\n\nimport { BaseEdge, type EdgeProps, getBezierPath } from \"@xyflow/react\";\nimport { memo } from \"react\";\nimport type { LineageGraphEdge } from \"../../../contexts/lineage/types\";\nimport { type ChangeStatus, getIconForChangeStatus } from \"../styles\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/**\n * Data structure for graph edge - re-exported from LineageGraphEdge for convenience\n */\nexport type GraphEdgeData = LineageGraphEdge[\"data\"];\n\n/**\n * Edge type for React Flow - uses LineageGraphEdge from contexts\n */\nexport type GraphEdgeType = LineageGraphEdge;\n\n/**\n * Props for the GraphEdge component with dependency injection\n */\nexport interface GraphEdgeProps extends EdgeProps<LineageGraphEdge> {\n /**\n * Dependency injection: function to determine if an edge is highlighted.\n * When not provided, the edge is always highlighted (fully visible).\n *\n * @param source - Source node ID\n * @param target - Target node ID\n * @returns Whether the edge should be highlighted\n *\n * @example\n * ```tsx\n * // Inject highlighting from context\n * const { isEdgeHighlighted } = useLineageViewContext();\n * <GraphEdge {...props} isEdgeHighlighted={isEdgeHighlighted} />\n * ```\n */\n isEdgeHighlighted?: (source: string, target: string) => boolean;\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Graph edge component for lineage visualization\n *\n * Renders a bezier edge with:\n * - Color based on change status (green for added, red for removed)\n * - Dashed line for changed edges\n * - Dimmed appearance for non-highlighted edges\n *\n * @param props - Edge props including source/target coordinates and data\n * @returns Rendered SVG edge element\n *\n * @example\n * ```tsx\n * // Basic usage (always highlighted)\n * <GraphEdge {...edgeProps} />\n *\n * // With dependency injection for highlighting\n * <GraphEdge\n * {...edgeProps}\n * isEdgeHighlighted={(source, target) => highlightedEdges.has(`${source}-${target}`)}\n * />\n * ```\n */\nfunction GraphEdgeComponent(props: GraphEdgeProps) {\n const {\n source,\n target,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n style: styleOverride = {},\n markerEnd,\n data,\n isEdgeHighlighted,\n } = props;\n\n const style: React.CSSProperties = {\n ...styleOverride,\n };\n\n // Apply change status styling\n if (data?.changeStatus) {\n // Cast to ChangeStatus from styles module (added, removed, modified, unchanged)\n const statusStyle = getIconForChangeStatus(\n data.changeStatus as ChangeStatus,\n );\n style.stroke = statusStyle.hexColor;\n style.strokeDasharray = \"5\";\n }\n\n // Apply highlighting filter via dependency injection\n // Default to highlighted (true) if no function provided\n const isHighlighted = isEdgeHighlighted\n ? isEdgeHighlighted(source, target)\n : true;\n\n if (!isHighlighted) {\n style.filter = \"opacity(0.2) grayscale(50%)\";\n }\n\n const [edgePath] = getBezierPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n });\n\n return (\n <>\n <BaseEdge\n path={edgePath}\n markerEnd={markerEnd}\n style={{ ...style, ...styleOverride }}\n />\n </>\n );\n}\n\n/**\n * Memoized GraphEdge component for performance optimization\n */\nexport const GraphEdge = memo(GraphEdgeComponent);\nGraphEdge.displayName = \"GraphEdge\";\n","\"use client\";\n\n/**\n * @file GraphEdgeOss.tsx\n * @description wrapper for GraphEdge that injects LineageViewContext dependencies\n *\n * This thin wrapper imports the core GraphEdge component from @datarecce/ui\n * and injects dependencies:\n * - isEdgeHighlighted from LineageViewContext\n */\n\nimport type { EdgeProps } from \"@xyflow/react\";\nimport type { LineageGraphEdge } from \"../..\";\nimport { useLineageViewContextSafe } from \"../../contexts\";\nimport { GraphEdge as GraphEdgeBase } from \"./edges\";\n\nimport \"../../styles\";\n\ntype GraphEdgeProps = EdgeProps<LineageGraphEdge>;\n\n/**\n * OSS GraphEdge component that wraps the UI package's GraphEdge\n * with context-based dependency injection.\n *\n * Injects:\n * - isEdgeHighlighted: from LineageViewContext for context-aware highlighting\n */\nexport default function GraphEdgeOss(props: GraphEdgeProps) {\n const { isEdgeHighlighted } = useLineageViewContextSafe();\n\n return <GraphEdgeBase {...props} isEdgeHighlighted={isEdgeHighlighted} />;\n}\n","\"use client\";\n\n/**\n * @file ActionTag.tsx\n * @description Pure presentation component for displaying action status in lineage nodes\n *\n * This component displays the status of an action (pending, running, skipped, error, or result).\n * It is designed to be used with lineage graph nodes and receives all data via props.\n *\n * Source: Simplified from OSS js/src/components/lineage/ActionTag.tsx\n */\n\nimport Box from \"@mui/material/Box\";\nimport Chip from \"@mui/material/Chip\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Stack from \"@mui/material/Stack\";\nimport Tooltip from \"@mui/material/Tooltip\";\nimport { memo } from \"react\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/**\n * Action status values\n */\nexport type ActionStatus =\n | \"pending\"\n | \"running\"\n | \"skipped\"\n | \"success\"\n | \"error\";\n\n/**\n * Progress information for running actions\n */\nexport interface ActionProgress {\n /** Progress percentage (0-1) */\n percentage?: number;\n /** Current step description */\n message?: string;\n}\n\n/**\n * Value diff result summary\n */\nexport interface ValueDiffResult {\n /** Number of mismatched columns */\n mismatchedColumns: number;\n /** Total number of columns compared */\n totalColumns: number;\n}\n\n/**\n * Row count diff result summary\n */\nexport interface RowCountDiffResult {\n /** Base row count (null if not available) */\n base: number | null;\n /** Current row count (null if not available) */\n current: number | null;\n}\n\n/**\n * Props for ActionTag component\n */\nexport interface ActionTagProps {\n /** Current status of the action */\n status: ActionStatus;\n /** Skip reason if status is 'skipped' */\n skipReason?: string;\n /** Error message if status is 'error' */\n errorMessage?: string;\n /** Progress info if status is 'running' */\n progress?: ActionProgress;\n /** Value diff result if this is a value diff action */\n valueDiffResult?: ValueDiffResult;\n /** Row count diff result if this is a row count diff action */\n rowCountDiffResult?: RowCountDiffResult;\n /** Run ID to display as fallback */\n runId?: string;\n /** Test ID for testing */\n \"data-testid\"?: string;\n}\n\n// =============================================================================\n// ICON COMPONENTS\n// =============================================================================\n\n/**\n * Info icon for tooltips\n */\nconst InfoIcon = () => (\n <svg\n stroke=\"currentColor\"\n fill=\"currentColor\"\n strokeWidth=\"0\"\n viewBox=\"0 0 256 256\"\n height=\"1em\"\n width=\"1em\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z\" />\n </svg>\n);\n\n/**\n * Warning icon for error states\n */\nconst WarningIcon = () => (\n <svg\n stroke=\"currentColor\"\n fill=\"currentColor\"\n strokeWidth=\"0\"\n viewBox=\"0 0 256 256\"\n height=\"1em\"\n width=\"1em\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z\" />\n </svg>\n);\n\n// =============================================================================\n// HELPER COMPONENTS\n// =============================================================================\n\n/**\n * Skipped status display\n */\nfunction SkippedTag({ skipReason }: { skipReason?: string }) {\n return (\n <Chip\n size=\"small\"\n label={\n <Stack\n direction=\"row\"\n sx={{\n fontSize: \"10pt\",\n color: \"grey.500\",\n alignItems: \"center\",\n gap: \"3px\",\n }}\n >\n <Box>Skipped</Box>\n {skipReason && (\n <Tooltip title={skipReason}>\n <Box component=\"span\" sx={{ display: \"flex\" }}>\n <InfoIcon />\n </Box>\n </Tooltip>\n )}\n </Stack>\n }\n sx={{ bgcolor: \"grey.100\" }}\n />\n );\n}\n\n/**\n * Error status display\n */\nfunction ErrorTag({ errorMessage }: { errorMessage?: string }) {\n return (\n <Stack\n direction=\"row\"\n sx={{ fontSize: \"10pt\", color: \"gray\", alignItems: \"center\" }}\n >\n <Box>Error</Box>\n {errorMessage && (\n <Tooltip title={errorMessage}>\n <Box component=\"span\" sx={{ display: \"flex\" }}>\n <WarningIcon />\n </Box>\n </Tooltip>\n )}\n </Stack>\n );\n}\n\n/**\n * Value diff result display\n */\nfunction ValueDiffTag({ result }: { result: ValueDiffResult }) {\n const { mismatchedColumns } = result;\n const hasIssues = mismatchedColumns > 0;\n\n return (\n <Chip\n size=\"small\"\n sx={{\n bgcolor: hasIssues ? \"error.light\" : \"success.light\",\n }}\n label={\n <Stack\n direction=\"row\"\n sx={{\n fontSize: \"10pt\",\n color: hasIssues ? \"error.main\" : \"success.main\",\n alignItems: \"center\",\n gap: \"3px\",\n }}\n >\n {hasIssues\n ? `${mismatchedColumns} columns mismatched`\n : \"All columns match\"}\n </Stack>\n }\n />\n );\n}\n\n/**\n * Row count diff result display\n */\nfunction RowCountDiffTag({ result }: { result: RowCountDiffResult }) {\n const { base, current } = result;\n const baseLabel = base === null ? \"N/A\" : base.toLocaleString();\n const currentLabel = current === null ? \"N/A\" : current.toLocaleString();\n\n // Determine change direction\n let changeIndicator = \"\";\n let changeColor = \"grey.500\";\n\n if (base !== null && current !== null) {\n if (current > base) {\n changeIndicator = \"↑\";\n changeColor = \"success.main\";\n } else if (current < base) {\n changeIndicator = \"↓\";\n changeColor = \"error.main\";\n } else {\n changeIndicator = \"=\";\n }\n }\n\n return (\n <Chip\n size=\"small\"\n sx={{ bgcolor: \"grey.100\" }}\n label={\n <Stack\n direction=\"row\"\n sx={{\n fontSize: \"10pt\",\n alignItems: \"center\",\n gap: \"3px\",\n }}\n >\n <Box>{baseLabel}</Box>\n <Box>→</Box>\n <Box>{currentLabel}</Box>\n {changeIndicator && (\n <Box component=\"span\" sx={{ color: changeColor, ml: 0.5 }}>\n {changeIndicator}\n </Box>\n )}\n </Stack>\n }\n />\n );\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * ActionTag - Pure presentation component for action status\n *\n * Displays the current status of an action with appropriate visual feedback:\n * - Pending: Spinner\n * - Running: Progress indicator (indeterminate or percentage)\n * - Skipped: Chip with optional reason tooltip\n * - Error: Error text with optional message tooltip\n * - Success with value diff: Match/mismatch summary\n * - Success with row count diff: Count comparison\n *\n * @example\n * ```tsx\n * // Pending action\n * <ActionTag status=\"pending\" />\n *\n * // Running with progress\n * <ActionTag status=\"running\" progress={{ percentage: 0.5 }} />\n *\n * // Skipped with reason\n * <ActionTag status=\"skipped\" skipReason=\"No changes detected\" />\n *\n * // Value diff result\n * <ActionTag\n * status=\"success\"\n * valueDiffResult={{ mismatchedColumns: 2, totalColumns: 10 }}\n * />\n * ```\n */\nfunction ActionTagComponent({\n status,\n skipReason,\n errorMessage,\n progress,\n valueDiffResult,\n rowCountDiffResult,\n runId,\n \"data-testid\": testId,\n}: ActionTagProps) {\n // Pending state\n if (status === \"pending\") {\n return (\n <CircularProgress size={16} data-testid={testId} data-status=\"pending\" />\n );\n }\n\n // Skipped state\n if (status === \"skipped\") {\n return (\n <Box data-testid={testId} data-status=\"skipped\">\n <SkippedTag skipReason={skipReason} />\n </Box>\n );\n }\n\n // Running state\n if (status === \"running\") {\n if (progress?.percentage === undefined) {\n return (\n <CircularProgress\n size={16}\n data-testid={testId}\n data-status=\"running\"\n />\n );\n }\n return (\n <CircularProgress\n variant=\"determinate\"\n value={progress.percentage * 100}\n size={16}\n data-testid={testId}\n data-status=\"running\"\n />\n );\n }\n\n // Error state\n if (status === \"error\") {\n return (\n <Box data-testid={testId} data-status=\"error\">\n <ErrorTag errorMessage={errorMessage} />\n </Box>\n );\n }\n\n // Success state with value diff result\n if (valueDiffResult) {\n return (\n <Box data-testid={testId} data-status=\"success\">\n <ValueDiffTag result={valueDiffResult} />\n </Box>\n );\n }\n\n // Success state with row count diff result\n if (rowCountDiffResult) {\n return (\n <Box data-testid={testId} data-status=\"success\">\n <RowCountDiffTag result={rowCountDiffResult} />\n </Box>\n );\n }\n\n // Fallback: show run ID\n return (\n <Box data-testid={testId} data-status=\"success\">\n {runId || \"Complete\"}\n </Box>\n );\n}\n\nexport const ActionTag = memo(ActionTagComponent);\nActionTag.displayName = \"ActionTag\";\n","/**\n * TimelineEvent - Renders a single event in the check timeline.\n *\n * Handles different event types:\n * - check_created: Shows creation message\n * - comment: Shows user comment with edit/delete options\n * - approval_change: Shows approval status change\n * - description_change: Shows description update\n * - name_change: Shows name update\n * - preset_applied: Shows preset application\n */\n\n\"use client\";\n\nimport MuiAvatar from \"@mui/material/Avatar\";\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Popover from \"@mui/material/Popover\";\nimport Stack from \"@mui/material/Stack\";\nimport TextField from \"@mui/material/TextField\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { formatDistanceToNow } from \"date-fns\";\nimport { type MouseEvent, useState } from \"react\";\nimport {\n PiBookmarkSimple,\n PiChatText,\n PiCheckCircle,\n PiCircle,\n PiNotePencil,\n PiPencilSimple,\n PiPlusCircle,\n PiTrashSimple,\n} from \"react-icons/pi\";\nimport { type CheckEvent, getEventIconType } from \"../../../api\";\nimport { useAvatar } from \"../../../hooks/useAvatar\";\nimport { useIsDark } from \"../../../hooks/useIsDark\";\nimport { MarkdownContent } from \"../../../primitives\";\n\ninterface TimelineEventProps {\n event: CheckEvent;\n currentUserId?: string;\n onEdit?: (eventId: string, content: string) => Promise<void>;\n onDelete?: (eventId: string) => Promise<void>;\n}\n\nfunction EventIcon({ event }: { event: CheckEvent }) {\n const iconType = getEventIconType(event);\n\n const iconMap = {\n create: PiPlusCircle,\n comment: PiChatText,\n approve: PiCheckCircle,\n unapprove: PiCircle,\n edit: PiNotePencil,\n preset: PiBookmarkSimple,\n };\n\n const colorMap: Record<string, string> = {\n create: \"primary.main\",\n comment: \"grey.500\",\n approve: \"success.main\",\n unapprove: \"grey.400\",\n edit: \"warning.main\",\n preset: \"secondary.main\",\n };\n\n const IconComponent = iconMap[iconType];\n const color = colorMap[iconType];\n\n return <Box component={IconComponent} sx={{ color, fontSize: 16 }} />;\n}\n\nfunction UserAvatar({ event }: { event: CheckEvent }) {\n const { actor } = event;\n const { avatarUrl } = useAvatar({ userId: actor.user_id });\n\n const displayName = actor.fullname || actor.login || \"User\";\n const initials = displayName.charAt(0).toUpperCase();\n\n return (\n <MuiAvatar\n src={avatarUrl || undefined}\n sx={{ width: 24, height: 24, fontSize: \"0.75rem\" }}\n >\n {initials}\n </MuiAvatar>\n );\n}\n\nfunction StateChangeEvent({ event }: { event: CheckEvent }) {\n const { actor } = event;\n const actorName = actor.fullname || actor.login || \"Someone\";\n const relativeTime = formatDistanceToNow(new Date(event.created_at), {\n addSuffix: true,\n });\n\n let message = \"\";\n switch (event.event_type) {\n case \"check_created\":\n message = \"created this check\";\n break;\n case \"approval_change\":\n message =\n event.new_value === \"true\"\n ? \"approved this check\"\n : \"unapproved this check\";\n break;\n case \"description_change\":\n message = \"updated the description\";\n break;\n case \"name_change\":\n message = \"renamed this check\";\n break;\n case \"preset_applied\":\n message = \"applied a preset\";\n break;\n default:\n message = \"made a change\";\n }\n\n return (\n <Box sx={{ display: \"flex\", gap: 1, alignItems: \"flex-start\", py: 1 }}>\n <Box sx={{ pt: \"2px\" }}>\n <EventIcon event={event} />\n </Box>\n <Box sx={{ flex: 1 }}>\n <Stack\n direction=\"row\"\n spacing={0.5}\n flexWrap=\"wrap\"\n alignItems=\"center\"\n >\n <UserAvatar event={event} />\n <Typography variant=\"body2\" fontWeight=\"500\">\n {actorName}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {message}\n </Typography>\n <Typography variant=\"caption\" color=\"text.disabled\">\n {relativeTime}\n </Typography>\n </Stack>\n </Box>\n </Box>\n );\n}\n\nfunction CommentEvent({\n event,\n currentUserId,\n onEdit,\n onDelete,\n}: {\n event: CheckEvent;\n currentUserId?: string;\n onEdit?: (eventId: string, content: string) => Promise<void>;\n onDelete?: (eventId: string) => Promise<void>;\n}) {\n const isDark = useIsDark();\n const [isEditing, setIsEditing] = useState(false);\n const [editContent, setEditContent] = useState(event.content || \"\");\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [deleteAnchorEl, setDeleteAnchorEl] = useState<HTMLElement | null>(\n null,\n );\n const isDeletePopoverOpen = Boolean(deleteAnchorEl);\n\n const { actor } = event;\n const actorName = actor.fullname || actor.login || \"Someone\";\n const relativeTime = formatDistanceToNow(new Date(event.created_at), {\n addSuffix: true,\n });\n const isAuthor =\n currentUserId && String(actor.user_id) === String(currentUserId);\n\n const handleStartEdit = () => {\n setEditContent(event.content || \"\");\n setIsEditing(true);\n };\n\n const handleCancelEdit = () => {\n setEditContent(event.content || \"\");\n setIsEditing(false);\n };\n\n const handleSaveEdit = async () => {\n const trimmed = editContent.trim();\n if (!trimmed || trimmed === event.content) {\n handleCancelEdit();\n return;\n }\n\n if (onEdit) {\n setIsSubmitting(true);\n try {\n await onEdit(event.id, trimmed);\n setIsEditing(false);\n } finally {\n setIsSubmitting(false);\n }\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === \"Escape\") {\n handleCancelEdit();\n } else if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n handleSaveEdit();\n }\n };\n\n const handleDeleteClick = (event: MouseEvent<HTMLButtonElement>) => {\n setDeleteAnchorEl(event.currentTarget);\n };\n\n const handleDeleteClose = () => {\n setDeleteAnchorEl(null);\n };\n\n const handleDelete = async () => {\n if (onDelete) {\n setIsDeleting(true);\n try {\n await onDelete(event.id);\n handleDeleteClose();\n } finally {\n setIsDeleting(false);\n }\n }\n };\n\n if (event.is_deleted) {\n return (\n <Box sx={{ display: \"flex\", gap: 1, alignItems: \"center\", py: 1 }}>\n <Box sx={{ pt: \"2px\", display: \"flex\", alignItems: \"center\" }}>\n <EventIcon event={event} />\n </Box>\n <Box sx={{ display: \"flex\", flex: 1, alignItems: \"center\" }}>\n <Typography variant=\"body2\" color=\"text.disabled\" fontStyle=\"italic\">\n Comment deleted\n </Typography>\n </Box>\n </Box>\n );\n }\n\n return (\n <Box sx={{ display: \"flex\", gap: 1, alignItems: \"flex-start\", py: 1 }}>\n <Box sx={{ pt: \"2px\" }}>\n <EventIcon event={event} />\n </Box>\n <Box sx={{ flex: 1 }}>\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{ mb: 0.5 }}\n flexWrap=\"wrap\"\n alignItems=\"center\"\n >\n <UserAvatar event={event} />\n <Typography variant=\"body2\" fontWeight=\"500\">\n {actorName}\n {isAuthor && (\n <Typography\n component=\"span\"\n variant=\"body2\"\n color=\"text.secondary\"\n >\n {\" \"}\n (Author)\n </Typography>\n )}\n </Typography>\n <Typography variant=\"caption\" color=\"text.disabled\">\n {relativeTime}\n </Typography>\n {event.is_edited && (\n <Typography variant=\"caption\" color=\"text.disabled\">\n (edited)\n </Typography>\n )}\n </Stack>\n\n {isEditing ? (\n // Edit mode\n <Box>\n <TextField\n value={editContent}\n onChange={(e) => setEditContent(e.target.value)}\n onKeyDown={handleKeyDown}\n size=\"small\"\n multiline\n minRows={3}\n fullWidth\n disabled={isSubmitting}\n autoFocus\n sx={{\n \"& .MuiOutlinedInput-root\": {\n bgcolor: \"background.paper\",\n \"&:focus-within\": {\n borderColor: \"primary.main\",\n },\n },\n }}\n />\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ mt: 1 }}\n justifyContent=\"flex-end\"\n >\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={handleCancelEdit}\n disabled={isSubmitting}\n >\n Cancel\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n onClick={handleSaveEdit}\n disabled={!editContent.trim() || isSubmitting}\n >\n {isSubmitting ? \"Saving...\" : \"Save\"}\n </Button>\n </Stack>\n </Box>\n ) : (\n // View mode\n <Box\n sx={{\n bgcolor: isDark ? \"grey.800\" : \"grey.50\",\n borderRadius: 1,\n p: 1,\n border: \"1px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.200\",\n position: \"relative\",\n \"&:hover .comment-actions\": {\n opacity: 1,\n },\n }}\n >\n <MarkdownContent content={event.content || \"\"} fontSize=\"sm\" />\n\n {/* Edit/Delete buttons - only visible to author on hover */}\n {isAuthor && (onEdit || onDelete) && (\n <Stack\n className=\"comment-actions\"\n direction=\"row\"\n spacing={0}\n sx={{\n position: \"absolute\",\n top: 4,\n right: 4,\n opacity: 0,\n transition: \"opacity 0.2s\",\n }}\n >\n {onEdit && (\n <MuiTooltip title=\"Edit comment\">\n <IconButton\n aria-label=\"Edit comment\"\n size=\"small\"\n onClick={handleStartEdit}\n >\n <PiPencilSimple />\n </IconButton>\n </MuiTooltip>\n )}\n {onDelete && (\n <>\n <MuiTooltip title=\"Delete comment\">\n <IconButton\n aria-label=\"Delete comment\"\n size=\"small\"\n color=\"error\"\n onClick={handleDeleteClick}\n >\n <PiTrashSimple />\n </IconButton>\n </MuiTooltip>\n <Popover\n open={isDeletePopoverOpen}\n anchorEl={deleteAnchorEl}\n onClose={handleDeleteClose}\n anchorOrigin={{\n vertical: \"bottom\",\n horizontal: \"center\",\n }}\n transformOrigin={{\n vertical: \"top\",\n horizontal: \"center\",\n }}\n >\n <Box sx={{ p: 2 }}>\n <Typography variant=\"body2\" sx={{ mb: 2 }}>\n Delete this comment?\n </Typography>\n <Stack\n direction=\"row\"\n spacing={1}\n justifyContent=\"flex-end\"\n >\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={handleDeleteClose}\n disabled={isDeleting}\n >\n Cancel\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"error\"\n onClick={handleDelete}\n disabled={isDeleting}\n >\n {isDeleting ? \"Deleting...\" : \"Delete\"}\n </Button>\n </Stack>\n </Box>\n </Popover>\n </>\n )}\n </Stack>\n )}\n </Box>\n )}\n </Box>\n </Box>\n );\n}\n\nexport function TimelineEventOss({\n event,\n currentUserId,\n onEdit,\n onDelete,\n}: TimelineEventProps) {\n if (event.event_type === \"comment\") {\n return (\n <CommentEvent\n event={event}\n currentUserId={currentUserId}\n onEdit={onEdit}\n onDelete={onDelete}\n />\n );\n }\n\n return <StateChangeEvent event={event} />;\n}\n","/**\n * CheckTimeline - Main timeline/conversation panel for a check.\n *\n * Displays a chronological list of events (comments, state changes)\n * and provides an input for adding new comments.\n *\n * This component is only rendered when connected to Recce Cloud.\n */\n\n\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Divider from \"@mui/material/Divider\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { cacheKeys } from \"../../../api\";\nimport { useApiConfig, useCheckEvents, useIsDark } from \"../../../hooks\";\nimport { fetchUser } from \"../../../lib/api/user\";\nimport { CommentInput } from \"../../../primitives\";\nimport { TimelineEventOss as TimelineEvent } from \"./TimelineEventOss\";\n\ninterface CheckTimelineProps {\n checkId: string;\n}\n\nexport function CheckTimelineOss({ checkId }: CheckTimelineProps) {\n const isDark = useIsDark();\n const { apiClient } = useApiConfig();\n const {\n events,\n isLoading,\n error,\n createComment,\n isCreatingComment,\n updateComment,\n deleteComment,\n } = useCheckEvents(checkId);\n\n // Get current user for determining edit/delete permissions\n const { data: currentUser } = useQuery({\n queryKey: cacheKeys.user(),\n queryFn: () => fetchUser(apiClient),\n retry: false,\n });\n\n const handleCreateComment = (content: string) => {\n createComment(content);\n };\n\n const handleEditComment = async (eventId: string, content: string) => {\n await updateComment({ eventId, content });\n };\n\n const handleDeleteComment = async (eventId: string) => {\n await deleteComment(eventId);\n };\n\n if (isLoading) {\n return (\n <Box\n sx={{\n height: \"100%\",\n p: 4,\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <CircularProgress size={20} />\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box\n sx={{\n height: \"100%\",\n p: 4,\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <Typography sx={{ fontSize: \"0.875rem\", color: \"error.main\" }}>\n Failed to load timeline\n </Typography>\n </Box>\n );\n }\n\n return (\n <Stack\n sx={{\n height: \"100%\",\n alignItems: \"stretch\",\n borderLeft: \"1px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.200\",\n }}\n spacing={0}\n >\n {/* Header */}\n <Box\n sx={{\n px: 3,\n py: 2,\n borderBottom: \"1px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.200\",\n }}\n >\n <Typography variant=\"subtitle2\" sx={{ fontWeight: 500 }}>\n Activity\n </Typography>\n </Box>\n\n {/* Events List - Scrollable */}\n <Box sx={{ flex: 1, overflowY: \"auto\", px: 3, py: 2 }}>\n {events.length === 0 ? (\n <Typography sx={{ fontSize: \"0.875rem\", color: \"grey.500\" }}>\n No activity yet\n </Typography>\n ) : (\n <Stack sx={{ alignItems: \"stretch\" }} spacing={0}>\n {events.map((event, index) => (\n <Box key={event.id}>\n <TimelineEvent\n event={event}\n currentUserId={currentUser?.id}\n onEdit={handleEditComment}\n onDelete={handleDeleteComment}\n />\n {index < events.length - 1 && (\n <Divider\n sx={{ borderColor: isDark ? \"grey.700\" : \"grey.100\" }}\n />\n )}\n </Box>\n ))}\n </Stack>\n )}\n </Box>\n\n {/* Comment Input - Fixed at bottom */}\n <Box\n sx={{\n px: 3,\n py: 3,\n borderTop: \"1px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.200\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n }}\n >\n <CommentInput\n onSubmit={handleCreateComment}\n isSubmitting={isCreatingComment}\n />\n </Box>\n </Stack>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\n\nexport interface SquareIconProps {\n /** The background color of the square icon */\n color: string;\n}\n\n/**\n * A small square icon used in chart legends\n */\nexport function SquareIcon({ color }: SquareIconProps) {\n return (\n <Box\n component=\"span\"\n sx={{\n display: \"inline-block\",\n width: \"10px\",\n height: \"10px\",\n bgcolor: color,\n mr: 1,\n borderRadius: \"4px\",\n }}\n />\n );\n}\n","\"use client\";\n\nimport Box, { type BoxProps } from \"@mui/material/Box\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMemo } from \"react\";\nimport { PiInfo } from \"react-icons/pi\";\nimport type { NodeColumnData } from \"../../api\";\nimport { useLineageGraphContext } from \"../../contexts\";\nimport { DropdownValuesInput } from \"../ui/DropdownValuesInput\";\n\nexport interface QueryFormProps extends BoxProps {\n defaultPrimaryKeys: string[] | undefined;\n onPrimaryKeysChange: (primaryKeys: string[]) => void;\n}\n\nexport const QueryForm = ({\n defaultPrimaryKeys,\n onPrimaryKeysChange,\n ...props\n}: QueryFormProps) => {\n const { lineageGraph, isActionAvailable } = useLineageGraphContext();\n\n const labelInfo =\n \"Provide a primary key to perform query diff in data warehouse and only return changed rows.\";\n\n const availableColumns = useMemo(() => {\n if (!lineageGraph) {\n return [];\n }\n const columnSet = new Set<string>();\n for (const modelName in lineageGraph.nodes) {\n const model = lineageGraph.nodes[modelName];\n const combinedColumns: Record<string, NodeColumnData | undefined> = {\n ...(model.data.data.base?.columns ?? {}),\n ...(model.data.data.current?.columns ?? {}),\n };\n\n Object.entries(combinedColumns).forEach(([columnName, col]) => {\n if (col?.unique) {\n columnSet.add(columnName);\n }\n });\n }\n return Array.from(columnSet).sort();\n }, [lineageGraph]);\n\n return (\n <Box sx={{ display: \"flex\" }} {...props}>\n <Stack spacing={0} sx={{ m: \"0 0.5rem\" }}>\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography\n component=\"label\"\n sx={{ fontSize: \"0.625rem\", color: \"text.secondary\" }}\n >\n Diff with Primary Key(s) (suggested)\n </Typography>\n <MuiTooltip title={labelInfo} placement=\"bottom-end\">\n <Box\n component=\"span\"\n sx={{ display: \"flex\", color: \"grey.600\", cursor: \"help\" }}\n >\n <PiInfo fontSize=\"0.75rem\" />\n </Box>\n </MuiTooltip>\n </Stack>\n <DropdownValuesInput\n className=\"no-track-pii-safe\"\n unitName=\"key\"\n defaultValues={defaultPrimaryKeys}\n suggestionList={availableColumns}\n onValuesChange={onPrimaryKeysChange}\n size=\"2xs\"\n width={\"240px\"}\n placeholder=\"Select or type to add keys\"\n disabled={!isActionAvailable(\"query_diff_with_primary_key\")}\n />\n </Stack>\n </Box>\n );\n};\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { RiTerminalBoxLine } from \"react-icons/ri\";\nimport { useRecceInstanceInfo } from \"../../contexts\";\nimport { getSettingsUrl } from \"../../utils\";\n\nexport interface SetupConnectionGuideProps {\n /** URL for the support calendar booking (e.g., \"https://cal.com/team/recce/chat\") */\n supportCalendarUrl?: string;\n}\n\n/**\n * SetupConnectionGuide - displays guidance when data warehouse connection is not configured\n *\n * This component shows a guide to help users connect to a data warehouse\n * when query functions are disabled due to missing connection.\n */\nexport function SetupConnectionGuide({\n supportCalendarUrl = \"https://cal.com/team/recce/chat\",\n}: SetupConnectionGuideProps) {\n const { data: instanceInfo } = useRecceInstanceInfo();\n\n return (\n <div className=\"flex flex-1 h-full min-h-0 m-2 p-4 bg-blue-50 rounded-lg shadow-md justify-center\">\n <div className=\"w-4/5 flex flex-col overflow-y-auto gap-6 px-8 pb-8\">\n <Stack alignItems=\"center\" spacing={2}>\n <Box\n sx={{\n p: 1,\n bgcolor: \"background.paper\",\n borderRadius: \"50%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: 2,\n }}\n >\n <Box\n component={RiTerminalBoxLine}\n sx={{ fontSize: 28, color: \"iochmara.500\" }}\n />\n </Box>\n <Typography variant=\"h5\" sx={{ mt: 2 }}>\n Wait, there's more!\n </Typography>\n <Typography sx={{ fontSize: \"1rem\", textAlign: \"center\" }}>\n Query functions disabled without a{\" \"}\n <Typography component=\"span\" sx={{ fontWeight: \"bold\" }}>\n data warehouse connection\n </Typography>\n </Typography>\n </Stack>\n <Stack sx={{ width: \"50%\", mt: 3, mx: \"auto\" }}>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n size=\"large\"\n onClick={() => {\n window.open(\n getSettingsUrl(instanceInfo, supportCalendarUrl),\n \"_blank\",\n );\n }}\n >\n Connect to Data Warehouse\n </Button>\n </Stack>\n </div>\n </div>\n );\n}\n\nexport default SetupConnectionGuide;\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiSwitch from \"@mui/material/Switch\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { PiInfoFill } from \"react-icons/pi\";\nimport {\n type QueryParams,\n type SubmitOptions,\n submitQuery,\n submitQueryBase,\n submitQueryDiff,\n waitRun,\n} from \"../../api\";\nimport { HistoryToggle } from \"../../components\";\nimport { SetupConnectionPopover } from \"../../components/app\";\nimport { BaseEnvironmentSetupGuide } from \"../../components/lineage\";\nimport {\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport {\n defaultSqlQuery,\n useApiConfig,\n useRecceQueryContext,\n} from \"../../hooks\";\nimport { RECCE_SUPPORT_CALENDAR_URL } from \"../../lib/const\";\nimport { QueryForm } from \"./QueryForm\";\nimport { SetupConnectionGuide } from \"./SetupConnectionGuide\";\nimport SqlEditor, { DualSqlEditor } from \"./SqlEditor\";\n\nconst QueryModeToggle = () => {\n const { isCustomQueries, setCustomQueries, sqlQuery, setBaseSqlQuery } =\n useRecceQueryContext();\n const handleToggle = () => {\n if (!isCustomQueries && setBaseSqlQuery) setBaseSqlQuery(sqlQuery);\n setCustomQueries(!isCustomQueries);\n };\n const customQueriesDescription =\n \"Custom queries allow you to use two SQL queries to compare results between current and base environments.\";\n return (\n <Box>\n <Stack\n direction=\"row\"\n spacing={0.5}\n alignItems=\"center\"\n fontSize=\"0.75rem\"\n >\n <Typography variant=\"body2\" sx={{ fontSize: \"0.75rem\" }}>\n Custom Queries\n </Typography>\n <MuiTooltip title={customQueriesDescription}>\n <Box component=\"span\" sx={{ display: \"flex\", color: \"grey.600\" }}>\n <PiInfoFill fontSize=\"1rem\" />\n </Box>\n </MuiTooltip>\n </Stack>\n <MuiSwitch\n size=\"small\"\n checked={isCustomQueries}\n onChange={handleToggle}\n color=\"primary\"\n />\n </Box>\n );\n};\n\nexport const QueryPageOss = () => {\n const {\n sqlQuery: _sqlQuery,\n baseSqlQuery,\n setSqlQuery,\n setBaseSqlQuery,\n primaryKeys,\n setPrimaryKeys,\n isCustomQueries,\n } = useRecceQueryContext();\n const { lineageGraph, envInfo } = useLineageGraphContext();\n const { featureToggles, singleEnv } = useRecceInstanceContext();\n\n let sqlQuery = _sqlQuery;\n if (envInfo?.adapterType === \"sqlmesh\" && _sqlQuery === defaultSqlQuery) {\n sqlQuery = \"select * from db.mymodel\";\n }\n\n if (featureToggles.mode === \"read only\") {\n sqlQuery = `--- Would like to do query here? Book a demo with us at ${RECCE_SUPPORT_CALENDAR_URL}\\n${sqlQuery}`;\n }\n\n const { showRunId } = useRecceActionContext();\n const { apiClient } = useApiConfig();\n const queryFn = async (type: \"query\" | \"query_base\" | \"query_diff\") => {\n function queryFactory(type: string) {\n switch (type) {\n case \"query\":\n return submitQuery;\n case \"query_base\":\n return submitQueryBase;\n case \"query_diff\":\n return submitQueryDiff;\n default:\n throw new Error(`Unknown query type: ${type}`);\n }\n }\n const sqlTemplate = type === \"query_base\" ? (baseSqlQuery ?? \"\") : sqlQuery;\n const runFn = queryFactory(type);\n const params: QueryParams = { sql_template: sqlTemplate };\n const options: SubmitOptions = { nowait: true };\n\n if (type === \"query_diff\") {\n params.primary_keys = primaryKeys;\n if (isCustomQueries) params.base_sql_template = baseSqlQuery;\n }\n const { run_id } = await runFn(params, options, apiClient);\n\n showRunId(run_id);\n\n return await waitRun(run_id, undefined, apiClient);\n };\n\n const { mutate: runQuery, isPending } = useMutation({\n mutationFn: queryFn,\n });\n\n const currentSchema = useMemo(() => {\n const initialValue = \"N/A\";\n // find the most common schema from the current lineage graph\n const countMap: Record<string, number> = {};\n if (!lineageGraph) {\n return initialValue;\n }\n\n for (const key in lineageGraph.nodes) {\n const schema = lineageGraph.nodes[key].data.data.current?.schema;\n if (schema) {\n countMap[schema] = (countMap[schema] || 0) + 1;\n }\n }\n // Find the most common value\n return Object.keys(countMap).reduce((mostCommon, current) => {\n if (countMap[current] > (countMap[mostCommon] || 0)) {\n return current;\n }\n return mostCommon;\n }, initialValue);\n }, [lineageGraph]);\n\n if (singleEnv || featureToggles.mode === \"metadata only\") {\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n <Box\n sx={{\n display: \"flex\",\n justifyContent: \"right\",\n alignItems: \"center\",\n padding: \"4pt 8pt\",\n gap: \"5px\",\n height: \"54px\",\n borderBottom: \"1px solid\",\n borderColor: \"divider\",\n }}\n >\n <HistoryToggle />\n <Box sx={{ flexGrow: 1 }} />\n {singleEnv ? (\n <MuiTooltip\n title=\"Please configure the base environment before running the diff\"\n placement=\"left\"\n >\n <span>\n <Button\n variant=\"contained\"\n disabled\n size=\"small\"\n sx={{ fontSize: \"14px\", mt: \"16px\" }}\n >\n Run Diff\n </Button>\n </span>\n </MuiTooltip>\n ) : (\n <SetupConnectionPopover\n display={featureToggles.mode === \"metadata only\"}\n >\n <Button\n variant=\"contained\"\n disabled\n size=\"small\"\n sx={{ fontSize: \"14px\", mt: \"16px\" }}\n >\n Run Diff\n </Button>\n </SetupConnectionPopover>\n )}\n </Box>\n <DualSqlEditor\n value={sqlQuery}\n onChange={setSqlQuery}\n onRun={() => {\n runQuery(\"query\");\n }}\n labels={[\"base (production)\", `current (${currentSchema})`]}\n SetupGuide={\n featureToggles.mode === \"metadata only\" ? (\n <SetupConnectionGuide />\n ) : (\n <BaseEnvironmentSetupGuide />\n )\n }\n />\n </Box>\n );\n }\n\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n <Box\n sx={{\n display: \"flex\",\n justifyContent: \"right\",\n alignItems: \"flex-end\",\n padding: \"4pt 8pt\",\n gap: \"5px\",\n height: \"54px\",\n borderBottom: \"1px solid\",\n borderColor: \"divider\",\n flex: \"0 0 54px\",\n }}\n >\n <HistoryToggle />\n <QueryModeToggle />\n <Box sx={{ flexGrow: 1 }} />\n <QueryForm\n defaultPrimaryKeys={primaryKeys}\n onPrimaryKeysChange={setPrimaryKeys}\n />\n <Button\n variant=\"contained\"\n onClick={() => {\n runQuery(\"query_diff\");\n }}\n disabled={isPending || featureToggles.disableDatabaseQuery}\n size=\"small\"\n >\n Run Diff\n </Button>\n </Box>\n\n <Box sx={{ width: \"100%\", flex: 1, overflowY: \"auto\" }}>\n {isCustomQueries ? (\n <DualSqlEditor\n value={sqlQuery}\n baseValue={baseSqlQuery}\n onChange={setSqlQuery}\n onChangeBase={setBaseSqlQuery}\n onRun={() => {\n runQuery(\"query\");\n }}\n onRunBase={() => {\n runQuery(\"query_base\");\n }}\n onRunDiff={() => {\n runQuery(\"query_diff\");\n }}\n />\n ) : (\n <SqlEditor\n value={sqlQuery}\n onChange={setSqlQuery}\n onRun={() => {\n runQuery(\"query\");\n }}\n onRunDiff={() => {\n runQuery(\"query_diff\");\n }}\n />\n )}\n </Box>\n </Box>\n );\n};\n","\"use client\";\n\n/**\n * @file GraphNodeOss.tsx\n * @description OSS wrapper for UI package LineageNode component\n *\n * This component wraps the @datarecce/ui LineageNode with OSS-specific\n * context integration. It extracts state from LineageViewContext and\n * LineageGraphContext and passes it as props to the presentation component.\n *\n * Migration: Phase 4 of lineage component migration plan\n *\n * OSS-specific functionality injected:\n * - Run type icons from registry (schema_diff, row_count_diff)\n * - ActionTag with OSS run result parsing\n * - NodeRunsAggregated with schema change detection\n */\n\nimport Box from \"@mui/material/Box\";\nimport Chip from \"@mui/material/Chip\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport { type NodeProps, useStore } from \"@xyflow/react\";\nimport { memo } from \"react\";\nimport type { LineageGraphNode } from \"../..\";\nimport { COLUMN_HEIGHT, isSchemaChanged } from \"../..\";\nimport { isRowCountDiffRun, type RowCountDiff } from \"../../api\";\nimport {\n useLineageGraphContext,\n useLineageViewContextSafe,\n} from \"../../contexts\";\nimport { useThemeColors } from \"../../hooks\";\nimport { deltaPercentageString } from \"../../utils\";\nimport { findByRunType } from \"../run\";\nimport {\n ActionTag,\n type ChangeCategory,\n LineageNode,\n type NodeChangeStatus,\n type SelectMode,\n} from \"./nodes\";\nimport { getIconForChangeStatus } from \"./styles\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport type GraphNodeProps = NodeProps<LineageGraphNode>;\n\n// =============================================================================\n// HELPER COMPONENTS\n// =============================================================================\n\n/**\n * Row count diff tag component with OSS icon injection\n */\nfunction RowCountDiffTag({ rowCount }: { rowCount: RowCountDiff }) {\n const base = rowCount.base;\n const current = rowCount.curr;\n const baseLabel = rowCount.base === null ? \"N/A\" : `${rowCount.base} Rows`;\n const currentLabel = rowCount.curr === null ? \"N/A\" : `${rowCount.curr} Rows`;\n\n let tagLabel: string;\n let chipColor: \"default\" | \"success\" | \"error\";\n\n if (base === null && current === null) {\n tagLabel = \"Failed to load\";\n chipColor = \"default\";\n } else if (base === null || current === null) {\n tagLabel = `${baseLabel} -> ${currentLabel}`;\n chipColor = base === null ? \"success\" : \"error\";\n } else if (base === current) {\n tagLabel = \"=\";\n chipColor = \"default\";\n } else {\n tagLabel = `${deltaPercentageString(base, current)} Rows`;\n chipColor = base < current ? \"success\" : \"error\";\n }\n\n const RowCountIcon = findByRunType(\"row_count_diff\").icon;\n\n return (\n <Chip\n size=\"small\"\n color={chipColor}\n icon={RowCountIcon ? <RowCountIcon /> : undefined}\n label={tagLabel}\n sx={{ height: 20, fontSize: \"0.7rem\" }}\n />\n );\n}\n\n/**\n * Node runs aggregated display component with OSS-specific icons\n * Shows schema diff indicator and row count diff for models\n */\nfunction NodeRunsAggregatedDisplay({\n id,\n inverted,\n}: {\n id: string;\n inverted: boolean;\n}) {\n const { lineageGraph, runsAggregated } = useLineageGraphContext();\n const { text, isDark } = useThemeColors();\n const runs = runsAggregated?.[id];\n const node = lineageGraph?.nodes[id];\n\n if (!runs && !node) {\n return null;\n }\n\n let schemaChanged: boolean | undefined;\n if (node?.data.data.base && node.data.data.current) {\n const baseColumns = node.data.data.base.columns;\n const currColumns = node.data.data.current.columns;\n schemaChanged = isSchemaChanged(baseColumns, currColumns);\n }\n\n let rowCountChanged: boolean | undefined;\n if (runs?.row_count_diff) {\n const rowCountDiff = runs.row_count_diff;\n const result = rowCountDiff.result as RowCountDiff;\n rowCountChanged = result.curr !== result.base;\n }\n\n const colorChanged = inverted\n ? text.inverted\n : getIconForChangeStatus(\"modified\").color;\n const colorUnchanged = inverted\n ? text.secondary\n : isDark\n ? \"grey.700\"\n : \"grey.100\";\n\n const SchemaDiffIcon = findByRunType(\"schema_diff\").icon;\n\n return (\n <Box sx={{ display: \"flex\", flex: 1, alignItems: \"center\" }}>\n {schemaChanged !== undefined && (\n <MuiTooltip\n title={`Schema (${schemaChanged ? \"changed\" : \"no change\"})`}\n enterDelay={500}\n >\n <Box sx={{ height: 16 }}>\n {SchemaDiffIcon && (\n <Box\n component={SchemaDiffIcon}\n sx={{ color: schemaChanged ? colorChanged : colorUnchanged }}\n />\n )}\n </Box>\n </MuiTooltip>\n )}\n <Box sx={{ flexGrow: 1 }} />\n {runs?.row_count_diff && rowCountChanged !== undefined && (\n <MuiTooltip\n title={`Row count (${rowCountChanged ? \"changed\" : \"=\"})`}\n enterDelay={500}\n >\n <Box>\n <RowCountDiffTag\n rowCount={runs.row_count_diff.result as RowCountDiff}\n />\n </Box>\n </MuiTooltip>\n )}\n </Box>\n );\n}\n\n/**\n * Action tag display component - bridges OSS run data to UI ActionTag\n * Parses OSS-specific run results and renders using UI package ActionTag\n */\nfunction ActionTagDisplay({\n nodeId,\n nodeName,\n}: {\n nodeId: string;\n nodeName: string;\n}) {\n const { getNodeAction } = useLineageViewContextSafe();\n const action = getNodeAction(nodeId);\n\n if (!action) {\n return null;\n }\n\n const { status, skipReason, run } = action;\n\n // Map OSS action status to UI ActionTag props\n if (status === \"pending\") {\n return <ActionTag status=\"pending\" />;\n }\n\n if (status === \"skipped\") {\n return <ActionTag status=\"skipped\" skipReason={skipReason} />;\n }\n\n if (!run) {\n return <ActionTag status=\"pending\" />;\n }\n\n const { error, run_id, progress } = run;\n\n if (status === \"running\") {\n return (\n <ActionTag\n status=\"running\"\n progress={{ percentage: progress?.percentage }}\n />\n );\n }\n\n if (error) {\n return <ActionTag status=\"error\" errorMessage={error} />;\n }\n\n // Value diff result - parse OSS format to UI format\n if (run.type === \"value_diff\" && run.result) {\n const r = run.result as { data: { data: unknown[][] } };\n let mismatched = 0;\n const totalColumns = r.data.data.length;\n\n for (const c of r.data.data) {\n if ((c[2] as number) < 1) {\n mismatched++;\n }\n }\n\n return (\n <ActionTag\n status=\"success\"\n valueDiffResult={{ mismatchedColumns: mismatched, totalColumns }}\n />\n );\n }\n\n // Row count diff result - use OSS RowCountDiffTag with icon\n if (isRowCountDiffRun(run) && run.result) {\n const result = run.result;\n const nodeResult = result[nodeName];\n if (nodeResult) {\n return <RowCountDiffTag rowCount={nodeResult} />;\n }\n }\n\n // Row count result\n if (run.type === \"row_count\" && run.result) {\n const result = run.result as Record<string, { curr: number | null }>;\n const nodeResult = result[nodeName];\n if (nodeResult?.curr !== undefined && nodeResult.curr !== null) {\n return (\n <Chip\n size=\"small\"\n label={`${nodeResult.curr.toLocaleString()} Rows`}\n sx={{ height: 20, fontSize: \"0.7rem\" }}\n />\n );\n }\n }\n\n return <ActionTag status=\"success\" runId={run_id} />;\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * GraphNode - OSS wrapper for UI package LineageNode\n *\n * This component integrates LineageViewContext and LineageGraphContext\n * with the pure presentation LineageNode from @datarecce/ui.\n */\nfunction GraphNodeComponent(nodeProps: GraphNodeProps) {\n const { data } = nodeProps;\n const { id, resourceType, changeStatus, name } = data;\n\n // Get zoom level for content visibility\n const showContent = useStore((s) => s.transform[2] > 0.3);\n\n // Get theme colors\n const { isDark } = useThemeColors();\n\n // Get context values\n const {\n interactive,\n selectNode,\n selectMode,\n focusedNode,\n getNodeAction,\n getNodeColumnSet,\n isNodeHighlighted,\n isNodeSelected,\n isNodeShowingChangeAnalysis,\n showContextMenu,\n viewOptions,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n } = useLineageViewContextSafe();\n const { isActionAvailable } = useLineageGraphContext();\n\n // Computed state\n const changeCategory = cll?.current.nodes[id]\n ?.change_category as ChangeCategory;\n const isHighlighted = isNodeHighlighted(id);\n const isSelected = isNodeSelected(id);\n const isFocusedByImpactRadius =\n viewOptions.column_level_lineage?.node_id === id &&\n viewOptions.column_level_lineage.column === undefined;\n const isFocused = focusedNode?.id === id || isFocusedByImpactRadius;\n const isShowingChangeAnalysis = isNodeShowingChangeAnalysis(id);\n const columnSet = getNodeColumnSet(data.id);\n const action =\n selectMode === \"action_result\" ? getNodeAction(data.id) : undefined;\n\n // Map to UI package types\n const nodeChangeStatus: NodeChangeStatus | undefined = changeStatus as\n | NodeChangeStatus\n | undefined;\n const nodeSelectMode: SelectMode = selectMode as SelectMode;\n\n // Create action tag if in action_result mode\n const actionTag =\n selectMode === \"action_result\" && action ? (\n <ActionTagDisplay nodeId={id} nodeName={name} />\n ) : undefined;\n\n // Create runs aggregated tag if model and not in action_result mode\n const runsAggregatedTag =\n selectMode !== \"action_result\" && data.resourceType === \"model\" ? (\n <NodeRunsAggregatedDisplay\n id={data.id}\n inverted={selectMode === \"selecting\" && isSelected}\n />\n ) : undefined;\n\n // Callbacks\n const handleSelect = (nodeId: string) => {\n selectNode(nodeId);\n };\n\n const handleContextMenu = (event: React.MouseEvent, _nodeId: string) => {\n showContextMenu(event, nodeProps as unknown as LineageGraphNode);\n };\n\n const handleShowImpactRadius = (nodeId: string) => {\n setChangeAnalysisMode(true);\n void showColumnLevelLineage({\n node_id: nodeId,\n change_analysis: true,\n no_upstream: true,\n });\n };\n\n return (\n <LineageNode\n id={id}\n data={{\n label: name,\n changeStatus: nodeChangeStatus,\n resourceType,\n }}\n // Interactive props\n interactive={interactive}\n selectMode={nodeSelectMode}\n isNodeSelected={isSelected}\n isFocused={isFocused}\n isHighlighted={isHighlighted}\n showContent={showContent}\n // Action display props\n actionTag={actionTag}\n showChangeAnalysis={isShowingChangeAnalysis}\n changeCategory={changeCategory}\n runsAggregatedTag={runsAggregatedTag}\n // Layout props\n hasParents={Object.keys(data.parents).length > 0}\n hasChildren={Object.keys(data.children).length > 0}\n columnCount={columnSet.size}\n columnHeight={COLUMN_HEIGHT}\n // Theme props\n isDark={isDark}\n // Callbacks\n onSelect={handleSelect}\n onContextMenu={handleContextMenu}\n onShowImpactRadius={\n changeStatus === \"modified\" && isActionAvailable(\"change_analysis\")\n ? handleShowImpactRadius\n : undefined\n }\n />\n );\n}\n\nexport const GraphNode = memo(GraphNodeComponent);\nGraphNode.displayName = \"GraphNode\";\n","import type { LineageGraphNode } from \"../../../contexts/lineage/types\";\nimport { colors } from \"../../../theme\";\nimport { GraphColumnNode } from \"../GraphColumnNodeOss\";\nimport GraphEdge from \"../GraphEdgeOss\";\nimport { GraphNode } from \"../GraphNodeOss\";\nimport { getIconForChangeStatus } from \"../styles\";\n\n/**\n * Node types configuration for ReactFlow.\n * Maps custom node type names to their React components.\n */\nexport const nodeTypes = {\n lineageGraphNode: GraphNode,\n lineageGraphColumnNode: GraphColumnNode,\n} as const;\n\n/**\n * Edge types configuration for ReactFlow.\n * Maps custom edge type names to their React components.\n */\nexport const edgeTypes = {\n lineageGraphEdge: GraphEdge,\n} as const;\n\n/**\n * Initial empty nodes array for ReactFlow initialization.\n */\nexport const initialNodes: LineageGraphNode[] = [];\n\n/**\n * Get the color for a node based on its change status.\n * Used by MiniMap for node coloring.\n *\n * @param node - The lineage graph node\n * @returns Hex color string\n */\nexport const getNodeColor = (node: LineageGraphNode): string => {\n return node.data.changeStatus\n ? getIconForChangeStatus(node.data.changeStatus).hexColor\n : colors.neutral[400];\n};\n","\"use client\";\n\n/**\n * @file LineageViewContextMenu.tsx\n * @description Context menu components for lineage graph nodes with dependency injection.\n *\n * These components provide right-click context menus for model nodes and column nodes\n * in the lineage visualization. They support:\n * - Query generation and navigation\n * - Row count, profile, value diff, and histogram diff actions\n * - Node selection (parent/child nodes)\n * - Column-level lineage (impact radius)\n *\n * The components use dependency injection for:\n * - Action execution (runAction callback)\n * - Navigation (onNavigate callback)\n * - Analytics tracking (onTrack callback)\n * - Histogram diff support checking (supportsHistogramDiff callback)\n * - Run type metadata (findByRunType callback)\n *\n * @example\n * ```tsx\n * <LineageViewContextMenu\n * x={100}\n * y={200}\n * node={selectedNode}\n * isOpen={true}\n * onClose={() => setMenuOpen(false)}\n * deps={{\n * runAction: (type, params, options) => executeAction(type, params, options),\n * onNavigate: (path) => router.push(path),\n * onTrack: (event, props) => analytics.track(event, props),\n * supportsHistogramDiff: (columnType) => checkHistogramSupport(columnType),\n * findByRunType: (type) => getRunTypeMetadata(type),\n * }}\n * />\n * ```\n */\n\nimport Box from \"@mui/material/Box\";\nimport Divider from \"@mui/material/Divider\";\nimport Menu from \"@mui/material/Menu\";\nimport MenuItem from \"@mui/material/MenuItem\";\nimport { type ReactNode, useState } from \"react\";\nimport { BiArrowFromBottom, BiArrowToBottom } from \"react-icons/bi\";\nimport { FaRegDotCircle } from \"react-icons/fa\";\nimport type { CllInput } from \"../../../api/cll\";\nimport type { SubmitRunTrackProps } from \"../../../api/runs\";\nimport {\n isLineageGraphColumnNode,\n isLineageGraphNode,\n type LineageGraphColumnNode,\n type LineageGraphNode,\n type LineageGraphNodes,\n} from \"../../../contexts/lineage/types\";\nimport { formatSelectColumns } from \"../../../utils/formatSelect\";\nimport type { IconComponent } from \"../../run/types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Tracking event types for context menu actions\n */\nexport type ContextMenuTrackEvent = \"explore_action\" | \"lineage_selection\";\n\n/**\n * Properties for explore action tracking\n */\nexport interface ExploreActionTrackProps {\n action: string;\n source: string;\n node_count: number;\n}\n\n/**\n * Properties for lineage selection tracking\n */\nexport interface LineageSelectionTrackProps {\n action: string;\n}\n\n/**\n * Run type metadata returned by findByRunType\n */\nexport interface RunTypeMetadata {\n title: string;\n icon: IconComponent;\n}\n\n/**\n * Dependency injection props for context menu components.\n * These allow the consumer to inject OSS-specific behavior.\n */\nexport interface LineageViewContextMenuDeps {\n /**\n * Execute a run action (e.g., row_count_diff, profile_diff).\n * @param type - The run type to execute\n * @param params - Parameters for the run\n * @param options - Options including showForm and trackProps\n */\n runAction?: (\n type: string,\n params: Record<string, unknown>,\n options: { showForm: boolean; trackProps: SubmitRunTrackProps },\n ) => void;\n\n /**\n * Navigate to a path in the application.\n * @param path - The path to navigate to (e.g., \"/query\")\n */\n onNavigate?: (path: string) => void;\n\n /**\n * Track analytics events.\n * @param event - The event type\n * @param props - Event properties\n */\n onTrack?: (\n event: ContextMenuTrackEvent,\n props: ExploreActionTrackProps | LineageSelectionTrackProps,\n ) => void;\n\n /**\n * Check if histogram diff is supported for a column type.\n * @param columnType - The column data type\n * @returns true if histogram diff is supported\n */\n supportsHistogramDiff?: (columnType: string) => boolean;\n\n /**\n * Get metadata for a run type (title, icon).\n * @param type - The run type\n * @returns Run type metadata or undefined\n */\n findByRunType?: (type: string) => RunTypeMetadata | undefined;\n\n /**\n * Set the SQL query in the query context.\n * @param query - The SQL query string\n */\n setSqlQuery?: (query: string) => void;\n\n /**\n * Set the primary keys in the query context.\n * @param keys - Array of primary key column names\n */\n setPrimaryKeys?: (keys: string[] | undefined) => void;\n\n /**\n * Get the primary key for a model.\n * @param modelName - The model name\n * @returns The primary key column name or undefined\n */\n getPrimaryKey?: (modelName: string) => string | undefined;\n\n /**\n * Wrapper component for disabled menu items (e.g., SetupConnectionPopover).\n * Rendered when mode is \"metadata only\".\n */\n DisabledItemWrapper?: React.ComponentType<{\n display: boolean;\n children: ReactNode;\n }>;\n}\n\n/**\n * Context menu view options passed from LineageViewContext\n */\nexport interface ContextMenuViewOptions {\n selectMode?: \"selecting\" | \"action_result\" | undefined;\n cll?: unknown;\n showColumnLevelLineage?: (params: CllInput) => Promise<void>;\n setChangeAnalysisMode?: (active: boolean) => void;\n selectParentNodes?: (nodeId: string, degree?: number) => void;\n selectChildNodes?: (nodeId: string, degree?: number) => void;\n getNodeColumnSet?: (nodeId: string) => Set<string>;\n}\n\n/**\n * Feature toggles that affect context menu behavior\n */\nexport interface ContextMenuFeatureToggles {\n disableDatabaseQuery?: boolean;\n disableViewActionDropdown?: boolean;\n mode?: string;\n}\n\n/**\n * Server flags that affect context menu behavior\n */\nexport interface ContextMenuServerFlags {\n single_env_onboarding?: boolean;\n}\n\n/**\n * Props for the main LineageViewContextMenu component\n */\nexport interface LineageViewContextMenuProps {\n x: number;\n y: number;\n node?: LineageGraphNodes;\n isOpen: boolean;\n onClose: () => void;\n deps?: LineageViewContextMenuDeps;\n viewOptions?: ContextMenuViewOptions;\n featureToggles?: ContextMenuFeatureToggles;\n serverFlags?: ContextMenuServerFlags;\n noCatalogCurrent?: boolean;\n isActionAvailable?: (actionName: string) => boolean;\n}\n\n/**\n * Props for ModelNodeContextMenu\n */\nexport interface ModelNodeContextMenuProps {\n x: number;\n y: number;\n node?: LineageGraphNode;\n isOpen: boolean;\n onClose: () => void;\n deps?: LineageViewContextMenuDeps;\n viewOptions?: ContextMenuViewOptions;\n featureToggles?: ContextMenuFeatureToggles;\n serverFlags?: ContextMenuServerFlags;\n noCatalogCurrent?: boolean;\n isActionAvailable?: (actionName: string) => boolean;\n}\n\n/**\n * Props for ColumnNodeContextMenu\n */\nexport interface ColumnNodeContextMenuProps {\n x: number;\n y: number;\n node?: LineageGraphColumnNode;\n isOpen: boolean;\n onClose: () => void;\n deps?: LineageViewContextMenuDeps;\n featureToggles?: ContextMenuFeatureToggles;\n serverFlags?: ContextMenuServerFlags;\n isActionAvailable?: (actionName: string) => boolean;\n}\n\n// ============================================================================\n// Internal Types\n// ============================================================================\n\ninterface ContextMenuItem {\n label?: string;\n itemIcon?: ReactNode;\n action?: () => void;\n isDisabled?: boolean;\n isSeparator?: boolean;\n}\n\ninterface ContextMenuProps {\n menuItems: ContextMenuItem[];\n open: boolean;\n onClose: () => void;\n x: number;\n y: number;\n featureToggles?: ContextMenuFeatureToggles;\n DisabledItemWrapper?: React.ComponentType<{\n display: boolean;\n children: ReactNode;\n }>;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * Explore action constants\n */\nexport const EXPLORE_ACTION = {\n ROW_COUNT: \"row_count\",\n ROW_COUNT_DIFF: \"row_count_diff\",\n PROFILE: \"profile\",\n PROFILE_DIFF: \"profile_diff\",\n VALUE_DIFF: \"value_diff\",\n HISTOGRAM_DIFF: \"histogram_diff\",\n TOP_K_DIFF: \"top_k_diff\",\n} as const;\n\n/**\n * Explore source constants\n */\nexport const EXPLORE_SOURCE = {\n LINEAGE_VIEW_CONTEXT_MENU: \"lineage_view_context_menu\",\n} as const;\n\n/**\n * Lineage selection action constants\n */\nexport const LINEAGE_SELECTION_ACTION = {\n SELECT_PARENT_NODES: \"select_parent_nodes\",\n SELECT_CHILD_NODES: \"select_child_nodes\",\n SELECT_ALL_UPSTREAM: \"select_all_upstream\",\n SELECT_ALL_DOWNSTREAM: \"select_all_downstream\",\n} as const;\n\n// ============================================================================\n// Internal Components\n// ============================================================================\n\n/**\n * Base context menu component that renders the MUI Menu with items.\n */\nconst ContextMenu = ({\n menuItems,\n open,\n onClose,\n x,\n y,\n featureToggles,\n DisabledItemWrapper,\n}: ContextMenuProps) => {\n // Default wrapper that just renders children\n const Wrapper =\n DisabledItemWrapper ??\n (({ children }: { children: ReactNode }) => <>{children}</>);\n const isMetadataOnlyMode = featureToggles?.mode === \"metadata only\";\n\n return (\n <Menu\n open={open}\n onClose={onClose}\n anchorReference=\"anchorPosition\"\n anchorPosition={{ top: y, left: x }}\n slotProps={{\n paper: {\n sx: { fontSize: \"0.85rem\", width: \"250px\" },\n },\n }}\n >\n {menuItems.length === 0 ? (\n <MenuItem disabled key=\"no action\">\n No action available\n </MenuItem>\n ) : (\n menuItems.map(\n ({ isSeparator, label, isDisabled, action, itemIcon }) => {\n if (isSeparator) {\n return <Divider key={label} />;\n }\n\n const menuItem = (\n <MenuItem\n key={label}\n disabled={isDisabled}\n onClick={() => {\n if (action) {\n action();\n }\n onClose();\n }}\n >\n {itemIcon} {label}\n </MenuItem>\n );\n\n // Wrap disabled items with DisabledItemWrapper if provided\n if (isDisabled && DisabledItemWrapper) {\n return (\n <Wrapper display={isMetadataOnlyMode} key={label}>\n {menuItem}\n </Wrapper>\n );\n }\n\n return menuItem;\n },\n )\n )}\n </Menu>\n );\n};\n\n// ============================================================================\n// Public Components\n// ============================================================================\n\n/**\n * Context menu for model/node right-click actions.\n *\n * Shows menu items for:\n * - Show Impact Radius (for modified nodes)\n * - Query / Query Related Columns / Query Modified Columns\n * - Row Count / Row Count Diff\n * - Profile / Profile Diff\n * - Value Diff\n * - Select Parent/Child Nodes\n */\nexport const ModelNodeContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n deps = {},\n viewOptions = {},\n featureToggles = {},\n serverFlags = {},\n noCatalogCurrent = false,\n isActionAvailable = () => true,\n}: ModelNodeContextMenuProps) => {\n const menuItems: ContextMenuItem[] = [];\n\n const {\n runAction,\n onNavigate,\n onTrack,\n findByRunType,\n setSqlQuery,\n setPrimaryKeys,\n getPrimaryKey,\n DisabledItemWrapper,\n } = deps;\n\n const {\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n } = viewOptions;\n\n const singleEnv = serverFlags.single_env_onboarding ?? false;\n const isQueryDisabled = featureToggles.disableDatabaseQuery ?? false;\n\n if (!node?.data) {\n return <></>;\n }\n\n const modelNode = node.data;\n const resourceType = modelNode.resourceType;\n const columns = getNodeColumnSet ? Array.from(getNodeColumnSet(node.id)) : [];\n const trackProps: SubmitRunTrackProps = {\n source: \"lineage_model_node\",\n };\n const changeStatus = modelNode.changeStatus;\n const primaryKey = getPrimaryKey?.(modelNode.name);\n\n // Show Impact Radius for modified nodes\n if (changeStatus === \"modified\") {\n menuItems.push({\n label: \"Show Impact Radius\",\n itemIcon: <FaRegDotCircle />,\n isDisabled: noCatalogCurrent || !isActionAvailable(\"change_analysis\"),\n action: () => {\n setChangeAnalysisMode?.(true);\n void showColumnLevelLineage?.({\n node_id: node.id,\n change_analysis: true,\n no_upstream: true,\n });\n },\n });\n }\n\n // Query actions for model/seed/snapshot resource types\n if (\n !selectMode &&\n resourceType &&\n [\"model\", \"seed\", \"snapshot\"].includes(resourceType)\n ) {\n if (menuItems.length > 0) {\n menuItems.push({\n label: \"select group one\",\n isSeparator: true,\n });\n }\n\n // Query action\n const queryRunType = singleEnv ? \"query\" : \"query_diff\";\n const queryRun = findByRunType?.(queryRunType);\n const baseColumns = Object.keys(modelNode.data.base?.columns ?? {});\n const currentColumns = Object.keys(modelNode.data.current?.columns ?? {});\n const formattedColumns = formatSelectColumns(baseColumns, currentColumns);\n let query = `select * from {{ ref(\"${modelNode.name}\") }}`;\n if (formattedColumns.length) {\n query = `select \\n ${formattedColumns.join(\"\\n \")}\\nfrom {{ ref(\"${modelNode.name}\") }}`;\n }\n\n if (queryRun) {\n menuItems.push({\n label: \"Query\",\n itemIcon: (\n <Box component={queryRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n setSqlQuery?.(query);\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys?.(\n primaryKey !== undefined ? [primaryKey] : undefined,\n );\n }\n onNavigate?.(\"/query\");\n },\n });\n }\n\n // Query Related Columns (when CLL is active)\n if (columns.length > 0 && queryRun) {\n if (cll !== undefined) {\n const allColumns = new Set<string>();\n if (primaryKey) {\n allColumns.add(primaryKey);\n }\n columns.forEach((column) => {\n allColumns.add(column);\n });\n\n menuItems.push({\n label: \"Query Related Columns\",\n itemIcon: (\n <Box component={queryRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n const relatedQuery = `select \\n ${Array.from(allColumns).join(\",\\n \")}\\nfrom {{ ref(\"${modelNode.name}\") }}`;\n setSqlQuery?.(relatedQuery);\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys?.(\n primaryKey !== undefined ? [primaryKey] : undefined,\n );\n }\n onNavigate?.(\"/query\");\n },\n });\n } else {\n // Query Modified Columns\n const changedColumns = Object.entries(modelNode.change?.columns ?? {})\n .filter(([, value]) => value === \"modified\")\n .map(([key]) => key);\n if (changedColumns.length > 0) {\n const allColumns = new Set<string>();\n if (primaryKey) {\n allColumns.add(primaryKey);\n }\n changedColumns.forEach((column) => {\n allColumns.add(column);\n });\n\n menuItems.push({\n label: \"Query Modified Columns\",\n itemIcon: (\n <Box component={queryRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n const modifiedQuery = `select \\n ${Array.from(allColumns).join(\",\\n \")}\\nfrom {{ ref(\"${modelNode.name}\") }}`;\n setSqlQuery?.(modifiedQuery);\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys?.(\n primaryKey !== undefined ? [primaryKey] : undefined,\n );\n }\n onNavigate?.(\"/query\");\n },\n });\n }\n }\n }\n\n // Row Count / Row Count Diff\n const rowCountRunType = singleEnv ? \"row_count\" : \"row_count_diff\";\n const rowCountRun = findByRunType?.(rowCountRunType);\n if (rowCountRun) {\n menuItems.push({\n label: rowCountRun.title,\n itemIcon: (\n <Box component={rowCountRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n onTrack?.(\"explore_action\", {\n action: singleEnv\n ? EXPLORE_ACTION.ROW_COUNT\n : EXPLORE_ACTION.ROW_COUNT_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n rowCountRunType,\n { node_names: [modelNode.name] },\n { showForm: false, trackProps },\n );\n },\n });\n }\n\n // Profile / Profile Diff\n const profileRunType = singleEnv ? \"profile\" : \"profile_diff\";\n const profileRun = findByRunType?.(profileRunType);\n if (profileRun) {\n menuItems.push({\n label: profileRun.title,\n itemIcon: (\n <Box component={profileRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n const profileColumns = getNodeColumnSet\n ? Array.from(getNodeColumnSet(node.id))\n : [];\n onTrack?.(\"explore_action\", {\n action: singleEnv\n ? EXPLORE_ACTION.PROFILE\n : EXPLORE_ACTION.PROFILE_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n profileRunType,\n { model: modelNode.name, columns: profileColumns },\n { showForm: true, trackProps },\n );\n },\n });\n }\n\n // Value Diff (multi-env only)\n if (!singleEnv) {\n const valueDiffRun = findByRunType?.(\"value_diff\");\n if (valueDiffRun) {\n menuItems.push({\n label: valueDiffRun.title,\n itemIcon: (\n <Box\n component={valueDiffRun.icon}\n sx={{ display: \"inline-flex\" }}\n />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n const valueDiffColumns = getNodeColumnSet\n ? Array.from(getNodeColumnSet(node.id))\n : [];\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.VALUE_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"value_diff\",\n { model: modelNode.name, columns: valueDiffColumns },\n { showForm: true, trackProps },\n );\n },\n });\n }\n }\n }\n\n // Select Parent/Child Nodes (multi-env only)\n if (!singleEnv) {\n if (menuItems.length > 0) {\n menuItems.push({\n label: \"select group two\",\n isSeparator: true,\n });\n }\n menuItems.push({\n label: \"Select Parent Nodes\",\n itemIcon: <BiArrowFromBottom />,\n action: () => {\n onTrack?.(\"lineage_selection\", {\n action: LINEAGE_SELECTION_ACTION.SELECT_PARENT_NODES,\n });\n selectParentNodes?.(node.id, 1);\n },\n });\n menuItems.push({\n label: \"Select Child Nodes\",\n itemIcon: <BiArrowToBottom />,\n action: () => {\n onTrack?.(\"lineage_selection\", {\n action: LINEAGE_SELECTION_ACTION.SELECT_CHILD_NODES,\n });\n selectChildNodes?.(node.id, 1);\n },\n });\n menuItems.push({\n label: \"Select All Upstream Nodes\",\n itemIcon: <BiArrowFromBottom />,\n action: () => {\n onTrack?.(\"lineage_selection\", {\n action: LINEAGE_SELECTION_ACTION.SELECT_ALL_UPSTREAM,\n });\n selectParentNodes?.(node.id);\n },\n });\n menuItems.push({\n label: \"Select All Downstream Nodes\",\n itemIcon: <BiArrowToBottom />,\n action: () => {\n onTrack?.(\"lineage_selection\", {\n action: LINEAGE_SELECTION_ACTION.SELECT_ALL_DOWNSTREAM,\n });\n selectChildNodes?.(node.id);\n },\n });\n }\n\n return (\n <ContextMenu\n x={x}\n y={y}\n menuItems={menuItems}\n open={isOpen}\n onClose={onClose}\n featureToggles={featureToggles}\n DisabledItemWrapper={DisabledItemWrapper}\n />\n );\n};\n\n/**\n * Context menu for column node right-click actions.\n *\n * Shows menu items for:\n * - Profile / Profile Diff\n * - Histogram Diff\n * - Top-K Diff\n * - Value Diff\n */\nexport const ColumnNodeContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n deps = {},\n featureToggles = {},\n serverFlags = {},\n isActionAvailable = () => true,\n}: ColumnNodeContextMenuProps) => {\n const menuItems: ContextMenuItem[] = [];\n\n const {\n runAction,\n onTrack,\n findByRunType,\n supportsHistogramDiff: checkHistogramSupport,\n DisabledItemWrapper,\n } = deps;\n\n const singleEnv = serverFlags.single_env_onboarding ?? false;\n const isQueryDisabled = featureToggles.disableDatabaseQuery ?? false;\n\n if (node?.data === undefined) {\n return <></>;\n }\n\n const columnNode = node.data;\n const modelNode = columnNode.node;\n const column = columnNode.column;\n const columnType = columnNode.type;\n const trackProps: SubmitRunTrackProps = {\n source: \"lineage_column_node\",\n };\n\n const handleProfileDiff = () => {\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.PROFILE_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"profile_diff\",\n { model: modelNode.name, columns: [column] },\n { showForm: false, trackProps },\n );\n };\n\n const handleHistogramDiff = () => {\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.HISTOGRAM_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"histogram_diff\",\n { model: modelNode.name, column_name: column, column_type: columnType },\n { showForm: false, trackProps },\n );\n };\n\n const handleTopkDiff = () => {\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.TOP_K_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"top_k_diff\",\n { model: modelNode.name, column_name: column, k: 50 },\n { showForm: false, trackProps },\n );\n };\n\n const handleValueDiff = () => {\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.VALUE_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"value_diff\",\n { model: modelNode.name, columns: [column] },\n { showForm: true, trackProps },\n );\n };\n\n const addedOrRemoved =\n modelNode.data.base?.columns?.[column] === undefined ||\n modelNode.data.current?.columns?.[column] === undefined;\n\n // Profile / Profile Diff\n const profileRunType = singleEnv ? \"profile\" : \"profile_diff\";\n const profileRun = findByRunType?.(profileRunType);\n if (profileRun) {\n menuItems.push({\n label: profileRun.title,\n itemIcon: (\n <Box component={profileRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n action: handleProfileDiff,\n isDisabled:\n addedOrRemoved || !isActionAvailable(\"profile_diff\") || isQueryDisabled,\n });\n }\n\n // Histogram Diff, Top-K Diff, Value Diff (multi-env only)\n if (!singleEnv) {\n const histogramRun = findByRunType?.(\"histogram_diff\");\n if (histogramRun) {\n const histogramSupported = checkHistogramSupport?.(columnType) ?? true;\n menuItems.push({\n label: histogramRun.title,\n itemIcon: (\n <Box component={histogramRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n action: handleHistogramDiff,\n isDisabled: addedOrRemoved || !histogramSupported || isQueryDisabled,\n });\n }\n\n const topKRun = findByRunType?.(\"top_k_diff\");\n if (topKRun) {\n menuItems.push({\n label: topKRun.title,\n itemIcon: (\n <Box component={topKRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n action: handleTopkDiff,\n isDisabled: addedOrRemoved || isQueryDisabled,\n });\n }\n\n const valueDiffRun = findByRunType?.(\"value_diff\");\n if (valueDiffRun) {\n menuItems.push({\n label: valueDiffRun.title,\n itemIcon: (\n <Box component={valueDiffRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n action: handleValueDiff,\n isDisabled: addedOrRemoved || isQueryDisabled,\n });\n }\n }\n\n return (\n <ContextMenu\n x={x}\n y={y}\n menuItems={menuItems}\n open={isOpen}\n onClose={onClose}\n featureToggles={featureToggles}\n DisabledItemWrapper={DisabledItemWrapper}\n />\n );\n};\n\n/**\n * Main context menu component that delegates to ModelNodeContextMenu\n * or ColumnNodeContextMenu based on node type.\n *\n * @example\n * ```tsx\n * <LineageViewContextMenu\n * x={event.clientX}\n * y={event.clientY}\n * node={selectedNode}\n * isOpen={menuOpen}\n * onClose={() => setMenuOpen(false)}\n * deps={contextMenuDeps}\n * viewOptions={lineageViewOptions}\n * featureToggles={featureToggles}\n * serverFlags={serverFlags}\n * noCatalogCurrent={!catalog?.current}\n * isActionAvailable={isActionAvailable}\n * />\n * ```\n */\nexport const LineageViewContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n deps = {},\n viewOptions = {},\n featureToggles = {},\n serverFlags = {},\n noCatalogCurrent = false,\n isActionAvailable = () => true,\n}: LineageViewContextMenuProps) => {\n if (featureToggles.disableViewActionDropdown) {\n return (\n <ContextMenu\n menuItems={[]}\n open={isOpen}\n onClose={onClose}\n x={x}\n y={y}\n featureToggles={featureToggles}\n DisabledItemWrapper={deps.DisabledItemWrapper}\n />\n );\n }\n\n if (node && isLineageGraphNode(node)) {\n return (\n <ModelNodeContextMenu\n x={x}\n y={y}\n isOpen={isOpen}\n onClose={onClose}\n node={node}\n deps={deps}\n viewOptions={viewOptions}\n featureToggles={featureToggles}\n serverFlags={serverFlags}\n noCatalogCurrent={noCatalogCurrent}\n isActionAvailable={isActionAvailable}\n />\n );\n }\n\n if (node && isLineageGraphColumnNode(node)) {\n return (\n <ColumnNodeContextMenu\n x={x}\n y={y}\n isOpen={isOpen}\n onClose={onClose}\n node={node}\n deps={deps}\n featureToggles={featureToggles}\n serverFlags={serverFlags}\n isActionAvailable={isActionAvailable}\n />\n );\n }\n\n return null;\n};\n\n/**\n * Hook to manage context menu state.\n * Returns props for LineageViewContextMenu and methods to show/close the menu.\n *\n * @example\n * ```tsx\n * const { props, showContextMenu, closeContextMenu } = useLineageViewContextMenu();\n *\n * const handleNodeContextMenu = (event: React.MouseEvent, node: LineageGraphNodes) => {\n * event.preventDefault();\n * showContextMenu(event.clientX, event.clientY, node);\n * };\n *\n * return (\n * <>\n * <LineageCanvas onNodeContextMenu={handleNodeContextMenu} />\n * <LineageViewContextMenu {...props} deps={deps} />\n * </>\n * );\n * ```\n */\nexport const useLineageViewContextMenu = () => {\n const [open, setOpen] = useState(false);\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n const [position, setPosition] = useState<{ x: number; y: number }>({\n x: 0,\n y: 0,\n });\n const [node, setNode] = useState<LineageGraphNodes>();\n\n const showContextMenu = (x: number, y: number, node: LineageGraphNodes) => {\n setPosition({ x, y });\n setNode(node);\n onOpen();\n };\n\n const closeContextMenu = () => {\n setPosition({ x: 0, y: 0 });\n setNode(undefined);\n onClose();\n };\n\n const props: Pick<\n LineageViewContextMenuProps,\n \"x\" | \"y\" | \"node\" | \"isOpen\" | \"onClose\"\n > = {\n x: position.x,\n y: position.y,\n node,\n isOpen: open,\n onClose,\n };\n\n return {\n props,\n showContextMenu,\n closeContextMenu,\n };\n};\n","import {\n IGNORE_SCREENSHOT_CLASS,\n useClipBoardToast,\n useCopyToClipboard,\n useThemeColors,\n} from \"../../../hooks\";\nimport { colors } from \"../../../theme\";\n\n/**\n * Hook that provides clipboard functionality for the lineage view.\n * Wraps useCopyToClipboard with lineage-specific configuration.\n *\n * @returns Object containing copyToClipboard function, ImageDownloadModal component, and ref\n */\nexport const useLineageCopyToClipboard = () => {\n const { isDark } = useThemeColors();\n const { successToast, failToast } = useClipBoardToast();\n\n return useCopyToClipboard({\n renderLibrary: \"html-to-image\",\n imageType: \"png\",\n shadowEffect: true,\n backgroundColor: isDark ? colors.neutral[900] : colors.neutral[50],\n ignoreElements: (element: Element) => {\n try {\n return element.classList.contains(IGNORE_SCREENSHOT_CLASS);\n } catch {\n if (element.className) {\n return element.className.includes(IGNORE_SCREENSHOT_CLASS);\n }\n return false;\n }\n },\n onSuccess: () => {\n successToast(\"Copied the Lineage View as an image to clipboard\");\n },\n onError: (error) => {\n console.error(\"Error taking screenshot\", error);\n failToast(\"Failed to copy image to clipboard\", error);\n },\n });\n};\n","import { useRouter } from \"next/navigation\";\nimport { useCallback } from \"react\";\nimport type { Check } from \"../../../api\";\nimport { useRouteConfig } from \"../../../contexts\";\n\n/**\n * Hook that provides navigation to a check's detail page.\n *\n * Uses the app router to navigate to /checks/?id={check_id}\n *\n * @returns A function that navigates to the given check's detail page\n */\nexport const useNavToCheck = () => {\n const router = useRouter();\n const { basePath } = useRouteConfig();\n\n return useCallback(\n (check: Check) => {\n if (check.check_id) {\n router.push(`${basePath}/checks/?id=${check.check_id}`);\n }\n },\n [router.push, basePath],\n );\n};\n","import type { RefObject } from \"react\";\nimport { useEffect, useRef } from \"react\";\n\n/**\n * Hook that observes element resize and calls a handler when the size changes\n * significantly (more than 10px difference in width or height).\n *\n * Used to trigger layout updates when the lineage view container is resized.\n *\n * @param ref - RefObject pointing to the HTML element to observe\n * @param handler - Callback function to invoke when resize is detected\n */\nexport const useResizeObserver = (\n ref: RefObject<HTMLElement | null>,\n handler: () => void,\n) => {\n const size = useRef({\n width: 0,\n height: 0,\n });\n\n useEffect(() => {\n const target = ref.current;\n const handleResize = (entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const newWidth = entry.contentRect.width;\n const newHeight = entry.contentRect.height;\n\n if (\n Math.abs(newHeight - size.current.height) > 10 ||\n Math.abs(newWidth - size.current.width) > 10\n ) {\n if (\n size.current.height > 0 &&\n newHeight > 0 &&\n size.current.width > 0 &&\n newWidth > 0\n ) {\n handler();\n }\n }\n size.current = {\n width: newWidth,\n height: newHeight,\n };\n }\n };\n\n const resizeObserver = new ResizeObserver(handleResize);\n\n if (target) {\n resizeObserver.observe(target);\n }\n\n return () => {\n if (target) {\n resizeObserver.unobserve(target);\n }\n };\n }, [handler, ref]);\n};\n","import { useCallback } from \"react\";\nimport type { LineageGraphNodes } from \"../../../contexts/lineage/types\";\nimport { isLineageGraphNode } from \"../../../contexts/lineage/types\";\nimport {\n type LineageViewRenderProps,\n trackLineageViewRender,\n} from \"../../../lib/api/track\";\n\n/**\n * Hook that provides a function to track lineage view render events.\n * Calculates node statistics and sends tracking data.\n *\n * @returns A memoized callback function for tracking lineage renders\n */\nexport const useTrackLineageRender = () => {\n return useCallback(\n (\n nodes: LineageGraphNodes[],\n currentViewMode: string,\n impactRadiusEnabled: boolean,\n cllColumnActive: boolean,\n rightSidebarOpen: boolean,\n ) => {\n const lineageGraphNodesOnly = nodes.filter(isLineageGraphNode);\n const grouped = Object.groupBy(\n lineageGraphNodesOnly,\n (node) => node.data.changeStatus ?? \"unchanged\",\n );\n // Prefix status counts with \"nodes_\"\n const statusCounts = Object.fromEntries(\n Object.entries(grouped).map(([status, nodes]) => [\n `nodes_${status}`,\n nodes?.length ?? 0,\n ]),\n );\n const trackingData = {\n node_count: lineageGraphNodesOnly.length,\n view_mode: currentViewMode,\n impact_radius_enabled: impactRadiusEnabled,\n right_sidebar_open: rightSidebarOpen,\n ...statusCounts,\n } as LineageViewRenderProps;\n // Only include cll_column_active when a column is being viewed\n if (cllColumnActive) {\n trackingData.cll_column_active = true;\n }\n trackLineageViewRender(trackingData);\n },\n [],\n );\n};\n","/**\n * @file useMultiNodesActionOss.ts\n * @description OSS wrapper for useMultiNodesAction hook.\n *\n * This wraps the base hook and adds Amplitude tracking.\n *\n * @see useMultiNodesAction for the base implementation\n */\n\nimport type { LineageGraphNode } from \"../index\";\nimport {\n EXPLORE_ACTION,\n EXPLORE_SOURCE,\n trackExploreAction,\n} from \"../lib/api/track\";\nimport {\n type MultiNodesActionCallbacks,\n type MultiNodesActionTrackProps,\n useMultiNodesAction as useMultiNodesActionBase,\n} from \"./useMultiNodesAction\";\n\n/**\n * Maps the generic action type to OSS-specific tracking constants.\n * This allows the base hook to use generic action names while\n * OSS uses its specific Amplitude tracking events.\n */\nconst actionTypeToExploreAction = {\n row_count: EXPLORE_ACTION.ROW_COUNT,\n row_count_diff: EXPLORE_ACTION.ROW_COUNT_DIFF,\n value_diff: EXPLORE_ACTION.VALUE_DIFF,\n} as const;\n\n/**\n * Tracking callback implementation for OSS.\n * Translates generic action tracking to Amplitude events.\n */\nconst handleTrackAction = (props: MultiNodesActionTrackProps) => {\n const exploreAction = actionTypeToExploreAction[props.action];\n if (exploreAction) {\n trackExploreAction({\n action: exploreAction,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_TOP_BAR,\n node_count: props.node_count,\n });\n }\n};\n\n/**\n * OSS wrapper for useMultiNodesAction that provides Amplitude tracking.\n *\n * @param nodes - Array of lineage graph nodes to operate on\n * @param callbacks - Lifecycle callbacks for action execution\n * @returns Object containing action state and operation methods\n */\nexport const useMultiNodesActionOss = (\n nodes: LineageGraphNode[],\n callbacks: MultiNodesActionCallbacks,\n) => {\n return useMultiNodesActionBase(nodes, {\n ...callbacks,\n onTrackAction: handleTrackAction,\n trackingSource: EXPLORE_SOURCE.LINEAGE_VIEW_TOP_BAR,\n });\n};\n","\"use client\";\n\n/**\n * @file useValueDiffAlertDialogOss.tsx\n * @description OSS wrapper for useValueDiffAlertDialog that adds tracking.\n *\n * This is a thin wrapper around the base hook that injects\n * tracking callbacks for analytics.\n */\n\nimport {\n EXPLORE_ACTION,\n EXPLORE_FORM_EVENT,\n trackExploreActionForm,\n} from \"../lib/api/track\";\nimport { useValueDiffAlertDialog as useBaseDialog } from \"./useValueDiffAlertDialog\";\n\n/**\n * Hook for displaying a value diff confirmation dialog with tracking.\n *\n * This wrapper adds tracking callbacks to the base hook.\n */\nfunction useValueDiffAlertDialogOss() {\n return useBaseDialog({\n onConfirm: () =>\n trackExploreActionForm({\n action: EXPLORE_ACTION.VALUE_DIFF,\n event: EXPLORE_FORM_EVENT.EXECUTE,\n }),\n onCancel: () =>\n trackExploreActionForm({\n action: EXPLORE_ACTION.VALUE_DIFF,\n event: EXPLORE_FORM_EVENT.CANCEL,\n }),\n });\n}\n\nexport default useValueDiffAlertDialogOss;\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport IconButton from \"@mui/material/IconButton\";\nimport React, { useState } from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport { SESSION_STORAGE_KEYS } from \"../../api\";\n\nexport interface NotificationProps {\n notification?: React.ReactNode;\n type: \"info\" | \"success\" | \"warning\" | \"error\";\n}\n\nexport function LineageViewNotification({\n notification,\n type,\n}: NotificationProps) {\n const notificationKey = SESSION_STORAGE_KEYS.lineageNotificationDismissed;\n\n // Initialize state from sessionStorage (lazy initialization)\n const [visible, setVisible] = useState(() => {\n const dismissed = sessionStorage.getItem(notificationKey);\n return dismissed !== \"true\";\n });\n\n if (notification === null || !visible) {\n return null;\n }\n\n const bgColor = {\n info: \"iochmara.light\",\n success: \"success.light\",\n warning: \"warning.light\",\n error: \"error.light\",\n }[type];\n\n return (\n <Box\n sx={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n p: \"5px 10px\",\n gap: \"5px\",\n alignItems: \"flex-start\",\n borderRadius: 1,\n boxShadow: 4,\n border: \"1px solid\",\n borderColor: \"neutral.light\",\n bgcolor: bgColor,\n }}\n >\n {notification}\n <Box sx={{ flex: 1 }} />\n <IconButton\n size=\"small\"\n onClick={() => {\n sessionStorage.setItem(notificationKey, \"true\");\n setVisible(false);\n }}\n >\n <IoClose />\n </IconButton>\n </Box>\n );\n}\n","\"use client\";\n\n/**\n * @file LineageViewContextMenuOss.tsx\n * @description Thin wrapper that imports from @datarecce/ui and injects OSS-specific implementations.\n *\n * This file serves as the integration layer between the @datarecce/ui library and the OSS application.\n * It injects:\n * - Amplitude analytics tracking\n * - Application routing\n * - Query context management\n * - Run type registry\n * - Histogram diff support checking\n */\n\nimport { useRouter } from \"next/navigation\";\nimport type {\n LineageGraphColumnNode,\n LineageGraphNode,\n LineageGraphNodes,\n} from \"../..\";\nimport {\n useLineageGraphContext,\n useLineageViewContextSafe,\n useRecceActionContext,\n useRecceInstanceContext,\n useRecceServerFlag,\n useRouteConfig,\n} from \"../../contexts\";\nimport { useModelColumns, useRecceQueryContext } from \"../../hooks\";\nimport { trackExploreAction, trackLineageSelection } from \"../../lib/api/track\";\nimport { SetupConnectionPopover } from \"../app\";\nimport { supportsHistogramDiff } from \"../histogram\";\nimport { findByRunType } from \"../run\";\nimport {\n ColumnNodeContextMenu as BaseColumnNodeContextMenu,\n LineageViewContextMenu as BaseLineageViewContextMenu,\n ModelNodeContextMenu as BaseModelNodeContextMenu,\n useLineageViewContextMenu as baseUseLineageViewContextMenu,\n EXPLORE_ACTION,\n EXPLORE_SOURCE,\n LINEAGE_SELECTION_ACTION,\n type LineageViewContextMenuDeps,\n} from \"./contextmenu\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface LineageViewContextMenuProps<T> {\n x: number;\n y: number;\n node?: T;\n isOpen: boolean;\n onClose: () => void;\n}\n\n// ============================================================================\n// Internal Hooks\n// ============================================================================\n\n/**\n * Hook to create the dependency injection props for context menu components.\n * Wires up OSS-specific implementations for tracking, navigation, etc.\n */\nconst useContextMenuDeps = (modelName?: string): LineageViewContextMenuDeps => {\n const { runAction } = useRecceActionContext();\n const { setSqlQuery, setPrimaryKeys } = useRecceQueryContext();\n const router = useRouter();\n const { primaryKey } = useModelColumns(modelName);\n const { basePath } = useRouteConfig();\n\n return {\n runAction: (type, params, options) => {\n runAction(type, params as Parameters<typeof runAction>[1], options);\n },\n onNavigate: (path) => {\n router.push(`${basePath}${path}`);\n },\n onTrack: (event, props) => {\n if (event === \"explore_action\") {\n trackExploreAction({\n action: (props as { action: string })\n .action as (typeof EXPLORE_ACTION)[keyof typeof EXPLORE_ACTION],\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: (props as { node_count: number }).node_count,\n });\n } else if (event === \"lineage_selection\") {\n trackLineageSelection({\n action: (props as { action: string })\n .action as (typeof LINEAGE_SELECTION_ACTION)[keyof typeof LINEAGE_SELECTION_ACTION],\n });\n }\n },\n supportsHistogramDiff,\n findByRunType: (type) => {\n const entry = findByRunType(type as Parameters<typeof findByRunType>[0]);\n return entry ? { title: entry.title, icon: entry.icon } : undefined;\n },\n setSqlQuery,\n setPrimaryKeys,\n getPrimaryKey: () => primaryKey,\n // Note: SetupConnectionPopover has a more restrictive children type (ReactElement vs ReactNode)\n // but it works correctly in practice since we always pass MenuItem elements\n DisabledItemWrapper:\n SetupConnectionPopover as LineageViewContextMenuDeps[\"DisabledItemWrapper\"],\n };\n};\n\n// ============================================================================\n// Exported Components\n// ============================================================================\n\n/**\n * OSS wrapper for ModelNodeContextMenu.\n * Injects OSS-specific dependencies and context.\n */\nexport const ModelNodeContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n}: LineageViewContextMenuProps<LineageGraphNode>) => {\n const {\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n } = useLineageViewContextSafe();\n const { featureToggles } = useRecceInstanceContext();\n const { isActionAvailable, lineageGraph } = useLineageGraphContext();\n const { data: flag } = useRecceServerFlag();\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n\n const deps = useContextMenuDeps(node?.data?.name);\n\n return (\n <BaseModelNodeContextMenu\n x={x}\n y={y}\n node={node}\n isOpen={isOpen}\n onClose={onClose}\n deps={deps}\n viewOptions={{\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n }}\n featureToggles={{\n disableDatabaseQuery: featureToggles.disableDatabaseQuery,\n mode: featureToggles.mode ?? undefined,\n }}\n serverFlags={{\n single_env_onboarding: flag?.single_env_onboarding,\n }}\n noCatalogCurrent={noCatalogCurrent}\n isActionAvailable={isActionAvailable}\n />\n );\n};\n\n/**\n * OSS wrapper for ColumnNodeContextMenu.\n * Injects OSS-specific dependencies and context.\n */\nexport const ColumnNodeContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n}: LineageViewContextMenuProps<LineageGraphColumnNode>) => {\n const { featureToggles } = useRecceInstanceContext();\n const { isActionAvailable } = useLineageGraphContext();\n const { data: flag } = useRecceServerFlag();\n\n const deps = useContextMenuDeps(node?.data?.node?.name);\n\n return (\n <BaseColumnNodeContextMenu\n x={x}\n y={y}\n node={node}\n isOpen={isOpen}\n onClose={onClose}\n deps={deps}\n featureToggles={{\n disableDatabaseQuery: featureToggles.disableDatabaseQuery,\n mode: featureToggles.mode ?? undefined,\n }}\n serverFlags={{\n single_env_onboarding: flag?.single_env_onboarding,\n }}\n isActionAvailable={isActionAvailable}\n />\n );\n};\n\n/**\n * OSS wrapper for LineageViewContextMenu.\n * Injects OSS-specific dependencies and context.\n */\nexport const LineageViewContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n}: LineageViewContextMenuProps<LineageGraphNodes>) => {\n const {\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n } = useLineageViewContextSafe();\n const { featureToggles } = useRecceInstanceContext();\n const { isActionAvailable, lineageGraph } = useLineageGraphContext();\n const { data: flag } = useRecceServerFlag();\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n\n // Get model name from either model node or column node\n const modelName =\n node?.type === \"lineageGraphNode\"\n ? (node as LineageGraphNode).data?.name\n : node?.type === \"lineageGraphColumnNode\"\n ? (node as LineageGraphColumnNode).data?.node?.name\n : undefined;\n\n const deps = useContextMenuDeps(modelName);\n\n return (\n <BaseLineageViewContextMenu\n x={x}\n y={y}\n node={node}\n isOpen={isOpen}\n onClose={onClose}\n deps={deps}\n viewOptions={{\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n }}\n featureToggles={{\n disableDatabaseQuery: featureToggles.disableDatabaseQuery,\n disableViewActionDropdown: featureToggles.disableViewActionDropdown,\n mode: featureToggles.mode ?? undefined,\n }}\n serverFlags={{\n single_env_onboarding: flag?.single_env_onboarding,\n }}\n noCatalogCurrent={noCatalogCurrent}\n isActionAvailable={isActionAvailable}\n />\n );\n};\n\n/**\n * Re-export the hook from @datarecce/ui.\n * This hook doesn't need any OSS-specific modifications.\n */\nexport const useLineageViewContextMenu = baseUseLineageViewContextMenu;\n","import dagre from \"@dagrejs/dagre\";\nimport { Position } from \"@xyflow/react\";\nimport {\n COLUMN_HEIGHT,\n type LineageGraph,\n type LineageGraphColumnNode,\n type LineageGraphEdge,\n type LineageGraphNode,\n type LineageGraphNodes,\n layoutWithDagre,\n type NodeColumnSetMap,\n} from \"../..\";\nimport type { ColumnLineageData } from \"../../api\";\n\n/**\n * Convert a LineageGraph to React Flow nodes and edges with column-level lineage support\n *\n * This OSS-specific function extends the basic toReactFlow functionality with:\n * - Column-level lineage (CLL) visualization\n * - Dynamic node heights based on column count\n * - Column nodes as child nodes within parent model nodes\n *\n * @param lineageGraph - The lineage graph to convert\n * @param options - Conversion options\n * @param options.selectedNodes - Optional filter for which nodes to include\n * @param options.cll - Column-level lineage data for adding column nodes\n * @param options.existingPositions - Map of node IDs to their existing positions. If provided, nodes will preserve their positions and layout will be skipped if all nodes have positions.\n * @returns Tuple of [nodes, edges, nodeColumnSetMap] where nodeColumnSetMap tracks columns per node\n *\n * @example\n * ```tsx\n * const [nodes, edges, columnSetMap] = toReactFlow(lineageGraph, {\n * selectedNodes: [\"model.project.orders\"],\n * cll: columnLineageData,\n * });\n * ```\n */\nexport function toReactFlow(\n lineageGraph: LineageGraph,\n options?: {\n selectedNodes?: string[];\n cll?: ColumnLineageData;\n existingPositions?: Map<string, { x: number; y: number }>;\n },\n): [LineageGraphNodes[], LineageGraphEdge[], NodeColumnSetMap] {\n const nodes: LineageGraphNodes[] = [];\n const edges: LineageGraphEdge[] = [];\n const { selectedNodes, cll, existingPositions } = options ?? {};\n\n const nodeColumnSetMap: NodeColumnSetMap = {};\n\n function getWeight(from?: string) {\n if (from === \"base\") {\n return 0;\n } else if (from === \"current\") {\n return 2;\n } else {\n return 1;\n }\n }\n\n function compareFn(\n a: LineageGraphNode | LineageGraphEdge,\n b: LineageGraphNode | LineageGraphEdge,\n ) {\n const weightA = getWeight(a.data?.from);\n const weightB = getWeight(b.data?.from);\n\n if (weightA < weightB) {\n return -1;\n } else if (weightA > weightB) {\n return 1;\n }\n return 0;\n }\n\n const filterSet =\n selectedNodes !== undefined ? new Set(selectedNodes) : undefined;\n const sortedNodes = Object.values(lineageGraph.nodes).sort(compareFn);\n for (const node of sortedNodes) {\n if (filterSet && !filterSet.has(node.id)) {\n continue;\n }\n\n // add column nodes\n const nodeColumnSet = new Set<string>();\n let columnIndex = 0;\n if (cll) {\n const maybeCurrent = cll.current as unknown as\n | ColumnLineageData[\"current\"]\n | undefined;\n const parentMap = maybeCurrent?.parent_map[node.id] ?? new Set<string>();\n\n for (const parentKey of parentMap) {\n const source = parentKey;\n const target = node.id;\n\n edges.push({\n id: `m2c_${source}_${target}`,\n source,\n target,\n style: {\n zIndex: 9999,\n },\n });\n }\n\n for (const columnName of Object.keys(\n node.data.data.current?.columns ?? {},\n )) {\n const columnKey = `${node.id}_${columnName}`;\n const maybeCurrent = cll.current as unknown as\n | ColumnLineageData[\"current\"]\n | undefined;\n const column = maybeCurrent?.columns[columnKey];\n const parentMap =\n maybeCurrent?.parent_map[columnKey] ?? new Set<string>();\n\n if (column == null) {\n continue;\n }\n\n nodes.push({\n id: columnKey,\n position: { x: 10, y: 70 + columnIndex * COLUMN_HEIGHT },\n parentId: node.id,\n extent: \"parent\",\n draggable: false,\n className: \"no-track-pii-safe\",\n data: {\n node: node.data,\n column: column.name,\n type: column.type,\n transformationType: column.transformation_type,\n changeStatus: column.change_status,\n },\n style: {\n zIndex: 9999,\n },\n type: \"lineageGraphColumnNode\",\n targetPosition: Position.Left,\n sourcePosition: Position.Right,\n } as LineageGraphColumnNode);\n\n for (const parentColumn of parentMap) {\n const source = parentColumn;\n const target = columnKey;\n\n edges.push({\n id: `${source}_${target}`,\n source,\n target,\n style: {\n zIndex: 9999,\n },\n });\n }\n\n columnIndex++;\n nodeColumnSet.add(column.name);\n }\n }\n\n nodeColumnSetMap[node.id] = nodeColumnSet;\n\n let height = 60;\n if (columnIndex > 0) {\n height += 20 + columnIndex * COLUMN_HEIGHT;\n }\n\n const existingPosition = existingPositions?.get(node.id);\n nodes.unshift({\n id: node.id,\n position: existingPosition ?? { x: 0, y: 0 },\n width: 300,\n height: height,\n className: \"no-track-pii-safe\",\n data: {\n ...node.data,\n },\n type: \"lineageGraphNode\",\n targetPosition: Position.Left,\n sourcePosition: Position.Right,\n style: {\n width: 300,\n height: height,\n },\n } as LineageGraphNode);\n }\n\n const sortedEdges = Object.values(lineageGraph.edges).sort(compareFn);\n for (const edge of sortedEdges) {\n if (\n filterSet &&\n (!filterSet.has(edge.source) || !filterSet.has(edge.target))\n ) {\n continue;\n }\n\n edges.push({\n id: edge.id,\n type: \"lineageGraphEdge\",\n source: edge.source,\n target: edge.target,\n data: {\n ...edge.data,\n },\n } as LineageGraphEdge);\n }\n\n // Only run layout if any parent node is missing a position\n const needsLayout = nodes.some(\n (node) =>\n node.type === \"lineageGraphNode\" && !existingPositions?.has(node.id),\n );\n\n if (needsLayout) {\n layout(nodes, edges);\n }\n\n return [nodes, edges, nodeColumnSetMap];\n}\n\n/**\n * Apply dagre layout to lineage graph nodes and edges\n *\n * This is a thin wrapper around layoutWithDagre from @datarecce/ui\n * that provides the dagre library instance.\n *\n * @param nodes - Array of lineage graph nodes\n * @param edges - Array of lineage graph edges\n * @param direction - Layout direction (\"LR\" for left-to-right, \"TB\" for top-to-bottom)\n */\nexport const layout = (\n nodes: LineageGraphNodes[],\n edges: LineageGraphEdge[],\n direction = \"LR\",\n): void => {\n layoutWithDagre(dagre, nodes, edges, direction);\n};\n","import MuiAlert from \"@mui/material/Alert\";\nimport Box from \"@mui/material/Box\";\nimport \"./style.css\";\nimport type {\n CellClickedEvent,\n GridApi,\n GridReadyEvent,\n RowClassParams,\n} from \"ag-grid-community\";\nimport {\n forwardRef,\n Ref,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport type { NodeData } from \"../../api\";\nimport { useLineageGraphContext, useLineageViewContext } from \"../../contexts\";\nimport { trackColumnLevelLineage } from \"../../lib/api/track\";\nimport type {\n SchemaDiffRow,\n SchemaRow,\n} from \"../../lib/dataGrid/generators/toSchemaDataGrid\";\nimport {\n type DataGridHandle,\n EmptyRowsRenderer,\n ScreenshotDataGrid,\n} from \"../../primitives\";\nimport { createDataGridFromData } from \"../ui/dataGrid\";\n\nexport function SchemaLegend() {\n return (\n <Box\n sx={{\n display: \"flex\",\n gap: 2,\n px: 1,\n py: 0.5,\n fontSize: \"0.75rem\",\n color: \"text.secondary\",\n }}\n >\n <span>\n <span className=\"schema-change-badge schema-change-badge-added\">+</span>{\" \"}\n added\n </span>\n <span>\n <span className=\"schema-change-badge schema-change-badge-removed\">\n -\n </span>{\" \"}\n removed\n </span>\n <span>\n <span className=\"schema-change-badge schema-change-badge-changed\">\n ~\n </span>{\" \"}\n changed\n </span>\n </Box>\n );\n}\n\ninterface SchemaViewProps {\n base?: NodeData;\n current?: NodeData;\n enableScreenshot?: boolean;\n showMenu?: boolean;\n /** Per-column change status from breaking change analysis */\n columnChanges?: Record<string, \"added\" | \"removed\" | \"modified\"> | null;\n /** Callback when user clicks a definition-changed badge to view SQL diff */\n onViewCode?: () => void;\n}\n\nfunction PrivateSingleEnvSchemaView(\n { current, showMenu = true }: { current?: NodeData; showMenu?: boolean },\n ref: Ref<DataGridHandle>,\n) {\n const lineageViewContext = useLineageViewContext();\n const [gridApi, setGridApi] = useState<GridApi<SchemaRow> | null>(null);\n const [cllRunningMap, setCllRunningMap] = useState<Map<string, boolean>>(\n new Map(),\n );\n const { columns, rows } = useMemo(() => {\n return createDataGridFromData(\n { type: \"schema_single\", columns: current?.columns },\n { node: current, cllRunningMap, showMenu },\n );\n }, [current, cllRunningMap, showMenu]);\n\n const { lineageGraph, isActionAvailable } = useLineageGraphContext();\n const changeAnalysisAvailable = isActionAvailable(\"change_analysis\");\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n let catalogMissingMessage = undefined;\n if (noCatalogCurrent) {\n catalogMissingMessage =\n \"catalog.json not found. Run `recce debug` to troubleshoot.\";\n }\n\n const noSchemaCurrent = current && current.columns === undefined;\n let schemaMissingMessage = undefined;\n if (noSchemaCurrent) {\n schemaMissingMessage =\n \"Node schema not found in catalog.json. Please regenerate your catalog.json to update.\";\n }\n\n const handleViewCll = async (columnName: string) => {\n if (!changeAnalysisAvailable) return;\n trackColumnLevelLineage({ action: \"view\", source: \"schema_column\" });\n setCllRunningMap((prev) => new Map(prev).set(columnName, true));\n const modelId = current?.id;\n if (modelId) {\n await lineageViewContext?.showColumnLevelLineage({\n node_id: modelId,\n column: columnName,\n });\n }\n setCllRunningMap((prev) => new Map(prev).set(columnName, false));\n };\n\n const getRowId = useCallback(\n (params: { data: SchemaRow }) => {\n const modelId = current?.id;\n return `${modelId}-${params.data.name}`;\n },\n [current?.id],\n );\n\n const cll = lineageViewContext?.viewOptions.column_level_lineage;\n const selectedRowId = cll ? `${cll.node_id}-${cll.column}` : null;\n\n // Update row selection when cll changes\n useEffect(() => {\n if (!gridApi) return;\n gridApi.deselectAll();\n if (selectedRowId) {\n const rowNode = gridApi.getRowNode(selectedRowId);\n if (rowNode) {\n rowNode.setSelected(true);\n }\n }\n }, [gridApi, selectedRowId]);\n\n const handleGridReady = useCallback((event: GridReadyEvent<SchemaRow>) => {\n setGridApi(event.api);\n }, []);\n\n const getRowClass = (_params: RowClassParams<SchemaRow>) => {\n if (lineageViewContext !== undefined && changeAnalysisAvailable) {\n return \"row-normal row-selectable\";\n }\n return \"row-normal\";\n };\n\n const handleCellClicked = async (event: CellClickedEvent<SchemaRow>) => {\n // Skip if clicking on the menu button\n const target = event.event?.target as HTMLElement | undefined;\n if (target?.closest(\".row-context-menu\")) {\n return;\n }\n const row = event.data;\n if (row) {\n await handleViewCll(row.name);\n }\n };\n\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n {catalogMissingMessage ? (\n <MuiAlert severity=\"warning\" sx={{ fontSize: \"12px\", p: 1 }}>\n {catalogMissingMessage}\n </MuiAlert>\n ) : schemaMissingMessage ? (\n <MuiAlert severity=\"warning\" sx={{ fontSize: \"12px\", p: 1 }}>\n {schemaMissingMessage}\n </MuiAlert>\n ) : (\n <></>\n )}\n\n {rows.length > 0 && (\n <ScreenshotDataGrid\n style={{\n blockSize: \"auto\",\n maxHeight: \"100%\",\n overflow: \"auto\",\n fontSize: \"10pt\",\n borderWidth: 1,\n }}\n columns={columns}\n rows={rows}\n renderers={{ noRowsFallback: <EmptyRowsRenderer /> }}\n ref={ref}\n getRowId={getRowId}\n getRowClass={getRowClass}\n onCellClicked={handleCellClicked}\n onGridReady={handleGridReady}\n rowSelection={{ mode: \"singleRow\", checkboxes: false }}\n containerClassName=\"no-track-pii-safe\"\n rowClassName=\"no-track-pii-safe\"\n />\n )}\n </Box>\n );\n}\n\nexport function PrivateSchemaView(\n {\n base,\n current,\n showMenu = true,\n columnChanges,\n onViewCode,\n }: SchemaViewProps,\n ref: Ref<DataGridHandle>,\n) {\n const lineageViewContext = useLineageViewContext();\n const [gridApi, setGridApi] = useState<GridApi<SchemaDiffRow> | null>(null);\n const [cllRunningMap, setCllRunningMap] = useState<Map<string, boolean>>(\n new Map(),\n );\n const { columns, rows } = useMemo(() => {\n const resourceType = current?.resource_type ?? base?.resource_type;\n const node =\n resourceType &&\n [\"model\", \"seed\", \"snapshot\", \"source\"].includes(resourceType)\n ? (current ?? base)\n : undefined;\n\n return createDataGridFromData(\n { type: \"schema_diff\", base: base?.columns, current: current?.columns },\n { node, cllRunningMap, showMenu, columnChanges, onViewCode },\n );\n }, [base, current, cllRunningMap, showMenu, columnChanges, onViewCode]);\n\n const { lineageGraph, isActionAvailable } = useLineageGraphContext();\n const changeAnalysisAvailable = isActionAvailable(\"change_analysis\");\n const noCatalogBase = !lineageGraph?.catalogMetadata.base;\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n let catalogMissingMessage = undefined;\n if (noCatalogBase && noCatalogCurrent) {\n catalogMissingMessage =\n \"catalog.json not found on both environments. Run `recce debug` to troubleshoot.\";\n } else if (noCatalogBase) {\n catalogMissingMessage =\n \"catalog.json not found on base environment. Run `recce debug` to troubleshoot.\";\n } else if (noCatalogCurrent) {\n catalogMissingMessage =\n \"catalog.json not found on current environment. Run `recce debug` to troubleshoot.\";\n }\n\n const noSchemaBase = base && base.columns === undefined;\n const noSchemaCurrent = current && current.columns === undefined;\n let schemaMissingMessage = undefined;\n if (noSchemaBase && noSchemaCurrent) {\n schemaMissingMessage =\n \"Node schema not found in catalog.json on both environments. Please regenerate your catalog.json to update.\";\n } else if (noSchemaCurrent) {\n schemaMissingMessage =\n \"Node schema not found in catalog.json on current environment. Please regenerate your catalog.json to update.\";\n } else if (noSchemaBase) {\n schemaMissingMessage =\n \"Node schema not found in catalog.json on base environment. Please regenerate your catalog.json to update.\";\n }\n\n const handleViewCll = async (columnName: string) => {\n if (!changeAnalysisAvailable) return;\n trackColumnLevelLineage({ action: \"view\", source: \"schema_column\" });\n setCllRunningMap((prev) => new Map(prev).set(columnName, true));\n const modelId = current?.id ?? base?.id;\n if (modelId) {\n await lineageViewContext?.showColumnLevelLineage({\n node_id: modelId,\n column: columnName,\n });\n }\n setCllRunningMap((prev) => new Map(prev).set(columnName, false));\n };\n\n const getRowId = useCallback(\n (params: { data: SchemaDiffRow }) => {\n const modelId = current?.id ?? base?.id;\n return `${modelId}-${params.data.name}`;\n },\n [current?.id, base?.id],\n );\n\n const cll = lineageViewContext?.viewOptions.column_level_lineage;\n const selectedRowId = cll ? `${cll.node_id}-${cll.column}` : null;\n\n // Update row selection when cll changes\n useEffect(() => {\n if (!gridApi) return;\n gridApi.deselectAll();\n if (selectedRowId) {\n const rowNode = gridApi.getRowNode(selectedRowId);\n if (rowNode) {\n rowNode.setSelected(true);\n }\n }\n }, [gridApi, selectedRowId]);\n\n const handleGridReady = useCallback(\n (event: GridReadyEvent<SchemaDiffRow>) => {\n setGridApi(event.api);\n },\n [],\n );\n\n const getRowClass = (params: RowClassParams<SchemaDiffRow>) => {\n const row = params.data;\n if (!row) return \"row-normal\";\n\n let className: string;\n if (row.baseIndex === undefined) {\n className = \"row-added\";\n } else if (row.currentIndex === undefined) {\n return \"row-removed\"; // removed column isn't selectable\n } else if (\n row.baseType !== row.currentType ||\n row.reordered === true ||\n row.definitionChanged === true\n ) {\n // Any change (structural or definition-only) gets the changed row background\n className = \"row-changed\";\n } else {\n className = \"row-normal\";\n }\n if (lineageViewContext !== undefined && changeAnalysisAvailable) {\n className += \" row-selectable\";\n }\n return className;\n };\n\n const handleCellClicked = async (event: CellClickedEvent<SchemaDiffRow>) => {\n // Skip if clicking on the menu button\n const target = event.event?.target as HTMLElement | undefined;\n if (target?.closest(\".row-context-menu\")) {\n return;\n }\n const row = event.data;\n if (!row) return;\n // Removed columns aren't clickable\n if (row.baseIndex !== undefined && row.currentIndex === undefined) {\n return;\n }\n await handleViewCll(row.name);\n };\n\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n {catalogMissingMessage ? (\n <MuiAlert severity=\"warning\" sx={{ fontSize: \"12px\", p: 1 }}>\n {catalogMissingMessage}\n </MuiAlert>\n ) : schemaMissingMessage ? (\n <MuiAlert severity=\"warning\" sx={{ fontSize: \"12px\", p: 1 }}>\n {schemaMissingMessage}\n </MuiAlert>\n ) : (\n <></>\n )}\n\n <SchemaLegend />\n {rows.length > 0 && (\n <ScreenshotDataGrid\n style={{\n blockSize: \"auto\",\n maxHeight: \"100%\",\n overflow: \"auto\",\n fontSize: \"0.8rem\",\n borderWidth: 1,\n }}\n columns={columns}\n rows={rows}\n rowHeight={35}\n renderers={{ noRowsFallback: <EmptyRowsRenderer /> }}\n className=\"rdg-light no-track-pii-safe\"\n ref={ref}\n getRowId={getRowId}\n getRowClass={getRowClass}\n onCellClicked={handleCellClicked}\n onGridReady={handleGridReady}\n rowSelection={{ mode: \"singleRow\", checkboxes: false }}\n containerClassName=\"no-track-pii-safe\"\n rowClassName=\"no-track-pii-safe\"\n />\n )}\n </Box>\n );\n}\n\nexport const SchemaView = forwardRef(PrivateSchemaView);\nexport const SingleEnvSchemaView = forwardRef(PrivateSingleEnvSchemaView);\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport IconButton from \"@mui/material/IconButton\";\nimport type { ComponentType } from \"react\";\nimport { useState } from \"react\";\nimport { FaExpandArrowsAlt } from \"react-icons/fa\";\nimport { IoClose } from \"react-icons/io5\";\n\n/**\n * Props for a code editor component.\n * Used for dependency injection to avoid coupling to a specific editor implementation.\n */\nexport interface CodeEditorProps {\n value: string;\n language?: string;\n readOnly?: boolean;\n lineNumbers?: boolean;\n wordWrap?: boolean;\n theme?: \"light\" | \"dark\";\n fontSize?: number;\n height?: string;\n className?: string;\n}\n\n/**\n * Props for a diff editor component.\n * Used for dependency injection to avoid coupling to a specific editor implementation.\n */\nexport interface DiffEditorProps {\n original: string;\n modified: string;\n language?: string;\n readOnly?: boolean;\n lineNumbers?: boolean;\n sideBySide?: boolean;\n theme?: \"light\" | \"dark\";\n height?: string;\n className?: string;\n}\n\n/**\n * Node data structure representing model information for SQL display.\n * Fields are optional to accommodate various node data structures from the API.\n */\nexport interface NodeSqlViewNodeData {\n resourceType?: string;\n data: {\n base?: { raw_code?: string; name?: string };\n current?: { raw_code?: string; name?: string };\n };\n name?: string;\n}\n\n/**\n * Props for the NodeSqlView component.\n *\n * This component uses dependency injection for editor components to avoid\n * coupling to specific editor implementations like CodeMirror.\n *\n * Editor component props use `ComponentType<any>` to allow implementations\n * with more specific prop types (e.g., language as a union type vs string).\n * The component internally passes the correct props matching the interfaces above.\n */\nexport interface NodeSqlViewProps {\n /**\n * Node containing model data with SQL code.\n */\n node: {\n data: NodeSqlViewNodeData;\n };\n /**\n * Whether the environment is single-env mode (no diff view).\n */\n isSingleEnv: boolean;\n /**\n * Code editor component for single-env mode.\n * Should accept props matching CodeEditorProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n CodeEditor: ComponentType<any>;\n /**\n * Diff editor component for comparing base vs current.\n * Should accept props matching DiffEditorProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n DiffEditor: ComponentType<any>;\n /**\n * Whether dark mode is enabled.\n */\n isDark?: boolean;\n}\n\n/**\n * Displays SQL code for a lineage node with an expandable dialog.\n *\n * In single-env mode, shows just the code. Otherwise, shows a diff view\n * comparing base and current versions.\n *\n * Editor components are injected as props to allow the consuming application\n * to provide its own editor implementations (e.g., CodeMirror, Monaco).\n */\nexport const NodeSqlView = ({\n node,\n isSingleEnv,\n CodeEditor,\n DiffEditor,\n isDark = false,\n}: NodeSqlViewProps) => {\n const [isOpen, setIsOpen] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n\n if (\n node.data.resourceType !== \"model\" &&\n node.data.resourceType !== \"snapshot\"\n ) {\n return \"Not available\";\n }\n\n const original = node.data.data.base?.raw_code;\n const modified = node.data.data.current?.raw_code;\n const modelName =\n node.data.data.base?.name ?? node.data.data.current?.name ?? \"\";\n\n return (\n <Box\n className=\"no-track-pii-safe\"\n sx={{ position: \"relative\", height: \"100%\" }}\n onMouseEnter={() => {\n setIsHovered(true);\n }}\n onMouseLeave={() => {\n setIsHovered(false);\n }}\n >\n {isSingleEnv ? (\n <CodeEditor\n language=\"sql\"\n value={original ?? \"\"}\n readOnly={true}\n lineNumbers={true}\n wordWrap={false}\n theme={isDark ? \"dark\" : \"light\"}\n />\n ) : (\n <DiffEditor\n original={original ?? \"\"}\n modified={modified ?? \"\"}\n language=\"sql\"\n readOnly={true}\n lineNumbers={true}\n sideBySide={false} // Inline diff mode\n theme={isDark ? \"dark\" : \"light\"}\n height=\"100%\"\n />\n )}\n <IconButton\n onClick={() => setIsOpen(true)}\n size=\"medium\"\n aria-label=\"Expand\"\n sx={{\n position: \"absolute\",\n top: \"5px\",\n right: \"20px\",\n opacity: isHovered ? 0.5 : 0.1,\n transition: \"opacity 0.3s ease-in-out\",\n }}\n >\n <FaExpandArrowsAlt />\n </IconButton>\n <MuiDialog\n open={isOpen}\n onClose={() => setIsOpen(false)}\n maxWidth=\"sm\"\n fullWidth\n slotProps={{\n paper: { sx: { height: \"75%\", overflowY: \"auto\" } },\n }}\n >\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n {isSingleEnv ? (\n <>\n <code>{modelName}</code> Model Code\n </>\n ) : (\n <>\n <code>{modelName}</code> Model Code Diff\n </>\n )}\n <Box sx={{ flex: 1 }} />\n <IconButton size=\"small\" onClick={() => setIsOpen(false)}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent>\n {isSingleEnv ? (\n <CodeEditor\n language=\"sql\"\n value={original ?? \"\"}\n fontSize={16}\n readOnly={true}\n lineNumbers={true}\n wordWrap={false}\n theme={isDark ? \"dark\" : \"light\"}\n />\n ) : (\n <DiffEditor\n original={original ?? \"\"}\n modified={modified ?? \"\"}\n language=\"sql\"\n theme={isDark ? \"dark\" : \"light\"}\n className=\"text-base\"\n />\n )}\n </DialogContent>\n </MuiDialog>\n </Box>\n );\n};\n","\"use client\";\n\nimport type { LineageGraphNode } from \"../..\";\nimport { useRecceServerFlag } from \"../../contexts\";\nimport { useIsDark } from \"../../hooks\";\nimport { CodeEditor, DiffEditor } from \"../../primitives\";\nimport { NodeSqlView as BaseNodeSqlView } from \"./NodeSqlView\";\n\ninterface NodeSqlViewProps {\n node: LineageGraphNode;\n}\n\n/**\n * wrapper for NodeSqlView that injects CodeMirror-based editors.\n *\n * This wrapper:\n * 1. Handles loading state from useRecceServerFlag\n * 2. Injects editor components (CodeEditor, DiffEditor)\n * 3. Provides dark mode detection via useIsDark hook\n *\n * The underlying BaseNodeSqlView from @datarecce/ui is framework-agnostic\n * and accepts editor components as props for dependency injection.\n */\nexport const NodeSqlViewOss = ({ node }: NodeSqlViewProps) => {\n const { data: flags, isLoading } = useRecceServerFlag();\n const isDark = useIsDark();\n\n if (isLoading) {\n return <></>;\n }\n\n return (\n <BaseNodeSqlView\n node={node}\n isSingleEnv={flags?.single_env_onboarding ?? false}\n CodeEditor={CodeEditor}\n DiffEditor={DiffEditor}\n isDark={isDark}\n />\n );\n};\n","/**\n * @file tagStyles.ts\n * @description Shared styling utilities for lineage node tags\n *\n * Provides reusable styles for tag components that display information\n * in lineage graph nodes (resource type, row count, etc.).\n *\n * Source: Extracted from OSS js/src/components/lineage/NodeTag.tsx\n */\n\nimport type { SxProps, Theme } from \"@mui/material/styles\";\n\n/**\n * Get root styles for tag components\n *\n * Creates a pill-shaped container with theme-aware colors.\n *\n * @param isDark - Whether dark mode is active\n * @returns SxProps for the tag container\n *\n * @example\n * ```tsx\n * const isDark = useIsDark();\n * <Box sx={getTagRootSx(isDark)}>Tag content</Box>\n * ```\n */\nexport const getTagRootSx = (isDark: boolean): SxProps<Theme> => ({\n display: \"inline-flex\",\n alignItems: \"center\",\n borderRadius: 16,\n px: 1,\n py: 0.25,\n fontSize: \"0.75rem\",\n bgcolor: isDark ? \"grey.700\" : \"grey.100\",\n color: isDark ? \"grey.100\" : \"inherit\",\n});\n\n/**\n * Styles for the leading element (icon) in a tag\n *\n * Provides consistent spacing and alignment for icons at the start of tags.\n *\n * @example\n * ```tsx\n * <Box sx={tagStartElementSx}>\n * <MyIcon />\n * </Box>\n * ```\n */\nexport const tagStartElementSx: SxProps<Theme> = {\n mr: 0.5,\n display: \"flex\",\n alignItems: \"center\",\n};\n","\"use client\";\n\n/**\n * @file ResourceTypeTag.tsx\n * @description Pure presentation component for displaying resource type with icon\n *\n * Shows the resource type (model, source, seed, etc.) as a tag with an icon.\n * Uses theme-aware styling for light/dark mode support.\n *\n * Source: Migrated from OSS js/src/components/lineage/NodeTag.tsx\n */\n\nimport Box from \"@mui/material/Box\";\nimport Tooltip from \"@mui/material/Tooltip\";\nimport { memo } from \"react\";\nimport { useIsDark } from \"../../../hooks/useIsDark\";\nimport { getIconForResourceType } from \"../styles\";\nimport { getTagRootSx, tagStartElementSx } from \"./tagStyles\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/**\n * Data required for ResourceTypeTag\n */\nexport interface ResourceTypeTagData {\n /** The resource type to display (model, source, seed, snapshot, etc.) */\n resourceType?: string;\n}\n\n/**\n * Props for ResourceTypeTag component\n */\nexport interface ResourceTypeTagProps {\n /** Node data containing the resource type */\n data: ResourceTypeTagData;\n /** Test ID for testing */\n \"data-testid\"?: string;\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * ResourceTypeTag - Displays the resource type with an icon\n *\n * Shows a pill-shaped tag with an icon representing the resource type\n * (model, source, seed, snapshot, metric, exposure, semantic_model).\n *\n * @example\n * ```tsx\n * // Basic usage\n * <ResourceTypeTag data={{ resourceType: \"model\" }} />\n *\n * // With snapshot type\n * <ResourceTypeTag data={{ resourceType: \"snapshot\" }} />\n * ```\n */\nfunction ResourceTypeTagComponent({\n data,\n \"data-testid\": testId,\n}: ResourceTypeTagProps) {\n const isDark = useIsDark();\n const { icon: ResourceTypeIcon } = getIconForResourceType(data.resourceType);\n\n return (\n <Tooltip arrow title=\"Type of resource\">\n <Box component=\"span\" sx={getTagRootSx(isDark)} data-testid={testId}>\n {ResourceTypeIcon && (\n <Box component=\"span\" sx={tagStartElementSx}>\n <ResourceTypeIcon />\n </Box>\n )}\n {data.resourceType}\n </Box>\n </Tooltip>\n );\n}\n\nexport const ResourceTypeTag = memo(ResourceTypeTagComponent);\nResourceTypeTag.displayName = \"ResourceTypeTag\";\n","/**\n * @file NodeTag.tsx\n * @description Tag components for lineage graph nodes\n *\n * Includes tag components that rely on app-level dependencies:\n * - RowCountDiffTag: Shows row count comparison between base and current\n * - RowCountTag: Shows current row count (single env mode)\n */\n\nimport Box from \"@mui/material/Box\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Skeleton from \"@mui/material/Skeleton\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { FiArrowRight } from \"react-icons/fi\";\nimport { PiRepeat } from \"react-icons/pi\";\nimport { RiArrowDownSFill, RiArrowUpSFill, RiSwapLine } from \"react-icons/ri\";\nimport type { RowCount, RowCountDiff } from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport type { LineageGraphNode } from \"../../contexts/lineage/types\";\nimport { useIsDark } from \"../../hooks\";\nimport { deltaPercentageString } from \"../../utils\";\nimport { SetupConnectionPopover } from \"../app\";\nimport { findByRunType } from \"../run\";\nimport { getTagRootSx, tagStartElementSx } from \"./tags\";\n\n// =============================================================================\n// INTERNAL COMPONENTS\n// =============================================================================\n\nfunction _RowCountByRate({ rowCount }: { rowCount: RowCountDiff }) {\n const base = rowCount.base;\n const current = rowCount.curr;\n const baseLabel = rowCount.base === null ? \"N/A\" : `${rowCount.base} rows`;\n const currentLabel = rowCount.curr === null ? \"N/A\" : `${rowCount.curr} rows`;\n\n if (base === null && current === null) {\n return <> Failed to load</>;\n }\n if (base === null || current === null) {\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography variant=\"body2\" component=\"span\">\n {baseLabel}\n </Typography>\n <FiArrowRight />\n <Typography variant=\"body2\" component=\"span\">\n {currentLabel}\n </Typography>\n </Stack>\n );\n }\n if (base === current) {\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography variant=\"body2\" component=\"span\">\n {currentLabel}\n </Typography>\n <Box component=\"span\" sx={{ color: \"grey.500\", display: \"flex\" }}>\n <RiSwapLine />\n </Box>\n <Typography variant=\"body2\" component=\"span\" sx={{ color: \"grey.500\" }}>\n No Change\n </Typography>\n </Stack>\n );\n }\n if (base < current) {\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography variant=\"body2\" component=\"span\">\n {currentLabel}\n </Typography>\n <Box component=\"span\" sx={{ color: \"success.main\", display: \"flex\" }}>\n <RiArrowUpSFill />\n </Box>\n <Typography\n variant=\"body2\"\n component=\"span\"\n sx={{ color: \"success.main\" }}\n >\n {deltaPercentageString(base, current)}\n </Typography>\n </Stack>\n );\n }\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography variant=\"body2\" component=\"span\">\n {currentLabel}\n </Typography>\n <Box component=\"span\" sx={{ color: \"error.main\", display: \"flex\" }}>\n <RiArrowDownSFill />\n </Box>\n <Typography variant=\"body2\" component=\"span\" sx={{ color: \"error.main\" }}>\n {deltaPercentageString(base, current)}\n </Typography>\n </Stack>\n );\n}\n\n// =============================================================================\n// EXPORTED COMPONENTS\n// =============================================================================\n\nexport interface RowCountDiffTagProps {\n node: LineageGraphNode;\n rowCount?: RowCountDiff;\n onRefresh?: () => void;\n isFetching?: boolean;\n error?: Error | null;\n}\n\n/**\n * RowCountDiffTag - Shows row count comparison between base and current\n *\n * This component is app-specific because it requires:\n * - SetupConnectionPopover for database connection prompts\n * - findByRunType for getting run type icons from registry\n * - runsAggregated context for cached row count data\n */\nexport function RowCountDiffTag({\n rowCount: fetchedRowCount,\n node,\n onRefresh,\n isFetching,\n}: RowCountDiffTagProps) {\n const isDark = useIsDark();\n const { featureToggles } = useRecceInstanceContext();\n const { runsAggregated } = useLineageGraphContext();\n const lastRowCount: RowCountDiff | undefined = runsAggregated?.[node.id]\n ?.row_count_diff.result as RowCountDiff | undefined;\n const RunTypeIcon = findByRunType(\"row_count_diff\").icon;\n\n // Calculate during render instead of effect\n const rowCount = fetchedRowCount ?? lastRowCount;\n const rowsToShow = rowCount;\n const label = rowCount\n ? `${rowCount.base ?? \"N/A\"} -> ${rowCount.curr ?? \"N/A\"} rows`\n : \"\";\n\n return (\n <MuiTooltip title={label}>\n <SetupConnectionPopover display={featureToggles.mode === \"metadata only\"}>\n <Box\n component=\"span\"\n sx={{\n ...getTagRootSx(isDark),\n gap: 0.5,\n }}\n >\n <RunTypeIcon />\n {rowsToShow != null || isFetching ? (\n isFetching ? (\n <Skeleton width={30} height={16} />\n ) : rowsToShow != null ? (\n <_RowCountByRate rowCount={rowsToShow} />\n ) : (\n <Typography variant=\"caption\">row count</Typography>\n )\n ) : (\n <Typography variant=\"caption\">row count</Typography>\n )}\n {onRefresh && (\n <IconButton\n aria-label=\"Query Row Count\"\n size=\"small\"\n onClick={onRefresh}\n disabled={featureToggles.disableDatabaseQuery}\n sx={{ p: 0, ml: 0.5 }}\n >\n <PiRepeat size={12} />\n </IconButton>\n )}\n </Box>\n </SetupConnectionPopover>\n </MuiTooltip>\n );\n}\n\nexport interface RowCountTagProps {\n node: LineageGraphNode;\n rowCount?: RowCount;\n onRefresh?: () => void;\n isFetching?: boolean;\n error?: Error | null;\n}\n\n/**\n * RowCountTag - Shows current row count (single environment mode)\n *\n * This component is app-specific because it requires:\n * - findByRunType for getting run type icons from registry\n * - runsAggregated context for cached row count data\n */\nexport function RowCountTag({\n rowCount: fetchedRowCount,\n node,\n onRefresh,\n isFetching,\n}: RowCountTagProps) {\n const isDark = useIsDark();\n const { runsAggregated } = useLineageGraphContext();\n const lastRowCount: RowCountDiff | undefined = runsAggregated?.[node.id]\n ?.row_count.result as RowCountDiff | undefined;\n\n const RunTypeIcon = findByRunType(\"row_count\").icon;\n\n let label;\n const rowCount = fetchedRowCount ?? lastRowCount;\n if (rowCount) {\n const rows = rowCount.curr ?? \"N/A\";\n label = `${rows} rows`;\n }\n\n return (\n <Box component=\"span\" sx={getTagRootSx(isDark)}>\n <Box component=\"span\" sx={tagStartElementSx}>\n <RunTypeIcon />\n </Box>\n {rowCount || isFetching ? (\n isFetching ? (\n <Skeleton width={30} height={16} />\n ) : (\n <Typography variant=\"caption\">{label}</Typography>\n )\n ) : (\n <Typography variant=\"caption\">row count</Typography>\n )}\n {onRefresh && (\n <IconButton\n aria-label=\"Query Row Count\"\n size=\"small\"\n onClick={onRefresh}\n disabled={node.data.from === \"base\"}\n sx={{ p: 0, ml: 0.5 }}\n >\n <PiRepeat size={12} />\n </IconButton>\n )}\n </Box>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport Tab from \"@mui/material/Tab\";\nimport Tabs from \"@mui/material/Tabs\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport {\n type ComponentType,\n type ReactElement,\n type ReactNode,\n useState,\n} from \"react\";\nimport { IoClose } from \"react-icons/io5\";\n\nimport type { NodeColumnData } from \"../../api/info\";\nimport { DisableTooltipMessages } from \"../../constants\";\nimport { isSchemaChanged } from \"../../utils/schemaDiff\";\nimport type { NodeSqlViewProps } from \"./NodeSqlView\";\n\n// =============================================================================\n// TYPE DEFINITIONS\n// =============================================================================\n\n/**\n * Node data structure for NodeView.\n * Represents the data needed to display node details.\n */\nexport interface NodeViewNodeData {\n id: string;\n data: {\n name: string;\n resourceType?: string;\n changeStatus?: string;\n from?: string;\n data: {\n base?: {\n raw_code?: string;\n name?: string;\n columns?: Record<string, NodeColumnData | undefined>;\n };\n current?: {\n raw_code?: string;\n name?: string;\n columns?: Record<string, NodeColumnData | undefined>;\n };\n };\n change?: {\n category: string;\n columns: Record<string, \"added\" | \"removed\" | \"modified\"> | null;\n };\n };\n}\n\n/**\n * Run type icon configuration for dependency injection.\n * Maps run type names to their icon components.\n */\nexport interface RunTypeIconMap {\n query?: ComponentType<{ fontSize?: string }>;\n row_count?: ComponentType<{ fontSize?: string }>;\n row_count_diff?: ComponentType<{ fontSize?: string }>;\n profile?: ComponentType<{ fontSize?: string }>;\n profile_diff?: ComponentType<{ fontSize?: string }>;\n query_diff?: ComponentType<{ fontSize?: string }>;\n value_diff?: ComponentType<{ fontSize?: string }>;\n top_k_diff?: ComponentType<{ fontSize?: string }>;\n histogram_diff?: ComponentType<{ fontSize?: string }>;\n schema_diff?: ComponentType<{ fontSize?: string }>;\n sandbox?: ComponentType<{ fontSize?: string }>;\n}\n\n/**\n * Callbacks for action button clicks.\n * Used for dependency injection of action handlers.\n */\nexport interface NodeViewActionCallbacks {\n /** Called when Query button is clicked */\n onQueryClick?: () => void;\n /** Called when Row Count button is clicked */\n onRowCountClick?: () => void;\n /** Called when Row Count Diff button is clicked */\n onRowCountDiffClick?: () => void;\n /** Called when Profile button is clicked */\n onProfileClick?: () => void;\n /** Called when Profile Diff button is clicked */\n onProfileDiffClick?: () => void;\n /** Called when Query Diff button is clicked */\n onQueryDiffClick?: () => void;\n /** Called when Value Diff button is clicked */\n onValueDiffClick?: () => void;\n /** Called when Top-K Diff button is clicked */\n onTopKDiffClick?: () => void;\n /** Called when Histogram Diff button is clicked */\n onHistogramDiffClick?: () => void;\n /** Called when Add Schema Diff button is clicked */\n onAddSchemaDiffClick?: () => void;\n /** Called when Sandbox button is clicked */\n onSandboxClick?: () => void;\n}\n\n/**\n * Props for NodeView component.\n *\n * Uses dependency injection for:\n * - Schema view components (for different consumers)\n * - SQL view component (NodeSqlView)\n * - Action button icons\n * - Notification components\n * - Connection popover wrapper\n *\n * Note: Injected component types use `any` to allow consumers to provide\n * components with more specific prop types than the base interface requires.\n * The consumer is responsible for ensuring type compatibility.\n */\nexport interface NodeViewProps {\n /** The node to display */\n node: NodeViewNodeData;\n /** Callback when close button is clicked */\n onCloseNode: () => void;\n /** Whether in single environment mode */\n isSingleEnv: boolean;\n /** Feature toggles for conditional UI */\n featureToggles?: {\n mode?: string | null;\n disableDatabaseQuery?: boolean;\n };\n\n // =========================================================================\n // DEPENDENCY INJECTION: Components\n // =========================================================================\n // Using ComponentType<any> for flexibility - consumers provide their own types\n\n /**\n * Schema view component for diff mode.\n * Should accept: { base?: NodeData, current?: NodeData }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n SchemaView?: ComponentType<any>;\n /**\n * Schema view component for single env mode.\n * Should accept: { current?: NodeData }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n SingleEnvSchemaView?: ComponentType<any>;\n /**\n * Node SQL view component.\n * Should accept: { node: LineageGraphNode }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n NodeSqlView?: ComponentType<any>;\n /**\n * Row count diff tag component.\n * Should accept: { node: LineageGraphNode, onRefresh?: () => void }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n RowCountDiffTag?: ComponentType<any>;\n /**\n * Row count tag component (single env).\n * Should accept: { node: LineageGraphNode, onRefresh?: () => void }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n RowCountTag?: ComponentType<any>;\n /**\n * Resource type tag component.\n * Should accept: { node: LineageGraphNode }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n ResourceTypeTag?: ComponentType<any>;\n /**\n * Notification component for single env mode.\n * Should accept: { onClose: () => void, children: ReactNode }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n NotificationComponent?: ComponentType<any>;\n /**\n * Wrapper component for buttons that need connection popover.\n * Should accept: { display: boolean, children: ReactNode }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n ConnectionPopoverWrapper?: ComponentType<any>;\n /**\n * Sandbox dialog component.\n * Should accept: { isOpen: boolean, onClose: () => void, current?: NodeData }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n SandboxDialog?: ComponentType<any>;\n\n // =========================================================================\n // DEPENDENCY INJECTION: Icons\n // =========================================================================\n\n /** Map of run type names to icon components */\n runTypeIcons?: RunTypeIconMap;\n\n // =========================================================================\n // DEPENDENCY INJECTION: Callbacks\n // =========================================================================\n\n /** Action callbacks for button clicks */\n actionCallbacks?: NodeViewActionCallbacks;\n /** Check if an action is available */\n isActionAvailable?: (runType: string) => boolean;\n}\n\n// =============================================================================\n// INTERNAL COMPONENTS\n// =============================================================================\n\ninterface TabPanelProps {\n children?: ReactNode;\n index: number;\n value: number;\n}\n\nfunction TabPanel({ children, value, index }: TabPanelProps) {\n return value === index ? <>{children}</> : null;\n}\n\n// =============================================================================\n// DEFAULT IMPLEMENTATIONS\n// =============================================================================\n\n/** Default icon placeholder when no icon is provided */\nconst DefaultIcon = () => <span />;\n\n/** Default connection wrapper that just renders children */\nconst DefaultConnectionWrapper = ({\n children,\n}: {\n display: boolean;\n children: ReactNode;\n}) => <>{children}</>;\n\n/** Default check for action availability - always available */\nconst defaultIsActionAvailable = () => true;\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\n/**\n * Gets the disable reason for an action button.\n */\nfunction getDisableReason(\n isAddedOrRemoved: boolean,\n runType: string,\n isActionAvailable: (runType: string) => boolean,\n): string {\n if (isAddedOrRemoved) {\n return DisableTooltipMessages.add_or_remove;\n }\n if (!isActionAvailable(runType)) {\n return \"This action is not supported yet.\";\n }\n return \"\";\n}\n\n// =============================================================================\n// SUB-COMPONENTS\n// =============================================================================\n\ninterface SingleEnvActionButtonsProps {\n node: NodeViewNodeData;\n actionCallbacks?: NodeViewActionCallbacks;\n runTypeIcons?: RunTypeIconMap;\n isActionAvailable: (runType: string) => boolean;\n}\n\nfunction SingleEnvActionButtons({\n node,\n actionCallbacks,\n runTypeIcons,\n isActionAvailable,\n}: SingleEnvActionButtonsProps) {\n const isAddedOrRemoved =\n node.data.changeStatus === \"added\" || node.data.changeStatus === \"removed\";\n\n const QueryIcon = runTypeIcons?.query ?? DefaultIcon;\n const RowCountIcon = runTypeIcons?.row_count ?? DefaultIcon;\n const ProfileIcon = runTypeIcons?.profile ?? DefaultIcon;\n\n return (\n <Stack direction=\"row\" alignItems=\"center\" flexWrap=\"wrap\" gap={1}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<QueryIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onQueryClick}\n sx={{ textTransform: \"none\" }}\n >\n Query\n </Button>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<RowCountIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onRowCountClick}\n sx={{ textTransform: \"none\" }}\n >\n Row Count\n </Button>\n <MuiTooltip\n title={getDisableReason(isAddedOrRemoved, \"profile\", isActionAvailable)}\n placement=\"top\"\n >\n <span>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<ProfileIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onProfileClick}\n disabled={isAddedOrRemoved}\n sx={{ textTransform: \"none\" }}\n >\n Profile\n </Button>\n </span>\n </MuiTooltip>\n </Stack>\n );\n}\n\ninterface ExploreHeaderButtonsProps {\n node: NodeViewNodeData;\n actionCallbacks?: NodeViewActionCallbacks;\n runTypeIcons?: RunTypeIconMap;\n featureToggles?: NodeViewProps[\"featureToggles\"];\n ConnectionPopoverWrapper: ComponentType<{\n display: boolean;\n children: ReactNode;\n }>;\n}\n\nfunction ExploreHeaderButtons({\n actionCallbacks,\n runTypeIcons,\n featureToggles,\n ConnectionPopoverWrapper,\n}: ExploreHeaderButtonsProps) {\n const metadataOnly = featureToggles?.mode === \"metadata only\";\n\n const SchemaDiffIcon = runTypeIcons?.schema_diff ?? DefaultIcon;\n const SandboxIcon = runTypeIcons?.sandbox ?? DefaultIcon;\n\n return (\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ mr: 1 }}\n flexWrap=\"wrap\"\n gap={1}\n >\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<SchemaDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onAddSchemaDiffClick}\n sx={{ textTransform: \"none\" }}\n >\n Add schema diff to checklist\n </Button>\n <ConnectionPopoverWrapper display={metadataOnly}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<SandboxIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onSandboxClick}\n disabled={featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Sandbox\n </Button>\n </ConnectionPopoverWrapper>\n </Stack>\n );\n}\n\ninterface DiffActionButtonsProps {\n node: NodeViewNodeData;\n actionCallbacks?: NodeViewActionCallbacks;\n runTypeIcons?: RunTypeIconMap;\n featureToggles?: NodeViewProps[\"featureToggles\"];\n isActionAvailable: (runType: string) => boolean;\n ConnectionPopoverWrapper: ComponentType<{\n display: boolean;\n children: ReactNode;\n }>;\n}\n\nfunction DiffActionButtons({\n node,\n actionCallbacks,\n runTypeIcons,\n featureToggles,\n isActionAvailable,\n ConnectionPopoverWrapper,\n}: DiffActionButtonsProps) {\n const metadataOnly = featureToggles?.mode === \"metadata only\";\n const isAddedOrRemoved =\n node.data.changeStatus === \"added\" || node.data.changeStatus === \"removed\";\n\n const QueryDiffIcon = runTypeIcons?.query_diff ?? DefaultIcon;\n const RowCountDiffIcon = runTypeIcons?.row_count_diff ?? DefaultIcon;\n const ProfileDiffIcon = runTypeIcons?.profile_diff ?? DefaultIcon;\n const ValueDiffIcon = runTypeIcons?.value_diff ?? DefaultIcon;\n const TopKDiffIcon = runTypeIcons?.top_k_diff ?? DefaultIcon;\n const HistogramDiffIcon = runTypeIcons?.histogram_diff ?? DefaultIcon;\n\n const wrapButton = (\n button: ReactElement<{\n ref?: React.Ref<HTMLElement>;\n [key: string]: unknown;\n }>,\n runType: string,\n ) => {\n if (metadataOnly) {\n return (\n <ConnectionPopoverWrapper display={true}>\n {button}\n </ConnectionPopoverWrapper>\n );\n }\n\n const tooltipContent = getDisableReason(\n isAddedOrRemoved,\n runType,\n isActionAvailable,\n );\n return (\n <MuiTooltip title={tooltipContent} placement=\"top\">\n <span>{button}</span>\n </MuiTooltip>\n );\n };\n\n return (\n <Stack direction=\"row\" alignItems=\"center\" flexWrap=\"wrap\" gap={2}>\n <Typography variant=\"caption\" fontWeight=\"bold\">\n Diff\n </Typography>\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n flexWrap=\"wrap\"\n gap={1}\n width=\"93%\"\n >\n <ConnectionPopoverWrapper display={metadataOnly}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<RowCountDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onRowCountDiffClick}\n disabled={featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Row Count\n </Button>\n </ConnectionPopoverWrapper>\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<ProfileDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onProfileDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Profile\n </Button>,\n \"profile_diff\",\n )}\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<ValueDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onValueDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Value\n </Button>,\n \"value_diff\",\n )}\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<TopKDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onTopKDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Top-K\n </Button>,\n \"top_k_diff\",\n )}\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<HistogramDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onHistogramDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Histogram\n </Button>,\n \"histogram_diff\",\n )}\n <ConnectionPopoverWrapper display={metadataOnly}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<QueryDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onQueryDiffClick}\n disabled={featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Query\n </Button>\n </ConnectionPopoverWrapper>\n </Stack>\n </Stack>\n );\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * NodeView Component\n *\n * Displays detailed information about a lineage node including:\n * - Node name and metadata\n * - Action buttons for various operations (Query, Profile, Diff, etc.)\n * - Tabs for Columns and Code views\n *\n * Uses dependency injection for:\n * - Schema view components (different for OSS vs Cloud)\n * - Action button handlers\n * - Icon components from run registry\n * - Connection popover for database setup prompts\n *\n * @example\n * ```tsx\n * import { NodeView } from '@datarecce/ui/advanced';\n *\n * <NodeView\n * node={selectedNode}\n * onCloseNode={() => setSelectedNode(null)}\n * isSingleEnv={false}\n * SchemaView={MySchemaView}\n * NodeSqlView={MyNodeSqlView}\n * actionCallbacks={{\n * onQueryClick: handleQuery,\n * onProfileDiffClick: handleProfileDiff,\n * }}\n * />\n * ```\n */\nexport function NodeView({\n node,\n onCloseNode,\n isSingleEnv,\n featureToggles,\n // Injected components\n SchemaView,\n SingleEnvSchemaView,\n NodeSqlView,\n RowCountDiffTag,\n RowCountTag,\n ResourceTypeTag,\n NotificationComponent,\n ConnectionPopoverWrapper = DefaultConnectionWrapper,\n SandboxDialog,\n // Injected icons\n runTypeIcons,\n // Injected callbacks\n actionCallbacks,\n isActionAvailable = defaultIsActionAvailable,\n}: NodeViewProps) {\n const withColumns =\n node.data.resourceType === \"model\" ||\n node.data.resourceType === \"seed\" ||\n node.data.resourceType === \"source\" ||\n node.data.resourceType === \"snapshot\";\n\n const [isSandboxOpen, setIsSandboxOpen] = useState(false);\n const [isNotificationOpen, setIsNotificationOpen] = useState(true);\n const [tabValue, setTabValue] = useState(0);\n\n const { base, current } = node.data.data;\n const hasSchemaChanges =\n !isSingleEnv && isSchemaChanged(base?.columns, current?.columns) === true;\n const hasCodeChanges =\n !isSingleEnv &&\n base?.raw_code != null &&\n current?.raw_code != null &&\n base.raw_code !== current.raw_code;\n\n const isModelSeedOrSnapshot =\n node.data.resourceType === \"model\" ||\n node.data.resourceType === \"seed\" ||\n node.data.resourceType === \"snapshot\";\n\n // Extended callbacks that include sandbox open\n const extendedCallbacks: NodeViewActionCallbacks = {\n ...actionCallbacks,\n onSandboxClick: () => {\n actionCallbacks?.onSandboxClick?.();\n setIsSandboxOpen(true);\n },\n };\n\n return (\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n {/* Header row: name + close button */}\n <Stack direction=\"row\" alignItems=\"center\">\n <Box sx={{ flex: \"0 1 20%\", p: 2 }}>\n <Typography\n variant=\"subtitle1\"\n fontWeight={600}\n className=\"no-track-pii-safe\"\n >\n {node.data.name}\n </Typography>\n </Box>\n <Box sx={{ flexGrow: 1 }} />\n {!isSingleEnv && isModelSeedOrSnapshot && (\n <ExploreHeaderButtons\n node={node}\n actionCallbacks={extendedCallbacks}\n runTypeIcons={runTypeIcons}\n featureToggles={featureToggles}\n ConnectionPopoverWrapper={ConnectionPopoverWrapper}\n />\n )}\n <Box sx={{ flex: \"0 1 1%\" }}>\n <IconButton size=\"small\" onClick={onCloseNode}>\n <IoClose />\n </IconButton>\n </Box>\n </Stack>\n\n {/* Tags row: resource type, row count */}\n <Box sx={{ color: \"text.secondary\", pl: 2 }}>\n <Stack direction=\"row\" spacing={1}>\n {ResourceTypeTag && <ResourceTypeTag node={node} />}\n {isModelSeedOrSnapshot &&\n (isSingleEnv\n ? RowCountTag && (\n <RowCountTag\n node={node}\n onRefresh={actionCallbacks?.onRowCountClick}\n />\n )\n : RowCountDiffTag && (\n <RowCountDiffTag\n node={node}\n onRefresh={actionCallbacks?.onRowCountDiffClick}\n />\n ))}\n </Stack>\n </Box>\n\n {/* Action buttons row */}\n {isModelSeedOrSnapshot && (\n <Box sx={{ pl: 2, py: 1 }}>\n {isSingleEnv ? (\n <SingleEnvActionButtons\n node={node}\n actionCallbacks={actionCallbacks}\n runTypeIcons={runTypeIcons}\n isActionAvailable={isActionAvailable}\n />\n ) : (\n <DiffActionButtons\n node={node}\n actionCallbacks={extendedCallbacks}\n runTypeIcons={runTypeIcons}\n featureToggles={featureToggles}\n isActionAvailable={isActionAvailable}\n ConnectionPopoverWrapper={ConnectionPopoverWrapper}\n />\n )}\n </Box>\n )}\n\n {/* Content area: tabs for columns and code */}\n {withColumns && (\n <Box\n sx={{\n overflow: \"auto\",\n display: \"flex\",\n flexDirection: \"column\",\n flex: 1,\n minHeight: 0,\n }}\n >\n {/* Notification for single env mode */}\n {isSingleEnv && isNotificationOpen && NotificationComponent && (\n <Box sx={{ p: 1.5 }}>\n <NotificationComponent\n onClose={() => setIsNotificationOpen(false)}\n >\n <Typography variant=\"body2\">\n Enable the Recce Checklist and start adding checks for better\n data validation and review.\n </Typography>\n </NotificationComponent>\n </Box>\n )}\n\n {/* Tabs */}\n <Tabs\n value={tabValue}\n onChange={(_, newValue) => setTabValue(newValue)}\n sx={{ borderBottom: 1, borderColor: \"divider\" }}\n >\n <Tab\n label={\n <Box\n component=\"span\"\n sx={{ display: \"flex\", alignItems: \"center\", gap: 0.75 }}\n >\n Columns\n {hasSchemaChanges && (\n <Box\n component=\"span\"\n sx={{\n color: \"amber.main\",\n fontSize: \"0.5rem\",\n lineHeight: 1,\n }}\n >\n ●\n </Box>\n )}\n </Box>\n }\n />\n <Tab\n label={\n <Box\n component=\"span\"\n sx={{ display: \"flex\", alignItems: \"center\", gap: 0.75 }}\n >\n Code\n {hasCodeChanges && (\n <Box\n component=\"span\"\n sx={{\n color: \"amber.main\",\n fontSize: \"0.5rem\",\n lineHeight: 1,\n }}\n >\n ●\n </Box>\n )}\n </Box>\n }\n />\n </Tabs>\n\n {/* Tab panels */}\n <Box sx={{ overflow: \"auto\", height: \"calc(100% - 48px)\" }}>\n <TabPanel value={tabValue} index={0}>\n <Box sx={{ overflowY: \"auto\", height: \"100%\" }}>\n {isSingleEnv\n ? SingleEnvSchemaView && (\n <SingleEnvSchemaView current={node.data.data.current} />\n )\n : SchemaView && (\n <SchemaView\n base={node.data.data.base}\n current={node.data.data.current}\n columnChanges={node.data.change?.columns}\n onViewCode={() => setTabValue(1)}\n />\n )}\n </Box>\n </TabPanel>\n <TabPanel value={tabValue} index={1}>\n <Box sx={{ height: \"100%\" }}>\n {NodeSqlView && <NodeSqlView node={node} />}\n </Box>\n </TabPanel>\n </Box>\n </Box>\n )}\n\n {/* Sandbox dialog */}\n {SandboxDialog && (\n <SandboxDialog\n isOpen={isSandboxOpen}\n onClose={() => setIsSandboxOpen(false)}\n current={node.data.data.current}\n />\n )}\n </Box>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Chip from \"@mui/material/Chip\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport { alpha } from \"@mui/material/styles\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { format, formatDistanceToNow, parseISO } from \"date-fns\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { useState } from \"react\";\nimport { AiOutlineExperiment } from \"react-icons/ai\";\nimport { IoClose } from \"react-icons/io5\";\nimport { VscFeedback } from \"react-icons/vsc\";\nimport { useLineageGraphContext } from \"../../contexts/lineage\";\nimport { colors } from \"../../theme\";\nimport { VSplit } from \"../ui/Split\";\n\n/**\n * Props for a diff editor component used in the sandbox.\n * Supports editing the modified version while keeping original read-only.\n */\nexport interface SandboxDiffEditorProps {\n original: string;\n modified: string;\n language?: string;\n readOnly?: boolean;\n lineNumbers?: boolean;\n sideBySide?: boolean;\n theme?: \"light\" | \"dark\";\n height?: string;\n onModifiedChange?: (value: string) => void;\n}\n\n/**\n * Node data structure representing model information for the sandbox.\n */\nexport interface SandboxNodeData {\n id?: string;\n name?: string;\n raw_code?: string;\n}\n\n/**\n * Props for the QueryForm slot component.\n */\nexport interface SandboxQueryFormProps {\n defaultPrimaryKeys: string[];\n onPrimaryKeysChange: (primaryKeys: string[]) => void;\n}\n\n/**\n * Props for the RunResultPane slot component.\n */\nexport interface SandboxRunResultPaneProps {\n onClose: () => void;\n disableAddToChecklist?: boolean;\n}\n\n/**\n * Tracking event types for the sandbox.\n */\nexport interface SandboxTrackingCallbacks {\n onPreviewChange?: (params: {\n action: \"run\" | \"close\";\n node?: string;\n status?: \"success\" | \"failure\";\n }) => void;\n onFeedbackSubmit?: (params: {\n feedback: \"like\" | \"dislike\" | \"form\";\n node?: string;\n }) => void;\n onSingleEnvironment?: (params: {\n action: string;\n from: string;\n node?: string;\n }) => void;\n}\n\n/**\n * Props for the SandboxView component.\n *\n * This component uses dependency injection for editor and form components\n * to avoid coupling to specific implementations.\n */\nexport interface SandboxViewProps {\n /**\n * Whether the sandbox dialog is open.\n */\n isOpen: boolean;\n\n /**\n * Callback when the dialog is closed.\n */\n onClose: () => void;\n\n /**\n * Current node data containing model info and SQL code.\n */\n current?: SandboxNodeData;\n\n /**\n * Height of the dialog (not used in fullscreen mode).\n */\n height?: string;\n\n /**\n * Diff editor component for SQL editing.\n * Should accept props matching SandboxDiffEditorProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n DiffEditor: ComponentType<any>;\n\n /**\n * Query form component for primary key selection.\n * Should accept props matching SandboxQueryFormProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n QueryForm: ComponentType<any>;\n\n /**\n * Run result pane component for displaying query results.\n * Should accept props matching SandboxRunResultPaneProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n RunResultPane: ComponentType<any>;\n\n /**\n * Whether dark mode is enabled.\n */\n isDark?: boolean;\n\n /**\n * Primary keys for query diffing.\n */\n primaryKeys?: string[];\n\n /**\n * Callback when primary keys change.\n */\n onPrimaryKeysChange?: (keys: string[]) => void;\n\n /**\n * Whether a query is currently running.\n */\n isPending?: boolean;\n\n /**\n * Callback to run the query.\n */\n onRunQuery?: () => void;\n\n /**\n * Callback when run result pane opens (before running query).\n */\n onRunResultOpen?: () => void;\n\n /**\n * Callback when modified code changes.\n */\n onModifiedCodeChange?: (code: string) => void;\n\n /**\n * Callback to show feedback toast.\n */\n onShowFeedback?: () => void;\n\n /**\n * Tracking callbacks for analytics.\n */\n tracking?: SandboxTrackingCallbacks;\n\n /**\n * Logo image URL for the header.\n * @default \"/logo/recce-logo-white.png\"\n */\n logoUrl?: string;\n\n /**\n * Brand name to display in header.\n * @default \"RECCE\"\n */\n brandName?: string;\n}\n\n/**\n * Helper function to format timestamp for display.\n */\nfunction formatTimestamp(timestamp: string): string {\n const date = parseISO(timestamp);\n return format(date, \"yyyy-MM-dd'T'HH:mm:ss\");\n}\n\ninterface SandboxTopBarProps {\n current?: SandboxNodeData;\n primaryKeys: string[];\n onPrimaryKeysChange: (primaryKeys: string[]) => void;\n onRunResultOpen: () => void;\n runQuery: () => void;\n isPending: boolean;\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n QueryForm: ComponentType<any>;\n}\n\nfunction SandboxTopBar({\n current,\n primaryKeys,\n onPrimaryKeysChange,\n onRunResultOpen,\n runQuery,\n isPending,\n QueryForm,\n}: SandboxTopBarProps) {\n return (\n <Stack\n direction=\"row\"\n justifyContent=\"flex-end\"\n alignItems=\"center\"\n sx={{\n p: \"4pt 8pt\",\n gap: \"5px\",\n height: \"54px\",\n borderBottom: \"1px solid\",\n borderBottomColor: \"divider\",\n flex: \"0 0 54px\",\n }}\n >\n <Box>\n <Typography\n variant=\"h6\"\n component=\"h2\"\n sx={{ display: \"flex\", alignItems: \"center\", gap: \"5px\" }}\n >\n <Box component={AiOutlineExperiment} sx={{ fontSize: \"1.2em\" }} />\n Sandbox\n </Typography>\n <Typography sx={{ fontSize: \"0.75rem\", color: \"grey.500\" }}>\n Compare the run results based on the modified SQL code of model{\" \"}\n <b>{current?.name}</b>\n </Typography>\n </Box>\n <Box sx={{ flexGrow: 1 }} />\n <QueryForm\n defaultPrimaryKeys={primaryKeys}\n onPrimaryKeysChange={onPrimaryKeysChange}\n />\n <MuiTooltip title=\"Run diff to see the changes\">\n <Button\n size=\"small\"\n sx={{ mt: \"16px\", fontSize: \"14px\" }}\n onClick={() => {\n onRunResultOpen();\n runQuery();\n }}\n color=\"iochmara\"\n variant=\"contained\"\n disabled={isPending}\n >\n {isPending ? \"Running...\" : \"Run Diff\"}\n </Button>\n </MuiTooltip>\n </Stack>\n );\n}\n\ninterface SandboxEditorLabelsProps {\n currentModelID: string;\n height?: string;\n flex?: string;\n isDark?: boolean;\n}\n\nfunction SandboxEditorLabels({\n currentModelID,\n height = \"32px\",\n flex = \"0 0 auto\",\n isDark = false,\n}: SandboxEditorLabelsProps) {\n const { lineageGraph, envInfo } = useLineageGraphContext();\n const widthOfBar = \"50%\";\n const margin = \"0 16px\";\n\n const currentTime = formatTimestamp(\n envInfo?.dbt?.current?.generated_at ?? \"\",\n );\n const latestUpdateDistanceToNow = formatDistanceToNow(currentTime, {\n addSuffix: true,\n });\n let schema = \"N/A\";\n if (lineageGraph?.nodes[currentModelID]) {\n const value = lineageGraph.nodes[currentModelID];\n if (value.data.data.current?.schema) {\n schema = value.data.data.current.schema;\n }\n }\n\n return (\n <Stack\n direction=\"row\"\n sx={{\n gap: 0,\n height,\n flex,\n fontSize: \"14px\",\n alignItems: \"center\",\n m: 0,\n bgcolor: isDark\n ? alpha(colors.neutral[700], 0.5)\n : alpha(colors.neutral[100], 0.5),\n }}\n >\n <Stack sx={{ width: widthOfBar }}>\n <Typography sx={{ fontWeight: \"bold\", margin }}>\n ORIGINAL (Schema: {schema}, Last Updated: {latestUpdateDistanceToNow})\n </Typography>\n </Stack>\n <Stack sx={{ width: widthOfBar }}>\n <Typography sx={{ fontWeight: \"bold\", margin }}>\n SANDBOX EDITOR\n </Typography>\n </Stack>\n </Stack>\n );\n}\n\ninterface SqlPreviewProps {\n current?: SandboxNodeData;\n onChange: (value: string) => void;\n isDark?: boolean;\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n DiffEditor: ComponentType<any>;\n}\n\nfunction SqlPreview({\n current,\n onChange,\n isDark,\n DiffEditor,\n}: SqlPreviewProps) {\n return (\n <DiffEditor\n original={current?.raw_code ?? \"\"}\n modified={current?.raw_code ?? \"\"}\n language=\"sql\"\n readOnly={false}\n lineNumbers={true}\n sideBySide={true}\n theme={isDark ? \"dark\" : \"light\"}\n height=\"100%\"\n onModifiedChange={onChange}\n />\n );\n}\n\n/**\n * SandboxView Component\n *\n * A modal dialog for previewing and comparing SQL code changes in a sandbox environment.\n * Allows editing modified SQL and running diff queries against the data warehouse.\n *\n * Components are injected as props to allow the consuming application\n * to provide its own implementations (e.g., specific editors, form components).\n */\nexport function SandboxView({\n isOpen,\n onClose,\n current,\n DiffEditor,\n QueryForm,\n RunResultPane,\n isDark = false,\n primaryKeys = [],\n onPrimaryKeysChange,\n isPending = false,\n onRunQuery,\n onRunResultOpen,\n onModifiedCodeChange,\n onShowFeedback,\n tracking,\n logoUrl = \"/logo/recce-logo-white.png\",\n brandName = \"RECCE\",\n}: SandboxViewProps) {\n const [isRunResultOpen, setIsRunResultOpen] = useState(false);\n\n const handleModifiedCodeChange = (code: string) => {\n onModifiedCodeChange?.(code);\n };\n\n const handleRunResultOpen = () => {\n setIsRunResultOpen(true);\n onRunResultOpen?.();\n };\n\n const handleRunQuery = () => {\n onRunQuery?.();\n };\n\n const handleClose = () => {\n onClose();\n setIsRunResultOpen(false);\n tracking?.onPreviewChange?.({ action: \"close\", node: current?.name });\n };\n\n const handlePrimaryKeysChange = (keys: string[]) => {\n onPrimaryKeysChange?.(keys);\n };\n\n return (\n <MuiDialog\n open={isOpen}\n onClose={handleClose}\n maxWidth={false}\n fullWidth\n slotProps={{\n paper: {\n sx: {\n width: \"100%\",\n height: \"100%\",\n maxWidth: \"100%\",\n maxHeight: \"100%\",\n m: 0,\n },\n },\n }}\n >\n <Box\n sx={{\n height: \"40px\",\n bgcolor: \"cyan.600\",\n px: 0,\n py: 2,\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ height: \"100%\", gap: \"10px\" }}\n >\n <Box\n component=\"img\"\n sx={{ width: \"20px\", height: \"20px\", ml: \"18px\" }}\n src={logoUrl}\n alt=\"logo\"\n />\n <Typography\n variant=\"h6\"\n component=\"h1\"\n sx={{\n fontFamily: '\"Montserrat\", sans-serif',\n fontSize: \"1.125rem\",\n color: \"common.white\",\n }}\n >\n {brandName}\n </Typography>\n <Chip\n label=\"Experiment\"\n size=\"small\"\n variant=\"outlined\"\n sx={{\n fontSize: \"0.875rem\",\n color: \"common.white\",\n borderColor: \"rgba(255,255,255,0.5)\",\n }}\n />\n </Stack>\n <IconButton\n aria-label=\"close\"\n onClick={handleClose}\n sx={{\n position: \"absolute\",\n right: 8,\n top: 4,\n color: \"common.white\",\n }}\n >\n <IoClose />\n </IconButton>\n </Box>\n <DialogContent sx={{ p: 0 }}>\n {/* Constant props to avoid react-split destroy/recreate */}\n <VSplit\n sizes={isRunResultOpen ? [50, 50] : [100, 0]}\n minSize={0}\n gutterSize={5}\n className={isRunResultOpen ? undefined : \"split-gutter-hidden\"}\n style={{\n flex: \"1\",\n contain: \"size\",\n height: \"100%\",\n }}\n >\n <Stack sx={{ height: \"100%\", m: 0, p: 0 }}>\n <SandboxTopBar\n current={current}\n primaryKeys={primaryKeys}\n onPrimaryKeysChange={handlePrimaryKeysChange}\n onRunResultOpen={handleRunResultOpen}\n runQuery={handleRunQuery}\n isPending={isPending}\n QueryForm={QueryForm}\n />\n <SandboxEditorLabels\n height=\"32px\"\n flex=\"0 0 auto\"\n currentModelID={current?.id ?? \"\"}\n isDark={isDark}\n />\n <SqlPreview\n current={current}\n onChange={handleModifiedCodeChange}\n isDark={isDark}\n DiffEditor={DiffEditor}\n />\n </Stack>\n {isRunResultOpen ? (\n <RunResultPane\n onClose={() => setIsRunResultOpen(false)}\n disableAddToChecklist\n />\n ) : (\n <Box />\n )}\n </VSplit>\n </DialogContent>\n {/* Fixed position button */}\n <Box sx={{ position: \"fixed\", bottom: 16, right: 16, opacity: 0.5 }}>\n <MuiTooltip title=\"Give us feedback\">\n <IconButton\n aria-label=\"feedback\"\n size=\"medium\"\n onClick={() => {\n onShowFeedback?.();\n }}\n >\n <VscFeedback />\n </IconButton>\n </MuiTooltip>\n </Box>\n </MuiDialog>\n );\n}\n","import { useMutation } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport {\n LOCAL_STORAGE_KEYS,\n type QueryParams,\n type SubmitOptions,\n submitQueryDiff,\n waitRun,\n} from \"../../api\";\nimport { useRecceActionContext, useRecceServerFlag } from \"../../contexts\";\nimport {\n useApiConfig,\n useFeedbackCollectionToast,\n useGuideToast,\n useIsDark,\n useRecceQueryContext,\n} from \"../../hooks\";\nimport {\n trackPreviewChange,\n trackPreviewChangeFeedback,\n trackSingleEnvironment,\n} from \"../../lib/api/track\";\nimport { DiffEditor } from \"../../primitives\";\nimport { QueryForm } from \"../query\";\nimport { RunResultPaneOss as RunResultPane } from \"../run\";\nimport {\n SandboxView as BaseSandboxView,\n type SandboxNodeData,\n} from \"./SandboxView\";\n\ninterface SandboxViewProps {\n isOpen: boolean;\n onClose: () => void;\n current?: SandboxNodeData;\n height?: string;\n}\n\n/**\n * OSS wrapper for SandboxView that injects OSS-specific implementations.\n *\n * This wrapper:\n * 1. Handles query execution with React Query mutations\n * 2. Injects OSS-specific components (DiffEditor, QueryForm, RunResultPane)\n * 3. Provides OSS-specific tracking and feedback toasts\n * 4. Manages run state via useRecceActionContext\n *\n * The underlying BaseSandboxView from @datarecce/ui is framework-agnostic\n * and accepts components as props for dependency injection.\n */\nexport function SandboxViewOss({ isOpen, onClose, current }: SandboxViewProps) {\n const [modifiedCode, setModifiedCode] = useState<string>(\n current?.raw_code ?? \"\",\n );\n const [prevIsOpen, setPrevIsOpen] = useState(isOpen);\n const { showRunId, clearRunResult } = useRecceActionContext();\n const { primaryKeys, setPrimaryKeys } = useRecceQueryContext();\n const { data: flags, isLoading } = useRecceServerFlag();\n const { apiClient } = useApiConfig();\n const isDark = useIsDark();\n\n // Reset modifiedCode when modal opens\n if (isOpen !== prevIsOpen) {\n setPrevIsOpen(isOpen);\n if (isOpen) {\n setModifiedCode(current?.raw_code ?? \"\");\n }\n }\n\n const queryFn = async () => {\n const sqlTemplate = modifiedCode;\n const runFn = submitQueryDiff;\n const params: QueryParams = {\n current_model: current?.name ?? \"\",\n primary_keys: primaryKeys,\n sql_template: sqlTemplate,\n };\n const options: SubmitOptions = { nowait: true };\n\n const { run_id } = await runFn(params, options, apiClient);\n\n showRunId(run_id);\n\n return await waitRun(run_id, undefined, apiClient);\n };\n\n const { mutate: runQuery, isPending } = useMutation({\n mutationFn: queryFn,\n onSuccess(data) {\n if (data.error) {\n trackPreviewChange({\n action: \"run\",\n node: current?.name,\n status: \"failure\",\n });\n } else {\n trackPreviewChange({\n action: \"run\",\n node: current?.name,\n status: \"success\",\n });\n setTimeout(() => {\n feedbackToast();\n }, 1000);\n if (!isLoading && flags?.single_env_onboarding) {\n setTimeout(() => {\n prepareEnvToast();\n }, 2000);\n }\n }\n },\n });\n\n const { feedbackToast, closeToast } = useFeedbackCollectionToast({\n feedbackId: LOCAL_STORAGE_KEYS.previewChangeFeedbackID,\n description: \"Enjoy preview change?\",\n\n onFeedbackSubmit: (feedback: string) => {\n switch (feedback) {\n case \"like\":\n trackPreviewChangeFeedback({ feedback: \"like\", node: current?.name });\n break;\n case \"dislike\":\n trackPreviewChangeFeedback({\n feedback: \"dislike\",\n node: current?.name,\n });\n break;\n case \"link\":\n trackPreviewChangeFeedback({ feedback: \"form\", node: current?.name });\n break;\n default:\n console.log(\"Not support feedback type\");\n }\n },\n externalLink:\n \"https://docs.google.com/forms/d/e/1FAIpQLSd7Lei7Ijwo7MinWaI0K6rzZi_21gV1BKetmiNEX254kDziDA/viewform?usp=header\",\n externalLinkText: \"Give us feedback\",\n });\n\n const { guideToast: prepareEnvToast, closeGuideToast } = useGuideToast({\n guideId: LOCAL_STORAGE_KEYS.prepareEnvGuideID,\n description: \"Want to compare data changes with production data?\",\n externalLink: \"https://docs.reccehq.com/get-started/#prepare-dbt-artifacts\",\n externalLinkText: \"Learn how.\",\n onExternalLinkClick: () => {\n trackSingleEnvironment({\n action: \"external_link\",\n from: \"preview_changes\",\n node: current?.name,\n });\n },\n });\n\n const handleClose = () => {\n onClose();\n clearRunResult();\n closeToast();\n closeGuideToast();\n };\n\n return (\n <BaseSandboxView\n isOpen={isOpen}\n onClose={handleClose}\n current={current}\n DiffEditor={DiffEditor}\n QueryForm={QueryForm}\n RunResultPane={RunResultPane}\n isDark={isDark}\n primaryKeys={primaryKeys ?? []}\n onPrimaryKeysChange={setPrimaryKeys}\n isPending={isPending}\n onRunQuery={runQuery}\n onModifiedCodeChange={setModifiedCode}\n onShowFeedback={() => feedbackToast(true)}\n tracking={{\n onPreviewChange: trackPreviewChange,\n }}\n />\n );\n}\n","\"use client\";\n\n/**\n * @file NodeViewOss.tsx\n * @description wrapper for NodeView that injects dependencies.\n *\n * This wrapper:\n * 1. Provides OSS-specific schema view components\n * 2. Injects action callbacks that integrate with OSS contexts\n * 3. Provides run type icons from the OSS registry\n * 4. Handles navigation and tracking\n */\n\nimport Typography from \"@mui/material/Typography\";\nimport { useRouter } from \"next/navigation\";\nimport { useMemo } from \"react\";\nimport type { LineageGraphNode } from \"../..\";\nimport { createSchemaDiffCheck } from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n useRouteConfig,\n} from \"../../contexts\";\nimport {\n useApiConfig,\n useModelColumns,\n useRecceQueryContext,\n} from \"../../hooks\";\nimport {\n EXPLORE_ACTION,\n EXPLORE_SOURCE,\n trackExploreAction,\n trackPreviewChange,\n} from \"../../lib/api/track\";\nimport { formatSelectColumns } from \"../../utils\";\nimport { SetupConnectionPopover } from \"../app\";\nimport { LearnHowLink, RecceNotification } from \"../onboarding-guide\";\nimport { findByRunType } from \"../run\";\nimport { SchemaView, SingleEnvSchemaView } from \"../schema\";\nimport { NodeSqlViewOss } from \"./NodeSqlViewOss\";\nimport { RowCountDiffTag, RowCountTag } from \"./NodeTag\";\nimport {\n NodeView as BaseNodeView,\n type NodeViewActionCallbacks,\n type RunTypeIconMap,\n} from \"./NodeView\";\nimport { SandboxViewOss } from \"./SandboxViewOss\";\nimport { ResourceTypeTag as ResourceTypeTagBase } from \"./tags\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface NodeViewProps {\n node: LineageGraphNode;\n onCloseNode: () => void;\n}\n\nconst ResourceTypeTag = ({ node }: { node: LineageGraphNode }) => (\n <ResourceTypeTagBase data={{ resourceType: node.data.resourceType }} />\n);\n\n// =============================================================================\n// OSS-SPECIFIC WRAPPER COMPONENTS\n// =============================================================================\n\n/**\n * Notification component that includes the LearnHowLink.\n */\nfunction OssNotificationComponent({ onClose }: { onClose: () => void }) {\n return (\n <RecceNotification onClose={onClose} align=\"flex-start\">\n <Typography variant=\"body2\">\n Enable the Recce Checklist and start adding checks for better data\n validation and review.\n <br />\n <LearnHowLink />\n </Typography>\n </RecceNotification>\n );\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * OSS wrapper for NodeView that injects OSS-specific dependencies.\n *\n * This wrapper provides:\n * - OSS-specific schema view components\n * - Action callbacks that integrate with OSS contexts (tracking, navigation)\n * - Run type icons from the OSS registry\n * - Connection popover wrapper for database setup prompts\n * - Sandbox dialog component\n */\nexport function NodeViewOss({ node, onCloseNode }: NodeViewProps) {\n const router = useRouter();\n const { runAction } = useRecceActionContext();\n const { isActionAvailable, envInfo } = useLineageGraphContext();\n const { singleEnv: isSingleEnvOnboarding, featureToggles } =\n useRecceInstanceContext();\n const { setSqlQuery, setPrimaryKeys } = useRecceQueryContext();\n const { primaryKey } = useModelColumns(node.data.name);\n const { apiClient } = useApiConfig();\n const { basePath } = useRouteConfig();\n\n // Build run type icons map from OSS registry\n const runTypeIcons: RunTypeIconMap = useMemo(\n () => ({\n query: findByRunType(\"query\").icon,\n row_count: findByRunType(\"row_count\").icon,\n row_count_diff: findByRunType(\"row_count_diff\").icon,\n profile: findByRunType(\"profile\").icon,\n profile_diff: findByRunType(\"profile_diff\").icon,\n query_diff: findByRunType(\"query_diff\").icon,\n value_diff: findByRunType(\"value_diff\").icon,\n top_k_diff: findByRunType(\"top_k_diff\").icon,\n histogram_diff: findByRunType(\"histogram_diff\").icon,\n schema_diff: findByRunType(\"schema_diff\").icon,\n sandbox: findByRunType(\"sandbox\").icon,\n }),\n [],\n );\n\n // Build query string for this node\n const baseColumns = Object.keys(node.data.data.base?.columns ?? {});\n const currentColumns = Object.keys(node.data.data.current?.columns ?? {});\n const formattedColumns = formatSelectColumns(baseColumns, currentColumns);\n const query = useMemo(() => {\n if (formattedColumns.length) {\n return `select \\n ${formattedColumns.join(\"\\n \")}\\nfrom {{ ref(\"${node.data.name}\") }}`;\n }\n return `select * from {{ ref(\"${node.data.name}\") }}`;\n }, [formattedColumns, node.data.name]);\n\n // Action callbacks for the base component\n const actionCallbacks: NodeViewActionCallbacks = useMemo(\n () => ({\n onQueryClick: () => {\n if (envInfo?.adapterType === \"dbt\") {\n setSqlQuery(query);\n } else if (envInfo?.adapterType === \"sqlmesh\") {\n setSqlQuery(`select * from ${node.data.name}`);\n }\n router.push(`${basePath}/query`);\n },\n\n onRowCountClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.ROW_COUNT,\n source: EXPLORE_SOURCE.SCHEMA_ROW_COUNT_BUTTON,\n node_count: 1,\n });\n runAction(\n \"row_count\",\n { node_names: [node.data.name] },\n { showForm: false, showLast: false },\n );\n },\n\n onRowCountDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.ROW_COUNT_DIFF,\n source: EXPLORE_SOURCE.SCHEMA_ROW_COUNT_BUTTON,\n node_count: 1,\n });\n runAction(\n \"row_count_diff\",\n { node_names: [node.data.name] },\n { showForm: false, showLast: false },\n );\n },\n\n onProfileClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.PROFILE,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_SINGLE_ENV,\n node_count: 1,\n });\n runAction(\n \"profile\",\n { model: node.data.name },\n { showForm: true, showLast: false },\n );\n },\n\n onProfileDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.PROFILE_DIFF,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,\n node_count: 1,\n });\n runAction(\n \"profile_diff\",\n { model: node.data.name },\n { showForm: true, showLast: false },\n );\n },\n\n onQueryDiffClick: () => {\n if (envInfo?.adapterType === \"dbt\") {\n setSqlQuery(query);\n } else if (envInfo?.adapterType === \"sqlmesh\") {\n setSqlQuery(`select * from ${node.data.name}`);\n }\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys(primaryKey !== undefined ? [primaryKey] : undefined);\n }\n router.push(`${basePath}/query`);\n },\n\n onValueDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.VALUE_DIFF,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,\n node_count: 1,\n });\n runAction(\n \"value_diff\",\n { model: node.data.name },\n { showForm: true, showLast: false },\n );\n },\n\n onTopKDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.TOP_K_DIFF,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,\n node_count: 1,\n });\n runAction(\n \"top_k_diff\",\n { model: node.data.name, column_name: \"\", k: 50 },\n { showForm: true },\n );\n },\n\n onHistogramDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.HISTOGRAM_DIFF,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,\n node_count: 1,\n });\n runAction(\n \"histogram_diff\",\n { model: node.data.name, column_name: \"\", column_type: \"\" },\n { showForm: true },\n );\n },\n\n onAddSchemaDiffClick: async () => {\n const check = await createSchemaDiffCheck(\n { node_id: node.id },\n apiClient,\n );\n router.push(`${basePath}/checks/?id=${check.check_id}`);\n },\n\n onSandboxClick: () => {\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys(primaryKey !== undefined ? [primaryKey] : undefined);\n }\n trackPreviewChange({\n action: \"explore\",\n node: node.data.name,\n });\n },\n }),\n [\n node,\n query,\n envInfo,\n setSqlQuery,\n runAction,\n isActionAvailable,\n setPrimaryKeys,\n primaryKey,\n apiClient,\n router.push,\n basePath,\n ],\n );\n\n return (\n <BaseNodeView\n node={node}\n onCloseNode={onCloseNode}\n isSingleEnv={isSingleEnvOnboarding ?? false}\n featureToggles={featureToggles}\n // Schema components\n SchemaView={SchemaView}\n SingleEnvSchemaView={SingleEnvSchemaView}\n NodeSqlView={NodeSqlViewOss}\n // Tag components\n ResourceTypeTag={ResourceTypeTag}\n RowCountDiffTag={RowCountDiffTag}\n RowCountTag={RowCountTag}\n // Notification for single env\n NotificationComponent={OssNotificationComponent}\n // Connection popover wrapper\n ConnectionPopoverWrapper={SetupConnectionPopover}\n // Sandbox dialog\n SandboxDialog={SandboxViewOss}\n // Icons\n runTypeIcons={runTypeIcons}\n // Callbacks\n actionCallbacks={actionCallbacks}\n isActionAvailable={isActionAvailable}\n />\n );\n}\n","import type { ColumnLineageData } from \"../../api/cll\";\nimport type { LineageDiffData } from \"../../api/info\";\n\n/**\n * Extract change analysis data from a CLL response and merge it into\n * an existing LineageDiffData object.\n *\n * This replaces the old pattern of refetching the entire lineage after\n * a CLL call with change_analysis: true. Instead, we patch the cached\n * diff directly so React Query triggers a re-render.\n */\nexport function patchLineageDiffFromCll(\n existingDiff: LineageDiffData,\n cllData: ColumnLineageData,\n): LineageDiffData {\n const patch: LineageDiffData = { ...existingDiff };\n\n for (const [nodeId, cllNode] of Object.entries(cllData.current.nodes)) {\n if (!cllNode.change_status) {\n continue;\n }\n\n // Build column change map from CLL node's columns\n let columns: Record<string, \"added\" | \"removed\" | \"modified\"> | null = null;\n if (cllNode.columns) {\n const columnChanges: Record<string, \"added\" | \"removed\" | \"modified\"> =\n {};\n let hasChanges = false;\n for (const col of Object.values(cllNode.columns)) {\n if (col.change_status) {\n columnChanges[col.name] = col.change_status;\n hasChanges = true;\n }\n }\n if (hasChanges) {\n columns = columnChanges;\n }\n }\n\n patch[nodeId] = {\n change_status: cllNode.change_status,\n change: cllNode.change_category\n ? { category: cllNode.change_category, columns }\n : null,\n };\n }\n\n return patch;\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { PiInfo } from \"react-icons/pi\";\n\nimport type { RecceFeatureToggles } from \"../../contexts/instance\";\n\n/**\n * Props for the SetupConnectionBanner component.\n *\n * Uses dependency injection for context dependencies to enable reuse\n * across different applications (OSS Recce, Recce Cloud).\n */\nexport interface SetupConnectionBannerProps {\n /**\n * Feature toggles from RecceInstanceContext.\n * Used to determine if banner should be shown (metadata only mode).\n */\n featureToggles: RecceFeatureToggles;\n\n /**\n * URL to open when user clicks \"Connect to Data Warehouse\" button.\n * Injected because URL generation varies by application context.\n */\n settingsUrl: string;\n}\n\n/**\n * Banner component that prompts users to set up a data warehouse connection.\n *\n * Displays when the Recce instance is in \"metadata only\" mode, indicating\n * that query functions are disabled without a data warehouse connection.\n *\n * @example\n * ```tsx\n * import { SetupConnectionBanner } from \"@datarecce/ui\";\n *\n * function MyComponent() {\n * const { featureToggles } = useRecceInstanceContext();\n * const { data: instanceInfo } = useRecceInstanceInfo();\n * const settingsUrl = getSettingsUrl(instanceInfo);\n *\n * return (\n * <SetupConnectionBanner\n * featureToggles={featureToggles}\n * settingsUrl={settingsUrl}\n * />\n * );\n * }\n * ```\n */\nexport function SetupConnectionBanner({\n featureToggles,\n settingsUrl,\n}: SetupConnectionBannerProps) {\n if (featureToggles.mode !== \"metadata only\") {\n return null;\n }\n\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n width: \"100%\",\n px: 1,\n py: 0.25,\n bgcolor: \"cyan.50\",\n }}\n >\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ flex: 1, fontSize: \"0.875rem\", color: \"cyan.600\" }}\n spacing={1}\n >\n <Box component={PiInfo} />\n <Typography sx={{ fontSize: \"inherit\", color: \"inherit\" }}>\n Query functions disabled without a data warehouse connection.\n </Typography>\n <Button\n sx={{ bgcolor: \"iochmara.400\" }}\n size=\"small\"\n variant=\"contained\"\n onClick={() => {\n window.open(settingsUrl, \"_blank\");\n }}\n >\n Connect to Data Warehouse\n </Button>\n </Stack>\n </Box>\n );\n}\n\nexport default SetupConnectionBanner;\n","\"use client\";\n\nimport { useRecceInstanceContext, useRecceInstanceInfo } from \"../../contexts\";\nimport { RECCE_SUPPORT_CALENDAR_URL } from \"../../lib/const\";\nimport { getSettingsUrl } from \"../../utils\";\nimport {\n SetupConnectionBanner as BaseSetupConnectionBanner,\n type SetupConnectionBannerProps as BaseSetupConnectionBannerProps,\n} from \"./SetupConnectionBanner\";\n\n/**\n * Props for the OSS SetupConnectionBanner wrapper.\n * Extends the base props but makes injected dependencies optional\n * since they're provided by OSS-specific contexts.\n */\nexport type SetupConnectionBannerOssProps =\n Partial<BaseSetupConnectionBannerProps>;\n\n/**\n * OSS wrapper for SetupConnectionBanner.\n *\n * Provides OSS-specific context integration by automatically:\n * - Fetching feature toggles from RecceInstanceContext\n * - Generating settings URL from instance info\n */\nexport default function SetupConnectionBannerOss(\n props: SetupConnectionBannerOssProps,\n) {\n const { featureToggles } = useRecceInstanceContext();\n const { data: instanceInfo } = useRecceInstanceInfo();\n\n return (\n <BaseSetupConnectionBanner\n featureToggles={props.featureToggles ?? featureToggles}\n settingsUrl={\n props.settingsUrl ??\n getSettingsUrl(instanceInfo, RECCE_SUPPORT_CALENDAR_URL)\n }\n />\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Link from \"@mui/material/Link\";\nimport List from \"@mui/material/List\";\nimport ListItem from \"@mui/material/ListItem\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { RiMindMap, RiTerminalBoxLine } from \"react-icons/ri\";\n\n// =============================================================================\n// TYPE DEFINITIONS\n// =============================================================================\n\n/**\n * Props for BaseEnvironmentSetupGuide component.\n * Uses dependency injection for external URLs to support different consumers.\n */\nexport interface BaseEnvironmentSetupGuideProps {\n /**\n * URL to navigate to when \"Start Now\" button is clicked.\n * @default \"https://docs.reccehq.com/get-started/#prepare-dbt-artifacts\"\n */\n docsUrl?: string;\n /**\n * Callback when the \"Start Now\" button is clicked.\n * If provided, will be called instead of opening the URL.\n */\n onStartClick?: () => void;\n}\n\n/**\n * Props for BaseEnvironmentSetupNotification component.\n * Uses dependency injection for external URLs to support different consumers.\n */\nexport interface BaseEnvironmentSetupNotificationProps {\n /**\n * URL for the documentation link.\n * @default \"https://docs.reccehq.com/configure-diff/\"\n */\n docsUrl?: string;\n}\n\n// =============================================================================\n// COMPONENTS\n// =============================================================================\n\n/**\n * BaseEnvironmentSetupGuide Component\n *\n * Full-page guide displayed when Recce is running in single environment mode\n * (limited functionality mode). Explains the benefits of setting up a base\n * environment and provides a call-to-action to configure it.\n *\n * @example\n * ```tsx\n * import { BaseEnvironmentSetupGuide } from '@datarecce/ui/components/lineage';\n *\n * // Default usage with standard docs URL\n * <BaseEnvironmentSetupGuide />\n *\n * // Custom docs URL for different consumer\n * <BaseEnvironmentSetupGuide\n * docsUrl=\"https://cloud.reccehq.com/docs/setup\"\n * />\n *\n * // Custom click handler\n * <BaseEnvironmentSetupGuide\n * onStartClick={() => navigateToSetup()}\n * />\n * ```\n */\nexport function BaseEnvironmentSetupGuide({\n docsUrl = \"https://docs.reccehq.com/get-started/#prepare-dbt-artifacts\",\n onStartClick,\n}: BaseEnvironmentSetupGuideProps = {}) {\n const handleStartClick = () => {\n if (onStartClick) {\n onStartClick();\n } else {\n window.open(docsUrl, \"_blank\");\n }\n };\n\n return (\n <Stack\n sx={{\n flex: 1,\n height: \"100%\",\n minHeight: 0,\n m: 1,\n p: 2,\n bgcolor: \"iochmara.50\",\n borderRadius: 2,\n boxShadow: 2,\n justifyContent: \"center\",\n }}\n >\n <Stack\n sx={{ width: \"80%\", overflowY: \"auto\", gap: 3, px: 4, pb: 4 }}\n alignSelf=\"center\"\n >\n <Stack alignItems=\"center\" spacing={2}>\n <Box\n sx={{\n p: 1,\n bgcolor: \"background.paper\",\n borderRadius: \"50%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: 2,\n }}\n >\n <Box\n component={RiTerminalBoxLine}\n sx={{ fontSize: 28, color: \"iochmara.500\" }}\n />\n </Box>\n <Typography variant=\"h5\" sx={{ mt: 2 }}>\n Wait, there's more!\n </Typography>\n <Typography sx={{ textAlign: \"center\" }}>\n Recce is currently running in{\" \"}\n <Typography component=\"span\" sx={{ fontWeight: \"bold\" }}>\n limited functionality mode\n </Typography>{\" \"}\n so you can run queries but{\" \"}\n <Typography component=\"span\" sx={{ fontWeight: \"bold\" }}>\n can't diff the results yet!\n </Typography>\n </Typography>\n </Stack>\n <Stack spacing={1}>\n <Typography>\n To unlock the full power of Recce, set up a base environment of dbt\n artifacts for comparison.\n </Typography>\n <Typography>Once configured, you'll be able to:</Typography>\n <List sx={{ listStyleType: \"disc\", pl: 2 }}>\n <ListItem sx={{ display: \"list-item\", p: 0 }}>\n <Typography>Run statistical data diffs</Typography>\n </ListItem>\n <ListItem sx={{ display: \"list-item\", p: 0 }}>\n <Typography>Run query diffs</Typography>\n </ListItem>\n <ListItem sx={{ display: \"list-item\", p: 0 }}>\n <Typography>Save checks to your Recce Checklist</Typography>\n </ListItem>\n <ListItem sx={{ display: \"list-item\", p: 0 }}>\n <Typography>...and more!</Typography>\n </ListItem>\n </List>\n <Typography>\n Take the next step toward better data impact assessment.\n </Typography>\n </Stack>\n <Stack sx={{ width: \"100%\", mt: 3 }}>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n size=\"large\"\n onClick={handleStartClick}\n >\n Start Now\n </Button>\n </Stack>\n </Stack>\n </Stack>\n );\n}\n\n/**\n * BaseEnvironmentSetupNotification Component\n *\n * Compact notification/banner component displayed in the lineage view sidebar\n * when in single environment mode. Provides quick guidance on how to set up\n * full environment comparison.\n *\n * @example\n * ```tsx\n * import { BaseEnvironmentSetupNotification } from '@datarecce/ui/components/lineage';\n *\n * // Default usage with standard docs URL\n * <BaseEnvironmentSetupNotification />\n *\n * // Custom docs URL for different consumer\n * <BaseEnvironmentSetupNotification\n * docsUrl=\"https://cloud.reccehq.com/docs/configure\"\n * />\n * ```\n */\nexport function BaseEnvironmentSetupNotification({\n docsUrl = \"https://docs.reccehq.com/configure-diff/\",\n}: BaseEnvironmentSetupNotificationProps = {}) {\n return (\n <Stack direction=\"row\" spacing=\"10px\" alignItems=\"flex-start\">\n <Box\n component={RiMindMap}\n sx={{ color: \"iochmara.main\", fontSize: 20 }}\n />\n <Stack spacing=\"5px\">\n <Typography sx={{ fontWeight: 700 }}>\n Single Environment Mode{\" \"}\n <Typography\n component=\"span\"\n sx={{ color: \"error.main\", fontWeight: 600 }}\n >\n Limited Functionality\n </Typography>\n </Typography>\n\n <Typography sx={{ fontSize: \"0.875rem\" }}>\n Single Environment Mode allows you to explore your dbt project but\n won't show data comparisons between environments.\n </Typography>\n <Typography sx={{ fontSize: \"0.875rem\" }}>\n To set up full environment comparison:\n </Typography>\n <List sx={{ pl: 2 }}>\n <ListItem sx={{ p: 0, display: \"list-item\" }}>\n <Typography sx={{ fontSize: \"0.875rem\" }}>\n Run `recce debug` for setup assistance\n </Typography>\n </ListItem>\n <ListItem sx={{ p: 0, display: \"list-item\" }}>\n <Typography sx={{ fontSize: \"0.875rem\" }}>\n Visit{\" \"}\n <Link\n sx={{ color: \"primary.main\", fontWeight: 500 }}\n target=\"_blank\"\n href={docsUrl}\n >\n docs\n </Link>{\" \"}\n for configuration details\n </Typography>\n </ListItem>\n </List>\n </Stack>\n </Stack>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Stack from \"@mui/material/Stack\";\nimport type { LineageDiffViewOptions } from \"../../../api\";\n\n/**\n * Loading state component for LineageView.\n * Displays a centered spinner while lineage data is loading.\n */\nexport function LineageViewLoading() {\n return (\n <Box\n sx={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <CircularProgress size={48} />\n </Box>\n );\n}\n\n/**\n * Props for LineageViewError component.\n */\nexport interface LineageViewErrorProps {\n /** The error message to display */\n error: string;\n /** Callback to retry loading the lineage data */\n onRetry?: () => void;\n}\n\n/**\n * Error state component for LineageView.\n * Displays error message with a retry button.\n */\nexport function LineageViewError({ error, onRetry }: LineageViewErrorProps) {\n return (\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Stack alignItems=\"center\" spacing={1}>\n <Box>\n Failed to load lineage data. This could be because the server has been\n terminated or there is a network error.\n </Box>\n <Box>[Reason: {error}]</Box>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n onClick={() => {\n if (onRetry) {\n onRetry();\n }\n }}\n >\n Retry\n </Button>\n </Stack>\n </Box>\n );\n}\n\n/**\n * Props for LineageViewNoChanges component.\n */\nexport interface LineageViewNoChangesProps {\n /** Current view options */\n viewOptions: LineageDiffViewOptions;\n /** Callback to change view options */\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n}\n\n/**\n * Empty state component when no changes are detected in changed_models view mode.\n * Provides a button to switch to \"all\" view mode.\n */\nexport function LineageViewNoChanges({\n viewOptions,\n onViewOptionsChanged,\n}: LineageViewNoChangesProps) {\n return (\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Stack alignItems=\"center\" spacing={1}>\n <>No change detected</>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n onClick={async () => {\n await onViewOptionsChanged({\n ...viewOptions,\n view_mode: \"all\",\n });\n }}\n >\n Show all nodes\n </Button>\n </Stack>\n </Box>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport type { ReactNode } from \"react\";\nimport { VscHistory } from \"react-icons/vsc\";\nimport { useRecceActionContext } from \"../../contexts\";\nimport { trackHistoryAction } from \"../../lib/api/track\";\n\nexport function HistoryToggle(): ReactNode {\n const { isHistoryOpen, showHistory } = useRecceActionContext();\n\n if (isHistoryOpen) {\n return;\n }\n\n return (\n <Box>\n <Box sx={{ fontSize: \"8pt\" }}>History</Box>\n\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<VscHistory />}\n onClick={() => {\n trackHistoryAction({ name: isHistoryOpen ? \"hide\" : \"show\" });\n showHistory();\n }}\n >\n Show\n </Button>\n </Box>\n );\n}\n","\"use client\";\n\n/**\n * @file LineageViewTopBar.tsx\n * @description Core LineageViewTopBar component with dependency injection.\n *\n * This component provides the top toolbar for the lineage view with filtering,\n * mode selection, package filtering, and action menus. It uses dependency injection\n * for consumer-specific features like routing, tracking, and run type icons.\n *\n * @example\n * ```tsx\n * import { LineageViewTopBar } from '@datarecce/ui/components/lineage';\n *\n * <LineageViewTopBar\n * viewOptions={viewOptions}\n * onViewOptionsChanged={handleViewOptionsChanged}\n * lineageGraph={lineageGraph}\n * featureToggles={featureToggles}\n * serverFlags={serverFlags}\n * selectedNodes={selectedNodes}\n * focusedNode={focusedNode}\n * onDeselect={handleDeselect}\n * onRunRowCount={handleRunRowCount}\n * onRunRowCountDiff={handleRunRowCountDiff}\n * onRunValueDiff={handleRunValueDiff}\n * onAddLineageDiffCheck={handleAddLineageDiffCheck}\n * onAddSchemaDiffCheck={handleAddSchemaDiffCheck}\n * runTypeIcons={runTypeIcons}\n * historyToggleSlot={<HistoryToggle />}\n * setupConnectionPopoverSlot={SetupConnectionPopover}\n * />\n * ```\n */\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Checkbox from \"@mui/material/Checkbox\";\nimport Divider from \"@mui/material/Divider\";\nimport FormControlLabel from \"@mui/material/FormControlLabel\";\nimport ListItemIcon from \"@mui/material/ListItemIcon\";\nimport ListItemText from \"@mui/material/ListItemText\";\nimport ListSubheader from \"@mui/material/ListSubheader\";\nimport Menu from \"@mui/material/Menu\";\nimport MenuItem from \"@mui/material/MenuItem\";\nimport Radio from \"@mui/material/Radio\";\nimport RadioGroup from \"@mui/material/RadioGroup\";\nimport Stack from \"@mui/material/Stack\";\nimport TextField from \"@mui/material/TextField\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport {\n type ComponentType,\n type CSSProperties,\n type MouseEvent,\n type ReactNode,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { FiPackage } from \"react-icons/fi\";\nimport { PiCaretDown } from \"react-icons/pi\";\nimport type { RecceServerFlags } from \"../../../api/flag\";\nimport type { LineageDiffViewOptions } from \"../../../api/lineagecheck\";\nimport type { RecceFeatureToggles } from \"../../../contexts/instance/types\";\nimport type {\n LineageGraph,\n LineageGraphNode,\n} from \"../../../contexts/lineage/types\";\nimport { useIsDark } from \"../../../hooks/useIsDark\";\nimport type { IconComponent } from \"../../run/types\";\nimport { getIconForResourceType } from \"../styles\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Icons for run types used in action menus.\n * Consumers inject their own icons based on their run type registry.\n */\nexport interface RunTypeIcons {\n /** Icon for row_count_diff action */\n rowCountDiff: IconComponent;\n /** Icon for value_diff action */\n valueDiff: IconComponent;\n /** Icon for lineage_diff action */\n lineageDiff: IconComponent;\n /** Icon for schema_diff action */\n schemaDiff: IconComponent;\n}\n\n/**\n * Props for the SetupConnectionPopover slot component.\n */\nexport interface SetupConnectionPopoverSlotProps {\n /** Whether to display the popover */\n display: boolean;\n /** Child element to wrap */\n children: ReactNode;\n}\n\n/**\n * Props for the LineageViewTopBar component.\n */\nexport interface LineageViewTopBarProps {\n // View options and state\n /** Current view options for filtering and display mode */\n viewOptions: LineageDiffViewOptions;\n /** Callback when view options change */\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n /** The lineage graph data */\n lineageGraph?: LineageGraph;\n /** Feature toggles controlling what is enabled */\n featureToggles: RecceFeatureToggles;\n /** Server-side feature flags */\n serverFlags?: RecceServerFlags;\n\n // Selection state\n /** Currently focused node (hovered/highlighted) */\n focusedNode?: LineageGraphNode;\n /** Currently selected nodes for batch operations */\n selectedNodes: LineageGraphNode[];\n /** Callback to clear selection */\n onDeselect: () => void;\n\n // Action callbacks\n /** Callback for running row count (single env mode) */\n onRunRowCount?: () => Promise<void>;\n /** Callback for running row count diff */\n onRunRowCountDiff?: () => Promise<void>;\n /** Callback for running value diff */\n onRunValueDiff?: () => Promise<void>;\n /** Callback for adding a lineage diff check */\n onAddLineageDiffCheck?: (\n viewMode?: LineageDiffViewOptions[\"view_mode\"],\n ) => void;\n /** Callback for adding a schema diff check */\n onAddSchemaDiffCheck?: () => void;\n\n // Dependency injection slots\n /** Icons for run types in action menus */\n runTypeIcons: RunTypeIcons;\n /** Slot for history toggle component */\n historyToggleSlot?: ReactNode;\n /** Component for setup connection popover wrapper */\n SetupConnectionPopoverSlot?: ComponentType<SetupConnectionPopoverSlotProps>;\n}\n\n// ============================================================================\n// Internal Components\n// ============================================================================\n\nconst getCodeBlockSx = (isDark: boolean) => ({\n fontSize: \"8pt\",\n bgcolor: isDark ? \"grey.700\" : \"grey.100\",\n px: 0.5,\n borderRadius: 1,\n});\n\nconst SelectFilterTooltip = () => {\n const isDark = useIsDark();\n const codeBlockSx = getCodeBlockSx(isDark);\n return (\n <Stack alignItems=\"flex-start\" spacing={0}>\n <Typography fontSize=\"10pt\" color=\"text.secondary\" pb={1}>\n Select nodes by dbt node selector syntax\n </Typography>\n <Typography fontSize=\"8pt\">\n <Box component=\"code\" sx={codeBlockSx}>\n model_name\n </Box>{\" \"}\n Select a node\n </Typography>\n <Typography fontSize=\"8pt\">\n <Box component=\"code\" sx={codeBlockSx}>\n model_name+\n </Box>{\" \"}\n Select downstream nodes\n </Typography>\n <Typography fontSize=\"8pt\">\n <Box component=\"code\" sx={codeBlockSx}>\n +model_name\n </Box>{\" \"}\n Select upstream nodes\n </Typography>\n <Typography fontSize=\"8pt\">\n <Box component=\"code\" sx={codeBlockSx}>\n model*\n </Box>{\" \"}\n Select by wildcard\n </Typography>\n </Stack>\n );\n};\n\ninterface ViewModeSelectMenuProps {\n isDisabled: boolean;\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n}\n\nconst ViewModeSelectMenu = ({\n isDisabled,\n viewOptions,\n onViewOptionsChanged,\n}: ViewModeSelectMenuProps) => {\n const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);\n const open = Boolean(anchorEl);\n\n const viewMode = viewOptions.view_mode ?? \"changed_models\";\n const label = viewMode === \"changed_models\" ? \"Changed Models\" : \"All\";\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n const handleSelect = (newViewMode: LineageDiffViewOptions[\"view_mode\"]) => {\n onViewOptionsChanged({\n ...viewOptions,\n view_mode: newViewMode,\n });\n handleClose();\n };\n\n const ModelIcon = getIconForResourceType(\"model\").icon;\n\n return (\n <>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n onClick={handleClick}\n disabled={isDisabled}\n startIcon={ModelIcon && <ModelIcon />}\n endIcon={<PiCaretDown />}\n sx={{ minWidth: 100, textTransform: \"none\", fontSize: \"0.75rem\" }}\n >\n {label}\n </Button>\n <Menu anchorEl={anchorEl} open={open} onClose={handleClose}>\n <ListSubheader sx={{ lineHeight: \"32px\", bgcolor: \"transparent\" }}>\n mode\n </ListSubheader>\n <RadioGroup value={viewMode}>\n <MenuItem onClick={() => handleSelect(\"changed_models\")}>\n <FormControlLabel\n value=\"changed_models\"\n control={<Radio size=\"small\" sx={{ py: 0 }} />}\n label=\"Changed Models\"\n sx={{ m: 0 }}\n />\n </MenuItem>\n <MenuItem onClick={() => handleSelect(\"all\")}>\n <FormControlLabel\n value=\"all\"\n control={<Radio size=\"small\" sx={{ py: 0 }} />}\n label=\"All\"\n sx={{ m: 0 }}\n />\n </MenuItem>\n </RadioGroup>\n </Menu>\n </>\n );\n};\n\ninterface PackageSelectMenuProps {\n isDisabled: boolean;\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n lineageGraph?: LineageGraph;\n}\n\nconst PackageSelectMenu = ({\n isDisabled,\n viewOptions,\n onViewOptionsChanged,\n lineageGraph,\n}: PackageSelectMenuProps) => {\n const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);\n const open = Boolean(anchorEl);\n\n // get unique package names\n const available = new Set<string>();\n const nodes = Object.values(lineageGraph?.nodes ?? {});\n for (const node of nodes) {\n if (node.data.packageName) {\n available.add(node.data.packageName);\n }\n }\n\n const projectName = lineageGraph?.manifestMetadata.current?.project_name;\n\n const selected = viewOptions.packages\n ? new Set(viewOptions.packages)\n : projectName\n ? new Set([projectName])\n : available;\n const isSelectAll = selected.size === available.size;\n const isSelectNone = selected.size === 0;\n const label =\n selected.size === 1\n ? Array.from(selected)[0]\n : isSelectAll\n ? \"All Packages\"\n : isSelectNone\n ? \"No Package\"\n : `${selected.size} Packages`;\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n const handleSelectAll = () => {\n if (isSelectAll) {\n onViewOptionsChanged({\n ...viewOptions,\n packages: [],\n });\n } else {\n onViewOptionsChanged({\n ...viewOptions,\n packages: Array.from(available),\n });\n }\n };\n\n const handleSelect = (pkg: string) => {\n const newSelected = new Set(selected);\n if (newSelected.has(pkg)) {\n newSelected.delete(pkg);\n } else {\n newSelected.add(pkg);\n }\n onViewOptionsChanged({\n ...viewOptions,\n packages: Array.from(newSelected),\n });\n };\n\n return (\n <>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n onClick={handleClick}\n disabled={isDisabled}\n startIcon={<FiPackage />}\n endIcon={<PiCaretDown />}\n sx={{ minWidth: 100, textTransform: \"none\", fontSize: \"0.75rem\" }}\n >\n {label}\n </Button>\n <Menu anchorEl={anchorEl} open={open} onClose={handleClose}>\n <ListSubheader sx={{ lineHeight: \"32px\", bgcolor: \"transparent\" }}>\n Select Packages\n </ListSubheader>\n <MenuItem onClick={handleSelectAll}>\n <Checkbox\n checked={isSelectAll}\n indeterminate={!isSelectAll && !isSelectNone}\n size=\"small\"\n sx={{ py: 0 }}\n />\n <ListItemText>Select All</ListItemText>\n </MenuItem>\n\n <Divider />\n\n {Array.from(available).map((pkg) => (\n <MenuItem key={pkg} onClick={() => handleSelect(pkg)}>\n <Checkbox checked={selected.has(pkg)} size=\"small\" sx={{ py: 0 }} />\n <ListItemText className=\"no-track-pii-safe\">{pkg}</ListItemText>\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n};\n\ninterface NodeSelectionInputProps {\n value: string;\n onChange: (value: string) => void;\n isDisabled?: boolean;\n tooltipComponent?: ReactNode;\n showTooltip?: boolean;\n}\n\nconst NodeSelectionInput = ({\n value,\n onChange,\n isDisabled,\n tooltipComponent,\n showTooltip = true,\n}: NodeSelectionInputProps) => {\n const [inputValue, setInputValue] = useState(value);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (inputRef.current) {\n inputRef.current.value = value;\n }\n }, [value]);\n\n return (\n <MuiTooltip\n title={showTooltip ? tooltipComponent : \"\"}\n placement=\"bottom-start\"\n slotProps={{\n tooltip: {\n sx: {\n width: \"18.75rem\",\n p: 2,\n boxShadow: 3,\n border: 1,\n borderRadius: 1,\n color: \"text.primary\",\n bgcolor: \"background.paper\",\n },\n },\n }}\n >\n <TextField\n inputRef={inputRef}\n size=\"small\"\n placeholder=\"with selectors\"\n disabled={isDisabled}\n value={inputValue}\n onChange={(event) => {\n setInputValue(event.target.value);\n }}\n onKeyUp={(event) => {\n if (event.key === \"Enter\") {\n onChange(inputValue);\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n setInputValue(value);\n if (inputRef.current) {\n inputRef.current.blur();\n }\n }\n }}\n onBlur={() => {\n setInputValue(value);\n }}\n sx={{\n \"& .MuiInputBase-root\": {\n width: \"18.75rem\",\n height: 24,\n fontSize: \"0.75rem\",\n },\n \"& .MuiInputBase-input\": {\n py: 0.5,\n px: 1,\n },\n }}\n />\n </MuiTooltip>\n );\n};\n\ninterface SelectFilterProps {\n isDisabled: boolean;\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n showTooltip?: boolean;\n}\n\nconst SelectFilter = ({\n isDisabled,\n viewOptions,\n onViewOptionsChanged,\n showTooltip,\n}: SelectFilterProps) => {\n return (\n <NodeSelectionInput\n isDisabled={isDisabled}\n value={viewOptions.select ?? \"\"}\n onChange={(value) => {\n onViewOptionsChanged({\n ...viewOptions,\n select: value ? value : undefined,\n });\n }}\n tooltipComponent={<SelectFilterTooltip />}\n showTooltip={showTooltip}\n />\n );\n};\n\ninterface ExcludeFilterProps {\n isDisabled: boolean;\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n}\n\nconst ExcludeFilter = ({\n isDisabled,\n viewOptions,\n onViewOptionsChanged,\n}: ExcludeFilterProps) => {\n return (\n <NodeSelectionInput\n isDisabled={isDisabled}\n value={viewOptions.exclude ?? \"\"}\n onChange={(value) => {\n onViewOptionsChanged({\n ...viewOptions,\n exclude: value ? value : undefined,\n });\n }}\n />\n );\n};\n\ninterface ControlItemProps {\n label?: string;\n children: ReactNode;\n style?: CSSProperties;\n}\n\nconst ControlItem = ({ label, children, style }: ControlItemProps) => {\n return (\n <Box style={style} sx={{ maxWidth: 300 }}>\n <Typography fontSize=\"8pt\">\n {(label ?? \"\").trim() || <> </>}\n </Typography>\n {children}\n </Box>\n );\n};\n\n// ============================================================================\n// Default Setup Connection Popover (passthrough)\n// ============================================================================\n\nconst DefaultSetupConnectionPopover = ({\n children,\n}: SetupConnectionPopoverSlotProps) => {\n return <>{children}</>;\n};\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * LineageViewTopBar Component\n *\n * Top toolbar for the lineage view providing:\n * - View mode selection (Changed Models vs All)\n * - Package filtering\n * - Node selector filters (Select, Exclude)\n * - Actions menu for diff operations and checklist additions\n * - Multi-node selection controls\n *\n * This component uses dependency injection for consumer-specific features\n * like history toggle, setup connection popover, and run type icons.\n */\nexport const LineageViewTopBar = ({\n viewOptions,\n onViewOptionsChanged,\n lineageGraph,\n featureToggles,\n serverFlags,\n focusedNode,\n selectedNodes,\n onDeselect,\n onRunRowCount,\n onRunRowCountDiff,\n onRunValueDiff,\n onAddLineageDiffCheck,\n onAddSchemaDiffCheck,\n runTypeIcons,\n historyToggleSlot,\n SetupConnectionPopoverSlot = DefaultSetupConnectionPopover,\n}: LineageViewTopBarProps) => {\n const isSingleEnvOnboarding = serverFlags?.single_env_onboarding;\n\n const [actionsAnchorEl, setActionsAnchorEl] = useState<HTMLElement | null>(\n null,\n );\n const actionsOpen = Boolean(actionsAnchorEl);\n\n const isMultiSelect = selectedNodes.length > 0;\n const isFilterDisabled = isMultiSelect;\n\n const handleActionsClick = (event: MouseEvent<HTMLButtonElement>) => {\n setActionsAnchorEl(event.currentTarget);\n };\n\n const handleActionsClose = () => {\n setActionsAnchorEl(null);\n };\n\n // Get icons from dependency injection\n const RowCountDiffIcon = runTypeIcons.rowCountDiff;\n const ValueDiffIcon = runTypeIcons.valueDiff;\n const LineageDiffIcon = runTypeIcons.lineageDiff;\n const SchemaDiffIcon = runTypeIcons.schemaDiff;\n\n return (\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n borderBottom={1}\n borderColor=\"neutral.light\"\n sx={{ width: \"100%\", p: \"4pt 8pt\", gap: \"0.5rem\" }}\n >\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ flex: 1, gap: \"0.5rem\" }}\n >\n {historyToggleSlot}\n <ControlItem label=\"Mode\" style={{ flexShrink: 1 }}>\n <ViewModeSelectMenu\n isDisabled={isFilterDisabled}\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n />\n </ControlItem>\n <ControlItem label=\"Package\" style={{ flexShrink: 1 }}>\n <PackageSelectMenu\n isDisabled={isFilterDisabled}\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n lineageGraph={lineageGraph}\n />\n </ControlItem>\n <ControlItem label=\"Select\" style={{ flexShrink: 1 }}>\n <SelectFilter\n isDisabled={isFilterDisabled}\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n showTooltip={isSingleEnvOnboarding}\n />\n </ControlItem>\n <ControlItem label=\"Exclude\" style={{ flexShrink: 1 }}>\n <ExcludeFilter\n isDisabled={isFilterDisabled}\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n />\n </ControlItem>\n <Box sx={{ flexGrow: 1 }} />\n {isMultiSelect && (\n <>\n <ControlItem label=\"\" style={{ flexShrink: 0 }}>\n <Typography fontSize=\"9pt\" color=\"text.secondary\">\n {selectedNodes.length > 1\n ? `${selectedNodes.length} nodes selected`\n : `${selectedNodes.length} node selected`}\n </Typography>\n </ControlItem>\n\n <ControlItem label=\"\">\n <Button\n variant=\"outlined\"\n color=\"neutral\"\n size=\"xsmall\"\n onClick={() => {\n onDeselect();\n }}\n sx={{ textTransform: \"none\", fontSize: \"9pt\" }}\n >\n Deselect\n </Button>\n </ControlItem>\n {isSingleEnvOnboarding && (\n <ControlItem label=\"Explore\">\n <Box sx={{ display: \"inline-flex\" }}>\n <Button\n size=\"xsmall\"\n color=\"neutral\"\n variant=\"outlined\"\n onClick={handleActionsClick}\n endIcon={<PiCaretDown />}\n sx={{ textTransform: \"none\", fontSize: \"0.75rem\" }}\n >\n Actions\n </Button>\n <Menu\n anchorEl={actionsAnchorEl}\n open={actionsOpen}\n onClose={handleActionsClose}\n anchorOrigin={{ vertical: \"bottom\", horizontal: \"right\" }}\n transformOrigin={{ vertical: \"top\", horizontal: \"right\" }}\n >\n <MenuItem\n disabled={featureToggles.disableDatabaseQuery}\n onClick={async () => {\n await onRunRowCount?.();\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <RowCountDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Row Count</ListItemText>\n </MenuItem>\n </Menu>\n </Box>\n </ControlItem>\n )}\n </>\n )}\n {!isSingleEnvOnboarding && (\n <ControlItem label=\"Explore\">\n <Box sx={{ display: \"inline-flex\" }}>\n <Button\n size=\"xsmall\"\n color=\"neutral\"\n variant=\"outlined\"\n disabled={featureToggles.disableViewActionDropdown}\n onClick={handleActionsClick}\n endIcon={<PiCaretDown />}\n sx={{ textTransform: \"none\", fontSize: \"0.75rem\" }}\n >\n Actions\n </Button>\n <Menu\n anchorEl={actionsAnchorEl}\n open={actionsOpen}\n onClose={handleActionsClose}\n anchorOrigin={{ vertical: \"bottom\", horizontal: \"right\" }}\n transformOrigin={{ vertical: \"top\", horizontal: \"right\" }}\n >\n <ListSubheader\n sx={{ lineHeight: \"32px\", bgcolor: \"transparent\" }}\n >\n Diff\n </ListSubheader>\n <SetupConnectionPopoverSlot\n display={featureToggles.mode === \"metadata only\"}\n >\n <MenuItem\n disabled={featureToggles.disableDatabaseQuery}\n onClick={async () => {\n await onRunRowCountDiff?.();\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <RowCountDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Row Count Diff</ListItemText>\n </MenuItem>\n </SetupConnectionPopoverSlot>\n <SetupConnectionPopoverSlot\n display={featureToggles.mode === \"metadata only\"}\n >\n <MenuItem\n disabled={featureToggles.disableDatabaseQuery}\n onClick={async () => {\n await onRunValueDiff?.();\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <ValueDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Value Diff</ListItemText>\n </MenuItem>\n </SetupConnectionPopoverSlot>\n\n <Divider />\n\n <ListSubheader\n sx={{ lineHeight: \"32px\", bgcolor: \"transparent\" }}\n >\n Add to Checklist\n </ListSubheader>\n <MenuItem\n onClick={() => {\n onAddLineageDiffCheck?.(viewOptions.view_mode);\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <LineageDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Lineage Diff</ListItemText>\n </MenuItem>\n <MenuItem\n onClick={() => {\n onAddSchemaDiffCheck?.();\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <SchemaDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Schema Diff</ListItemText>\n </MenuItem>\n </Menu>\n </Box>\n </ControlItem>\n )}\n </Stack>\n </Stack>\n );\n};\n","\"use client\";\n\n/**\n * @file LineageViewTopBarOss.tsx\n * @description OSS wrapper for the LineageViewTopBar component.\n *\n * This thin wrapper imports the core component from @datarecce/ui and injects\n * OSS-specific implementations including:\n * - Run type icons from the local registry\n * - HistoryToggle component\n * - SetupConnectionPopover component\n * - Context hooks for state management\n */\n\nimport type { ReactElement, Ref } from \"react\";\nimport {\n useLineageGraphContext,\n useLineageViewContextSafe,\n useRecceInstanceContext,\n useRecceServerFlag,\n} from \"../../../contexts\";\nimport { SetupConnectionPopover } from \"../../app\";\nimport { findByRunType } from \"../../run\";\nimport { HistoryToggle } from \"../../shared\";\nimport {\n LineageViewTopBar as LineageViewTopBarCore,\n type SetupConnectionPopoverSlotProps,\n} from \"./LineageViewTopBar\";\n\n/**\n * SetupConnectionPopover wrapper to adapt to the slot props interface.\n * We need to cast the children to match SetupConnectionPopover's expected type.\n */\nconst SetupConnectionPopoverSlot = ({\n display,\n children,\n}: SetupConnectionPopoverSlotProps) => {\n return (\n <SetupConnectionPopover display={display}>\n {\n children as ReactElement<{\n ref?: Ref<HTMLElement>;\n [key: string]: unknown;\n }>\n }\n </SetupConnectionPopover>\n );\n};\n\n/**\n * LineageViewTopBar component for OSS.\n *\n * Wraps the core @datarecce/ui component with OSS-specific implementations:\n * - Injects run type icons from the local registry\n * - Provides HistoryToggle slot\n * - Provides SetupConnectionPopover slot\n * - Connects to LineageViewContext for state\n */\nexport const LineageViewTopBarOss = () => {\n const {\n deselect,\n focusedNode,\n selectedNodes,\n viewOptions,\n onViewOptionsChanged,\n runRowCount,\n runRowCountDiff,\n runValueDiff,\n addLineageDiffCheck,\n addSchemaDiffCheck,\n } = useLineageViewContextSafe();\n\n const { lineageGraph } = useLineageGraphContext();\n const { featureToggles } = useRecceInstanceContext();\n const { data: flags } = useRecceServerFlag();\n const runTypeIcons = {\n rowCountDiff: findByRunType(\"row_count_diff\").icon,\n valueDiff: findByRunType(\"value_diff\").icon,\n lineageDiff: findByRunType(\"lineage_diff\").icon,\n schemaDiff: findByRunType(\"schema_diff\").icon,\n };\n\n return (\n <LineageViewTopBarCore\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n lineageGraph={lineageGraph}\n featureToggles={featureToggles}\n serverFlags={flags}\n focusedNode={focusedNode}\n selectedNodes={selectedNodes}\n onDeselect={deselect}\n onRunRowCount={runRowCount}\n onRunRowCountDiff={runRowCountDiff}\n onRunValueDiff={runValueDiff}\n onAddLineageDiffCheck={addLineageDiffCheck}\n onAddSchemaDiffCheck={addSchemaDiffCheck}\n runTypeIcons={runTypeIcons}\n historyToggleSlot={<HistoryToggle />}\n SetupConnectionPopoverSlot={SetupConnectionPopoverSlot}\n />\n );\n};\n","\"use client\";\n\nimport type { Check, ServerInfoResult } from \"../../api\";\nimport {\n type CllInput,\n type ColumnLineageData,\n cacheKeys,\n createLineageDiffCheck,\n createSchemaDiffCheck,\n getCll,\n isHistogramDiffRun,\n isProfileDiffRun,\n isTopKDiffRun,\n isValueDiffDetailRun,\n isValueDiffRun,\n type LineageDiffViewOptions,\n select,\n} from \"../../api\";\nimport {\n LineageViewContext,\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport {\n isLineageGraphColumnNode,\n isLineageGraphNode,\n type LineageGraphColumnNode,\n type LineageGraphEdge,\n type LineageGraphNode,\n type LineageGraphNodes,\n type LineageViewContextType,\n} from \"../../contexts/lineage/types\";\nimport {\n type NodeColumnSetMap,\n selectDownstream,\n selectUpstream,\n union,\n} from \"../../contexts/lineage/utils\";\nimport {\n IGNORE_SCREENSHOT_CLASS,\n useApiConfig,\n useRun,\n useThemeColors,\n} from \"../../hooks\";\nimport { useMultiNodesActionOss as useMultiNodesAction } from \"../../hooks/useMultiNodesActionOss\";\nimport useValueDiffAlertDialog from \"../../hooks/useValueDiffAlertDialogOss\";\nimport {\n trackCopyToClipboard,\n trackMultiNodesAction,\n} from \"../../lib/api/track\";\nimport \"../../styles\";\nimport Box from \"@mui/material/Box\";\nimport Divider from \"@mui/material/Divider\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n Background,\n BackgroundVariant,\n ControlButton,\n Controls,\n getNodesBounds,\n MiniMap,\n Node,\n Panel,\n ReactFlow,\n useEdgesState,\n useNodesState,\n useReactFlow,\n} from \"@xyflow/react\";\nimport \"@xyflow/react/dist/style.css\";\nimport { AxiosError } from \"axios\";\nimport React, {\n forwardRef,\n Ref,\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { FiCopy } from \"react-icons/fi\";\nimport { colors } from \"../../theme\";\nimport { LineageViewNotification } from \"../notifications\";\nimport { HSplit, toaster } from \"../ui\";\nimport { ActionControlOss } from \"./ActionControlOss\";\nimport { ColumnLevelLineageControlOss } from \"./ColumnLevelLineageControlOss\";\nimport { edgeTypes, getNodeColor, initialNodes, nodeTypes } from \"./config\";\nimport {\n useLineageCopyToClipboard,\n useNavToCheck,\n useResizeObserver,\n useTrackLineageRender,\n} from \"./hooks\";\nimport {\n LineageViewContextMenu,\n useLineageViewContextMenu,\n} from \"./LineageViewContextMenuOss\";\nimport { LineageLegend } from \"./legend\";\nimport { toReactFlow } from \"./lineage\";\nimport { NodeViewOss as NodeView } from \"./NodeViewOss\";\nimport { patchLineageDiffFromCll } from \"./patchLineageDiffFromCll\";\nimport SetupConnectionBanner from \"./SetupConnectionBannerOss\";\nimport { BaseEnvironmentSetupNotification } from \"./SingleEnvironmentQueryView\";\nimport {\n LineageViewError,\n LineageViewLoading,\n LineageViewNoChanges,\n} from \"./states\";\nimport { LineageViewTopBarOss as LineageViewTopBar } from \"./topbar/LineageViewTopBarOss\";\n\nexport interface LineageViewProps {\n viewOptions?: LineageDiffViewOptions;\n interactive?: boolean;\n weight?: number;\n height?: number;\n\n // to be removed\n // viewMode?: \"changed_models\" | \"all\"; // deprecated\n filterNodes?: (key: string, node: LineageGraphNode) => boolean;\n}\n\nexport interface LineageViewRef {\n copyToClipboard: () => void;\n}\n\nexport function PrivateLineageView(\n { interactive = false, ...props }: LineageViewProps,\n ref: Ref<LineageViewRef>,\n) {\n const { isDark } = useThemeColors();\n const { apiClient } = useApiConfig();\n const queryClient = useQueryClient();\n const reactFlow = useReactFlow();\n const refResize = useRef<HTMLDivElement>(null);\n const {\n copyToClipboard,\n ImageDownloadModal,\n ref: refReactFlow,\n } = useLineageCopyToClipboard();\n const [nodes, setNodes, onNodesChange] =\n useNodesState<LineageGraphNodes>(initialNodes);\n const [edges, setEdges, onEdgesChange] = useEdgesState<LineageGraphEdge>([]);\n\n const {\n lineageGraph,\n refetchLineageGraph,\n isLoading,\n error,\n refetchRunsAggregated,\n } = useLineageGraphContext();\n\n const { featureToggles, singleEnv } = useRecceInstanceContext();\n const { runId, showRunId, closeRunResult, runAction, isRunResultOpen } =\n useRecceActionContext();\n const { run } = useRun(runId);\n\n const [viewOptions, setViewOptions] = useState<LineageDiffViewOptions>({\n ...props.viewOptions,\n });\n\n const trackLineageRender = useTrackLineageRender();\n\n const cllHistory = useRef<(CllInput | undefined)[]>([]).current;\n\n const [cll, setCll] = useState<ColumnLineageData | undefined>(undefined);\n const [changeAnalysisMode, setChangeAnalysisMode] = useState(false);\n // Ref mirror of changeAnalysisMode for reading inside useLayoutEffect\n // without adding it as a dependency (avoids re-running layout on toggle).\n const changeAnalysisModeRef = useRef(false);\n changeAnalysisModeRef.current = changeAnalysisMode;\n const actionGetCll = useMutation({\n mutationFn: (input: CllInput) => getCll(input, apiClient),\n });\n // Guard against useLayoutEffect re-entry after cache patching.\n // When setQueryData patches lineage.diff, queryServerInfo.data changes,\n // lineageGraph recomputes via useMemo, and the effect re-fires. This ref\n // tells the effect to reuse the previous CLL result instead of re-calling\n // the API and re-patching (which would loop infinitely).\n const cllCachePatchRef = useRef<{\n pending: boolean;\n cllData?: ColumnLineageData;\n }>({ pending: false });\n const [nodeColumnSetMap, setNodeColumSetMap] = useState<NodeColumnSetMap>({});\n\n const findNodeByName = useCallback(\n (name: string) => {\n return nodes.filter(isLineageGraphNode).find((n) => n.data.name === name);\n },\n [nodes],\n );\n\n // Expose the function to the parent via the ref\n useImperativeHandle(ref, () => ({\n copyToClipboard,\n }));\n\n const isModelsChanged = useMemo(() => {\n return !!(lineageGraph && lineageGraph.modifiedSet.length > 0);\n }, [lineageGraph]);\n\n /**\n * View mode\n * - all: show all nodes\n * - changed_models: show only changed models\n */\n const viewMode = viewOptions.view_mode ?? \"changed_models\";\n const filteredNodeIds: string[] = useMemo(() => {\n return nodes\n .filter((node) => node.type === \"lineageGraphNode\")\n .map((node) => node.id);\n }, [nodes]);\n const filteredNodes = useMemo(() => {\n if (!lineageGraph) {\n return [];\n }\n\n return filteredNodeIds.map((nodeId) => lineageGraph.nodes[nodeId]);\n }, [lineageGraph, filteredNodeIds]);\n\n /**\n * Focused node: the node that is currently focused. Show the NodeView when a node is focused\n */\n const [focusedNodeId, setFocusedNodeId] = useState<string>();\n const focusedNode = focusedNodeId\n ? lineageGraph?.nodes[focusedNodeId]\n : undefined;\n\n /**\n * Select mode: the behavior of clicking on nodes\n * - (undefined): no selection\n * - selecting: selecting nodes\n * - action_result: take action on selected nodes\n */\n const [selectMode, setSelectMode] = useState<\"selecting\" | \"action_result\">();\n const [selectedNodeIds, setSelectedNodeIds] = useState<Set<string>>(\n new Set(),\n );\n const selectedNodes = useMemo(() => {\n if (!lineageGraph) {\n return [];\n }\n\n const nodeIds = Array.from(selectedNodeIds);\n return nodeIds.map((nodeId) => lineageGraph.nodes[nodeId]);\n }, [lineageGraph, selectedNodeIds]);\n const multiNodeAction = useMultiNodesAction(\n selectedNodes.length > 0 ? selectedNodes : filteredNodes,\n {\n onActionStarted: () => {\n setSelectMode(\"action_result\");\n },\n onActionNodeUpdated: (updated: LineageGraphNode) => {\n // trigger a re-render by updating the nodes\n setNodes((nodes) =>\n nodes.map((node) => {\n if (node.id === updated.id) {\n return {\n ...node,\n };\n }\n return node;\n }),\n );\n },\n onActionCompleted: () => {\n return void 0;\n },\n },\n );\n\n /**\n * Helper to extract node positions for preserving layout.\n *\n * IMPORTANT: We read positions from React Flow's internal store via getNodes()\n * rather than from React state. This ensures we capture the actual rendered\n * positions, which may differ from React state due to:\n * - fitView adjustments\n * - Node measuring/resizing\n * - Internal React Flow position updates\n *\n * This fixes where clicking between columns caused graph reflow\n * because React state positions were stale relative to what React Flow rendered.\n */\n const getNodePositions = useCallback(() => {\n const positions = new Map<string, { x: number; y: number }>();\n // Get nodes directly from React Flow's internal store, not React state\n const currentNodes = reactFlow.getNodes();\n for (const node of currentNodes) {\n // Only capture parent node positions, not column nodes\n if (!node.parentId && node.position) {\n positions.set(node.id, { x: node.position.x, y: node.position.y });\n }\n }\n return positions;\n }, [reactFlow]);\n\n /**\n * Highlighted nodes: the nodes that are highlighted. The behavior of highlighting depends on the select mode\n *\n * - Default: nodes in the impact radius, or all nodes if the impact radius is not available\n * - Focus: upstream/downstream nodes of the focused node\n * - Multi-select: nodes in the impact radius, or all nodes if the impact radius is not available\n * - Action Result: selected nodes\n */\n const highlighted = useMemo<Set<string>>(() => {\n if (!lineageGraph) {\n return new Set<string>();\n }\n\n let highlightedModels: Set<string> = new Set<string>();\n if (cll) {\n for (const [nodeId, node] of Object.entries(cll.current.nodes)) {\n if (node.impacted !== false) {\n highlightedModels.add(nodeId);\n }\n }\n for (const columnId of Object.keys(cll.current.columns)) {\n highlightedModels.add(columnId);\n }\n } else if (selectMode === \"action_result\") {\n const nodeIds = Object.keys(multiNodeAction.actionState.actions);\n highlightedModels = new Set(nodeIds);\n } else if (focusedNode) {\n highlightedModels = union(\n selectUpstream(lineageGraph, [focusedNode.id]),\n selectDownstream(lineageGraph, [focusedNode.id]),\n );\n } else if (isModelsChanged) {\n highlightedModels = selectDownstream(\n lineageGraph,\n lineageGraph.modifiedSet,\n );\n } else {\n highlightedModels = new Set(filteredNodeIds);\n }\n\n // Add columns in the highlighted models\n return new Set<string>(highlightedModels);\n }, [\n lineageGraph,\n cll,\n selectMode,\n focusedNode,\n isModelsChanged,\n multiNodeAction.actionState.actions,\n filteredNodeIds,\n ]);\n\n const lineageViewContextMenu = useLineageViewContextMenu();\n\n const closeContextMenu = () => {\n lineageViewContextMenu.closeContextMenu();\n };\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: Intentionally only run when lineageGraph changes (initial load/refetch).\n useLayoutEffect(() => {\n const t = async () => {\n let filteredNodeIds: string[] | undefined = undefined;\n\n if (!lineageGraph) {\n return;\n }\n\n if (viewOptions.node_ids) {\n filteredNodeIds = viewOptions.node_ids;\n } else {\n const packageName = lineageGraph.manifestMetadata.current?.project_name;\n const viewMode =\n viewOptions.view_mode ?? (isModelsChanged ? \"changed_models\" : \"all\");\n\n const newViewOptions: LineageDiffViewOptions = {\n view_mode: viewMode,\n packages: packageName ? [packageName] : undefined,\n ...props.viewOptions,\n };\n\n try {\n const result = await select(\n {\n select: newViewOptions.select,\n exclude: newViewOptions.exclude,\n packages: newViewOptions.packages,\n view_mode: newViewOptions.view_mode,\n },\n apiClient,\n );\n filteredNodeIds = result.nodes;\n } catch (_) {\n // fallback behavior\n newViewOptions.view_mode = \"all\";\n const result = await select(\n {\n select: newViewOptions.select,\n exclude: newViewOptions.exclude,\n packages: newViewOptions.packages,\n view_mode: newViewOptions.view_mode,\n },\n apiClient,\n );\n filteredNodeIds = result.nodes;\n }\n\n setViewOptions(newViewOptions);\n }\n\n let cll: ColumnLineageData | undefined;\n if (viewOptions.column_level_lineage) {\n if (cllCachePatchRef.current.pending) {\n // Effect re-fired because our setQueryData updated lineageGraph.\n // Reuse the previous CLL result; skip the API call and re-patch.\n cll = cllCachePatchRef.current.cllData;\n cllCachePatchRef.current = { pending: false };\n } else {\n const cllApiInput: CllInput = {\n ...viewOptions.column_level_lineage,\n change_analysis:\n viewOptions.column_level_lineage.change_analysis ??\n changeAnalysisModeRef.current,\n };\n try {\n cll = await actionGetCll.mutateAsync(cllApiInput);\n // Patch the lineage diff cache with change data from CLL\n const cllResult = cll;\n if (cllApiInput.change_analysis && cllResult) {\n cllCachePatchRef.current = { pending: true, cllData: cllResult };\n queryClient.setQueryData(\n cacheKeys.lineage(),\n (old: ServerInfoResult | undefined) => {\n if (!old) return old;\n return {\n ...old,\n lineage: {\n ...old.lineage,\n diff: patchLineageDiffFromCll(\n old.lineage.diff,\n cllResult,\n ),\n },\n };\n },\n );\n }\n } catch (e) {\n if (e instanceof AxiosError) {\n const e2 = e as AxiosError<{ detail?: string }>;\n toaster.create({\n title: \"Column Level Lineage error\",\n description: e2.response?.data.detail ?? e.message,\n type: \"error\",\n closable: true,\n });\n return;\n }\n }\n }\n } else {\n // CLL disabled — clear any pending guard so stale data isn't reused\n // if CLL is re-enabled after a toggle-off.\n cllCachePatchRef.current = { pending: false };\n }\n\n const [nodes, edges, nodeColumnSetMap] = await toReactFlow(lineageGraph, {\n selectedNodes: filteredNodeIds,\n cll: cll,\n });\n setNodes(nodes);\n setEdges(edges);\n setNodeColumSetMap(nodeColumnSetMap);\n setCll(cll);\n\n // Track lineage view render\n // Note: this call is intentionally duplicated in refreshLayout. The two\n // sites read changeAnalysisMode differently (ref here vs. state there)\n // due to the useLayoutEffect closure, so they can't share a helper\n // without reintroducing stale-closure risks.\n trackLineageRender(\n nodes,\n viewOptions.view_mode ?? \"changed_models\",\n changeAnalysisModeRef.current,\n !!viewOptions.column_level_lineage?.column,\n !!focusedNodeId || !!run,\n );\n };\n\n void t();\n // Intentionally only run when lineageGraph changes (initial load/refetch).\n // viewOptions changes are handled separately by handleViewOptionsChanged.\n // Other dependencies (setNodes, setEdges, actionGetCll) are stable.\n // changeAnalysisModeRef is a ref to avoid stale closure issues.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [lineageGraph]);\n\n const onNodeViewClosed = () => {\n setFocusedNodeId(undefined);\n };\n\n const centerNode = async (nodeId: string) => {\n let node = nodes.find((n) => n.id === nodeId);\n if (!node) {\n return;\n }\n\n if (node.parentId) {\n const parentId = node.parentId;\n node = nodes.find((n) => n.id === parentId) ?? node;\n }\n\n if (node.measured != null) {\n const { width, height } = node.measured;\n if (width && height) {\n const x = node.position.x + width / 2;\n const y = node.position.y + height / 2;\n const zoom = reactFlow.getZoom();\n\n await reactFlow.setCenter(x, y, { zoom, duration: 200 });\n }\n }\n };\n\n const navToCheck = useNavToCheck();\n\n useResizeObserver(refResize, async () => {\n if (selectMode !== \"selecting\") {\n if (!focusedNodeId) {\n await reactFlow.fitView({ nodes, duration: 200 });\n } else {\n await centerNode(focusedNodeId);\n }\n }\n });\n\n const showColumnLevelLineage = async (\n columnLevelLineage?: CllInput,\n previous = false,\n ) => {\n const previousColumnLevelLineage = viewOptions.column_level_lineage;\n\n // Clear change analysis mode when CLL is turned off entirely\n if (!columnLevelLineage) {\n setChangeAnalysisMode(false);\n }\n\n // Preserve positions when:\n // 1. CLL is being turned OFF (previous exists but new one is undefined)\n // 2. Switching between columns (both previous and new have CLL)\n // This prevents jarring graph reflow when the user clicks between columns\n const shouldPreservePositions = previousColumnLevelLineage !== undefined;\n\n await handleViewOptionsChanged(\n {\n ...viewOptions,\n column_level_lineage: columnLevelLineage,\n },\n false,\n shouldPreservePositions, // preserve positions when CLL was previously active\n );\n\n if (!previous) {\n cllHistory.push(previousColumnLevelLineage);\n }\n if (columnLevelLineage?.node_id) {\n setFocusedNodeId(columnLevelLineage.node_id);\n } else if (!columnLevelLineage) {\n // Only clear focus when CLL is turned off, not for Impact Radius\n setFocusedNodeId(undefined);\n }\n };\n\n const resetColumnLevelLineage = async (previous?: boolean) => {\n if (previous) {\n if (cllHistory.length === 0) {\n return;\n }\n const previousCll = cllHistory.pop();\n if (previousCll) {\n await showColumnLevelLineage(previousCll, true);\n } else {\n await showColumnLevelLineage(undefined, true);\n }\n } else {\n await showColumnLevelLineage(undefined, true);\n }\n };\n\n const onColumnNodeClick = (\n event: React.MouseEvent,\n node: LineageGraphColumnNode,\n ) => {\n if (selectMode) {\n return;\n }\n\n void showColumnLevelLineage({\n node_id: node.data.node.id,\n column: node.data.column,\n });\n };\n\n const onNodeClick = (event: React.MouseEvent, node: Node) => {\n if (!interactive) return;\n if (!lineageGraph) {\n return;\n }\n\n if (isLineageGraphColumnNode(node as LineageGraphNodes)) {\n onColumnNodeClick(event, node as LineageGraphColumnNode);\n return;\n }\n\n closeContextMenu();\n if (!selectMode) {\n setFocusedNodeId(node.id);\n } else if (selectMode === \"action_result\") {\n const action = multiNodeAction.actionState.actions[node.id];\n if (action.run?.run_id) {\n showRunId(action.run.run_id);\n }\n setFocusedNodeId(node.id);\n } else {\n const newSet = new Set(selectedNodeIds);\n if (selectedNodeIds.has(node.id)) {\n newSet.delete(node.id);\n } else {\n newSet.add(node.id);\n }\n setSelectedNodeIds(newSet);\n if (newSet.size === 0) {\n setSelectMode(undefined);\n }\n }\n };\n\n const refreshLayout = async (options: {\n viewOptions?: LineageDiffViewOptions;\n fitView?: boolean;\n preservePositions?: boolean;\n }) => {\n let { viewOptions: newViewOptions = viewOptions } = options;\n const { fitView, preservePositions = false } = options;\n\n let selectedNodes: string[] | undefined = undefined;\n\n if (!lineageGraph) {\n return;\n }\n\n const reselect =\n viewOptions.select !== newViewOptions.select ||\n viewOptions.exclude !== newViewOptions.exclude ||\n viewOptions.packages !== newViewOptions.packages ||\n viewOptions.view_mode !== newViewOptions.view_mode;\n\n if (reselect) {\n try {\n const result = await select(\n {\n select: newViewOptions.select,\n exclude: newViewOptions.exclude,\n packages: newViewOptions.packages,\n view_mode: newViewOptions.view_mode,\n },\n apiClient,\n );\n // focus to unfocus the model or column node\n newViewOptions = { ...newViewOptions, column_level_lineage: undefined };\n selectedNodes = result.nodes;\n } catch (e) {\n if (e instanceof AxiosError) {\n const e2 = e as AxiosError<{ detail?: string }>;\n toaster.create({\n title: \"Select node error\",\n description: e2.response?.data.detail ?? e.message,\n type: \"error\",\n closable: true,\n });\n }\n return;\n }\n setFocusedNodeId(undefined);\n } else {\n selectedNodes = nodes.map((n) => n.id);\n }\n\n let cll: ColumnLineageData | undefined;\n if (newViewOptions.column_level_lineage) {\n const cllApiInput: CllInput = {\n ...newViewOptions.column_level_lineage,\n change_analysis:\n newViewOptions.column_level_lineage.change_analysis ??\n changeAnalysisMode,\n };\n try {\n cll = await actionGetCll.mutateAsync(cllApiInput);\n // Patch the lineage diff cache with change data from CLL.\n // Also set the guard ref so the useLayoutEffect that re-fires\n // (due to lineageGraph recomputing) skips the redundant CLL call.\n const cllResult = cll;\n if (cllApiInput.change_analysis && cllResult) {\n cllCachePatchRef.current = { pending: true, cllData: cllResult };\n queryClient.setQueryData(\n cacheKeys.lineage(),\n (old: ServerInfoResult | undefined) => {\n if (!old) return old;\n return {\n ...old,\n lineage: {\n ...old.lineage,\n diff: patchLineageDiffFromCll(old.lineage.diff, cllResult),\n },\n };\n },\n );\n }\n } catch (e) {\n if (e instanceof AxiosError) {\n const e2 = e as AxiosError<{ detail?: string }>;\n toaster.create({\n title: \"Column Level Lineage error\",\n description: e2.response?.data.detail ?? e.message,\n type: \"error\",\n closable: true,\n });\n return;\n }\n }\n } else {\n // Clear change analysis mode when CLL is cleared by any path\n // (reselect, selectParentNodes, selectChildNodes, etc.)\n setChangeAnalysisMode(false);\n }\n\n // Capture positions if preservePositions is true\n let existingPositions: Map<string, { x: number; y: number }> | undefined;\n if (preservePositions) {\n existingPositions = getNodePositions();\n }\n\n const [newNodes, newEdges, newNodeColumnSetMap] = await toReactFlow(\n lineageGraph,\n {\n selectedNodes,\n cll,\n existingPositions,\n },\n );\n setNodes(newNodes);\n setEdges(newEdges);\n setNodeColumSetMap(newNodeColumnSetMap);\n setCll(cll);\n\n // Track lineage view render\n trackLineageRender(\n newNodes,\n newViewOptions.view_mode ?? \"changed_models\",\n changeAnalysisMode,\n !!newViewOptions.column_level_lineage?.column,\n !!focusedNodeId || !!run,\n );\n\n // Close the run result view if the run result node is not in the new nodes\n if (\n run &&\n (isTopKDiffRun(run) ||\n isProfileDiffRun(run) ||\n isHistogramDiffRun(run) ||\n isValueDiffRun(run) ||\n isValueDiffDetailRun(run))\n ) {\n if (run.params?.model && !findNodeByName(run.params.model)) {\n closeRunResult();\n }\n }\n\n if (fitView) {\n await new Promise((resolve) => setTimeout(resolve, 1));\n (() => {\n void reactFlow.fitView({ nodes: newNodes, duration: 200 });\n })();\n }\n };\n\n const handleViewOptionsChanged = async (\n newViewOptions: LineageDiffViewOptions,\n fitView = true,\n preservePositions = false,\n ) => {\n setViewOptions(newViewOptions);\n await refreshLayout({\n viewOptions: newViewOptions,\n fitView,\n preservePositions,\n });\n };\n\n const valueDiffAlertDialog = useValueDiffAlertDialog();\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: handleViewOptionsChanged and onNodeClick are intentionally omitted\n useEffect(() => {\n const runResultType = run?.type;\n if (!interactive) {\n // Skip the following logic if the view is not interactive\n return;\n }\n if (!isRunResultOpen) {\n // Skip the following logic if the run result is not open\n return;\n }\n if (\n !runResultType ||\n [\"query_diff\", \"query\", \"row_count\", \"row_count_diff\"].includes(\n runResultType,\n )\n ) {\n // Skip the following logic if the run result type is not related to a node\n return;\n }\n\n if (!selectMode) {\n // Skip the following logic if the select mode is not single\n let selectedRunModel = undefined;\n if (\n isTopKDiffRun(run) ||\n isProfileDiffRun(run) ||\n isHistogramDiffRun(run) ||\n isValueDiffRun(run) ||\n isValueDiffDetailRun(run)\n ) {\n selectedRunModel = run.params?.model;\n }\n\n // Create a mock MouseEvent\n const mockEvent = new MouseEvent(\"click\", {\n bubbles: true,\n cancelable: true,\n view: window,\n }) as unknown as React.MouseEvent;\n\n if (selectedRunModel) {\n // If the run result is related to a node, select the node to show NodeView\n const node = findNodeByName(selectedRunModel);\n if (!node) {\n // Cannot find the node in the current nodes, try to change the view mode to 'all'\n void handleViewOptionsChanged({\n ...viewOptions,\n view_mode: \"all\",\n });\n } else if (isLineageGraphNode(node) && focusedNode !== node.data.data) {\n // Only select the node if it is not already selected\n onNodeClick(mockEvent, node);\n }\n } else {\n // If the run result is not related to a node, close the NodeView\n onNodeViewClosed();\n }\n }\n // handleViewOptionsChanged and onNodeClick are intentionally omitted to prevent\n // unnecessary re-runs. These functions are called conditionally within the effect\n // and don't need to trigger the effect when they change.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n run,\n viewOptions,\n isRunResultOpen,\n selectMode,\n findNodeByName,\n focusedNode,\n interactive,\n ]);\n\n const selectParentNodes = (nodeId: string, degree = 1000) => {\n if (selectMode === \"action_result\" || lineageGraph === undefined) return;\n\n if (!selectMode) {\n setSelectMode(\"selecting\");\n multiNodeAction.reset();\n if (viewOptions.column_level_lineage) {\n void handleViewOptionsChanged({\n ...viewOptions,\n column_level_lineage: undefined,\n });\n }\n }\n\n const upstream = selectUpstream(lineageGraph, [nodeId], degree);\n setSelectedNodeIds(union(selectedNodeIds, upstream));\n };\n\n const selectChildNodes = (nodeId: string, degree = 1000) => {\n if (selectMode === \"action_result\" || lineageGraph === undefined) return;\n\n if (!selectMode) {\n setSelectMode(\"selecting\");\n multiNodeAction.reset();\n if (viewOptions.column_level_lineage) {\n void handleViewOptionsChanged({\n ...viewOptions,\n column_level_lineage: undefined,\n });\n }\n }\n\n const downstream = selectDownstream(lineageGraph, [nodeId], degree);\n setSelectedNodeIds(union(selectedNodeIds, downstream));\n };\n\n const onNodeContextMenu = (\n event: React.MouseEvent,\n node: LineageGraphNodes,\n ) => {\n if (!interactive) {\n return;\n }\n if (selectMode === \"action_result\") {\n return;\n }\n // Only show context menu when selectMode is action\n // Prevent native context menu from showing\n event.preventDefault();\n const reactFlowDiv = refReactFlow.current as unknown as HTMLDivElement;\n const pane = reactFlowDiv.getBoundingClientRect();\n const x = event.clientX - pane.left;\n const y = event.clientY - pane.top + reactFlowDiv.offsetTop;\n lineageViewContextMenu.showContextMenu(x, y, node);\n };\n\n const selectNode = (nodeId: string) => {\n if (!selectMode) {\n if (!lineageGraph) {\n return;\n }\n\n setSelectedNodeIds(new Set([nodeId]));\n setSelectMode(\"selecting\");\n setFocusedNodeId(undefined);\n multiNodeAction.reset();\n } else if (selectMode === \"selecting\") {\n const newSelectedNodeIds = new Set(selectedNodeIds);\n if (selectedNodeIds.has(nodeId)) {\n newSelectedNodeIds.delete(nodeId);\n } else {\n newSelectedNodeIds.add(nodeId);\n }\n\n setSelectedNodeIds(newSelectedNodeIds);\n if (newSelectedNodeIds.size === 0) {\n setSelectMode(undefined);\n }\n }\n };\n const deselect = () => {\n setSelectMode(undefined);\n setSelectedNodeIds(new Set());\n setFocusedNodeId(undefined);\n closeRunResult();\n refetchRunsAggregated?.();\n };\n\n const contextValue: LineageViewContextType = {\n interactive,\n nodes,\n focusedNode,\n selectedNodes,\n viewOptions,\n showContextMenu: onNodeContextMenu,\n onViewOptionsChanged: handleViewOptionsChanged,\n selectMode,\n selectNode,\n selectParentNodes,\n selectChildNodes,\n deselect,\n isNodeHighlighted: (nodeId: string) => highlighted.has(nodeId),\n isNodeSelected: (nodeId: string) => selectedNodeIds.has(nodeId),\n isEdgeHighlighted: (source, target) => {\n if (!cll) {\n return highlighted.has(source) && highlighted.has(target);\n } else {\n if (!(source in cll.current.parent_map)) {\n return false;\n }\n return target in cll.current.parent_map[source];\n }\n },\n isNodeShowingChangeAnalysis: (nodeId: string) => {\n if (!lineageGraph || !changeAnalysisMode) {\n return false;\n }\n\n const node =\n nodeId in lineageGraph.nodes ? lineageGraph.nodes[nodeId] : undefined;\n\n const cll = viewOptions.column_level_lineage;\n if (cll?.node_id && !cll.column) {\n return cll.node_id === nodeId && !!node?.data.changeStatus;\n }\n return !!node?.data.changeStatus;\n },\n changeAnalysisMode,\n setChangeAnalysisMode,\n getNodeAction: (nodeId: string) => {\n return multiNodeAction.actionState.actions[nodeId];\n },\n getNodeColumnSet: (nodeId: string) => {\n if (!(nodeId in nodeColumnSetMap)) {\n return new Set<string>();\n }\n\n return new Set(nodeColumnSetMap[nodeId]);\n },\n runRowCount: async () => {\n if (selectMode === \"selecting\") {\n await multiNodeAction.runRowCount();\n trackMultiNodesAction({ type: \"row_count\", selected: \"multi\" });\n } else if (focusedNode) {\n runAction(\n \"row_count\",\n { node_names: [focusedNode.data.name] },\n { showForm: false, showLast: false },\n );\n trackMultiNodesAction({ type: \"row_count\", selected: \"single\" });\n } else {\n runAction(\"row_count\", {\n select: viewOptions.select,\n exclude: viewOptions.exclude,\n packages: viewOptions.packages,\n view_mode: viewOptions.view_mode,\n });\n trackMultiNodesAction({ type: \"row_count\", selected: \"none\" });\n }\n },\n runRowCountDiff: async () => {\n if (selectMode === \"selecting\") {\n await multiNodeAction.runRowCountDiff();\n trackMultiNodesAction({ type: \"row_count_diff\", selected: \"multi\" });\n } else if (focusedNode) {\n runAction(\n \"row_count_diff\",\n { node_names: [focusedNode.data.name] },\n { showForm: false, showLast: false },\n );\n trackMultiNodesAction({ type: \"row_count_diff\", selected: \"single\" });\n } else {\n runAction(\"row_count_diff\", {\n select: viewOptions.select,\n exclude: viewOptions.exclude,\n packages: viewOptions.packages,\n view_mode: viewOptions.view_mode,\n });\n trackMultiNodesAction({ type: \"row_count_diff\", selected: \"none\" });\n }\n },\n runValueDiff: async () => {\n if (focusedNode) {\n runAction(\n \"value_diff\",\n {\n model: focusedNode.data.name,\n },\n { showForm: true, showLast: false },\n );\n trackMultiNodesAction({ type: \"value_diff\", selected: \"single\" });\n } else {\n const nodeCount =\n selectMode === \"selecting\"\n ? selectedNodes.length\n : filteredNodeIds.length;\n if (await valueDiffAlertDialog.confirm(nodeCount)) {\n await multiNodeAction.runValueDiff();\n trackMultiNodesAction({\n type: \"value_diff\",\n selected: selectMode === \"selecting\" ? \"multi\" : \"none\",\n });\n }\n }\n },\n addLineageDiffCheck: async () => {\n // create lineage diff check based on the current lineage view (ignoring focus/selection).\n const check = await createLineageDiffCheck(viewOptions, apiClient);\n // the mode is tracked for analytics purposes.\n let selectedMode: \"multi\" | \"single\" | \"none\";\n if (selectMode === \"selecting\") {\n selectedMode = \"multi\";\n } else if (focusedNode) {\n selectedMode = \"single\";\n } else {\n selectedMode = \"none\";\n }\n trackMultiNodesAction({ type: \"lineage_diff\", selected: selectedMode });\n\n if (check) {\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n navToCheck(check);\n }\n },\n addSchemaDiffCheck: async () => {\n let check: Check | undefined = undefined;\n\n if (selectMode === \"selecting\") {\n if (selectedNodes.length > 0) {\n check = await multiNodeAction.addSchemaDiffCheck();\n deselect();\n trackMultiNodesAction({ type: \"schema_diff\", selected: \"multi\" });\n }\n } else if (focusedNode) {\n check = await createSchemaDiffCheck(\n {\n node_id: focusedNode.id,\n },\n apiClient,\n );\n trackMultiNodesAction({ type: \"schema_diff\", selected: \"single\" });\n } else {\n check = await createSchemaDiffCheck(\n {\n select: viewOptions.select,\n exclude: viewOptions.exclude,\n packages: viewOptions.packages,\n view_mode: viewOptions.view_mode,\n },\n apiClient,\n );\n trackMultiNodesAction({ type: \"schema_diff\", selected: \"none\" });\n }\n\n if (check) {\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n navToCheck(check);\n }\n },\n cancel: multiNodeAction.cancel,\n actionState: multiNodeAction.actionState,\n\n // Column Level Lineage\n centerNode,\n cll,\n showColumnLevelLineage,\n resetColumnLevelLineage,\n };\n\n if (isLoading) {\n return <LineageViewLoading />;\n }\n\n if (error) {\n return <LineageViewError error={error} onRetry={refetchLineageGraph} />;\n }\n\n if (!lineageGraph || nodes == initialNodes) {\n return <></>;\n }\n\n if (viewMode === \"changed_models\" && !lineageGraph.modifiedSet.length) {\n return (\n <LineageViewNoChanges\n viewOptions={viewOptions}\n onViewOptionsChanged={handleViewOptionsChanged}\n />\n );\n }\n return (\n <LineageViewContext.Provider value={contextValue}>\n {/* Constant props to avoid react-split destroy/recreate.\n minSize={0} (was 400) lets users drag the panel smaller; the default\n snapOffset (30px) prevents it from reaching 0px in practice. */}\n <HSplit\n sizes={focusedNode ? [70, 30] : [100, 0]}\n minSize={0}\n gutterSize={5}\n className={focusedNode ? undefined : \"split-gutter-hidden\"}\n style={{ height: \"100%\", width: \"100%\" }}\n >\n <Stack\n ref={refResize}\n divider={<Divider sx={{ borderColor: \"grey.200\" }} />}\n spacing={0}\n sx={{ contain: \"strict\", position: \"relative\" }}\n >\n {interactive && (\n <>\n <LineageViewTopBar />\n {featureToggles.mode === \"metadata only\" && (\n <SetupConnectionBanner />\n )}\n </>\n )}\n <ReactFlow\n proOptions={{\n hideAttribution: true,\n }}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n nodes={nodes}\n edges={edges}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n onNodeClick={onNodeClick}\n onNodeContextMenu={onNodeContextMenu}\n onClick={closeContextMenu}\n onInit={async () => {\n if (isModelsChanged) {\n await reactFlow.fitView();\n } else {\n const bounds = getNodesBounds(nodes, {});\n await reactFlow.setCenter(\n bounds.x + bounds.width / 2,\n bounds.y + bounds.height / 2,\n {\n zoom: 1,\n },\n );\n }\n }}\n maxZoom={1}\n minZoom={0.1}\n nodesDraggable={interactive}\n ref={refReactFlow as unknown as Ref<HTMLDivElement>}\n colorMode={isDark ? \"dark\" : \"light\"}\n >\n <Background\n id=\"lineage-bg\"\n variant={BackgroundVariant.Dots}\n color={isDark ? colors.neutral[700] : colors.neutral[300]}\n gap={20}\n size={2}\n />\n <Controls\n showInteractive={false}\n position=\"top-right\"\n className={IGNORE_SCREENSHOT_CLASS}\n style={{\n backgroundColor: isDark ? colors.neutral[700] : undefined,\n borderColor: isDark ? colors.neutral[600] : undefined,\n }}\n >\n <ControlButton\n title=\"copy image\"\n onClick={async () => {\n await copyToClipboard();\n trackCopyToClipboard({\n type: viewMode,\n from: \"lineage_view\",\n });\n }}\n style={{\n backgroundColor: isDark ? colors.neutral[700] : undefined,\n color: isDark ? colors.neutral[200] : undefined,\n }}\n >\n <Box component={FiCopy} />\n </ControlButton>\n </Controls>\n <ImageDownloadModal />\n <Panel position=\"bottom-left\">\n <Stack spacing=\"5px\">\n {isModelsChanged && <LineageLegend variant=\"changeStatus\" />}\n {viewOptions.column_level_lineage && (\n <LineageLegend variant=\"transformation\" />\n )}\n </Stack>\n </Panel>\n <Panel position=\"top-center\">\n <LineageViewNotification\n notification={\n singleEnv ? <BaseEnvironmentSetupNotification /> : null\n }\n type={\"info\"}\n />\n </Panel>\n <Panel position=\"top-left\">\n <Stack spacing=\"5px\">\n <ColumnLevelLineageControlOss action={actionGetCll} />\n {nodes.length == 0 && (\n <Typography\n sx={{ fontSize: \"1.25rem\", color: \"grey\", opacity: 0.5 }}\n >\n No nodes\n </Typography>\n )}\n </Stack>\n </Panel>\n <MiniMap\n nodeColor={getNodeColor}\n nodeStrokeWidth={3}\n zoomable\n pannable\n bgColor={isDark ? colors.neutral[800] : undefined}\n maskColor={\n isDark ? `${colors.neutral[900]}99` : `${colors.neutral[100]}99`\n }\n />\n {selectMode === \"action_result\" && (\n <Panel\n position=\"bottom-center\"\n className={IGNORE_SCREENSHOT_CLASS}\n >\n <ActionControlOss\n onClose={() => {\n deselect();\n }}\n />\n </Panel>\n )}\n </ReactFlow>\n <LineageViewContextMenu {...lineageViewContextMenu.props} />\n </Stack>\n {focusedNode ? (\n <Box\n sx={{\n borderLeft: \"solid 1px\",\n borderColor: \"divider\",\n height: \"100%\",\n }}\n >\n <NodeView node={focusedNode} onCloseNode={onNodeViewClosed} />\n </Box>\n ) : (\n <Box></Box>\n )}\n </HSplit>\n {valueDiffAlertDialog.AlertDialog}\n </LineageViewContext.Provider>\n );\n}\n\nexport const LineageViewOss = forwardRef<LineageViewRef, LineageViewProps>(\n PrivateLineageView,\n);\n","\"use client\";\n\nimport { ReactFlowProvider } from \"@xyflow/react\";\nimport { LineageViewOss } from \"./LineageViewOss\";\n\nexport function LineagePageOss() {\n return (\n <ReactFlowProvider>\n <LineageViewOss interactive />\n </ReactFlowProvider>\n );\n}\n","import MuiAvatar from \"@mui/material/Avatar\";\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Divider from \"@mui/material/Divider\";\nimport ListItemIcon from \"@mui/material/ListItemIcon\";\nimport ListItemText from \"@mui/material/ListItemText\";\nimport Menu from \"@mui/material/Menu\";\nimport MenuItem from \"@mui/material/MenuItem\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { type MouseEvent, useState } from \"react\";\nimport { FaCloud, FaUser } from \"react-icons/fa\";\nimport { cacheKeys } from \"../../api\";\nimport { useApiConfig } from \"../../hooks\";\nimport { fetchGitHubAvatar, fetchUser } from \"../../lib/api/user\";\nimport {\n PUBLIC_CLOUD_WEB_URL,\n RECCE_SUPPORT_CALENDAR_URL,\n} from \"../../lib/const\";\n\nexport default function AvatarDropdown() {\n const { apiClient } = useApiConfig();\n const {\n data: user,\n isLoading,\n error,\n } = useQuery({\n queryKey: cacheKeys.user(),\n queryFn: () => fetchUser(apiClient),\n retry: false,\n });\n\n const { data: avatarUrl } = useQuery({\n queryKey: [\"github-avatar\", user?.id],\n queryFn: () => (user ? fetchGitHubAvatar(user.id) : Promise.resolve(null)),\n enabled: !!user?.id && user.login_type === \"github\",\n retry: false,\n staleTime: 5 * 60 * 1000, // Cache for 5 minutes\n });\n\n const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);\n const open = Boolean(anchorEl);\n\n const handleClick = (event: MouseEvent<HTMLElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n const showUserInfo = !isLoading && !error && user;\n\n // Get initials for avatar fallback\n const getInitials = (name?: string) => {\n if (!name) return \"U\";\n return name.charAt(0).toUpperCase();\n };\n\n return (\n <>\n {isLoading ? (\n <Box\n onClick={handleClick}\n sx={{\n width: 32,\n height: 32,\n borderRadius: \"50%\",\n bgcolor: \"background.paper\",\n color: \"primary.main\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n cursor: \"pointer\",\n }}\n >\n <CircularProgress size={16} />\n </Box>\n ) : (\n <MuiAvatar\n onClick={handleClick}\n src={avatarUrl || undefined}\n sx={{\n width: 28,\n height: 28,\n cursor: \"pointer\",\n outline: \"1px solid white\",\n fontSize: \"0.875rem\",\n }}\n >\n {getInitials(user?.login)}\n </MuiAvatar>\n )}\n <Menu\n anchorEl={anchorEl}\n open={open}\n onClose={handleClose}\n slotProps={{\n paper: {\n sx: {\n bgcolor: \"background.paper\",\n borderColor: \"divider\",\n boxShadow: 3,\n minWidth: 180,\n },\n },\n }}\n >\n <Box sx={{ px: 2, py: 1.5 }}>\n {isLoading && (\n <Box sx={{ display: \"flex\", alignItems: \"center\", gap: 1 }}>\n <Typography variant=\"body2\" color=\"text.primary\">\n Loading...\n </Typography>\n <CircularProgress size={16} />\n </Box>\n )}\n {error && (\n <Typography variant=\"caption\" color=\"error\">\n Failed to load user information\n </Typography>\n )}\n {showUserInfo && (\n <>\n <Typography variant=\"body2\" fontWeight=\"600\" color=\"text.primary\">\n {user.login}\n </Typography>\n {user.email && (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {user.email}\n </Typography>\n )}\n </>\n )}\n </Box>\n <Divider />\n <MenuItem\n onClick={() => {\n window.open(PUBLIC_CLOUD_WEB_URL, \"_blank\");\n handleClose();\n }}\n >\n <ListItemIcon>\n <FaCloud />\n </ListItemIcon>\n <ListItemText>Recce Cloud</ListItemText>\n </MenuItem>\n <MenuItem\n onClick={() => {\n window.open(RECCE_SUPPORT_CALENDAR_URL, \"_blank\");\n handleClose();\n }}\n >\n <ListItemIcon>\n <FaUser />\n </ListItemIcon>\n <ListItemText>Get live support</ListItemText>\n </MenuItem>\n </Menu>\n </>\n );\n}\n","\"use client\";\n\nimport IconButton from \"@mui/material/IconButton\";\nimport Tooltip from \"@mui/material/Tooltip\";\nimport { useTheme } from \"next-themes\";\nimport { useEffect, useState } from \"react\";\nimport { PiMoon, PiSun } from \"react-icons/pi\";\n\n/**\n * Display Mode Toggle - switches between light and dark themes\n *\n * Uses next-themes to persist the user's preference.\n * Defaults to system preference; explicit user choice is stored in localStorage.\n */\nexport const DisplayModeToggleOss = () => {\n const { setTheme, resolvedTheme } = useTheme();\n const [mounted, setMounted] = useState(false);\n\n // Avoid hydration mismatch by only rendering after mount\n useEffect(() => {\n setMounted(true);\n }, []);\n\n const toggleTheme = () => {\n setTheme(resolvedTheme === \"dark\" ? \"light\" : \"dark\");\n };\n\n // Don't render anything until mounted to avoid hydration mismatch\n if (!mounted) {\n return (\n <IconButton\n size=\"small\"\n sx={{\n color: \"rgba(255, 255, 255, 0.8)\",\n \"&:hover\": { bgcolor: \"rgba(255, 255, 255, 0.1)\" },\n }}\n disabled\n >\n <PiSun style={{ width: 18, height: 18 }} />\n </IconButton>\n );\n }\n\n const isDark = resolvedTheme === \"dark\";\n\n return (\n <Tooltip title={isDark ? \"Switch to light mode\" : \"Switch to dark mode\"}>\n <IconButton\n size=\"small\"\n onClick={toggleTheme}\n sx={{\n color: \"rgba(255, 255, 255, 0.8)\",\n \"&:hover\": { bgcolor: \"rgba(255, 255, 255, 0.1)\" },\n }}\n aria-label={isDark ? \"Switch to light mode\" : \"Switch to dark mode\"}\n >\n {isDark ? (\n <PiSun style={{ width: 18, height: 18 }} />\n ) : (\n <PiMoon style={{ width: 18, height: 18 }} />\n )}\n </IconButton>\n </Tooltip>\n );\n};\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport Divider from \"@mui/material/Divider\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Link from \"@mui/material/Link\";\nimport Stack from \"@mui/material/Stack\";\nimport Table from \"@mui/material/Table\";\nimport TableBody from \"@mui/material/TableBody\";\nimport TableCell from \"@mui/material/TableCell\";\nimport TableContainer from \"@mui/material/TableContainer\";\nimport TableHead from \"@mui/material/TableHead\";\nimport TableRow from \"@mui/material/TableRow\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { isEmpty } from \"lodash\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport { LuExternalLink } from \"react-icons/lu\";\nimport { PiInfo } from \"react-icons/pi\";\nimport {\n type EnvInfo as EnvInfoType,\n useLineageGraphContext,\n} from \"../../contexts\";\nimport {\n type EnvironmentConfigProps,\n trackEnvironmentConfig,\n} from \"../../lib/api/track\";\nimport { extractSchemas, formatTimestamp, formatTimeToNow } from \"../../utils\";\n\nfunction buildEnvironmentTrackingData(\n envInfo: EnvInfoType | undefined,\n reviewMode: boolean | undefined,\n baseSchemas: Set<string>,\n currentSchemas: Set<string>,\n): EnvironmentConfigProps {\n const git = envInfo?.git;\n const pr = envInfo?.pullRequest;\n const dbtBase = envInfo?.dbt?.base;\n const dbtCurrent = envInfo?.dbt?.current;\n\n const trackingData: EnvironmentConfigProps = {\n review_mode: reviewMode || false,\n adapter_type: envInfo?.adapterType || null,\n has_git_info: !isEmpty(git),\n has_pr_info: !isEmpty(pr),\n };\n\n // DBT-specific tracking\n if (envInfo?.adapterType === \"dbt\") {\n trackingData.base = {\n schema_count: baseSchemas.size,\n dbt_version: dbtBase?.dbt_version || null,\n timestamp: dbtBase?.generated_at || null,\n };\n trackingData.current = {\n schema_count: currentSchemas.size,\n dbt_version: dbtCurrent?.dbt_version || null,\n timestamp: dbtCurrent?.generated_at || null,\n };\n trackingData.schemas_match =\n baseSchemas.size === currentSchemas.size &&\n Array.from(baseSchemas).every((s) => currentSchemas.has(s));\n }\n\n // SQLMesh-specific tracking\n if (envInfo?.adapterType === \"sqlmesh\") {\n trackingData.base = {\n has_env: !!envInfo.sqlmesh?.base_env,\n };\n trackingData.current = {\n has_env: !!envInfo.sqlmesh?.current_env,\n };\n }\n\n return trackingData;\n}\n\nfunction renderInfoEntries(info: object): React.JSX.Element[] {\n if (Object.values(info).every((value) => value === null)) {\n return [\n <Box key={\"no info\"} sx={{ ml: \"10px\" }}>\n No information\n </Box>,\n ];\n }\n\n return Object.entries(info)\n .filter(\n ([key, value]) => key !== \"url\" && value !== null && value !== undefined,\n )\n .map(([key, value]) => (\n <li key={key} style={{ marginLeft: \"10px\" }}>\n {key}: {value}\n </li>\n ));\n}\n\nexport function EnvInfo() {\n const { envInfo, reviewMode, lineageGraph } = useLineageGraphContext();\n const [open, setOpen] = useState(false);\n const git = envInfo?.git;\n const pr = envInfo?.pullRequest;\n const reviewInfo = { ...git, ...pr };\n\n const dbtBase = envInfo?.dbt?.base;\n const dbtCurrent = envInfo?.dbt?.current;\n\n const baseTime = dbtBase?.generated_at\n ? formatTimestamp(dbtBase.generated_at)\n : \"\";\n const currentTime = dbtCurrent?.generated_at\n ? formatTimestamp(dbtCurrent.generated_at)\n : \"\";\n let baseRelativeTime = \"\";\n let currentRelativeTime = \"\";\n if (dbtBase) {\n baseRelativeTime = dbtBase.generated_at\n ? formatTimeToNow(dbtBase.generated_at)\n : \"\";\n }\n if (dbtCurrent) {\n currentRelativeTime = dbtCurrent.generated_at\n ? formatTimeToNow(dbtCurrent.generated_at)\n : \"\";\n }\n const [baseSchemas, currentSchemas] = extractSchemas(lineageGraph);\n\n // Track environment configuration once at startup\n const hasTrackedRef = useRef(false);\n useEffect(() => {\n if (!hasTrackedRef.current && envInfo) {\n hasTrackedRef.current = true;\n const trackingData = buildEnvironmentTrackingData(\n envInfo,\n reviewMode,\n baseSchemas,\n currentSchemas,\n );\n trackEnvironmentConfig(trackingData);\n }\n }, [envInfo, reviewMode, baseSchemas, currentSchemas]);\n\n const handleOpen = () => setOpen(true);\n const handleClose = () => setOpen(false);\n\n return (\n <>\n <MuiTooltip title=\"Environment Info\" placement=\"bottom-end\">\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n cursor: \"pointer\",\n \"&:hover\": {\n color: \"text.primary\",\n },\n }}\n onClick={handleOpen}\n >\n <Stack\n direction=\"column\"\n sx={{\n display: { xs: \"none\", lg: \"flex\" },\n fontSize: \"0.875rem\",\n }}\n >\n <Box sx={{ display: \"flex\", alignItems: \"baseline\", gap: 0.5 }}>\n <Typography\n component=\"span\"\n noWrap\n sx={{ color: \"warning.main\", maxWidth: 128 }}\n className=\"no-track-pii-safe\"\n >\n {Array.from(baseSchemas).join(\", \")}\n </Typography>\n <Typography component=\"span\" noWrap>\n ({baseRelativeTime})\n </Typography>\n </Box>\n <Box sx={{ display: \"flex\", alignItems: \"baseline\", gap: 0.5 }}>\n <Typography\n component=\"span\"\n noWrap\n sx={{ color: \"primary.main\", maxWidth: 128 }}\n className=\"no-track-pii-safe\"\n >\n {Array.from(currentSchemas).join(\", \")}\n </Typography>\n <Typography component=\"span\" noWrap>\n ({currentRelativeTime})\n </Typography>\n </Box>\n </Stack>\n <IconButton size=\"small\" aria-label=\"Environment Info\">\n <Box\n component={PiInfo}\n sx={{ fontSize: 16, verticalAlign: \"middle\" }}\n />\n </IconButton>\n </Box>\n </MuiTooltip>\n <MuiDialog open={open} onClose={handleClose} maxWidth=\"sm\" fullWidth>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n Environment Information\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent>\n <Stack direction=\"column\" spacing={1}>\n {reviewMode ? (\n <Stack direction=\"column\" spacing={0.5}>\n <Typography variant=\"h6\" sx={{ fontSize: \"1rem\" }}>\n Review Information\n </Typography>\n <ul style={{ margin: 0, paddingLeft: \"20px\" }}>\n {reviewInfo.url && (\n <li style={{ marginLeft: \"10px\" }}>\n url:{\" \"}\n <Link\n href={reviewInfo.url}\n target=\"_blank\"\n sx={{ color: \"primary.main\" }}\n >\n {reviewInfo.url} <LuExternalLink />\n </Link>\n </li>\n )}\n {!isEmpty(reviewInfo) && renderInfoEntries(reviewInfo)}\n </ul>\n </Stack>\n ) : (\n <Stack direction=\"column\" spacing={0.5}>\n <Typography variant=\"h6\" sx={{ fontSize: \"1rem\" }}>\n Dev Information\n </Typography>\n <ul style={{ margin: 0, paddingLeft: \"20px\" }}>\n {git && renderInfoEntries(git)}\n </ul>\n </Stack>\n )}\n <Divider />\n {envInfo?.adapterType === \"dbt\" && (\n <Stack direction=\"column\" spacing={0.5}>\n <Typography variant=\"h6\" sx={{ fontSize: \"1rem\" }}>\n DBT\n </Typography>\n <TableContainer\n sx={{ border: 1, borderColor: \"divider\", maxHeight: \"30rem\" }}\n >\n <Table size=\"small\" stickyHeader>\n <TableHead>\n <TableRow>\n <TableCell />\n <TableCell>base</TableCell>\n <TableCell>current</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n <TableRow>\n <TableCell>schema</TableCell>\n <TableCell className=\"no-track-pii-safe\">\n {Array.from(baseSchemas).map((item) => (\n <MuiTooltip\n key={item}\n title={item}\n placement=\"bottom\"\n >\n <div className=\"max-w-72 truncate\">{item}</div>\n </MuiTooltip>\n ))}\n </TableCell>\n <TableCell className=\"no-track-pii-safe\">\n {Array.from(currentSchemas).map((item) => (\n <MuiTooltip\n key={item}\n title={item}\n placement=\"bottom\"\n >\n <div className=\"max-w-72 truncate\">{item}</div>\n </MuiTooltip>\n ))}\n </TableCell>\n </TableRow>\n <TableRow>\n <TableCell>version</TableCell>\n <TableCell>{dbtBase?.dbt_version}</TableCell>\n <TableCell>{dbtCurrent?.dbt_version}</TableCell>\n </TableRow>\n <TableRow>\n <TableCell>timestamp</TableCell>\n <TableCell>{baseTime}</TableCell>\n <TableCell>{currentTime}</TableCell>\n </TableRow>\n </TableBody>\n </Table>\n </TableContainer>\n </Stack>\n )}\n {envInfo?.adapterType === \"sqlmesh\" && (\n <Stack direction=\"column\" spacing={0.5}>\n <Typography variant=\"h6\" sx={{ fontSize: \"1rem\" }}>\n SQLMesh\n </Typography>\n <TableContainer\n sx={{ border: 1, borderColor: \"divider\", maxHeight: \"30rem\" }}\n >\n <Table stickyHeader>\n <TableHead>\n <TableRow>\n <TableCell />\n <TableCell>base</TableCell>\n <TableCell>current</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n <TableRow>\n <TableCell>Environment</TableCell>\n <TableCell className=\"no-track-pii-safe\">\n {envInfo.sqlmesh?.base_env}\n </TableCell>\n <TableCell className=\"no-track-pii-safe\">\n {envInfo.sqlmesh?.current_env}\n </TableCell>\n </TableRow>\n </TableBody>\n </Table>\n </TableContainer>\n </Stack>\n )}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button color=\"iochmara\" onClick={handleClose}>\n Close\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Checkbox from \"@mui/material/Checkbox\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport FormControlLabel from \"@mui/material/FormControlLabel\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport TextField from \"@mui/material/TextField\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { isAxiosError } from \"axios\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport { LuSave } from \"react-icons/lu\";\nimport { PiPencil } from \"react-icons/pi\";\nimport {\n cacheKeys,\n LOCAL_STORAGE_KEYS,\n rename,\n saveAs,\n useChecks,\n} from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks\";\nimport { formatRunDateTime } from \"../run\";\nimport { toaster } from \"../ui\";\n\nconst useRecceToast = () => {\n const toastSuccess = (message: string) => {\n toaster.create({\n description: message,\n type: \"success\",\n duration: 5000,\n closable: true,\n });\n };\n\n const toastError = (message: string, error?: Error) => {\n let errorMessage = message;\n if (error != null) {\n if (isAxiosError<{ detail?: string }>(error)) {\n errorMessage = `${message}. ${String(error.response?.data?.detail)}`;\n } else {\n errorMessage = `${message}. ${error}`;\n }\n }\n\n toaster.create({\n description: errorMessage,\n type: \"error\",\n duration: 5000,\n closable: true,\n });\n };\n\n return { toastSuccess, toastError };\n};\n\nconst useClosePrompt = (prompt: boolean) => {\n useEffect(() => {\n const handleBeforeUnload = (e: BeforeUnloadEvent) => {\n e.preventDefault();\n };\n\n if (prompt) {\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\n }\n\n return () => {\n if (prompt) {\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\n }\n };\n }, [prompt]);\n};\n\ninterface FilenameState {\n newFileName: string;\n errorMessage?: string;\n modified?: boolean;\n overwriteWithMethod?: \"save\" | \"rename\";\n bypass?: boolean;\n}\n\nexport const Filename = () => {\n const { featureToggles } = useRecceInstanceContext();\n const { fileName, cloudMode, isDemoSite, envInfo } = useLineageGraphContext();\n const { apiClient } = useApiConfig();\n const [modalOpen, setModalOpen] = useState(false);\n const [overwriteOpen, setOverwriteOpen] = useState(false);\n const isStateless = !fileName && !cloudMode && !isDemoSite;\n const { data: checks } = useChecks(isStateless);\n const hasNonPresetChecks =\n checks != undefined &&\n checks.filter((check) => !check.is_preset).length > 0;\n useClosePrompt(isStateless && hasNonPresetChecks);\n\n const [\n { newFileName, errorMessage, modified, overwriteWithMethod, bypass },\n setState,\n ] = useState<FilenameState>({\n newFileName: fileName ?? \"recce_state.json\",\n bypass: false,\n });\n\n const inputRef = useRef<HTMLInputElement>(null);\n const { toastSuccess, toastError } = useRecceToast();\n const queryClient = useQueryClient();\n\n const handleOpen = () => {\n setState({\n newFileName: fileName ?? \"recce_state.json\",\n modified: !fileName,\n bypass: false,\n });\n\n setModalOpen(true);\n };\n\n const handleModalClose = () => setModalOpen(false);\n const handleOverwriteClose = () => setOverwriteOpen(false);\n\n const handleAction = async (\n method: \"save\" | \"rename\",\n overwrite?: boolean,\n ) => {\n if (!newFileName) {\n return;\n }\n\n const bypassOverwrite =\n localStorage.getItem(LOCAL_STORAGE_KEYS.bypassSaveOverwrite) === \"true\";\n\n try {\n if (method === \"save\") {\n await saveAs(\n {\n filename: newFileName,\n overwrite: overwrite ?? bypassOverwrite,\n },\n apiClient,\n );\n } else {\n await rename(\n {\n filename: newFileName,\n overwrite: overwrite ?? bypassOverwrite,\n },\n apiClient,\n );\n }\n toastSuccess(\n method === \"save\"\n ? \"Save file successfully\"\n : \"Rename file successfully\",\n );\n await queryClient.invalidateQueries({ queryKey: cacheKeys.lineage() });\n if (bypass) {\n localStorage.setItem(LOCAL_STORAGE_KEYS.bypassSaveOverwrite, \"true\");\n }\n } catch (error: unknown) {\n if (isAxiosError(error)) {\n if (error.response?.status === 409) {\n setState((s) => ({\n ...s,\n overwriteWithMethod: method,\n }));\n\n setOverwriteOpen(true);\n return;\n }\n }\n toastError(\n method === \"save\" ? \"Save file failed\" : \"Rename file failed\",\n error as Error,\n );\n } finally {\n handleModalClose();\n }\n };\n\n const handleOvewriteBack = () => {\n handleOverwriteClose();\n setModalOpen(true);\n setState((s) => {\n return {\n ...s,\n overwriteWithMethod: undefined,\n };\n });\n };\n\n if (cloudMode || isDemoSite) {\n return <></>;\n }\n\n const titleNewInstance =\n \"New Instance\" + (hasNonPresetChecks ? \" (unsaved)\" : \"\");\n let titleReadOnlyState;\n if (featureToggles.disableSaveToFile && fileName) {\n const generatedAt = envInfo?.stateMetadata?.generated_at;\n const formattedDate = generatedAt\n ? formatRunDateTime(new Date(generatedAt))\n : null;\n titleReadOnlyState = formattedDate\n ? `${fileName} (${formattedDate})`\n : null;\n }\n\n return (\n <>\n <Stack direction=\"row\" alignItems=\"center\" justifyContent=\"center\">\n <Box sx={{ fontWeight: 600 }}>\n {titleReadOnlyState ?? fileName ?? titleNewInstance}\n </Box>\n {!featureToggles.disableSaveToFile && (\n <MuiTooltip\n title={fileName ? \"Change Filename\" : \"Save\"}\n enterDelay={1000}\n >\n <IconButton\n onClick={handleOpen}\n aria-label={fileName ? \"Change Filename\" : \"Save\"}\n size=\"small\"\n >\n <Box\n component={fileName ? PiPencil : LuSave}\n sx={{ fontSize: 16, verticalAlign: \"middle\" }}\n />\n </IconButton>\n </MuiTooltip>\n )}\n </Stack>\n <MuiDialog open={modalOpen} onClose={handleModalClose}>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n {fileName ? \"Change Filename\" : \"Save File\"}\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleModalClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent\n onKeyDown={(e) => {\n e.stopPropagation();\n }}\n >\n <TextField\n inputRef={inputRef}\n value={newFileName}\n label=\"File name\"\n placeholder=\"Enter filename\"\n error={!!errorMessage}\n helperText={errorMessage}\n fullWidth\n size=\"small\"\n sx={{ mt: 1 }}\n onChange={(e) => {\n const value = e.target.value;\n let newErrorMessage: string | undefined = undefined;\n\n if (!value) {\n newErrorMessage = \"Filename cannot be empty.\";\n } else if (!value.endsWith(\".json\")) {\n newErrorMessage = \"Filename must end with .json.\";\n } else if (!/^[a-zA-Z0-9 _-]+\\.json$/.test(value)) {\n newErrorMessage =\n \"Invalid filename. Only alphanumeric, space, _ and - are allowed.\";\n } else if (fileName && value === fileName) {\n newErrorMessage = \"Filename is the same as the current one.\";\n }\n\n setState((s) => {\n return {\n ...s,\n modified: true,\n newFileName: value,\n errorMessage: newErrorMessage,\n };\n });\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n if (errorMessage) {\n return;\n }\n\n if (!fileName) {\n void handleAction(\"save\");\n } else {\n void handleAction(\"rename\");\n }\n } else if (e.key === \"Escape\") {\n handleModalClose();\n }\n }}\n />\n </DialogContent>\n <DialogActions sx={{ gap: \"5px\" }}>\n <Button\n size=\"small\"\n color={fileName ? \"inherit\" : \"iochmara\"}\n variant=\"contained\"\n onClick={async () => {\n await handleAction(\"save\");\n }}\n disabled={!newFileName || !!errorMessage || !modified}\n >\n {fileName ? \"Save as New File\" : \"Confirm\"}\n </Button>\n {fileName && (\n <Button\n size=\"small\"\n color=\"iochmara\"\n variant=\"contained\"\n onClick={async () => {\n await handleAction(\"rename\");\n }}\n disabled={!newFileName || !!errorMessage || !modified}\n >\n Rename\n </Button>\n )}\n </DialogActions>\n </MuiDialog>\n <MuiDialog open={overwriteOpen} onClose={handleOverwriteClose}>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n Overwrite File?\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleOverwriteClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent\n sx={{\n borderTop: \"solid 1px\",\n borderBottom: \"solid 1px\",\n borderColor: \"divider\",\n }}\n onKeyDown={(e) => {\n e.stopPropagation();\n }}\n >\n <Typography sx={{ fontSize: \"12pt\" }}>\n {overwriteWithMethod === \"save\"\n ? \"Saving a file with this name will overwrite the existing file. Are you sure you wish to continue?\"\n : \"Renaming the file with this name will overwrite the existing file. Are you sure you wish to continue?\"}\n </Typography>\n\n <FormControlLabel\n control={\n <Checkbox\n size=\"small\"\n checked={bypass}\n onChange={(e) => {\n setState((s) => ({ ...s, bypass: e.target.checked }));\n }}\n />\n }\n label={\n <Typography sx={{ fontWeight: \"bold\", pt: \"8px\" }}>\n Don't show this again\n </Typography>\n }\n />\n </DialogContent>\n <DialogActions sx={{ gap: \"5px\" }}>\n <Button variant=\"outlined\" onClick={handleOvewriteBack} size=\"small\">\n Back\n </Button>\n <Button\n size=\"small\"\n color=\"iochmara\"\n variant=\"contained\"\n onClick={() => {\n if (!overwriteWithMethod) {\n return;\n }\n\n void handleAction(overwriteWithMethod, true);\n handleOverwriteClose();\n }}\n >\n Overwrite\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n};\n","\"use client\";\n\nimport Button from \"@mui/material/Button\";\nimport Dialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogContentText from \"@mui/material/DialogContentText\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport { useState } from \"react\";\nimport { MdWarningAmber } from \"react-icons/md\";\nimport { useRecceInstanceContext } from \"../../contexts\";\n\nconst SESSION_KEY = \"recce-python-deprecation-dismissed\";\n\nexport function PythonDeprecationModal() {\n const { pythonVersion } = useRecceInstanceContext();\n const [dismissed, setDismissed] = useState(\n () =>\n typeof window !== \"undefined\" &&\n sessionStorage.getItem(SESSION_KEY) === \"true\",\n );\n\n const shouldShow =\n !dismissed &&\n typeof pythonVersion === \"string\" &&\n pythonVersion.startsWith(\"3.9\");\n\n if (!shouldShow) {\n return null;\n }\n\n const handleDismiss = () => {\n sessionStorage.setItem(SESSION_KEY, \"true\");\n setDismissed(true);\n };\n\n return (\n <Dialog open onClose={handleDismiss}>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\", gap: 1 }}>\n <MdWarningAmber\n style={{ color: \"rgb(237 108 2)\", fontSize: \"1.5rem\" }}\n />\n Python 3.9 Deprecation Notice\n </DialogTitle>\n <DialogContent>\n <DialogContentText>\n Python 3.9 support will be removed in a future release of Recce.\n Please upgrade to Python 3.10 or later to continue receiving updates.\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleDismiss} variant=\"contained\">\n Got it\n </Button>\n </DialogActions>\n </Dialog>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport IconButton from \"@mui/material/IconButton\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport { format } from \"date-fns\";\nimport saveAs from \"file-saver\";\nimport { PiExport } from \"react-icons/pi\";\nimport { exportState } from \"../../api\";\nimport { useRecceInstanceContext } from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks\";\nimport { trackStateAction } from \"../../lib/api/track\";\nimport { toaster } from \"../ui\";\n\nexport function StateExporter() {\n const { featureToggles } = useRecceInstanceContext();\n const { apiClient } = useApiConfig();\n\n const handleExport = async () => {\n try {\n const jsonData = await exportState(apiClient);\n const jsonString = JSON.stringify(jsonData, null, 2);\n const blob = new Blob([jsonString], { type: \"application/json\" });\n\n const now = new Date();\n const fileName = `recce-state-${format(now, \"yyyy-MM-dd-HH-mm-ss\")}.json`;\n\n saveAs(blob, fileName);\n } catch (error) {\n console.error(\"Export failed\", error);\n toaster.create({\n title: \"Export failed\",\n description: String(error),\n type: \"error\",\n duration: 5000,\n closable: true,\n });\n }\n };\n\n return (\n <MuiTooltip title=\"Export\">\n <IconButton\n size=\"small\"\n aria-label=\"Export state\"\n onClick={async () => {\n await handleExport();\n trackStateAction({ name: \"export\" });\n }}\n disabled={featureToggles.disableExportStateFile}\n >\n <Box\n component={PiExport}\n sx={{ verticalAlign: \"middle\", width: \"16px\", height: \"16px\" }}\n />\n </IconButton>\n </MuiTooltip>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useEffect, useState } from \"react\";\nimport { PiCheckCircle, PiCopy } from \"react-icons/pi\";\nimport { TbCloudUpload } from \"react-icons/tb\";\nimport { useCopyToClipboard, useInterval } from \"usehooks-ts\";\nimport { useRecceInstanceContext } from \"../../contexts\";\nimport { useClipBoardToast, useRecceShareStateContext } from \"../../hooks\";\nimport { trackShareState } from \"../../lib/api/track\";\nimport AuthModal from \"./AuthModal\";\n\nconst LOADING_MESSAGES = [\n \"Processing...\", // 0-30s\n \"Still processing, please wait...\", // 30-60s\n \"Almost there, thanks for your patience...\", // 60s+\n];\n\nexport function TopLevelShare() {\n const { successToast, failToast } = useClipBoardToast();\n const [, copyToClipboard] = useCopyToClipboard();\n const { authed } = useRecceInstanceContext();\n const { shareUrl, isLoading, error, handleShareClick } =\n useRecceShareStateContext();\n const [showModal, setShowModal] = useState(false);\n const [messageIndex, setMessageIndex] = useState(0);\n const [prevIsLoading, setPrevIsLoading] = useState(isLoading);\n\n // Reset message index when loading starts (during render)\n if (isLoading !== prevIsLoading) {\n setPrevIsLoading(isLoading);\n if (isLoading) {\n // Loading just started, reset to 0\n setMessageIndex(0);\n }\n }\n\n // Increment message index every 30 seconds while loading\n useInterval(\n () => {\n setMessageIndex((prev) =>\n Math.min(prev + 1, LOADING_MESSAGES.length - 1),\n );\n },\n isLoading ? 30000 : null,\n );\n\n // Show error toast when sharing fails\n useEffect(() => {\n if (error) {\n failToast(\"Failed to share state\", error);\n }\n }, [error, failToast]);\n\n const handleCopy = async () => {\n try {\n await copyToClipboard(String(shareUrl));\n successToast(\"Copied the link to clipboard\");\n } catch (error) {\n failToast(\"Failed to copy the link\", error);\n }\n };\n\n if (!authed) {\n return (\n <Stack direction=\"row\" sx={{ flex: 1, alignItems: \"center\" }}>\n <Button\n size=\"xsmall\"\n color=\"neutral\"\n variant=\"outlined\"\n onClick={() => {\n setShowModal(true);\n }}\n startIcon={<TbCloudUpload />}\n >\n Share\n </Button>\n {showModal && (\n <AuthModal\n parentOpen={showModal}\n handleParentClose={setShowModal}\n ignoreCookie\n variant=\"enable-share\"\n />\n )}\n </Stack>\n );\n }\n\n return (\n <Stack direction=\"row\" sx={{ flex: 1, alignItems: \"center\", gap: \"5px\" }}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<TbCloudUpload />}\n endIcon={\n shareUrl ? (\n <Box component={PiCheckCircle} sx={{ color: \"success.main\" }} />\n ) : undefined\n }\n onClick={async () => {\n await handleShareClick();\n trackShareState({ name: \"create\" });\n }}\n disabled={isLoading}\n >\n {isLoading ? \"Sharing...\" : \"Share\"}\n </Button>\n {isLoading && (\n <Typography sx={{ fontSize: 14, color: \"grey.500\" }}>\n {LOADING_MESSAGES[messageIndex]}\n </Typography>\n )}\n <Stack direction=\"row\" spacing={0.5} alignItems=\"center\">\n {shareUrl && (\n <>\n <Box\n sx={{\n overflowX: \"auto\",\n whiteSpace: \"nowrap\",\n maxWidth: \"350px\",\n }}\n >\n <Typography sx={{ fontSize: 14 }}>{shareUrl}</Typography>\n </Box>\n <IconButton\n size=\"small\"\n aria-label=\"Copy the share URL\"\n onClick={async () => {\n await handleCopy();\n trackShareState({ name: \"copy\" });\n }}\n >\n <PiCopy />\n </IconButton>\n </>\n )}\n </Stack>\n </Stack>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport FormControlLabel from \"@mui/material/FormControlLabel\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Radio from \"@mui/material/Radio\";\nimport RadioGroup from \"@mui/material/RadioGroup\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport React, { useCallback, useState } from \"react\";\nimport { IoClose, IoSync } from \"react-icons/io5\";\nimport { PiInfo } from \"react-icons/pi\";\nimport {\n cacheKeys,\n isStateSyncing,\n type SyncStateInput,\n syncState,\n} from \"../../api\";\nimport { useRecceInstanceInfo, useRouteConfig } from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks\";\nimport { toaster } from \"../ui\";\n\nfunction isCheckDetailPage(href: string): boolean {\n const pattern =\n /^\\/checks\\/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;\n return pattern.test(href);\n}\n\nexport function StateSpinner() {\n return (\n <MuiTooltip title=\"Syncing\">\n <Box sx={{ mx: \"10px\" }}>\n <CircularProgress size={20} />\n </Box>\n </MuiTooltip>\n );\n}\n\nexport function StateSynchronizer() {\n const [isSyncing, setSyncing] = useState(false);\n const queryClient = useQueryClient();\n const { apiClient } = useApiConfig();\n const router = useRouter();\n const pathname = usePathname();\n const { basePath } = useRouteConfig();\n const [open, setOpen] = useState(false);\n const [syncOption, setSyncOption] = useState<\n \"overwrite\" | \"revert\" | \"merge\" | \"\"\n >(\"\");\n const { data: instanceInfo } = useRecceInstanceInfo();\n\n const handleClose = () => setOpen(false);\n\n const handleSync = useCallback(\n async (input: SyncStateInput) => {\n setOpen(false);\n setSyncing(true);\n\n const response = await syncState(input, apiClient);\n if (response.status === \"conflict\") {\n setOpen(true);\n setSyncing(false);\n return;\n }\n\n while (await isStateSyncing(apiClient)) {\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n\n toaster.create({\n description: \"Sync Completed\",\n type: \"success\",\n duration: 5000,\n closable: true,\n });\n\n setSyncing(false);\n setSyncOption(\"\");\n\n await queryClient.invalidateQueries({ queryKey: cacheKeys.lineage() });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.runs() });\n\n if (isCheckDetailPage(pathname)) {\n router.push(`${basePath}/checks`);\n }\n },\n [queryClient, pathname, apiClient, router.push, basePath],\n );\n\n if (isSyncing) return <StateSpinner />;\n return (\n <>\n <MuiTooltip title=\"Sync with Cloud\">\n <IconButton\n size=\"small\"\n aria-label=\"Sync state\"\n onClick={() =>\n handleSync(instanceInfo?.session_id ? { method: \"merge\" } : {})\n }\n >\n <Box\n component={IoSync}\n sx={{ fontSize: 16, verticalAlign: \"middle\" }}\n />\n </IconButton>\n </MuiTooltip>\n <MuiDialog open={open} onClose={handleClose}>\n <DialogTitle\n sx={{ display: \"flex\", alignItems: \"center\", fontWeight: \"bold\" }}\n >\n Sync with Cloud\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent>\n <Typography>\n New changes have been detected in the cloud. Please choose a method\n to sync your state\n </Typography>\n <Box sx={{ mt: \"5px\" }}>\n <RadioGroup\n value={syncOption}\n onChange={(e) => {\n setSyncOption(\n e.target.value as \"merge\" | \"overwrite\" | \"revert\",\n );\n }}\n >\n <Stack direction=\"column\">\n {/* Merge */}\n <FormControlLabel\n value=\"merge\"\n control={<Radio />}\n label={\n <Stack direction=\"row\" alignItems=\"center\">\n Merge\n <MuiTooltip title=\"This will merge the local and remote states.\">\n <Box\n component={PiInfo}\n sx={{ ml: 2, cursor: \"pointer\" }}\n />\n </MuiTooltip>\n </Stack>\n }\n />\n\n {/* Overwrite */}\n <FormControlLabel\n value=\"overwrite\"\n control={<Radio />}\n label={\n <Stack direction=\"row\" alignItems=\"center\">\n Overwrite\n <MuiTooltip title=\"This will overwrite the remote state file with the local state.\">\n <Box\n component={PiInfo}\n sx={{ ml: 2, cursor: \"pointer\" }}\n />\n </MuiTooltip>\n </Stack>\n }\n />\n\n {/* Revert */}\n <FormControlLabel\n value=\"revert\"\n control={<Radio />}\n label={\n <Stack direction=\"row\" alignItems=\"center\">\n Revert\n <MuiTooltip title=\"This will discard local changes and revert to the cloud state.\">\n <Box\n component={PiInfo}\n sx={{ ml: 2, cursor: \"pointer\" }}\n />\n </MuiTooltip>\n </Stack>\n }\n />\n </Stack>\n </RadioGroup>\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleClose} sx={{ mr: 1 }}>\n Cancel\n </Button>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n onClick={() => handleSync({ method: syncOption || undefined })}\n disabled={!syncOption}\n >\n Sync\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n}\n","\"use client\";\n\n\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Tab from \"@mui/material/Tab\";\nimport MuiTabs from \"@mui/material/Tabs\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport NextLink from \"next/link\";\nimport { usePathname } from \"next/navigation\";\nimport React, { type ReactNode, useEffect, useMemo, useRef } from \"react\";\nimport { type Check, cacheKeys, listChecks } from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n useRecceServerFlag,\n} from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks/useApiConfig\";\nimport { trackNavigation } from \"../../lib/api/track\";\nimport { EnvInfo } from \"./EnvInfo\";\nimport { Filename } from \"./Filename\";\nimport { PythonDeprecationModal } from \"./PythonDeprecationModal\";\nimport { StateExporter } from \"./StateExporter\";\nimport { TopLevelShare } from \"./StateSharing\";\nimport { StateSynchronizer } from \"./StateSynchronizer\";\n\n/**\n * Route configuration for tabs\n */\nconst ROUTE_CONFIG = [\n { path: \"/lineage\", name: \"Lineage\" },\n { path: \"/query\", name: \"Query\" },\n { path: \"/checks\", name: \"Checklist\" },\n] as const;\n\ninterface TabBadgeProps<T> {\n queryKey: string[];\n fetchCallback: () => Promise<T>;\n selectCallback?: (data: T) => number;\n}\n\nfunction TabBadge<T>({\n queryKey,\n fetchCallback,\n selectCallback,\n}: TabBadgeProps<T>): ReactNode {\n const {\n data: count,\n isLoading,\n error,\n } = useQuery({\n queryKey: queryKey,\n queryFn: fetchCallback,\n select: selectCallback,\n });\n\n if (isLoading || error || count === 0) {\n return <></>;\n }\n\n return (\n <Box\n bgcolor=\"brand.main\"\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n p={1}\n borderRadius=\"100%\"\n color=\"white\"\n fontWeight={700}\n fontSize=\"0.75rem\"\n >\n <span>{count}</span>\n </Box>\n );\n}\n\nfunction ChecklistBadge(): ReactNode {\n const { apiClient } = useApiConfig();\n return (\n <TabBadge<Check[]>\n queryKey={cacheKeys.checks()}\n fetchCallback={() => listChecks(apiClient)}\n selectCallback={(checks: Check[]) => {\n return checks.filter((check) => !check.is_checked).length;\n }}\n />\n );\n}\n\n// NavBar component with Next.js Link navigation\nexport const NavBarOss = () => {\n const pathname = usePathname();\n const { isDemoSite, isLoading, cloudMode } = useLineageGraphContext();\n const { featureToggles } = useRecceInstanceContext();\n const { data: flag, isLoading: isFlagLoading } = useRecceServerFlag();\n\n // Track navigation changes with previous pathname\n const prevPathnameRef = useRef<string | null>(null);\n useEffect(() => {\n if (prevPathnameRef.current && prevPathnameRef.current !== pathname) {\n trackNavigation({ from: prevPathnameRef.current, to: pathname });\n }\n prevPathnameRef.current = pathname;\n }, [pathname]);\n\n // Get current tab value from pathname\n const currentTab = useMemo(() => {\n if (pathname.startsWith(\"/checks\")) return \"/checks\";\n if (pathname.startsWith(\"/query\")) return \"/query\";\n if (pathname.startsWith(\"/runs\")) return \"/runs\";\n return \"/lineage\";\n }, [pathname]);\n\n return (\n <Box sx={{ borderBottom: \"1px solid lightgray\", px: \"12px\" }}>\n {/* Grid layout outside Tabs so MUI Tabs can find tab children */}\n <Box\n sx={{\n display: \"grid\",\n gridTemplateColumns: \"1fr auto 1fr\",\n width: \"100%\",\n alignItems: \"center\",\n }}\n >\n {/* Left section: Tabs */}\n <MuiTabs\n value={currentTab}\n sx={{ borderBottom: \"none\", minHeight: \"auto\" }}\n >\n {ROUTE_CONFIG.map(({ path, name }) => {\n const disable = name === \"Query\" && flag?.single_env_onboarding;\n\n // Don't render hidden tabs\n if (disable) {\n return null;\n }\n\n if (name === \"Checklist\" && ChecklistBadge) {\n return (\n <Tab\n key={path}\n value={path}\n disabled={isLoading || isFlagLoading}\n sx={{\n p: 0,\n }}\n label={\n <Box\n sx={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}\n >\n <NextLink\n href={path}\n style={{\n textDecoration: \"none\",\n color: \"inherit\",\n padding: \"0.875rem 1.1875rem\",\n display: \"flex\",\n gap: 3,\n alignItems: \"center\",\n }}\n >\n {name} <ChecklistBadge />\n </NextLink>\n </Box>\n }\n />\n );\n }\n\n return (\n <Tab\n key={path}\n value={path}\n disabled={isLoading || isFlagLoading}\n sx={{\n p: 0,\n }}\n label={\n <Box\n sx={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}\n >\n <NextLink\n href={path}\n style={{\n textDecoration: \"none\",\n color: \"inherit\",\n padding: \"0.875rem 1.1875rem\",\n }}\n >\n {name}\n </NextLink>\n </Box>\n }\n />\n );\n })}\n </MuiTabs>\n\n {/* Center section: Filename and TopLevelShare */}\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\",\n justifyContent: \"center\",\n }}\n >\n {!isLoading && !isDemoSite && <Filename />}\n {!isLoading &&\n !isDemoSite &&\n !flag?.single_env_onboarding &&\n !featureToggles.disableShare && <TopLevelShare />}\n </Box>\n\n {/* Right section: EnvInfo, StateSynchronizer, StateExporter */}\n {!isLoading && (\n <Box\n sx={{\n display: \"flex\",\n justifyContent: \"right\",\n alignItems: \"center\",\n mr: \"8px\",\n }}\n >\n <EnvInfo />\n {cloudMode && <StateSynchronizer />}\n <StateExporter />\n </Box>\n )}\n </Box>\n <PythonDeprecationModal />\n </Box>\n );\n};\n","\"use client\";\n\nimport Badge from \"@mui/material/Badge\";\nimport Box from \"@mui/material/Box\";\nimport { IoWarning } from \"react-icons/io5\";\nimport { useIdleTimeout } from \"../../contexts\";\nimport { formatDuration } from \"../../utils\";\n\n/**\n * Warning threshold in seconds - badge appears when remaining time is below this\n */\nconst WARNING_THRESHOLD_SECONDS = 60;\n\n/**\n * Badge component that displays idle timeout warning countdown\n * Only shows when remaining time is below the warning threshold\n * Styled as a warning indicator to draw user attention\n */\nexport function IdleTimeoutBadge() {\n const { remainingSeconds, isEnabled } = useIdleTimeout();\n\n // Don't render if idle timeout is not configured\n if (!isEnabled || remainingSeconds === null) {\n return null;\n }\n\n // Only show when below warning threshold\n if (remainingSeconds > WARNING_THRESHOLD_SECONDS) {\n return null;\n }\n\n return (\n <Badge\n color=\"warning\"\n variant=\"standard\"\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 1,\n fontSize: \"0.75rem\",\n mr: 2,\n }}\n >\n <Box component={IoWarning} sx={{ display: \"inline-flex\" }} />\n Idle timeout: {formatDuration(remainingSeconds, \"compact\")}\n </Badge>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Link from \"@mui/material/Link\";\nimport Typography from \"@mui/material/Typography\";\nimport React, { useEffect, useMemo } from \"react\";\nimport { useVersionNumber } from \"../../api\";\nimport { toaster } from \"../ui\";\n\nexport const RecceVersionBadgeOss = () => {\n const { version, latestVersion } = useVersionNumber();\n const versionFormatRegex = useMemo(\n () => new RegExp(\"^\\\\d+\\\\.\\\\d+\\\\.\\\\d+$\"),\n [],\n );\n\n useEffect(() => {\n if (versionFormatRegex.test(version) && version !== latestVersion) {\n const storageKey = \"recce-update-toast-shown\";\n const hasShownForThisVersion = sessionStorage.getItem(storageKey);\n if (hasShownForThisVersion) {\n return;\n }\n // Defer toast creation to next tick to avoid React's flushSync error\n // This prevents \"flushSync called from inside lifecycle method\" when\n // the toast library tries to immediately update DOM during render cycle\n setTimeout(() => {\n toaster.create({\n id: \"recce-update-available\", // Fixed ID prevents duplicates\n title: \"Update available\",\n description: (\n <span>\n A new version of Recce (v{latestVersion}) is available.\n <br />\n Please run{\" \"}\n <Box\n component=\"code\"\n sx={{\n bgcolor: \"grey.200\",\n px: 0.5,\n py: 0.25,\n borderRadius: 0.5,\n fontFamily: \"monospace\",\n fontSize: \"0.875em\",\n }}\n >\n pip install --upgrade recce\n </Box>{\" \"}\n to update Recce.\n <br />\n <Link\n sx={{\n color: \"primary.main\",\n fontWeight: \"bold\",\n \"&:hover\": { textDecoration: \"underline\" },\n }}\n href={`https://github.com/DataRecce/recce/releases/tag/v${latestVersion}`}\n target=\"_blank\"\n >\n Click here to view the detail of latest release\n </Link>\n </span>\n ),\n duration: 60 * 1000,\n closable: true,\n });\n sessionStorage.setItem(storageKey, \"true\");\n }, 0);\n }\n }, [version, latestVersion, versionFormatRegex]);\n\n if (!versionFormatRegex.test(version)) {\n // If the version is not in the format of x.y.z, don't apply\n return (\n <Typography\n component=\"span\"\n sx={{\n fontSize: \"sm\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n }}\n >\n {version}\n </Typography>\n );\n }\n\n // Link to the release page on GitHub if the version is in the format of x.y.z\n return (\n <Link\n href={`https://github.com/DataRecce/recce/releases/tag/v${version}`}\n sx={{\n \"&:hover\": { textDecoration: \"none\" },\n fontSize: \"sm\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n }}\n target=\"_blank\"\n >\n {version}\n </Link>\n );\n};\n","\"use client\";\n\nimport Badge from \"@mui/material/Badge\";\nimport Box from \"@mui/material/Box\";\nimport Link from \"@mui/material/Link\";\nimport Stack from \"@mui/material/Stack\";\nimport { useTheme } from \"@mui/material/styles\";\nimport Typography from \"@mui/material/Typography\";\nimport React, { useState } from \"react\";\nimport { IconType } from \"react-icons\";\nimport { FaGithub, FaQuestionCircle, FaSlack } from \"react-icons/fa\";\nimport { VscGitPullRequest } from \"react-icons/vsc\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport { PUBLIC_CLOUD_WEB_URL } from \"../../lib/const\";\nimport { colors } from \"../../theme\";\nimport { IdleTimeoutBadge } from \"../timeout/IdleTimeoutBadge\";\nimport AuthModal from \"./AuthModal\";\nimport AvatarDropdown from \"./AvatarDropdown\";\nimport { DisplayModeToggleOss as DisplayModeToggle } from \"./DisplayModeToggleOss\";\nimport { RecceVersionBadgeOss as RecceVersionBadge } from \"./RecceVersionBadgeOss\";\n\ninterface LinkIconProps {\n icon: IconType;\n href: string;\n sx?: object;\n}\n\nfunction LinkIcon({ icon: IconComponent, href, sx, ...props }: LinkIconProps) {\n const theme = useTheme();\n return (\n <Link\n sx={{ height: \"20px\", color: \"common.white\", ...sx }}\n href={href}\n target=\"_blank\"\n {...props}\n >\n <IconComponent\n style={{ color: theme.palette.common.white, width: 20, height: 20 }}\n />\n </Link>\n );\n}\n\nexport const TopBarOss = () => {\n const { reviewMode, isDemoSite, envInfo, cloudMode } =\n useLineageGraphContext();\n const { featureToggles, authed } = useRecceInstanceContext();\n const { url: prURL, id: prID } = envInfo?.pullRequest ?? {};\n const demoPrId = prURL ? prURL.split(\"/\").pop() : null;\n const brandLink =\n cloudMode || authed ? PUBLIC_CLOUD_WEB_URL : \"https://reccehq.com/\";\n const [showModal, setShowModal] = useState(false);\n\n return (\n <Box\n sx={{\n display: \"flex\",\n gap: \"10px\",\n minHeight: \"40px\",\n alignItems: \"center\",\n bgcolor: colors.brand[400],\n }}\n >\n <Link\n href={brandLink}\n target=\"_blank\"\n sx={{ \"&:hover\": { textDecoration: \"none\" } }}\n >\n <Box sx={{ display: \"flex\", gap: \"10px\", alignItems: \"center\" }}>\n <Box\n component=\"img\"\n sx={{ width: 20, height: 20, ml: \"18px\" }}\n src=\"/logo/recce-logo-white.png\"\n alt=\"recce-logo-white\"\n />\n <Typography\n variant=\"h4\"\n sx={{\n fontFamily: '\"Montserrat\", sans-serif',\n color: \"common.white\",\n fontSize: \"1.25rem\",\n }}\n >\n RECCE\n </Typography>\n </Box>\n </Link>\n <DisplayModeToggle />\n <RecceVersionBadge />\n {(featureToggles.mode ?? reviewMode) && (\n <Badge\n sx={{\n fontSize: \"0.875rem\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n borderColor: \"rgba(255,255,255,0.8)\",\n }}\n >\n {featureToggles.mode ?? \"review mode\"}\n </Badge>\n )}\n {cloudMode && prID && (\n <Badge\n sx={{\n fontSize: \"0.875rem\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n borderColor: \"rgba(255,255,255,0.8)\",\n }}\n >\n <Stack direction=\"row\" spacing={1} alignItems=\"center\">\n <Box>cloud mode</Box>\n <Box\n sx={{\n borderLeft: \"1px solid rgba(255,255,255,0.8)\",\n pl: \"8px\",\n }}\n >\n <Link\n href={prURL}\n sx={{ \"&:hover\": { textDecoration: \"none\" } }}\n target=\"_blank\"\n >\n <VscGitPullRequest\n style={{\n color: \"rgba(255,255,255,0.8)\",\n width: 12,\n height: 12,\n marginRight: 2,\n display: \"inline\",\n verticalAlign: \"middle\",\n }}\n />\n <Typography\n component=\"span\"\n sx={{ color: \"rgba(255,255,255,0.8)\", display: \"inline\" }}\n >{`#${String(prID)}`}</Typography>\n </Link>\n </Box>\n </Stack>\n </Badge>\n )}\n {isDemoSite && prURL && demoPrId && (\n <Badge\n sx={{\n fontSize: \"0.875rem\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n borderColor: \"rgba(255,255,255,0.8)\",\n }}\n >\n <Stack direction=\"row\" spacing={1} alignItems=\"center\">\n <Box>demo mode</Box>\n <Box\n sx={{\n borderLeft: \"1px solid rgba(255,255,255,0.8)\",\n pl: \"8px\",\n }}\n >\n <Link\n href={prURL}\n sx={{ \"&:hover\": { textDecoration: \"none\" } }}\n target=\"_blank\"\n >\n <VscGitPullRequest\n style={{\n color: \"rgba(255,255,255,0.8)\",\n width: 12,\n height: 12,\n marginRight: 2,\n display: \"inline\",\n verticalAlign: \"middle\",\n }}\n />\n <Typography\n component=\"span\"\n sx={{ color: \"rgba(255,255,255,0.8)\", display: \"inline\" }}\n >{`#${demoPrId}`}</Typography>\n </Link>\n </Box>\n </Stack>\n </Badge>\n )}\n <Box sx={{ flex: 1 }} />\n\n {(isDemoSite || featureToggles.mode === \"read only\") && (\n <>\n <LinkIcon icon={FaGithub} href=\"https://github.com/DataRecce/recce\" />\n <LinkIcon\n icon={FaSlack}\n href=\"https://getdbt.slack.com/archives/C05C28V7CPP\"\n />\n <LinkIcon\n sx={{ mr: 2 }}\n icon={FaQuestionCircle}\n href=\"https://docs.reccehq.com\"\n />\n </>\n )}\n {!isDemoSite && featureToggles.mode !== \"read only\" && (\n <>\n <IdleTimeoutBadge />\n {authed || cloudMode ? (\n <Box sx={{ mr: 2 }}>\n <AvatarDropdown />\n </Box>\n ) : (\n <>\n <Box\n component=\"button\"\n sx={{\n color: \"common.white\",\n fontSize: \"0.875rem\",\n fontWeight: 600,\n bgcolor: \"brand.700\",\n borderRadius: 1,\n px: 3,\n py: 1,\n mr: 2,\n cursor: \"pointer\",\n border: \"none\",\n }}\n onClick={() => {\n setShowModal(true);\n }}\n >\n Connect to Cloud\n </Box>\n {showModal && (\n <AuthModal\n parentOpen={showModal}\n handleParentClose={setShowModal}\n ignoreCookie\n variant=\"user-profile\"\n />\n )}\n </>\n )}\n </>\n )}\n </Box>\n );\n};\n","/**\n * MainLayout - Handles parallel route visibility and main app structure\n *\n * This component manages the visibility of the @lineage parallel route\n * while keeping it mounted to preserve React state (React Flow graph state, etc.)\n */\n\n\"use client\";\n\nimport \"@fontsource/montserrat/800.css\";\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport { usePathname } from \"next/navigation\";\nimport React, { type ReactNode, Suspense, useEffect } from \"react\";\nimport {\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n useRecceServerFlag,\n useRouteConfig,\n} from \"../../contexts\";\nimport { useIsDark } from \"../../hooks/useIsDark\";\nimport { trackInit } from \"../../lib/api/track\";\nimport { RunListOss, RunResultPaneOss as RunResultPane } from \"../run\";\nimport { HSplit, VSplit } from \"../ui\";\nimport AuthModal from \"./AuthModal\";\nimport { NavBarOss as NavBar } from \"./NavBarOss\";\nimport { TopBarOss as TopBar } from \"./TopBarOss\";\n\ninterface MainLayoutProps {\n children: ReactNode;\n /** Parallel route slot from @lineage */\n lineage: ReactNode;\n}\n\nfunction MainContentLoading(): ReactNode {\n return (\n <Box\n sx={{\n display: \"flex\",\n height: \"100%\",\n alignItems: \"center\",\n justifyContent: \"center\",\n contain: \"size\",\n }}\n >\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n <CircularProgress size={48} />\n </Box>\n </Box>\n );\n}\n\nexport function MainLayout({ children, lineage }: MainLayoutProps) {\n const pathname = usePathname();\n const { isDemoSite, isLoading, isCodespace } = useLineageGraphContext();\n const { featureToggles } = useRecceInstanceContext();\n\n // Determine if lineage route is active (handle trailing slashes)\n const normalizedPath = pathname.replace(/\\/$/, \"\") || \"/\";\n const isLineageRoute =\n normalizedPath === \"/lineage\" || normalizedPath === \"/\";\n\n useEffect(() => {\n trackInit();\n }, []);\n\n return (\n <Box\n sx={{\n display: \"flex\",\n flexDirection: \"column\",\n height: \"100vh\",\n overflow: \"hidden\",\n }}\n >\n <TopBar />\n <NavBar />\n <Main isLineageRoute={isLineageRoute} lineage={lineage}>\n {children}\n </Main>\n {!isLoading &&\n !isDemoSite &&\n !isCodespace &&\n featureToggles.mode === null && <AuthModal />}\n </Box>\n );\n}\n\n// Main content area with parallel route handling\ninterface MainProps {\n children: ReactNode;\n lineage: ReactNode;\n isLineageRoute: boolean;\n /** Disable internal history panel (RunListOss) - cloud uses its own */\n disableInternalHistory?: boolean;\n}\n\nexport function Main({\n children,\n lineage,\n isLineageRoute,\n disableInternalHistory = false,\n}: MainProps) {\n const { isRunResultOpen, isHistoryOpen, closeRunResult } =\n useRecceActionContext();\n const { basePath } = useRouteConfig();\n const { data: flag } = useRecceServerFlag();\n const pathname = usePathname();\n const isDark = useIsDark();\n\n const _isRunResultOpen =\n isRunResultOpen && !pathname.startsWith(`${basePath}/checks`);\n // When disableInternalHistory is true, always hide internal history panel\n // Cloud mode uses its own RunHistoryDrawer instead\n const _isHistoryOpen = disableInternalHistory\n ? false\n : isHistoryOpen && !pathname.startsWith(`${basePath}/checks`);\n\n // Keep gutterSize and minSize constant to avoid react-split's destroy/recreate\n // path (triggered when these props change). Only changing `sizes` uses the\n // simpler `setSizes()` code path, which avoids DOM measurement race conditions\n // that can cause the result pane to appear at the top of the page.\n return (\n <HSplit\n sizes={_isHistoryOpen ? [20, 80] : [0, 100]}\n minSize={0}\n gutterSize={5}\n className={_isHistoryOpen ? undefined : \"split-gutter-hidden\"}\n style={{ height: \"100%\" }}\n >\n {/* suppressHydrationWarning: react-split adds inline styles after mount */}\n <Box style={{ contain: \"size\" }} suppressHydrationWarning>\n {_isHistoryOpen && <RunListOss />}\n </Box>\n <VSplit\n sizes={_isRunResultOpen ? [50, 50] : [100, 0]}\n minSize={0}\n gutterSize={5}\n className={_isRunResultOpen ? undefined : \"split-gutter-hidden\"}\n style={{ flex: \"1\", contain: \"size\" }}\n >\n <Suspense fallback={<MainContentLoading />}>\n {/* suppressHydrationWarning: react-split adds inline styles (height, width)\n to children after mount, causing expected server/client mismatches */}\n <Box\n sx={{\n p: 0,\n contain: \"content\",\n height: \"100%\",\n position: \"relative\",\n }}\n suppressHydrationWarning\n >\n {/*\n * Lineage parallel route - always mounted but visibility controlled\n * This replaces the old RouteAlwaysMount pattern\n */}\n <Box\n sx={{\n display: isLineageRoute ? \"block\" : \"none\",\n height: \"100%\",\n position: isLineageRoute ? \"relative\" : \"absolute\",\n inset: 0,\n }}\n >\n {lineage}\n </Box>\n\n {/* Other route content */}\n {!isLineageRoute && children}\n </Box>\n </Suspense>\n {/* suppressHydrationWarning: react-split adds inline styles after mount */}\n <Box\n sx={{ height: \"100%\", bgcolor: isDark ? \"grey.900\" : \"grey.50\" }}\n suppressHydrationWarning\n >\n {_isRunResultOpen ? (\n <RunResultPane\n onClose={closeRunResult}\n isSingleEnvironment={!!flag?.single_env_onboarding}\n />\n ) : null}\n </Box>\n </VSplit>\n </HSplit>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Link from \"@mui/material/Link\";\nimport MuiPopover from \"@mui/material/Popover\";\nimport { ReactElement, useCallback, useRef, useState } from \"react\";\nimport { RECCE_SUPPORT_CALENDAR_URL } from \"../../lib/const\";\n\ninterface SetupConnectionPopoverProps {\n children: ReactElement<{\n ref?: React.Ref<HTMLElement>;\n [key: string]: unknown;\n }>;\n display: boolean;\n}\n\nexport default function SetupConnectionPopover({\n children,\n display,\n}: SetupConnectionPopoverProps) {\n const [hovered, setHovered] = useState(false);\n const timeoutRef = useRef<NodeJS.Timeout | null>(null);\n const anchorRef = useRef<HTMLDivElement | null>(null);\n\n const handleMouseEnter = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n setHovered(true);\n }, []);\n\n const handleMouseLeave = useCallback(() => {\n timeoutRef.current = setTimeout(() => {\n setHovered(false);\n }, 100);\n }, []);\n\n if (!display) {\n return children;\n }\n\n return (\n <>\n <Box\n ref={anchorRef}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n sx={{ display: \"contents\" }}\n >\n {children}\n </Box>\n <MuiPopover\n open={hovered}\n anchorEl={anchorRef.current}\n onClose={() => setHovered(false)}\n anchorOrigin={{\n vertical: \"bottom\",\n horizontal: \"left\",\n }}\n transformOrigin={{\n vertical: \"top\",\n horizontal: \"left\",\n }}\n disableAutoFocus\n disableEnforceFocus\n sx={{ pointerEvents: \"none\" }}\n slotProps={{\n paper: {\n onMouseEnter: handleMouseEnter,\n onMouseLeave: handleMouseLeave,\n sx: {\n bgcolor: \"grey.600\",\n color: \"white\",\n p: 1.5,\n pointerEvents: \"auto\",\n },\n },\n }}\n >\n Connect to a data warehouse to unlock Diff.{\" \"}\n <Link\n href={RECCE_SUPPORT_CALENDAR_URL}\n target=\"_blank\"\n sx={{ color: \"white\", textDecoration: \"underline\" }}\n >\n Learn more\n </Link>\n .\n </MuiPopover>\n </>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport React, { ChangeEvent, useCallback, useRef, useState } from \"react\";\nimport { FaFileImport } from \"react-icons/fa6\";\nimport { IoClose } from \"react-icons/io5\";\nimport { PiInfo } from \"react-icons/pi\";\nimport { cacheKeys, importState } from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n useRouteConfig,\n useRunsAggregated,\n} from \"../../contexts\";\nimport { useApiConfig, useIsDark } from \"../../hooks\";\nimport { trackStateAction } from \"../../lib/api/track\";\nimport { toaster } from \"../ui\";\n\nexport function StateImporter({ checksOnly = true }: { checksOnly?: boolean }) {\n const isDark = useIsDark();\n const { featureToggles } = useRecceInstanceContext();\n const queryClient = useQueryClient();\n const { apiClient } = useApiConfig();\n const hiddenFileInput = useRef<HTMLInputElement>(null);\n const cancelRef = useRef<HTMLButtonElement>(null);\n const [selectedFile, setSelectedFile] = useState<File | null>(null);\n const [open, setOpen] = useState(false);\n const router = useRouter();\n const pathname = usePathname();\n const { basePath } = useRouteConfig();\n const [, refetchRunsAggregated] = useRunsAggregated();\n\n const handleImport = useCallback(async () => {\n if (!selectedFile) {\n return;\n }\n\n try {\n const { runs, checks } = await importState(\n selectedFile,\n checksOnly,\n apiClient,\n );\n refetchRunsAggregated?.();\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.runs() });\n if (pathname.includes(\"/checks\")) {\n router.push(`${basePath}/checks`);\n }\n const description = checksOnly\n ? `${checks} checks imported successfully`\n : `${runs} runs and ${checks} checks imported successfully`;\n toaster.create({\n description: description,\n type: \"info\",\n duration: 5000,\n closable: true,\n });\n } catch (error) {\n console.error(\"Import failed\", error);\n toaster.create({\n title: \"Import failed\",\n description: String(error),\n type: \"error\",\n duration: 5000,\n closable: true,\n });\n }\n\n setOpen(false);\n }, [\n queryClient,\n selectedFile,\n pathname,\n refetchRunsAggregated,\n checksOnly,\n apiClient,\n router.push,\n basePath,\n ]);\n\n const handleClick = () => {\n if (hiddenFileInput.current) {\n hiddenFileInput.current.click();\n }\n };\n\n const handleFileSelect = (event: ChangeEvent<HTMLInputElement>) => {\n if (event.target.files?.length === 1) {\n setSelectedFile(event.target.files[0]);\n setOpen(true);\n }\n\n if (hiddenFileInput.current) {\n hiddenFileInput.current.value = \"\";\n }\n };\n\n const handleClose = () => setOpen(false);\n\n const warningSubject = checksOnly ? \"checks\" : \"runs and checks\";\n const { isDemoSite } = useLineageGraphContext();\n return (\n <>\n <MuiTooltip\n title={\n \"Import Checklist from State File\" +\n (isDemoSite ? \" (Disabled in the demo site)\" : \"\")\n }\n >\n <IconButton\n sx={{\n pt: \"6px\",\n color: isDark ? \"grey.300\" : \"grey.600\",\n \"&:hover\": { color: isDark ? \"grey.100\" : \"grey.800\" },\n fontSize: 20,\n }}\n aria-label=\"Import state\"\n onClick={() => {\n handleClick();\n trackStateAction({ name: \"import\" });\n }}\n disabled={featureToggles.disableImportStateFile || isDemoSite}\n >\n <FaFileImport />\n </IconButton>\n </MuiTooltip>\n <input\n type=\"file\"\n style={{ display: \"none\" }}\n ref={hiddenFileInput}\n onChange={handleFileSelect}\n />\n <MuiDialog\n open={open}\n onClose={handleClose}\n maxWidth=\"sm\"\n fullWidth\n aria-labelledby=\"import-dialog-title\"\n >\n <DialogTitle\n id=\"import-dialog-title\"\n sx={{ display: \"flex\", alignItems: \"center\", fontWeight: \"bold\" }}\n >\n Import state\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent>\n <Stack\n direction=\"column\"\n spacing={1}\n sx={{ px: \"5px\", borderRadius: 1 }}\n >\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Box component={PiInfo} sx={{ color: \"error.main\" }} />\n <Typography\n component=\"span\"\n sx={{ fontWeight: 500, color: \"error.main\" }}\n >\n Caution!\n </Typography>\n </Stack>\n <Typography>\n The current {warningSubject} will be{\" \"}\n <Typography component=\"span\" sx={{ fontWeight: 600 }}>\n merged\n </Typography>{\" \"}\n with the imported state\n </Typography>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button ref={cancelRef} onClick={handleClose}>\n Cancel\n </Button>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n onClick={handleImport}\n sx={{ ml: \"5px\" }}\n >\n Import\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport { ReactFlowProvider } from \"@xyflow/react\";\nimport { forwardRef, Ref } from \"react\";\nimport type { Check, LineageDiffViewOptions } from \"../../api\";\nimport type { LineageViewRef } from \"../lineage/LineageViewOss\";\nimport { LineageViewOss as LineageView } from \"../lineage/LineageViewOss\";\n\nexport interface LineageDiffViewProps {\n check: Check;\n}\n\nfunction _LineageDiffView(\n { check }: LineageDiffViewProps,\n ref: Ref<LineageViewRef>,\n) {\n const viewOptions = {\n ...(check.params as Record<string, unknown>),\n ...(check.view_options as LineageDiffViewOptions),\n };\n\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n <ReactFlowProvider>\n <LineageView viewOptions={viewOptions} interactive={false} ref={ref} />\n </ReactFlowProvider>\n </Box>\n );\n}\n\nexport const LineageDiffViewOss = forwardRef<\n LineageViewRef,\n LineageDiffViewProps\n>(_LineageDiffView);\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport List from \"@mui/material/List\";\nimport ListItem from \"@mui/material/ListItem\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport React, { forwardRef, useMemo, useState } from \"react\";\nimport type { IconType } from \"react-icons\";\nimport type { LineageGraphNode } from \"../..\";\nimport { HSplit, isSchemaChanged } from \"../..\";\nimport { type Check, cacheKeys, select } from \"../../api\";\nimport { useLineageGraphContext } from \"../../contexts\";\nimport { useApiConfig, useIsDark } from \"../../hooks\";\nimport type { DataGridHandle } from \"../../primitives\";\nimport {\n getIconForChangeStatus,\n getIconForResourceType,\n type IconComponent,\n} from \"../lineage\";\nimport { findByRunType } from \"../run\";\nimport { SchemaView } from \"../schema\";\n\ninterface SchemaDiffViewProps {\n check: Check;\n}\n\nexport interface SchemaDiffParams {\n node_id?: string | string[];\n select?: string;\n exclude?: string;\n view_mode?: \"all\" | \"changed_models\";\n packages?: string[];\n}\n\nconst NodelistItem = ({\n node,\n selected,\n onSelect,\n schemaChanged,\n isDark,\n}: {\n node: LineageGraphNode;\n selected: boolean;\n onSelect: (nodeId: string) => void;\n schemaChanged: boolean;\n isDark: boolean;\n}) => {\n const { icon } = getIconForResourceType(node.data.resourceType);\n const { base, current } = node.data.data;\n\n let statusIcon: IconComponent | IconType | undefined;\n let statusColor: string | undefined;\n\n if (schemaChanged) {\n statusIcon = findByRunType(\"schema_diff\").icon;\n statusColor = getIconForChangeStatus(\"modified\").color;\n } else if (!base && current) {\n statusIcon = getIconForChangeStatus(\"added\").icon;\n statusColor = getIconForChangeStatus(\"added\").color;\n } else if (base && !current) {\n statusIcon = getIconForChangeStatus(\"removed\").icon;\n statusColor = getIconForChangeStatus(\"removed\").color;\n }\n\n return (\n <ListItem disablePadding>\n <Box\n sx={{\n display: \"flex\",\n width: \"100%\",\n fontSize: \"10pt\",\n p: \"5px 8px\",\n cursor: \"pointer\",\n \"&:hover\": { bgcolor: isDark ? \"grey.700\" : \"grey.200\" },\n bgcolor: selected ? (isDark ? \"grey.800\" : \"grey.100\") : \"inherit\",\n alignItems: \"center\",\n gap: \"5px\",\n }}\n onClick={() => {\n onSelect(node.id);\n }}\n >\n {icon && <Box component={icon} />}\n <Box\n sx={{\n flex: 1,\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n }}\n >\n {node.data.name}\n </Box>\n\n {statusIcon && statusColor && (\n <Box component={statusIcon} sx={{ color: statusColor }} />\n )}\n </Box>\n </ListItem>\n );\n};\n\nexport function PrivateSchemaDiffView(\n { check }: SchemaDiffViewProps,\n ref: React.Ref<DataGridHandle>,\n) {\n const isDark = useIsDark();\n const { apiClient } = useApiConfig();\n const { lineageGraph } = useLineageGraphContext();\n const params = check.params as SchemaDiffParams;\n\n const queryKey = [...cacheKeys.check(check.check_id), \"select\"];\n\n const { isLoading, error, data } = useQuery({\n queryKey,\n queryFn: async () =>\n select(\n {\n select: params.select,\n exclude: params.exclude,\n packages: params.packages,\n view_mode: params.view_mode,\n },\n apiClient,\n ),\n refetchOnMount: true,\n enabled: !params.node_id,\n });\n\n const [nodes, changedNodes] = useMemo(() => {\n const selectedNodes: LineageGraphNode[] = [];\n const changedNodes: string[] = [];\n const addedNodes: string[] = [];\n const removedNodes: string[] = [];\n\n if (params.node_id) {\n const nodeIds =\n params.node_id instanceof Array ? params.node_id : [params.node_id];\n for (const nodeId of nodeIds) {\n const node = lineageGraph?.nodes[nodeId];\n if (node) {\n selectedNodes.push(node);\n }\n }\n } else {\n for (const nodeId of data?.nodes ?? []) {\n const node = lineageGraph?.nodes[nodeId];\n if (node) {\n selectedNodes.push(node);\n }\n }\n }\n\n // filter that the resourec_type is mode,seed, source, or snapshot\n const filteredNodes = selectedNodes.filter(\n (node) =>\n node.data.resourceType === \"model\" ||\n node.data.resourceType === \"seed\" ||\n node.data.resourceType === \"source\" ||\n node.data.resourceType === \"snapshot\",\n );\n\n for (const node of filteredNodes) {\n if (\n isSchemaChanged(\n node.data.data.base?.columns,\n node.data.data.current?.columns,\n )\n ) {\n changedNodes.push(node.id);\n } else if (!node.data.data.base && node.data.data.current) {\n addedNodes.push(node.id);\n } else if (node.data.data.base && !node.data.data.current) {\n removedNodes.push(node.id);\n }\n }\n function sortScore(node: LineageGraphNode) {\n if (changedNodes.includes(node.id)) {\n return 3;\n }\n if (addedNodes.includes(node.id)) {\n return 2;\n }\n if (removedNodes.includes(node.id)) {\n return 1;\n }\n return 0;\n }\n\n //sort the selectedNodes from schemaChange and node name\n filteredNodes.sort((a, b) => {\n const scoreA = sortScore(a);\n const scoreB = sortScore(b);\n if (scoreA !== scoreB) {\n return scoreB - scoreA;\n } else {\n return a.data.name.localeCompare(b.data.name);\n }\n });\n\n return [filteredNodes, changedNodes];\n }, [params.node_id, data?.nodes, lineageGraph]);\n\n const [selected, setSelected] = useState<number>(0);\n\n if (isLoading) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n height: \"100%\",\n }}\n >\n Loading...\n </Box>\n );\n } else if (error) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n height: \"100%\",\n }}\n className=\"no-track-pii-safe\"\n >\n Error: {error.message}\n </Box>\n );\n } else if (nodes.length == 0) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n height: \"100%\",\n }}\n >\n No nodes matched\n </Box>\n );\n } else if (selected < nodes.length) {\n const node = nodes[selected];\n return (\n <HSplit sizes={[80, 20]} minSize={30} style={{ height: \"100%\" }}>\n <SchemaView\n base={node.data.data.base}\n current={node.data.data.current}\n columnChanges={node.data.change?.columns}\n enableScreenshot={true}\n showMenu={false}\n ref={ref}\n />\n <List\n sx={{\n overflow: \"auto\",\n bgcolor: \"background.paper\",\n listStyle: \"none\",\n }}\n >\n {nodes.map((node, i) => (\n <NodelistItem\n key={node.id}\n node={node}\n schemaChanged={changedNodes.includes(node.id)}\n selected={i === selected}\n isDark={isDark}\n onSelect={() => {\n setSelected(i);\n }}\n />\n ))}\n </List>\n </HSplit>\n );\n }\n\n // TODO: handle the edge case where the node is not found\n return <></>;\n}\n\nexport const SchemaDiffView = forwardRef(PrivateSchemaDiffView);\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport Divider from \"@mui/material/Divider\";\nimport Grid from \"@mui/material/Grid\";\nimport IconButton from \"@mui/material/IconButton\";\nimport ListItemIcon from \"@mui/material/ListItemIcon\";\nimport ListItemText from \"@mui/material/ListItemText\";\nimport Menu from \"@mui/material/Menu\";\nimport MenuItem from \"@mui/material/MenuItem\";\nimport Stack from \"@mui/material/Stack\";\nimport { useTheme } from \"@mui/material/styles\";\nimport Tab from \"@mui/material/Tab\";\nimport Tabs from \"@mui/material/Tabs\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { stripIndents } from \"common-tags\";\nimport { formatDistanceToNow } from \"date-fns\";\nimport { useRouter } from \"next/navigation\";\nimport React, {\n type MouseEvent,\n ReactNode,\n Ref,\n useCallback,\n useRef,\n useState,\n} from \"react\";\nimport { FaBookmark } from \"react-icons/fa6\";\nimport { IoMdCodeWorking } from \"react-icons/io\";\nimport { IoBookmarksOutline, IoClose } from \"react-icons/io5\";\nimport { PiCheckCircle, PiCopy, PiRepeat, PiTrashFill } from \"react-icons/pi\";\nimport { TbEdit } from \"react-icons/tb\";\nimport { VscCircleLarge, VscKebabVertical } from \"react-icons/vsc\";\nimport {\n type QueryDiffParams,\n type QueryParams,\n type QueryRunParams,\n} from \"../../api/adhocQuery\";\nimport { cacheKeys } from \"../../api/cacheKeys\";\nimport {\n type Check,\n deleteCheck,\n getCheck,\n markAsPresetCheck,\n updateCheck,\n} from \"../../api/checks\";\nimport { cancelRun, submitRunFromCheck } from \"../../api/runs\";\nimport type { Run, RunParamTypes } from \"../../api/types\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n useRouteConfig,\n} from \"../../contexts\";\nimport {\n useApiConfig,\n useClipBoardToast,\n useCopyToClipboardButton,\n useIsDark,\n useRecceCheckContext,\n useRecceQueryContext,\n useRun,\n} from \"../../hooks\";\nimport { trackCopyToClipboard } from \"../../lib/api/track\";\nimport {\n buildCheckDescription,\n buildCheckTitle,\n CheckBreadcrumb,\n CheckDescription,\n formatSqlAsMarkdown,\n generateCheckTemplate,\n isDisabledByNoResult,\n PresetCheckTemplateView,\n} from \"../../primitives\";\nimport { VSplit } from \"..\";\nimport { SetupConnectionPopover } from \"../app\";\nimport type { LineageViewRef } from \"../lineage/LineageViewOss\";\nimport { DualSqlEditor, SqlEditor } from \"../query\";\nimport {\n findByRunType,\n type IconComponent,\n RefTypes,\n RegistryEntry,\n RunViewOss,\n ViewOptionTypes,\n} from \"../run\";\nimport { toaster } from \"../ui\";\nimport { LineageDiffViewOss as LineageDiffView } from \"./LineageDiffViewOss\";\nimport { SchemaDiffView } from \"./SchemaDiffView\";\nimport { CheckTimelineOss as CheckTimeline } from \"./timeline/CheckTimelineOss\";\n\ninterface CheckDetailProps {\n checkId: string;\n refreshCheckList?: () => void;\n}\n\ntype TabValueList = \"result\" | \"query\";\n\nexport function CheckDetailOss({\n checkId,\n refreshCheckList,\n}: CheckDetailProps): ReactNode {\n const theme = useTheme();\n const isDark = useIsDark();\n const { apiClient } = useApiConfig();\n const { featureToggles, sessionId } = useRecceInstanceContext();\n const { setLatestSelectedCheckId } = useRecceCheckContext();\n const { cloudMode } = useLineageGraphContext();\n const queryClient = useQueryClient();\n const router = useRouter();\n const { basePath } = useRouteConfig();\n const { setSqlQuery, setBaseSqlQuery, setCustomQueries, setPrimaryKeys } =\n useRecceQueryContext();\n const { successToast, failToast } = useClipBoardToast();\n const [submittedRunId, setSubmittedRunId] = useState<string>();\n const [progress] = useState<Run[\"progress\"]>();\n const [isAborting, setAborting] = useState(false);\n const [isPresetCheckTemplateOpen, setIsPresetCheckTemplateOpen] =\n useState(false);\n const [menuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>(null);\n const menuOpen = Boolean(menuAnchorEl);\n\n const {\n isLoading,\n error,\n data: check,\n } = useQuery({\n queryKey: cacheKeys.check(checkId),\n queryFn: async () => getCheck(checkId, apiClient),\n refetchOnMount: true,\n });\n\n const trackedRunId = submittedRunId ?? check?.last_run?.run_id;\n const { run, error: rerunError } = useRun(trackedRunId);\n const isRunning = submittedRunId\n ? !run || run.status === \"Running\"\n : run?.status === \"Running\";\n\n const runTypeEntry = check?.type ? findByRunType(check.type) : undefined;\n\n let RunResultView: RegistryEntry[\"RunResultView\"] | undefined;\n if (runTypeEntry) {\n RunResultView =\n runTypeEntry.RunResultView as RegistryEntry[\"RunResultView\"];\n }\n\n const isPresetCheck = check?.is_preset ?? false;\n\n const lineageViewRef = useRef<LineageViewRef>(null);\n\n const { mutate } = useMutation({\n mutationFn: (check: Partial<Check>) =>\n updateCheck(checkId, check, apiClient),\n onSuccess: async () => {\n await queryClient.invalidateQueries({\n queryKey: cacheKeys.check(checkId),\n });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n },\n });\n\n const { mutate: handleDelete } = useMutation({\n mutationFn: () => deleteCheck(checkId, apiClient),\n onSuccess: async () => {\n setLatestSelectedCheckId(\"\");\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n router.push(`${basePath}/checks`);\n },\n });\n\n const { mutate: handleMarkAsPresetCheck, isPending: isMarkingAsPreset } =\n useMutation({\n mutationFn: async () => {\n if (!check) {\n throw new Error(\"Check not found\");\n }\n\n return await markAsPresetCheck(checkId, apiClient);\n },\n onSuccess: async () => {\n successToast(\"Check marked as preset successfully\");\n // Invalidate queries to refresh the check data\n await queryClient.invalidateQueries({\n queryKey: cacheKeys.check(checkId),\n });\n },\n onError: (error) => {\n failToast(\"Failed to mark check as preset\", error);\n },\n });\n\n const handleRerun = useCallback(async () => {\n const type = check?.type;\n if (!type) {\n return;\n }\n\n const submittedRun = await submitRunFromCheck(\n checkId,\n { nowait: true },\n apiClient,\n );\n setSubmittedRunId(submittedRun.run_id);\n await queryClient.invalidateQueries({ queryKey: cacheKeys.check(checkId) });\n if (refreshCheckList) refreshCheckList(); // refresh the checklist to fetch correct last run status\n }, [check, checkId, queryClient, refreshCheckList, apiClient]);\n\n const handleCancel = useCallback(async () => {\n setAborting(true);\n if (!trackedRunId) {\n return;\n }\n\n return await cancelRun(trackedRunId, apiClient);\n }, [trackedRunId, apiClient]);\n\n const handleCopy = async () => {\n if (!check) {\n return;\n }\n\n // Cast to Check<RunParamTypes> since we know the check params are valid\n const markdown = buildMarkdown(check as Check<RunParamTypes>);\n // @see https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts\n if (!window.isSecureContext) {\n failToast(\n \"Failed to copy the check to clipboard\",\n new Error(\n \"Copy to clipboard is available only in secure contexts (HTTPS)\",\n ),\n );\n return;\n }\n\n try {\n await navigator.clipboard.writeText(markdown);\n successToast(\"Copied the check to the clipboard\");\n } catch (err) {\n failToast(\"Failed to copy the check to clipboard\", err);\n }\n };\n\n const handleApproveCheck = useCallback(() => {\n const isChecked = check?.is_checked;\n mutate({ is_checked: !isChecked });\n if (!isChecked) {\n toaster.create({\n title: \"Marked as approved\",\n type: \"success\",\n duration: 2000,\n });\n }\n }, [check?.is_checked, mutate]);\n\n const handelUpdateViewOptions = (viewOptions: ViewOptionTypes) => {\n mutate({ view_options: viewOptions });\n };\n\n const handleUpdateDescription = (description?: string) => {\n mutate({ description });\n };\n\n const handleMenuClick = (event: MouseEvent<HTMLButtonElement>) => {\n setMenuAnchorEl(event.currentTarget);\n };\n\n const handleMenuClose = () => {\n setMenuAnchorEl(null);\n };\n\n const handleOpenQuery = useCallback(() => {\n if (!check) return;\n\n const params = check.params as QueryParams | QueryDiffParams;\n const sqlTemplate = params?.sql_template || \"\";\n\n // Set current SQL\n setSqlQuery(sqlTemplate);\n\n // Handle query_diff with custom queries (dual mode)\n if (\"base_sql_template\" in params && params.base_sql_template) {\n setBaseSqlQuery(params.base_sql_template);\n setCustomQueries(true);\n } else {\n setBaseSqlQuery(\"\"); // Clear stale base SQL from previous checks\n setCustomQueries(false);\n }\n\n // Set primary keys if available, otherwise clear stale primary keys\n if (\"primary_keys\" in params && params.primary_keys) {\n setPrimaryKeys(params.primary_keys);\n } else {\n setPrimaryKeys(undefined);\n }\n\n // Navigate to query page\n router.push(`${basePath}/query`);\n }, [\n check,\n router,\n basePath,\n setSqlQuery,\n setBaseSqlQuery,\n setCustomQueries,\n setPrimaryKeys,\n ]);\n\n const [tabValue, setTabValue] = useState<TabValueList>(\"result\");\n const { ref, onCopyToClipboard, onMouseEnter, onMouseLeave } =\n useCopyToClipboardButton();\n\n // Calculate during render instead of effect\n const presetCheckTemplate = generateCheckTemplate({\n name: check?.name ?? \"\",\n description: check?.description ?? \"\",\n type: check?.type ?? \"\",\n params: check?.params as Record<string, unknown>,\n viewOptions: check?.view_options as Record<string, unknown>,\n });\n\n if (isLoading) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n Loading\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n Error: <span className=\"no-track-pii-safe\">{error.message}</span>\n </Box>\n );\n }\n\n if (!check) {\n return (\n <VSplit\n minSize={100}\n sizes={[30, 70]}\n style={{ height: \"100%\", width: \"100%\", maxHeight: \"100%\" }}\n >\n <Box\n style={{ contain: \"strict\" }}\n sx={{ display: \"flex\", flexDirection: \"column\" }}\n >\n <Box\n sx={{\n display: \"flex\",\n p: \"0px 16px\",\n alignItems: \"center\",\n height: 40,\n }}\n >\n <CheckBreadcrumb name=\"Check not found\" disabled />\n </Box>\n </Box>\n </VSplit>\n );\n }\n\n const relativeTime = run?.run_at\n ? formatDistanceToNow(new Date(run.run_at), { addSuffix: true })\n : null;\n\n // Get the icon for the check type\n const checkTypeIcon: IconComponent | undefined = runTypeEntry?.icon;\n\n return (\n <Grid\n spacing={0}\n container\n sx={{\n height: \"100%\",\n width: \"100%\",\n }}\n >\n <Grid size={{ xs: 12, md: cloudMode ? 9 : 12 }} sx={{ height: \"100%\" }}>\n <VSplit\n minSize={100}\n sizes={[40, 60]}\n style={{ height: \"100%\", width: \"100%\", maxHeight: \"100%\" }}\n >\n <Box\n sx={{\n height: \"100%\",\n contain: \"strict\",\n display: \"flex\",\n flexDirection: \"row\",\n }}\n >\n {/* Main content area - takes remaining space */}\n <Box\n sx={{\n flex: 1,\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n minWidth: 0,\n }}\n >\n {/* Title bar with icon, name, and actions */}\n <Box\n sx={{\n display: \"flex\",\n p: \"0px 16px\",\n alignItems: \"center\",\n height: 40,\n borderBottom: \"2px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.300\",\n }}\n >\n {/* Check type icon */}\n {checkTypeIcon && (\n <Box\n component={checkTypeIcon}\n sx={{ fontSize: 20, mr: 1, flexShrink: 0 }}\n />\n )}\n <CheckBreadcrumb\n name={check.name}\n onNameChange={(name) => {\n mutate({ name });\n }}\n />\n <Box sx={{ flexGrow: 1 }} />\n <Stack direction=\"row\" spacing={2} sx={{ mr: \"10px\" }}>\n {relativeTime && (\n <Box\n sx={{\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n fontSize: \"0.75rem\",\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n {relativeTime}\n </Box>\n )}\n\n {/* Preset label moved to the right */}\n {isPresetCheck && (\n <MuiTooltip title=\"This is a preset check\">\n <Box\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n >\n <FaBookmark\n size=\"1rem\"\n color={theme.palette.iochmara.dark}\n />\n </Box>\n </MuiTooltip>\n )}\n\n <IconButton size=\"small\" onClick={handleMenuClick}>\n <VscKebabVertical />\n </IconButton>\n <Menu\n anchorEl={menuAnchorEl}\n open={menuOpen}\n onClose={handleMenuClose}\n >\n {sessionId && (\n <MenuItem\n onClick={() => {\n handleMarkAsPresetCheck();\n handleMenuClose();\n }}\n disabled={isMarkingAsPreset || isPresetCheck}\n >\n <ListItemIcon>\n <IoBookmarksOutline />\n </ListItemIcon>\n <ListItemText>Mark as Preset Check</ListItemText>\n </MenuItem>\n )}\n <MenuItem\n onClick={() => {\n setIsPresetCheckTemplateOpen(true);\n handleMenuClose();\n }}\n >\n <ListItemIcon>\n <IoMdCodeWorking />\n </ListItemIcon>\n <ListItemText>Get Preset Check Template</ListItemText>\n </MenuItem>\n <MenuItem\n onClick={() => {\n handleCopy();\n handleMenuClose();\n }}\n >\n <ListItemIcon>\n <PiCopy />\n </ListItemIcon>\n <ListItemText>Copy Markdown</ListItemText>\n </MenuItem>\n <Divider />\n <MenuItem\n onClick={() => {\n handleDelete();\n handleMenuClose();\n }}\n disabled={featureToggles.disableUpdateChecklist}\n sx={{ color: \"error.main\" }}\n >\n <ListItemIcon sx={{ color: \"error.main\" }}>\n <PiTrashFill />\n </ListItemIcon>\n <ListItemText>Delete</ListItemText>\n </MenuItem>\n </Menu>\n\n <MuiTooltip\n title={\n isDisabledByNoResult({\n type: check.type,\n hasResult: !!run?.result,\n hasError: !!run?.error,\n })\n ? \"Run the check first\"\n : check.is_checked\n ? \"Remove approval\"\n : \"Mark as approved\"\n }\n placement=\"bottom-end\"\n >\n <Button\n size=\"small\"\n color={check.is_checked ? \"success\" : \"neutral\"}\n variant={check.is_checked ? \"contained\" : \"outlined\"}\n onClick={() => {\n handleApproveCheck();\n }}\n disabled={\n isDisabledByNoResult({\n type: check.type,\n hasResult: !!run?.result,\n hasError: !!run?.error,\n }) || featureToggles.disableUpdateChecklist\n }\n startIcon={\n check.is_checked ? (\n <PiCheckCircle />\n ) : (\n <VscCircleLarge\n style={{\n color: isDark\n ? theme.palette.grey[600]\n : theme.palette.grey[400],\n }}\n />\n )\n }\n sx={{ flex: \"0 0 auto\", textTransform: \"none\" }}\n >\n {check.is_checked ? \"Approved\" : \"Pending\"}\n </Button>\n </MuiTooltip>\n </Stack>\n </Box>\n\n {/* Description area */}\n <Box sx={{ flex: 1, p: \"8px 16px\", minHeight: 100 }}>\n <CheckDescription\n key={check.check_id}\n value={check.description}\n onChange={handleUpdateDescription}\n disabled={featureToggles.disableUpdateChecklist}\n />\n </Box>\n </Box>\n </Box>\n <Box style={{ contain: \"strict\" }}>\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n borderBottom: 1,\n borderColor: \"divider\",\n height: 50,\n }}\n >\n <Tabs\n value={tabValue}\n onChange={(_, newValue) =>\n setTabValue(newValue as TabValueList)\n }\n >\n <Tab\n label=\"Result\"\n value=\"result\"\n sx={{ fontSize: \"0.75rem\", textTransform: \"none\" }}\n />\n {(check.type === \"query\" || check.type === \"query_diff\") && (\n <Tab\n label=\"Query\"\n value=\"query\"\n sx={{ fontSize: \"0.75rem\", textTransform: \"none\" }}\n />\n )}\n </Tabs>\n <Box sx={{ flexGrow: 1 }} />\n <Stack direction=\"row\" spacing={1} sx={{ mr: \"10px\" }}>\n {(check.type === \"query\" ||\n check.type === \"query_base\" ||\n check.type === \"query_diff\") && (\n <MuiTooltip title=\"Open in Query tab\">\n <Button\n variant=\"outlined\"\n color=\"neutral\"\n size=\"small\"\n onClick={handleOpenQuery}\n disabled={featureToggles.disableDatabaseQuery}\n startIcon={<TbEdit />}\n sx={{ textTransform: \"none\" }}\n >\n Open Query\n </Button>\n </MuiTooltip>\n )}\n {RunResultView && (\n <MuiTooltip title=\"Rerun\">\n <Button\n variant=\"outlined\"\n color=\"neutral\"\n size=\"small\"\n onClick={() => handleRerun()}\n disabled={\n featureToggles.disableDatabaseQuery || isRunning\n }\n startIcon={<PiRepeat />}\n sx={{ textTransform: \"none\" }}\n >\n {isRunning ? \"Running...\" : \"Rerun\"}\n </Button>\n </MuiTooltip>\n )}\n <Button\n variant=\"outlined\"\n color=\"neutral\"\n disabled={\n isDisabledByNoResult({\n type: check.type,\n hasResult: !!run?.result,\n hasError: !!run?.error,\n }) || tabValue !== \"result\"\n }\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n size=\"small\"\n onClick={async () => {\n if (check.type === \"lineage_diff\") {\n lineageViewRef.current?.copyToClipboard();\n } else {\n await onCopyToClipboard();\n }\n trackCopyToClipboard({ type: check.type, from: \"check\" });\n }}\n startIcon={<PiCopy />}\n sx={{ textTransform: \"none\" }}\n >\n Copy to Clipboard\n </Button>\n </Stack>\n </Box>\n <Box sx={{ flex: 1, contain: \"strict\" }}>\n {tabValue === \"result\" && (\n <Box sx={{ width: \"100%\", height: \"100%\" }}>\n {RunResultView &&\n (check.last_run || trackedRunId ? (\n <RunViewOss\n ref={ref as unknown as Ref<RefTypes>}\n isRunning={isRunning}\n isAborting={isAborting}\n run={\n trackedRunId\n ? run\n : // Cast from library Run to OSS Run\n (check.last_run as Run | undefined)\n }\n error={rerunError}\n progress={progress}\n RunResultView={RunResultView}\n viewOptions={check.view_options as ViewOptionTypes}\n onViewOptionsChanged={handelUpdateViewOptions}\n onCancel={handleCancel}\n onExecuteRun={handleRerun}\n />\n ) : (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n height: \"100%\",\n }}\n >\n <Stack spacing={2} alignItems=\"center\">\n <Box>\n This action is part of the initial preset and has\n not been performed yet. Once performed, the result\n will be shown here.\n </Box>\n <SetupConnectionPopover\n display={featureToggles.mode === \"metadata only\"}\n >\n <Button\n onClick={handleRerun}\n variant=\"contained\"\n size=\"small\"\n disabled={featureToggles.disableDatabaseQuery}\n >\n Run Query\n </Button>\n </SetupConnectionPopover>\n </Stack>\n </Box>\n ))}\n {check.type === \"schema_diff\" && (\n <SchemaDiffView\n key={check.check_id}\n check={check}\n ref={ref}\n />\n )}\n {check.type === \"lineage_diff\" && (\n <LineageDiffView\n key={check.check_id}\n check={check}\n ref={lineageViewRef}\n />\n )}\n </Box>\n )}\n {tabValue === \"query\" &&\n (check.type === \"query\" ||\n check.type === \"query_diff\" ||\n check.type === \"query_base\") && (\n <Box sx={{ height: \"100%\", width: \"100%\" }}>\n {(check.params as QueryParams).base_sql_template ? (\n <DualSqlEditor\n value={\n (check.params as QueryDiffParams).sql_template || \"\"\n }\n baseValue={\n (check.params as QueryDiffParams)\n .base_sql_template ?? \"\"\n }\n options={{ readOnly: true }}\n />\n ) : (\n <SqlEditor\n value={\n (check.params as QueryRunParams).sql_template || \"\"\n }\n options={{ readOnly: true }}\n />\n )}\n </Box>\n )}\n </Box>\n </Box>\n </Box>\n <MuiDialog\n open={isPresetCheckTemplateOpen}\n onClose={() => setIsPresetCheckTemplateOpen(false)}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Preset Check Template</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle2\" fontWeight=\"bold\" sx={{ mb: 2 }}>\n Please{\" \"}\n <Typography\n component=\"span\"\n sx={{\n cursor: \"pointer\",\n \"&:hover\": { textDecoration: \"underline\" },\n color: \"primary.main\",\n }}\n onClick={async () => {\n await navigator.clipboard.writeText(presetCheckTemplate);\n successToast(\"Copied the template to the clipboard\");\n }}\n >\n copy\n </Typography>{\" \"}\n the following template and paste it into the{\" \"}\n <Box\n component=\"span\"\n sx={{ px: 0.5, bgcolor: \"error.light\", borderRadius: 0.5 }}\n >\n recce.yml\n </Box>{\" \"}\n file.\n </Typography>\n <PresetCheckTemplateView yamlTemplate={presetCheckTemplate} />\n </DialogContent>\n <DialogActions>\n <IconButton\n size=\"small\"\n onClick={() => setIsPresetCheckTemplateOpen(false)}\n sx={{ position: \"absolute\", top: 8, right: 8 }}\n >\n <IoClose />\n </IconButton>\n </DialogActions>\n </MuiDialog>\n </VSplit>\n </Grid>\n {/* Timeline/Activity panel - fixed 20% width, hidden on mobile */}\n {cloudMode && (\n <Grid\n size={3}\n sx={{\n height: \"100%\",\n overflow: \"hidden\",\n flexShrink: 0,\n display: { xs: \"none\", md: \"block\" },\n }}\n >\n <CheckTimeline checkId={checkId} />\n </Grid>\n )}\n </Grid>\n );\n}\n\nfunction buildMarkdown(check: Check<RunParamTypes>) {\n const title = buildCheckTitle({\n name: check.name,\n isChecked: check.is_checked,\n });\n return stripIndents`\n <details><summary>${title}</summary>\n\n ${buildBody(check)}\n\n </details>`;\n}\n\nfunction buildBody(check: Check<RunParamTypes>) {\n const description = buildCheckDescription({ description: check.description });\n if (check.type === \"query\" || check.type === \"query_diff\") {\n const params = check.params;\n const sqlTemplate =\n params && \"sql_template\" in params ? params.sql_template : \"\";\n return `${description}\\n\\n${formatSqlAsMarkdown({ sql: sqlTemplate })}`;\n }\n\n return description;\n}\n","\"use client\";\n\n/**\n * CheckEmptyStateOss - wrapper around CheckEmptyState primitive\n *\n * Adds business logic:\n * - API calls to create schema diff check\n * - Navigation after check creation\n * - Query cache invalidation\n */\n\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useRouter } from \"next/navigation\";\nimport { TbChecklist } from \"react-icons/tb\";\nimport { type Check, cacheKeys, createSchemaDiffCheck } from \"../../api\";\nimport { useRouteConfig } from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks\";\nimport { CheckEmptyState as CheckEmptyStateUI } from \"../../primitives\";\n\nexport const CheckEmptyStateOss = () => {\n const queryClient = useQueryClient();\n const router = useRouter();\n const { apiClient } = useApiConfig();\n const { basePath } = useRouteConfig();\n\n const { mutate: createSchemaCheck, isPending } = useMutation({\n mutationFn: () =>\n createSchemaDiffCheck({ select: \"state:modified\" }, apiClient),\n onSuccess: async (check: Check) => {\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n router.push(`${basePath}/checks/?id=${check.check_id}`);\n },\n });\n\n const handleCreateSchemaCheck = () => {\n createSchemaCheck();\n };\n\n return (\n <CheckEmptyStateUI\n title=\"No checks yet\"\n description=\"Checks help you validate data quality and catch issues.\"\n icon={<TbChecklist size={48} />}\n actionText=\"Create Schema Diff Check\"\n onAction={handleCreateSchemaCheck}\n isLoading={isPending}\n helperText=\"The schema checks compare modified models between environments to detect changes, additions, or removals.\"\n />\n );\n};\n","\"use client\";\n\nimport {\n DragDropContext,\n Draggable,\n Droppable,\n type DropResult,\n} from \"@hello-pangea/dnd\";\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Checkbox from \"@mui/material/Checkbox\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport Divider from \"@mui/material/Divider\";\nimport FormControlLabel from \"@mui/material/FormControlLabel\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport { cacheKeys } from \"../../api/cacheKeys\";\nimport { type Check, updateCheck } from \"../../api/checks\";\nimport { useRecceInstanceContext } from \"../../contexts\";\nimport { useApiConfig, useRun } from \"../../hooks\";\nimport { toaster } from \"../ui\";\nimport {\n CheckCard,\n type CheckCardData,\n type CheckRunStatus,\n type CheckType,\n} from \"./CheckCard\";\nimport { isDisabledByNoResult } from \"./utils\";\n\n/**\n * Wrapper component that adapts Check data to CheckCard props.\n * Handles run data fetching and maps to UI primitive expectations.\n */\nconst ChecklistItem = ({\n check,\n selected,\n onSelect,\n onApprovalChange,\n}: {\n check: Check;\n selected: boolean;\n onSelect: (checkId: string) => void;\n onApprovalChange: (checkId: string, isApproved: boolean) => void;\n}) => {\n const { featureToggles } = useRecceInstanceContext();\n const trackedRunId = check.last_run?.run_id;\n const { run } = useRun(trackedRunId);\n\n const isNoResult = isDisabledByNoResult({\n type: check.type,\n hasResult: !!run?.result,\n hasError: !!run?.error,\n });\n\n const isApprovalDisabled =\n isNoResult || featureToggles.disableUpdateChecklist;\n\n // Map run status if available\n const getRunStatus = (): CheckRunStatus | undefined => {\n if (!run) return undefined;\n if (run.error) return \"error\";\n if (run.result) return \"success\";\n return undefined;\n };\n\n // Adapt Check to CheckCardData\n const checkCardData: CheckCardData = {\n id: check.check_id,\n name: check.name,\n type: check.type as CheckType,\n isApproved: check.is_checked,\n runStatus: getRunStatus(),\n isPreset: check.is_preset,\n };\n\n return (\n <CheckCard\n check={checkCardData}\n isSelected={selected}\n onClick={onSelect}\n onApprovalChange={onApprovalChange}\n disableApproval={isApprovalDisabled}\n disabledApprovalTooltip={isNoResult ? \"Run the check first\" : undefined}\n />\n );\n};\n\nexport const CheckListOss = ({\n checks,\n selectedItem,\n onCheckSelected,\n onChecksReordered,\n}: {\n checks: Check[];\n selectedItem: string | null;\n onCheckSelected: (checkId: string) => void;\n onChecksReordered: (source: number, destination: number) => void;\n}) => {\n const [bypassModal, setBypassModal] = useState(false);\n const [open, setOpen] = useState(false);\n const [pendingApprovalCheckId, setPendingApprovalCheckId] = useState<\n string | null\n >(null);\n const queryClient = useQueryClient();\n const { apiClient } = useApiConfig();\n\n // Mutation for updating check approval status\n const { mutate: updateApproval } = useMutation({\n mutationFn: ({\n checkId,\n isChecked,\n }: {\n checkId: string;\n isChecked: boolean;\n }) => updateCheck(checkId, { is_checked: isChecked }, apiClient),\n onSuccess: async (_, { checkId }) => {\n await queryClient.invalidateQueries({\n queryKey: cacheKeys.check(checkId),\n });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n },\n });\n\n const onDragEnd = (result: DropResult) => {\n if (!result.destination) {\n return;\n }\n\n onChecksReordered(result.source.index, result.destination.index);\n };\n\n const handleOpen = () => setOpen(true);\n const handleClose = () => {\n setOpen(false);\n setPendingApprovalCheckId(null);\n };\n\n const showApprovedToast = () => {\n toaster.create({\n title: \"Marked as approved\",\n type: \"success\",\n duration: 2000,\n });\n };\n\n /**\n * Handle approval change from CheckCard.\n * If approving (true), show modal or bypass based on localStorage.\n * If unapproving (false), update directly.\n */\n const handleApprovalChange = (checkId: string, isApproved: boolean) => {\n if (!isApproved) {\n // Unapproving - update directly\n updateApproval({ checkId, isChecked: false });\n } else {\n // Approving - check for bypass\n const bypassMarkAsApprovedWarning = localStorage.getItem(\n \"bypassMarkAsApprovedWarning\",\n );\n if (bypassMarkAsApprovedWarning === \"true\") {\n updateApproval({ checkId, isChecked: true });\n showApprovedToast();\n } else {\n setPendingApprovalCheckId(checkId);\n handleOpen();\n }\n }\n };\n\n const handleMarkAsApprovedConfirmed = () => {\n if (pendingApprovalCheckId) {\n updateApproval({ checkId: pendingApprovalCheckId, isChecked: true });\n if (bypassModal) {\n localStorage.setItem(\"bypassMarkAsApprovedWarning\", \"true\");\n }\n showApprovedToast();\n handleClose();\n setPendingApprovalCheckId(null);\n }\n };\n\n return (\n <>\n <DragDropContext onDragEnd={onDragEnd}>\n <Droppable droppableId=\"checklist\">\n {(provided) => (\n <Stack\n {...provided.droppableProps}\n ref={provided.innerRef}\n className=\"no-track-pii-safe\"\n sx={{\n width: \"100%\",\n flex: 1,\n overflow: \"auto\",\n }}\n spacing={0}\n >\n {checks.map((check, index) => (\n <Draggable\n key={check.check_id}\n draggableId={check.check_id}\n index={index}\n >\n {(provided, snapshot) => {\n // see https://github.com/atlassian/react-beautiful-dnd/issues/1881#issuecomment-691237307\n // Create a new style object instead of mutating the read-only one\n let style = provided.draggableProps.style;\n if (snapshot.isDragging && style && \"left\" in style) {\n const offset = { x: 0, y: 80 };\n style = {\n ...style,\n left: (style.left as number) - offset.x,\n top: (style.top as number) - offset.y,\n };\n }\n\n return (\n <Box\n ref={provided.innerRef}\n {...provided.draggableProps}\n {...provided.dragHandleProps}\n style={style}\n sx={{ width: \"100%\" }}\n borderBottom=\"1px solid\"\n borderColor=\"divider\"\n >\n <ChecklistItem\n key={check.check_id}\n check={check}\n selected={check.check_id === selectedItem}\n onSelect={onCheckSelected}\n onApprovalChange={handleApprovalChange}\n />\n </Box>\n );\n }}\n </Draggable>\n ))}\n {provided.placeholder}\n </Stack>\n )}\n </Droppable>\n </DragDropContext>\n <MuiDialog open={open} onClose={handleClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n Mark as Approved?\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <Divider />\n <DialogContent sx={{ fontSize: \"0.875rem\" }}>\n <Typography>\n Please ensure you have reviewed the contents of this check before\n marking it as approved.\n </Typography>\n <FormControlLabel\n control={\n <Checkbox\n checked={bypassModal}\n onChange={(e) => {\n setBypassModal(e.target.checked);\n }}\n size=\"small\"\n />\n }\n label={\n <Typography sx={{ fontWeight: \"bold\", pt: \"8px\" }}>\n Don't show this again\n </Typography>\n }\n />\n </DialogContent>\n <Divider />\n <DialogActions sx={{ gap: 0 }}>\n <Button variant=\"outlined\" size=\"small\" onClick={handleClose}>\n Cancel\n </Button>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n size=\"small\"\n onClick={handleMarkAsApprovedConfirmed}\n >\n Mark as approved\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n};\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Divider from \"@mui/material/Divider\";\nimport Stack from \"@mui/material/Stack\";\nimport { useTheme } from \"@mui/material/styles\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useRouter, useSearchParams } from \"next/navigation\";\nimport React, {\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport { cacheKeys, listChecks, reorderChecks } from \"../../api\";\nimport { useRouteConfig } from \"../../contexts\";\nimport { useApiConfig, useRecceCheckContext } from \"../../hooks\";\nimport { StateImporter } from \"../app\";\nimport { HSplit } from \"../ui\";\nimport { CheckDetailOss as CheckDetail } from \"./CheckDetailOss\";\nimport { CheckEmptyStateOss as CheckEmptyState } from \"./CheckEmptyStateOss\";\nimport { CheckListOss as CheckList } from \"./CheckListOss\";\n\nexport const CheckPageContentOss = (): ReactNode => {\n const theme = useTheme();\n const isDark = theme.palette.mode === \"dark\";\n const borderColor = isDark ? \"grey.700\" : \"grey.300\";\n const router = useRouter();\n const searchParams = useSearchParams();\n const checkId = searchParams.get(\"id\");\n const { latestSelectedCheckId, setLatestSelectedCheckId } =\n useRecceCheckContext();\n const queryClient = useQueryClient();\n const { apiClient } = useApiConfig();\n const { basePath } = useRouteConfig();\n const selectedItem = checkId;\n\n useEffect(() => {\n if (selectedItem) {\n setLatestSelectedCheckId(selectedItem);\n }\n }, [selectedItem, setLatestSelectedCheckId]);\n\n const {\n isLoading,\n error,\n data: checks,\n status,\n refetch: refetchCheckList,\n } = useQuery({\n queryKey: cacheKeys.checks(),\n queryFn: () => listChecks(apiClient),\n refetchOnMount: true,\n });\n\n const handleSelectItem = useCallback(\n (checkId: string) => {\n router.push(`${basePath}/checks/?id=${checkId}`);\n },\n [router.push, basePath],\n );\n\n const [orderedChecks, setOrderedChecks] = useState(checks ?? []);\n const [prevChecks, setPrevChecks] = useState(checks);\n\n // Sync orderedChecks with checks when checks data changes (during render)\n if (checks !== prevChecks) {\n setPrevChecks(checks);\n setOrderedChecks(checks ?? []);\n }\n\n const { mutate: changeChecksOrder } = useMutation({\n mutationFn: (order: { source: number; destination: number }) =>\n reorderChecks(order, apiClient),\n onSuccess: async () => {\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n },\n });\n\n const handleDragEnd = useCallback(\n (source: number, destination: number) => {\n const updatedItems = [...orderedChecks];\n const [reorderedItem] = updatedItems.splice(source, 1);\n updatedItems.splice(destination, 0, reorderedItem);\n\n changeChecksOrder({\n source,\n destination,\n });\n\n setOrderedChecks(updatedItems);\n },\n [orderedChecks, changeChecksOrder],\n );\n\n // Memoized validation to avoid duplicate checks.some() calls\n const isValidSelection = useMemo(\n () =>\n Boolean(\n selectedItem &&\n checks?.some((check) => check.check_id === selectedItem),\n ),\n [selectedItem, checks],\n );\n\n useEffect(() => {\n if (status !== \"success\" || checks.length === 0) {\n return;\n }\n\n if (!isValidSelection) {\n // No selection or invalid selection - redirect to a valid check\n // First try latestSelectedCheckId if it's valid\n const isValidLatestSelectedCheckId =\n latestSelectedCheckId &&\n checks.some((check) => check.check_id === latestSelectedCheckId);\n\n if (isValidLatestSelectedCheckId) {\n router.replace(`${basePath}/checks/?id=${latestSelectedCheckId}`);\n } else {\n // Fall back to the first check\n router.replace(`${basePath}/checks/?id=${checks[0].check_id}`);\n }\n }\n }, [\n status,\n isValidSelection,\n checks,\n latestSelectedCheckId, // Fall back to the first check\n router.replace,\n basePath,\n ]);\n\n if (isLoading) {\n return null;\n }\n\n if (error) {\n return (\n <Box>\n Error: <span className=\"no-track-pii-safe\">{error.message}</span>\n </Box>\n );\n }\n\n if (!checks?.length) {\n return (\n <HSplit style={{ height: \"100%\" }} minSize={50} sizes={[20, 80]}>\n <Box\n sx={{\n borderRight: \"1px solid\",\n borderRightColor: borderColor,\n height: \"100%\",\n }}\n style={{ contain: \"size\" }}\n >\n <Stack\n sx={{ height: \"100%\", alignItems: \"stretch\" }}\n style={{ contain: \"strict\" }}\n spacing={0}\n >\n <Box\n sx={{\n display: \"flex\",\n justifyContent: \"flex-end\",\n p: \"0px 10px\",\n }}\n >\n <StateImporter checksOnly />\n </Box>\n <Divider />\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <Box sx={{ textAlign: \"center\", color: \"grey.500\" }}>\n No checks\n </Box>\n </Box>\n </Stack>\n </Box>\n <Box>\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <CheckEmptyState />\n </Box>\n </Box>\n </HSplit>\n );\n }\n\n return (\n <HSplit style={{ height: \"100%\" }} minSize={50} sizes={[20, 80]}>\n <Box sx={{ height: \"100%\" }} style={{ contain: \"size\" }}>\n <Stack\n sx={{ height: \"100%\", alignItems: \"stretch\" }}\n style={{ contain: \"strict\" }}\n spacing={0}\n >\n <Box\n sx={{ display: \"flex\", justifyContent: \"flex-end\", p: \"0px 10px\" }}\n >\n <StateImporter checksOnly />\n </Box>\n <Divider />\n <CheckList\n checks={orderedChecks}\n selectedItem={selectedItem}\n onCheckSelected={handleSelectItem}\n onChecksReordered={handleDragEnd}\n />\n </Stack>\n </Box>\n <Box sx={{ height: \"100%\" }}>\n {/* isValidSelection already checks selectedItem, but TS needs explicit check for type narrowing */}\n {isValidSelection && selectedItem && (\n <CheckDetail\n key={selectedItem}\n checkId={selectedItem}\n refreshCheckList={refetchCheckList}\n />\n )}\n </Box>\n </HSplit>\n );\n};\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport { useTheme } from \"@mui/material/styles\";\nimport React, { ReactNode } from \"react\";\nimport { HSplit } from \"../ui\";\n\n/**\n * Loading fallback - shows minimal UI while search params are being read\n */\nexport const CheckPageLoadingOss = (): ReactNode => {\n const theme = useTheme();\n const isDark = theme.palette.mode === \"dark\";\n const borderColor = isDark ? \"grey.700\" : \"grey.300\";\n\n return (\n <HSplit style={{ height: \"100%\" }} minSize={50} sizes={[20, 80]}>\n <Box\n sx={{\n borderRight: \"1px solid\",\n borderRightColor: borderColor,\n height: \"100%\",\n }}\n style={{ contain: \"size\" }}\n >\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <CircularProgress size={20} />\n </Box>\n </Box>\n <Box>\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <CircularProgress size={20} />\n </Box>\n </Box>\n </HSplit>\n );\n};\n","/**\n * @file types.ts\n * @description Type definitions for change summary components\n *\n * These types define the various change statuses that can occur in a\n * lineage graph, including node-level changes (added, removed, modified)\n * and column-level changes.\n */\n\nimport type { LineageGraph } from \"../../types\";\n\n/**\n * Change status for nodes and columns in a lineage graph\n *\n * Node changes:\n * - added: New resource added to the graph\n * - removed: Resource removed from the graph\n * - modified: Resource code was modified\n *\n * Column changes:\n * - col_added: New column added to a node\n * - col_removed: Column removed from a node\n * - col_changed: Column type or definition changed\n *\n * Folder changes:\n * - folder_changed: Files in a folder were modified\n *\n * null: No change detected\n */\nexport type ChangeStatus =\n // node change (code change - user edit)\n | \"added\"\n | \"removed\"\n | \"modified\"\n // column change\n | \"col_added\"\n | \"col_removed\"\n | \"col_changed\"\n // folder change\n | \"folder_changed\"\n | null;\n\n/**\n * Labels for each change status\n * Format: [shortLabel, longLabel]\n */\nexport const NODE_CHANGE_STATUS_MSGS: Record<\n NonNullable<ChangeStatus>,\n [string, string]\n> = {\n added: [\"Model Added\", \"Added resource\"],\n removed: [\"Model Removed\", \"Removed resource\"],\n modified: [\"Model Modified\", \"Modified resource\"],\n col_added: [\"Column Added\", \"Added column\"],\n col_removed: [\"Column Removed\", \"Removed column\"],\n col_changed: [\"Column Modified\", \"Modified column\"],\n folder_changed: [\"Modified\", \"Modified folder\"],\n};\n\n/**\n * Props for the ChangeSummary component\n */\nexport interface ChangeSummaryProps {\n lineageGraph: LineageGraph;\n}\n\n/**\n * Result of calculating column changes between base and current\n */\nexport interface ColumnChangeResult {\n adds: number;\n removes: number;\n modifies: number;\n}\n\n/**\n * Result of calculating all changes in a lineage graph\n */\nexport interface ChangeSummaryResult {\n adds: number;\n removes: number;\n modifies: number;\n col_added: number;\n col_removed: number;\n col_changed: number;\n}\n","/**\n * @file utils.ts\n * @description Utility functions for change summary calculations\n *\n * These functions calculate change statistics across a lineage graph,\n * including node-level and column-level changes.\n */\n\nimport type { NodeData } from \"../../api/info\";\nimport { token } from \"../../theme/colors\";\nimport type { LineageGraph } from \"../../types\";\nimport {\n IconAdded,\n IconChanged,\n type IconComponent,\n IconModified,\n IconRemoved,\n} from \"../lineage/styles\";\nimport type {\n ChangeStatus,\n ChangeSummaryResult,\n ColumnChangeResult,\n} from \"./types\";\n\n/**\n * Get icon and color for a given change status\n *\n * Maps change statuses to visual indicators (icons and colors) for\n * consistent display across the UI.\n *\n * @param changeStatus - The change status to get icon for\n * @returns Object with color and icon component\n */\nexport function getIconForChangeStatus(changeStatus?: ChangeStatus): {\n color: string;\n icon: IconComponent | undefined;\n} {\n const greenColor = String(token(\"colors.green.solid\"));\n const redColor = String(token(\"colors.red.solid\"));\n const amberColor = String(token(\"colors.amber.emphasized\"));\n\n if (changeStatus === \"added\") {\n return { color: greenColor, icon: IconAdded };\n } else if (changeStatus === \"removed\") {\n return { color: redColor, icon: IconRemoved };\n } else if (changeStatus === \"modified\") {\n return { color: amberColor, icon: IconModified };\n } else if (changeStatus === \"col_added\") {\n return { color: greenColor, icon: IconAdded };\n } else if (changeStatus === \"col_removed\") {\n return { color: redColor, icon: IconRemoved };\n } else if (changeStatus === \"col_changed\") {\n return { color: amberColor, icon: IconModified };\n } else if (changeStatus === \"folder_changed\") {\n return { color: amberColor, icon: IconChanged };\n }\n\n return { color: \"inherit\", icon: undefined };\n}\n\n/**\n * Calculate column changes between base and current node data\n *\n * Compares the columns in base and current versions of a node to\n * determine how many columns were added, removed, or modified.\n *\n * @param base - Base version node data\n * @param current - Current version node data\n * @returns Column change counts\n */\nexport function calculateColumnChange(\n base: NodeData | undefined,\n current: NodeData | undefined,\n): ColumnChangeResult {\n let adds = 0;\n let removes = 0;\n let modifies = 0;\n if (!base && !current) return { adds, removes, modifies };\n\n // Add columns\n if (current) {\n Object.keys(current.columns ?? {}).forEach((col) => {\n if (!base?.columns?.[col]) adds++;\n });\n }\n\n // Remove columns\n if (base) {\n Object.keys(base.columns ?? {}).forEach((col) => {\n if (!current?.columns?.[col]) removes++;\n });\n }\n\n // Modify columns\n if (current && base) {\n Object.keys(current.columns ?? {}).forEach((col) => {\n if (base.columns && current.columns?.[col] && base.columns[col]) {\n if (base.columns[col].type !== current.columns[col].type) modifies++;\n }\n });\n }\n\n return { adds, removes, modifies };\n}\n\n/**\n * Calculate all changes in a lineage graph\n *\n * Iterates through all modified nodes in the lineage graph and\n * aggregates node-level and column-level changes.\n *\n * @param lineageGraph - The lineage graph to analyze\n * @returns Aggregated change counts\n */\nexport function calculateChangeSummary(\n lineageGraph: LineageGraph,\n): ChangeSummaryResult {\n const modifiedSet = lineageGraph.modifiedSet;\n let adds = 0;\n let removes = 0;\n let modifies = 0;\n let col_added = 0;\n let col_removed = 0;\n let col_changed = 0;\n\n modifiedSet.forEach((nodeId) => {\n if (lineageGraph.nodes[nodeId].data.changeStatus === \"added\") adds++;\n else if (lineageGraph.nodes[nodeId].data.changeStatus === \"removed\")\n removes++;\n else if (lineageGraph.nodes[nodeId].data.changeStatus === \"modified\")\n modifies++;\n\n const base = lineageGraph.nodes[nodeId].data.data.base;\n const current = lineageGraph.nodes[nodeId].data.data.current;\n const columnChange = calculateColumnChange(base, current);\n col_added += columnChange.adds;\n col_removed += columnChange.removes;\n col_changed += columnChange.modifies;\n });\n\n return { adds, removes, modifies, col_added, col_removed, col_changed };\n}\n","/**\n * @file ChangeSummary.tsx\n * @description Change summary component for displaying node and column changes\n *\n * This component displays an overview of changes detected in a lineage graph,\n * including:\n * - Code changes (added, removed, modified nodes)\n * - Column changes (added, removed, modified columns)\n *\n * Each change type is displayed with its corresponding icon and count.\n */\n\nimport Box from \"@mui/material/Box\";\nimport Grid from \"@mui/material/Grid\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport type { ReactNode } from \"react\";\nimport { FiInfo } from \"react-icons/fi\";\n\nimport type { ChangeStatus, ChangeSummaryProps } from \"./types\";\nimport { NODE_CHANGE_STATUS_MSGS } from \"./types\";\nimport { calculateChangeSummary, getIconForChangeStatus } from \"./utils\";\n\n/**\n * Summary text display with optional tooltip\n *\n * @internal\n */\nfunction SummaryText({\n name,\n value,\n tip,\n}: {\n name: ReactNode;\n value: ReactNode;\n tip?: ReactNode;\n}) {\n return (\n <Stack alignItems=\"stretch\">\n <Typography sx={{ fontSize: \"0.875rem\", color: \"grey.600\" }}>\n {name}\n {tip && (\n <MuiTooltip title={tip}>\n <Box sx={{ display: \"inline-block\" }}>\n <Box\n component={FiInfo}\n sx={{ mx: \"2px\", fontSize: 12, verticalAlign: \"middle\" }}\n />\n </Box>\n </MuiTooltip>\n )}\n </Typography>\n {value}\n </Stack>\n );\n}\n\n/**\n * Label for a change status count with icon\n *\n * @internal\n */\nfunction ChangeStatusCountLabel({\n changeStatus,\n value,\n}: {\n changeStatus: ChangeStatus;\n value: number;\n}) {\n const [label] = changeStatus ? NODE_CHANGE_STATUS_MSGS[changeStatus] : [\"\"];\n const { icon, color } = getIconForChangeStatus(changeStatus);\n\n return (\n <Stack alignItems=\"stretch\">\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ fontSize: \"0.875rem\", color: \"grey.600\" }}\n >\n {icon && (\n <Box component={icon} sx={{ mr: \"5px\", color, fontSize: \"1rem\" }} />\n )}\n {label}\n </Stack>\n <Typography sx={{ fontSize: \"0.875rem\" }}>{value}</Typography>\n </Stack>\n );\n}\n\n/**\n * ChangeSummary component\n *\n * Displays a summary of all changes in a lineage graph, split into two sections:\n * - Code Changes: Added, removed, and modified nodes\n * - Column Changes: Added, removed, and modified columns\n *\n * @example\n * ```tsx\n * <ChangeSummary lineageGraph={lineageGraph} />\n * ```\n */\nexport function ChangeSummary({ lineageGraph }: ChangeSummaryProps) {\n const { adds, removes, modifies, col_added, col_removed, col_changed } =\n calculateChangeSummary(lineageGraph);\n\n return (\n <Grid\n container\n sx={{\n mb: \"10px\",\n borderTop: \"1px solid\",\n borderColor: \"divider\",\n p: \"2.5vw\",\n }}\n >\n <Grid size={6} sx={{ borderColor: \"divider\" }}>\n <SummaryText\n name=\"Code Changes\"\n value={\n <Grid container sx={{ width: \"100%\" }}>\n <Grid size={4}>\n <ChangeStatusCountLabel changeStatus=\"added\" value={adds} />\n </Grid>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"removed\"\n value={removes}\n />\n </Grid>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"modified\"\n value={modifies}\n />\n </Grid>\n </Grid>\n }\n />\n </Grid>\n <Grid\n size={6}\n sx={{ borderLeft: \"1px solid\", borderLeftColor: \"divider\", pl: \"12px\" }}\n >\n <SummaryText\n name=\"Column Changes\"\n value={\n <Grid container sx={{ width: \"100%\" }}>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"col_added\"\n value={col_added}\n />\n </Grid>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"col_removed\"\n value={col_removed}\n />\n </Grid>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"col_changed\"\n value={col_changed}\n />\n </Grid>\n </Grid>\n }\n />\n </Grid>\n </Grid>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Card from \"@mui/material/Card\";\nimport CardContent from \"@mui/material/CardContent\";\nimport CardHeader from \"@mui/material/CardHeader\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useEffect, useState } from \"react\";\nimport type { LineageGraph, LineageGraphNode } from \"../../contexts\";\nimport { mergeKeysWithStatus } from \"../../utils\";\nimport { ResourceTypeTag, RowCountDiffTag } from \"../lineage\";\nimport { SchemaView } from \"../schema\";\n\ninterface SchemaDiffCardProps {\n title: string;\n node: LineageGraphNode;\n}\n\nfunction SchemaDiffCard({ node, ...props }: SchemaDiffCardProps) {\n return (\n <Card sx={{ maxWidth: 500 }}>\n <CardHeader\n title={\n <Typography sx={{ fontSize: 18, fontWeight: \"bold\" }}>\n {props.title}\n </Typography>\n }\n subheader={\n <Stack direction=\"row\" spacing=\"8px\" sx={{ p: \"16px\" }}>\n <ResourceTypeTag data={{ resourceType: node.data.resourceType }} />\n {node.data.resourceType === \"model\" && (\n <RowCountDiffTag node={node} />\n )}\n </Stack>\n }\n />\n <CardContent>\n <Box sx={{ display: \"flex\" }}>\n <SchemaView\n base={node.data.data.base}\n current={node.data.data.current}\n columnChanges={node.data.change?.columns}\n />\n </Box>\n </CardContent>\n </Card>\n );\n}\n\nfunction listChangedNodes(lineageGraph: LineageGraph) {\n const changedNodes: LineageGraphNode[] = [];\n const allNodes = lineageGraph.nodes;\n lineageGraph.modifiedSet.forEach((nodeId) => {\n const node = allNodes[nodeId];\n const columnDiffStatus = mergeKeysWithStatus(\n Object.keys(node.data.data.base?.columns ?? {}),\n Object.keys(node.data.data.current?.columns ?? {}),\n );\n const isSchemaChanged = !Object.values(columnDiffStatus).every(\n (el) => el === undefined,\n );\n // We only want to show nodes that have real schema changes.\n // It doesn't include added or deleted model.\n if (isSchemaChanged && node.data.data.base && node.data.data.current)\n changedNodes.push(node);\n });\n return changedNodes;\n}\n\nexport interface SchemaSummaryProps {\n lineageGraph: LineageGraph;\n}\n\nexport function SchemaSummary({ lineageGraph }: SchemaSummaryProps) {\n const [changedNodes, setChangedNodes] = useState<LineageGraphNode[]>([]);\n\n useEffect(() => {\n setChangedNodes(listChangedNodes(lineageGraph));\n }, [lineageGraph]);\n\n return (\n <>\n <Box\n sx={{\n width: \"100%\",\n pb: \"10px\",\n mb: \"20px\",\n mt: \"20px\",\n }}\n >\n <Typography variant=\"h5\" sx={{ fontSize: 24 }}>\n Schema Summary\n </Typography>\n </Box>\n <Box sx={{ width: \"100%\", pb: \"10px\", mb: \"20px\" }}>\n {changedNodes.length === 0 ? (\n <Typography sx={{ fontSize: 18, color: \"grey.600\" }}>\n No schema changes detected.\n </Typography>\n ) : (\n <Box\n sx={{\n display: \"grid\",\n gridTemplateColumns: \"repeat(auto-fill, minmax(400px, 1fr))\",\n gap: \"2vw\",\n p: \"2.5vw\",\n width: \"100%\",\n bgcolor: \"lightgray\",\n }}\n >\n {changedNodes.map((node) => {\n return (\n <SchemaDiffCard\n key={node.id}\n title={node.data.name}\n node={node}\n />\n );\n })}\n </Box>\n )}\n </Box>\n </>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Typography from \"@mui/material/Typography\";\nimport { memo, useCallback, useMemo } from \"react\";\nimport type { Check } from \"../../providers/contexts/CheckContext\";\nimport { useCheckContext } from \"../../providers/contexts/CheckContext\";\nimport type { CheckAction, CheckActionType } from \"../check/CheckActions\";\nimport type { CheckCardData } from \"../check/CheckCard\";\nimport {\n CheckDetail,\n type CheckDetailProps,\n type CheckDetailTab,\n} from \"../check/CheckDetail\";\nimport { CheckEmptyState } from \"../check/CheckEmptyState\";\nimport { CheckList } from \"../check/CheckList\";\nimport { SplitPane } from \"../ui/SplitPane\";\n\n/**\n * Props for the ChecksView component.\n * Defines options for viewing and managing checks.\n */\nexport interface ChecksViewProps {\n /**\n * Optional checks data. If not provided, uses CheckContext.\n */\n checks?: Check[];\n\n /**\n * Loading state. If not provided, uses CheckContext.\n */\n isLoading?: boolean;\n\n /**\n * Error message. If not provided, uses CheckContext.\n */\n error?: string;\n\n /**\n * Currently selected check ID. If not provided, uses CheckContext.\n */\n selectedCheckId?: string;\n\n /**\n * Callback when a check is selected. If not provided, uses CheckContext.\n */\n onCheckSelect?: (checkId: string) => void;\n\n /**\n * Callback when check approval status changes.\n */\n onApprovalChange?: (checkId: string, isApproved: boolean) => void;\n\n /**\n * Callback when a check action is triggered.\n */\n onAction?: (checkId: string, actionType: CheckActionType) => void;\n\n /**\n * Callback when checks are reordered.\n */\n onReorder?: (sourceIndex: number, destinationIndex: number) => void;\n\n /**\n * Callback when description changes.\n */\n onDescriptionChange?: (checkId: string, description?: string) => void;\n\n /**\n * Callback when name changes.\n */\n onNameChange?: (checkId: string, name: string) => void;\n\n /**\n * Callback when create is clicked (in empty state).\n */\n onCreateCheck?: () => void;\n\n /**\n * Function to generate tabs for a check detail view.\n */\n getCheckTabs?: (check: Check) => CheckDetailTab[];\n\n /**\n * Function to generate primary actions for a check.\n */\n getPrimaryActions?: (check: Check) => CheckAction[];\n\n /**\n * Function to generate secondary actions for a check.\n */\n getSecondaryActions?: (check: Check) => CheckAction[];\n\n /**\n * Whether approval is disabled for all checks.\n */\n disableApproval?: boolean;\n\n /**\n * Tooltip for disabled approval.\n */\n disabledApprovalTooltip?: string;\n\n /**\n * Optional height for the view.\n * @default \"100%\"\n */\n height?: number | string;\n\n /**\n * Initial split pane size (percentage for list).\n * @default 30\n */\n listPaneSize?: number;\n\n /**\n * Minimum list pane size in pixels.\n * @default 200\n */\n minListSize?: number;\n\n /**\n * Maximum list pane size in pixels.\n * @default 500\n */\n maxListSize?: number;\n\n /**\n * Title for the check list.\n */\n listTitle?: string;\n\n /**\n * Optional CSS class name.\n */\n className?: string;\n}\n\n/**\n * ChecksView Component\n *\n * A high-level component for viewing and managing checks using a\n * split-pane layout with a list on the left and details on the right.\n *\n * Can receive data from:\n * 1. CheckContext (wrap with CheckProvider)\n * 2. Direct props (pass checks, selectedCheckId, etc.)\n *\n * @example Using with context\n * ```tsx\n * import { CheckProvider, ChecksView } from '@datarecce/ui';\n *\n * function App() {\n * const { checks, isLoading } = useChecksQuery();\n * const [selectedId, setSelectedId] = useState<string>();\n *\n * return (\n * <CheckProvider\n * checks={checks}\n * isLoading={isLoading}\n * selectedCheckId={selectedId}\n * onSelectCheck={setSelectedId}\n * >\n * <ChecksView\n * getCheckTabs={(check) => [\n * { id: 'result', label: 'Result', content: <ResultView check={check} /> },\n * ]}\n * />\n * </CheckProvider>\n * );\n * }\n * ```\n *\n * @example Using with direct props\n * ```tsx\n * import { ChecksView } from '@datarecce/ui';\n *\n * function App({ checks, selectedId, onSelect }) {\n * return (\n * <ChecksView\n * checks={checks}\n * selectedCheckId={selectedId}\n * onCheckSelect={onSelect}\n * onApprovalChange={(id, approved) => updateCheck(id, { is_checked: approved })}\n * />\n * );\n * }\n * ```\n */\nfunction ChecksViewComponent({\n checks: propChecks,\n isLoading: propIsLoading,\n error: propError,\n selectedCheckId: propSelectedCheckId,\n onCheckSelect: propOnCheckSelect,\n onApprovalChange,\n onAction,\n onReorder,\n onDescriptionChange,\n onNameChange,\n onCreateCheck,\n getCheckTabs,\n getPrimaryActions,\n getSecondaryActions,\n disableApproval = false,\n disabledApprovalTooltip,\n height = \"100%\",\n listPaneSize = 30,\n minListSize = 200,\n maxListSize = 500,\n listTitle,\n className,\n}: ChecksViewProps) {\n // Get data from context or props\n const contextValue = useCheckContext();\n\n const checks = propChecks ?? contextValue.checks;\n const isLoading =\n propIsLoading !== undefined ? propIsLoading : contextValue.isLoading;\n const error = propError ?? contextValue.error;\n const selectedCheckId = propSelectedCheckId ?? contextValue.selectedCheckId;\n const onSelectCheck = propOnCheckSelect ?? contextValue.onSelectCheck;\n\n // Convert Check to CheckCardData\n const checkCards = useMemo<CheckCardData[]>(\n () =>\n checks.map((check) => ({\n id: check.check_id,\n name: check.name,\n type: check.type as CheckCardData[\"type\"],\n description: check.description,\n isApproved: check.is_checked,\n })),\n [checks],\n );\n\n // Find selected check\n const selectedCheck = useMemo(\n () => checks.find((c) => c.check_id === selectedCheckId),\n [checks, selectedCheckId],\n );\n\n // Handle check selection\n const handleCheckSelect = useCallback(\n (checkId: string) => {\n onSelectCheck?.(checkId);\n },\n [onSelectCheck],\n );\n\n // Handle action\n const handleAction = useCallback(\n (checkId: string, actionType: CheckActionType) => {\n onAction?.(checkId, actionType);\n },\n [onAction],\n );\n\n // Handle description change\n const handleDescriptionChange = useCallback(\n (description?: string) => {\n if (selectedCheckId) {\n onDescriptionChange?.(selectedCheckId, description);\n }\n },\n [selectedCheckId, onDescriptionChange],\n );\n\n // Handle name change\n const handleNameChange = useCallback(\n (name: string) => {\n if (selectedCheckId) {\n onNameChange?.(selectedCheckId, name);\n }\n },\n [selectedCheckId, onNameChange],\n );\n\n // Loading state\n if (isLoading) {\n return (\n <Box\n className={className}\n sx={{\n width: \"100%\",\n height,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <CircularProgress />\n </Box>\n );\n }\n\n // Error state\n if (error) {\n return (\n <Box\n className={className}\n sx={{\n width: \"100%\",\n height,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Typography color=\"error\">{error}</Typography>\n </Box>\n );\n }\n\n // Empty state\n if (checks.length === 0) {\n return (\n <Box\n className={className}\n sx={{\n width: \"100%\",\n height,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <CheckEmptyState\n title=\"No checks yet\"\n description=\"Create your first check to start validating your data.\"\n actionText=\"Create Check\"\n onAction={onCreateCheck}\n />\n </Box>\n );\n }\n\n // Build detail props\n const detailTabs = selectedCheck ? getCheckTabs?.(selectedCheck) : undefined;\n const primaryActions = selectedCheck\n ? getPrimaryActions?.(selectedCheck)\n : undefined;\n const secondaryActions = selectedCheck\n ? getSecondaryActions?.(selectedCheck)\n : undefined;\n\n return (\n <Box className={className} sx={{ width: \"100%\", height }}>\n <SplitPane\n direction=\"horizontal\"\n sizes={[listPaneSize, 100 - listPaneSize]}\n minSizes={[minListSize, 200]}\n maxSizes={[maxListSize, Infinity]}\n >\n {/* Left pane: Check list */}\n <Box sx={{ height: \"100%\", overflow: \"auto\" }}>\n <CheckList\n checks={checkCards}\n selectedId={selectedCheckId}\n onCheckSelect={handleCheckSelect}\n onApprovalChange={onApprovalChange}\n onReorder={onReorder}\n disableApproval={disableApproval}\n disabledApprovalTooltip={disabledApprovalTooltip}\n title={listTitle}\n />\n </Box>\n\n {/* Right pane: Check detail */}\n <Box sx={{ height: \"100%\", overflow: \"auto\" }}>\n {selectedCheck ? (\n <CheckDetail\n checkId={selectedCheck.check_id}\n name={selectedCheck.name}\n type={selectedCheck.type}\n description={selectedCheck.description}\n isApproved={selectedCheck.is_checked}\n tabs={detailTabs}\n primaryActions={primaryActions}\n secondaryActions={secondaryActions}\n onAction={handleAction}\n onDescriptionChange={handleDescriptionChange}\n onNameChange={handleNameChange}\n />\n ) : (\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Typography color=\"text.secondary\">\n Select a check from the list to view details\n </Typography>\n </Box>\n )}\n </Box>\n </SplitPane>\n </Box>\n );\n}\n\n/**\n * Memoized ChecksView component for performance optimization.\n */\nexport const ChecksView = memo(ChecksViewComponent);\nChecksView.displayName = \"ChecksView\";\n","// @datarecce/ui - React component library for data validation interfaces\n\n// Version\nexport const VERSION = \"0.2.0\";\n\n// API utilities\nexport type {\n CatalogMetadata,\n GitInfo,\n LineageData,\n LineageDataFromMetadata,\n LineageDiffData,\n ManifestMetadata,\n ModelInfoResult,\n NodeColumnData,\n NodeData,\n PullRequestInfo,\n RecceInstanceInfo,\n RecceServerFlags,\n RunsAggregated,\n ServerInfoResult,\n ServerMode,\n SQLMeshInfo,\n StateMetadata,\n} from \"./api\";\nexport {\n aggregateRuns,\n cacheKeys,\n getLastKeepAliveTime,\n getModelInfo,\n getRecceInstanceInfo,\n getServerFlag,\n getServerInfo,\n markRelaunchHintCompleted,\n reorderChecks,\n resetKeepAliveState,\n sendKeepAlive,\n setKeepAliveCallback,\n useChecks,\n} from \"./api\";\n\n// Components - UI components for data validation interfaces\n// NOTE: LineageCanvasProps canonical in @datarecce/ui/types\nexport type {\n // High-level views (Layer 3)\n ChecksViewProps,\n // Data type icons\n ColumnTooltipInput,\n DataTypeIconProps,\n // UI primitives\n DiffTextProps,\n // Lineage\n LineageCanvasProps,\n LineageViewProps,\n LineageViewRef,\n SplitProps,\n SquareIconProps,\n TypeCategory,\n} from \"./components\";\nexport {\n // Data type icons\n buildColumnTooltip,\n // High-level views (Layer 3)\n ChecksView,\n classifyType,\n DataTypeIcon,\n // UI primitives\n DiffText,\n HSplit,\n // Lineage\n LineageCanvas,\n LineageView,\n SquareIcon,\n VSplit,\n} from \"./components\";\n\n// Result view factory and types\n// NOTE: canonical in @datarecce/ui/result\nexport type {\n CreatedResultViewProps,\n ResultViewConfig,\n ResultViewData,\n ResultViewProps,\n ResultViewRef,\n ResultViewTransformOptions,\n ScreenshotWrapperType,\n} from \"./components/result\";\nexport { createResultView } from \"./components/result\";\n// Constants - reusable constant values for UI components\nexport {\n type DisableTooltipMessageKey,\n DisableTooltipMessages,\n} from \"./constants\";\n// Contexts - React contexts for state management\n// NOTE: Context hooks canonical in @datarecce/ui/contexts\n// NOTE: Types canonical in @datarecce/ui/types\nexport type {\n // LineageViewContext types\n ActionMode,\n ActionState,\n // Action context\n AxiosQueryParams,\n // Lineage graph types (canonical: @datarecce/ui/types)\n EnvInfo,\n IdleTimeoutContextType,\n InstanceInfoType,\n LineageGraph,\n LineageGraphColumnNode,\n LineageGraphContextType,\n LineageGraphEdge,\n LineageGraphNode,\n LineageGraphNodes,\n LineageGraphProviderProps,\n LineageViewContextType,\n NodeAction,\n NodeColumnSetMap,\n RecceActionContextType,\n RecceActionOptions,\n RecceActionProviderProps,\n RecceFeatureMode,\n RecceFeatureToggles,\n SelectMode,\n SubmitRunTrackProps,\n} from \"./contexts\";\nexport {\n // Lineage graph utilities (canonical: @datarecce/ui/contexts)\n buildLineageGraph,\n COLUMN_HEIGHT,\n // Instance info\n defaultFeatureToggles,\n defaultInstanceInfo,\n getNeighborSet,\n // Idle timeout\n IdleTimeoutProvider,\n intersect,\n isLineageGraphColumnNode,\n isLineageGraphNode,\n // Lineage graph\n LineageGraphProvider,\n layoutWithDagre,\n // Action context\n RecceActionProvider,\n RecceInstanceInfoProvider,\n selectDownstream,\n selectUpstream,\n toReactFlowBasic,\n union,\n useIdleDetection,\n useIdleTimeout,\n useIdleTimeoutSafe,\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n useRecceInstanceInfo,\n useRecceServerFlag,\n useRunsAggregated,\n} from \"./contexts\";\n// Hooks - utility hooks for theming and data\nexport type {\n MultiNodesActionCallbacks,\n MultiNodesActionTracking,\n MultiNodesActionTrackProps,\n MultiNodesActionType,\n OSSCheckContext,\n OSSQueryContext,\n RecceActionOptions as OSSRecceActionOptions,\n ThemeColors,\n UseModelColumnsReturn,\n UseMultiNodesActionOptions,\n UseMultiNodesActionReturn,\n UseRunResult,\n} from \"./hooks\";\nexport {\n CheckContextAdapter,\n defaultSqlQuery,\n extractColumns,\n IGNORE_SCREENSHOT_CLASS,\n LineageGraphAdapter,\n QueryContextAdapter,\n RecceActionAdapter,\n RecceShareStateContextProvider,\n unionColumns,\n useCheckEvents,\n useCopyToClipboard,\n useCopyToClipboardButton,\n useCSVExport,\n useFeedbackCollectionToast,\n useImageDownloadModal,\n useIsDark,\n useModelColumns,\n useMultiNodesAction,\n useRecceCheckContext,\n useRecceQueryContext,\n useRecceShareStateContext,\n useRun,\n useThemeColors,\n} from \"./hooks\";\n// NOTE: SchemaDiffRow canonical in @datarecce/ui/types\nexport type {\n SchemaDataGridOptions,\n SchemaDataGridResult,\n SchemaDiffRow,\n SchemaRow,\n SingleEnvSchemaDataGridResult,\n} from \"./lib\";\n// Lib - library utilities including data grid generators\nexport {\n mergeColumns,\n toSchemaDataGrid,\n toSingleEnvDataGrid,\n} from \"./lib\";\n// Provider (main entry point) and Hooks - from providers module\n// NOTE: API hooks (`useApiClient`, `useApiConfig`, `useApiConfigOptional`) canonical in @datarecce/ui/contexts\nexport type {\n Check,\n CheckContextType,\n CheckProviderProps,\n NavigateOptions,\n QueryContextType,\n QueryProviderProps,\n QueryResult,\n RoutingConfig,\n RoutingContextValue,\n} from \"./providers\";\nexport {\n CheckProvider,\n QueryProvider,\n RecceProvider,\n type RecceProviderProps,\n useApiClient,\n useApiConfig,\n useApiConfigOptional,\n useAppLocation,\n useCheckContext,\n useQueryContext,\n useRecceTheme,\n useRouting,\n} from \"./providers\";\n// Theme - colors palette and MUI theme with CSS Variables\n// NOTE: `colors` and `getChartThemeColors` canonical in @datarecce/ui/theme\nexport type { Theme } from \"./theme\";\nexport { colors, theme } from \"./theme\";\n// Utils - utility functions for data manipulation and formatting\n// NOTE: `deltaPercentageString`, `formatTimestamp`, `formatTimeToNow`, `isSchemaChanged`\n// canonical in @datarecce/ui/utils\nexport {\n deltaPercentageString,\n extractSchemas,\n formatTimestamp,\n formatTimeToNow,\n isSchemaChanged,\n} from \"./utils\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"src-DPMlORPa.js","names":["ThemeProvider","BaseActionControl","BaseColumnLevelLineageControl","getIconForChangeStatus","GraphEdgeBase","Tooltip","RowCountDiffTag","Popover","TimelineEvent","RowCountDiffTag","getIconForChangeStatus","GraphEdge","getIconForChangeStatus","LineageViewContextMenu","useLineageViewContextMenu","useCopyToClipboard","EXPLORE_ACTION","EXPLORE_SOURCE","useMultiNodesActionBase","useBaseDialog","EXPLORE_ACTION","BaseLineageViewContextMenu","baseUseLineageViewContextMenu","BaseNodeSqlView","Tooltip","ResourceTypeTag","BaseSandboxView","RunResultPane","ResourceTypeTagBase","EXPLORE_ACTION","EXPLORE_SOURCE","BaseNodeView","BaseSetupConnectionBanner","LineageViewTopBarCore","useMultiNodesAction","useValueDiffAlertDialog","LineageViewTopBar","SetupConnectionBanner","NodeView","useTheme","Tooltip","formatTimestamp","saveAs","Dialog","MuiTabs","DisplayModeToggle","RecceVersionBadge","TopBar","NavBar","RunResultPane","LineageView","getIconForChangeStatus","LineageDiffView","CheckTimeline","CheckEmptyStateUI","CheckEmptyState","CheckList","CheckDetail","ResourceTypeTag"],"sources":["../src/providers/contexts/RoutingContext.tsx","../src/providers/RecceProvider.tsx","../src/components/lineage/controls/ActionControl.tsx","../src/components/lineage/controls/ColumnLevelLineageControl.tsx","../src/components/lineage/ActionControlOss.tsx","../src/components/lineage/ColumnLevelLineageControlOss.tsx","../src/components/lineage/GraphColumnNodeOss.tsx","../src/components/lineage/edges/GraphEdge.tsx","../src/components/lineage/GraphEdgeOss.tsx","../src/components/lineage/nodes/ActionTag.tsx","../src/components/check/timeline/TimelineEventOss.tsx","../src/components/check/timeline/CheckTimelineOss.tsx","../src/components/ui/SquareIcon.tsx","../src/components/query/QueryForm.tsx","../src/components/query/SetupConnectionGuide.tsx","../src/components/query/QueryPageOss.tsx","../src/components/lineage/GraphNodeOss.tsx","../src/components/lineage/config/nodeTypes.ts","../src/components/lineage/contextmenu/LineageViewContextMenu.tsx","../src/components/lineage/hooks/useLineageCopyToClipboard.ts","../src/components/lineage/hooks/useNavToCheck.ts","../src/components/lineage/hooks/useResizeObserver.ts","../src/components/lineage/hooks/useTrackLineageRender.ts","../src/hooks/useMultiNodesActionOss.ts","../src/hooks/useValueDiffAlertDialogOss.tsx","../src/components/notifications/LineageViewNotification.tsx","../src/components/lineage/LineageViewContextMenuOss.tsx","../src/components/lineage/lineage.ts","../src/components/schema/SchemaView.tsx","../src/components/lineage/NodeSqlView.tsx","../src/components/lineage/NodeSqlViewOss.tsx","../src/components/lineage/tags/tagStyles.ts","../src/components/lineage/tags/ResourceTypeTag.tsx","../src/components/lineage/NodeTag.tsx","../src/components/lineage/NodeView.tsx","../src/components/lineage/SandboxView.tsx","../src/components/lineage/SandboxViewOss.tsx","../src/components/lineage/NodeViewOss.tsx","../src/components/lineage/patchLineageDiffFromCll.ts","../src/components/lineage/SetupConnectionBanner.tsx","../src/components/lineage/SetupConnectionBannerOss.tsx","../src/components/lineage/SingleEnvironmentQueryView.tsx","../src/components/lineage/states/LineageViewStates.tsx","../src/components/shared/HistoryToggle.tsx","../src/components/lineage/topbar/LineageViewTopBar.tsx","../src/components/lineage/topbar/LineageViewTopBarOss.tsx","../src/components/lineage/LineageViewOss.tsx","../src/components/lineage/LineagePageOss.tsx","../src/components/app/AvatarDropdown.tsx","../src/components/app/DisplayModeToggleOss.tsx","../src/components/app/EnvInfo.tsx","../src/components/app/Filename.tsx","../src/components/app/PythonDeprecationModal.tsx","../src/components/app/StateExporter.tsx","../src/components/app/StateSharing.tsx","../src/components/app/StateSynchronizer.tsx","../src/components/app/NavBarOss.tsx","../src/components/timeout/IdleTimeoutBadge.tsx","../src/components/app/RecceVersionBadgeOss.tsx","../src/components/app/TopBarOss.tsx","../src/components/app/MainLayout.tsx","../src/components/app/SetupConnectionPopover.tsx","../src/components/app/StateImporter.tsx","../src/components/check/LineageDiffViewOss.tsx","../src/components/check/SchemaDiffView.tsx","../src/components/check/CheckDetailOss.tsx","../src/components/check/CheckEmptyStateOss.tsx","../src/components/check/CheckListOss.tsx","../src/components/check/CheckPageContentOss.tsx","../src/components/check/CheckPageLoadingOss.tsx","../src/components/summary/types.ts","../src/components/summary/utils.ts","../src/components/summary/ChangeSummary.tsx","../src/components/summary/SchemaSummary.tsx","../src/components/views/ChecksView.tsx","../src/index.ts"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n} from \"react\";\n\n/**\n * Navigation options for route changes\n */\nexport interface NavigateOptions {\n /** Replace current history entry instead of pushing */\n replace?: boolean;\n /** Scroll to top after navigation (default: true) */\n scroll?: boolean;\n}\n\n/**\n * Configuration for the routing provider\n */\nexport interface RoutingConfig {\n /** Base path prefix for all routes */\n basePath?: string;\n\n /**\n * Current pathname (provided by consumer's router)\n * If not provided, defaults to empty string\n */\n pathname?: string;\n\n /**\n * Navigation handler (provided by consumer's router)\n * Called when components need to navigate programmatically\n *\n * @example\n * ```tsx\n * // Next.js App Router\n * const router = useRouter();\n * <RecceProvider\n * routing={{\n * pathname: usePathname(),\n * onNavigate: (path, options) => {\n * options?.replace ? router.replace(path) : router.push(path);\n * }\n * }}\n * />\n * ```\n */\n onNavigate?: (path: string, options?: NavigateOptions) => void;\n}\n\n/**\n * Routing context value available to consumers\n */\nexport interface RoutingContextValue {\n /** Base path prefix */\n basePath: string;\n /** Build a full path with base path prefix */\n buildPath: (path: string) => string;\n /** Current pathname (if provided by consumer) */\n pathname: string;\n /** Navigate to a path */\n navigate: (path: string, options?: NavigateOptions) => void;\n}\n\nconst RoutingContext = createContext<RoutingContextValue | null>(null);\nRoutingContext.displayName = \"RecceRoutingContext\";\n\n/**\n * Hook to access routing context\n *\n * @returns Routing context with path utilities and navigation\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { pathname, navigate, buildPath } = useRouting();\n *\n * return (\n * <button onClick={() => navigate(buildPath('/checks'))}>\n * Go to Checks\n * </button>\n * );\n * }\n * ```\n */\nexport function useRouting(): RoutingContextValue {\n const context = useContext(RoutingContext);\n if (!context) {\n // Return sensible defaults if not within provider\n return {\n basePath: \"\",\n buildPath: (path: string) => path,\n pathname: \"\",\n navigate: (path: string) => {\n // Fallback: use window.location for navigation\n if (typeof window !== \"undefined\") {\n window.location.href = path;\n }\n },\n };\n }\n return context;\n}\n\n/**\n * Hook compatible with useAppLocation API\n *\n * Returns a tuple of [pathname, setLocation] for easy migration from\n * the OSS useAppLocation hook.\n *\n * @returns [pathname, navigate] tuple\n *\n * @example\n * ```tsx\n * const [location, setLocation] = useAppLocation();\n * setLocation('/checks?id=123');\n * setLocation('/checks', { replace: true });\n * ```\n */\nexport function useAppLocation(): [\n string,\n (path: string, options?: NavigateOptions) => void,\n] {\n const { pathname, navigate } = useRouting();\n return [pathname, navigate];\n}\n\ninterface RoutingProviderProps {\n children: ReactNode;\n config?: RoutingConfig;\n}\n\nexport function RoutingProvider({ children, config }: RoutingProviderProps) {\n const basePath = config?.basePath ?? \"\";\n const pathname = config?.pathname ?? \"\";\n const onNavigate = config?.onNavigate;\n\n const buildPath = useCallback(\n (path: string) => {\n if (!basePath) return path;\n return `${basePath}${path.startsWith(\"/\") ? path : `/${path}`}`;\n },\n [basePath],\n );\n\n const navigate = useCallback(\n (path: string, options?: NavigateOptions) => {\n if (onNavigate) {\n onNavigate(path, options);\n } else if (typeof window !== \"undefined\") {\n // Fallback: use window.location\n if (options?.replace) {\n window.history.replaceState(null, \"\", path);\n window.dispatchEvent(new PopStateEvent(\"popstate\"));\n } else {\n window.location.href = path;\n }\n }\n },\n [onNavigate],\n );\n\n const value = useMemo<RoutingContextValue>(\n () => ({\n basePath,\n buildPath,\n pathname,\n navigate,\n }),\n [basePath, buildPath, pathname, navigate],\n );\n\n return (\n <RoutingContext.Provider value={value}>{children}</RoutingContext.Provider>\n );\n}\n","\"use client\";\n\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport type { AxiosInstance } from \"axios\";\nimport { type ReactNode, useMemo } from \"react\";\n\nimport type { RecceActionProviderProps } from \"../contexts/action\";\nimport { RecceActionProvider } from \"../contexts/action\";\nimport { IdleTimeoutProvider } from \"../contexts/idle\";\nimport { RecceInstanceInfoProvider } from \"../contexts/instance\";\nimport type { LineageGraphProviderProps } from \"../contexts/lineage\";\nimport { LineageGraphProvider } from \"../contexts/lineage\";\nimport { ApiProvider } from \"./contexts/ApiContext\";\nimport type { Check, CheckProviderProps } from \"./contexts/CheckContext\";\nimport { CheckProvider } from \"./contexts/CheckContext\";\nimport type { QueryProviderProps, QueryResult } from \"./contexts/QueryContext\";\nimport { QueryProvider } from \"./contexts/QueryContext\";\nimport { RoutingProvider } from \"./contexts/RoutingContext\";\nimport { ThemeProvider } from \"./contexts/ThemeContext\";\n\n/**\n * Theme mode selection for RecceProvider.\n */\ntype ThemeMode = \"light\" | \"dark\" | \"system\";\n\n/**\n * Props for {@link RecceProvider}.\n */\ninterface RecceProviderProps {\n children: ReactNode;\n\n /**\n * API configuration - simple config OR custom client.\n */\n api:\n | {\n baseUrl: string;\n headers?: Record<string, string>;\n timeout?: number;\n }\n | {\n client: AxiosInstance;\n };\n\n /**\n * Theme mode.\n *\n * @defaultValue \"system\"\n */\n theme?: ThemeMode;\n\n /**\n * Routing configuration\n *\n * @example\n * ```tsx\n * // With Next.js App Router\n * const router = useRouter();\n * const pathname = usePathname();\n *\n * <RecceProvider\n * routing={{\n * basePath: '/app',\n * pathname,\n * onNavigate: (path, options) => {\n * options?.replace ? router.replace(path) : router.push(path);\n * }\n * }}\n * />\n * ```\n */\n routing?: {\n basePath?: string;\n pathname?: string;\n onNavigate?: (\n path: string,\n options?: { replace?: boolean; scroll?: boolean },\n ) => void;\n };\n\n /**\n * TanStack Query client configuration.\n */\n queryClient?: {\n staleTime?: number;\n gcTime?: number;\n };\n\n /**\n * Run action configuration\n * Props for RecceActionProvider - manages run execution and result display\n */\n runActions?: {\n /** Handler called when a run action is requested */\n onRunAction?: RecceActionProviderProps[\"onRunAction\"];\n /** Handler called when a run result should be shown */\n onShowRunId?: RecceActionProviderProps[\"onShowRunId\"];\n /** Initial run ID to display */\n initialRunId?: string;\n /** Initial state for history panel */\n initialHistoryOpen?: boolean;\n };\n\n /**\n * Lineage graph configuration\n * Props for LineageGraphProvider - manages lineage visualization\n */\n lineage?: {\n /** The processed lineage graph data */\n lineageGraph?: LineageGraphProviderProps[\"lineageGraph\"];\n /** Environment information (git, dbt, sqlmesh metadata) */\n envInfo?: LineageGraphProviderProps[\"envInfo\"];\n /** Whether in review mode (read-only checks) */\n reviewMode?: boolean;\n /** Whether in cloud mode (recce cloud) */\n cloudMode?: boolean;\n /** Whether in file mode (loading from file) */\n fileMode?: boolean;\n /** The state file name if in file mode */\n fileName?: string;\n /** Whether this is the demo site */\n isDemoSite?: boolean;\n /** Whether running in GitHub Codespace */\n isCodespace?: boolean;\n /** Loading state */\n isLoading?: boolean;\n /** Error message if loading failed */\n error?: string;\n /** Supported task types from server */\n supportTasks?: Record<string, boolean>;\n /** Callback to refetch the lineage graph */\n onRefetchLineageGraph?: () => void;\n /** Pre-aggregated run results by model */\n runsAggregated?: LineageGraphProviderProps[\"runsAggregated\"];\n /** Callback to refetch aggregated runs */\n onRefetchRunsAggregated?: () => void;\n };\n\n /**\n * Check management configuration\n * Props for CheckProvider - manages check CRUD operations\n */\n checks?: {\n /** List of checks */\n checks?: Check[];\n /** Loading state */\n isLoading?: boolean;\n /** Error message */\n error?: string;\n /** Currently selected check ID */\n selectedCheckId?: string;\n /** Handler for check selection */\n onSelectCheck?: CheckProviderProps[\"onSelectCheck\"];\n /** Handler for check creation */\n onCreateCheck?: CheckProviderProps[\"onCreateCheck\"];\n /** Handler for check updates */\n onUpdateCheck?: CheckProviderProps[\"onUpdateCheck\"];\n /** Handler for check deletion */\n onDeleteCheck?: CheckProviderProps[\"onDeleteCheck\"];\n /** Handler for check reordering */\n onReorderChecks?: CheckProviderProps[\"onReorderChecks\"];\n /** Callback to refetch checks */\n refetchChecks?: () => void;\n };\n\n /**\n * Query editor configuration\n * Props for QueryProvider - manages SQL query execution\n */\n query?: {\n /** Current SQL query */\n sql?: string;\n /** Whether query is executing */\n isExecuting?: boolean;\n /** Error message */\n error?: string;\n /** Query result for base environment */\n baseResult?: QueryResult;\n /** Query result for current environment */\n currentResult?: QueryResult;\n /** Handler for SQL changes */\n onSqlChange?: QueryProviderProps[\"onSqlChange\"];\n /** Handler for query execution */\n onExecute?: QueryProviderProps[\"onExecute\"];\n /** Handler for query cancellation */\n onCancel?: QueryProviderProps[\"onCancel\"];\n };\n\n /**\n * Feature flags - control which contexts are enabled\n * Set to false to skip a context entirely (reduces overhead if unused)\n */\n features?: {\n /** Enable RecceInstanceInfoProvider (instance info and feature toggles) */\n enableInstance?: boolean;\n /** Enable IdleTimeoutProvider (session timeout and keep-alive) */\n enableIdleTimeout?: boolean;\n /** Enable LineageGraphProvider (lineage visualization) */\n enableLineage?: boolean;\n /** Enable RecceActionProvider (run execution) */\n enableRunActions?: boolean;\n /** Enable CheckProvider (check management) */\n enableChecks?: boolean;\n /** Enable QueryProvider (SQL query editor) */\n enableQuery?: boolean;\n };\n}\n\n// Create a default query client\nconst createDefaultQueryClient = (\n options?: RecceProviderProps[\"queryClient\"],\n) =>\n new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: options?.staleTime ?? 1000 * 60, // 1 minute\n gcTime: options?.gcTime ?? 1000 * 60 * 5, // 5 minutes\n retry: 1,\n refetchOnWindowFocus: false,\n },\n },\n });\n\n/**\n * RecceProvider - The single entry point for @datarecce/ui\n *\n * This provider orchestrates ALL Recce contexts in the correct order.\n * It follows a props-driven design - pass your data and callbacks, no internal fetching.\n *\n * **Architecture:**\n * - Foundation layer: QueryClient → API → Theme → Routing\n * - Data layer: Instance → Idle → Lineage → RunActions\n * - UI layer: Checks → Query\n *\n * **Feature Flags:**\n * Use `features` prop to disable unused contexts and reduce overhead.\n *\n * @example\n * ```tsx\n * // Minimal setup (only API + Theme)\n * <RecceProvider api={{ baseUrl: \"/api\" }}>\n * <MyApp />\n * </RecceProvider>\n *\n * // Full setup with all features\n * <RecceProvider\n * api={{ baseUrl: \"/api\" }}\n * theme=\"dark\"\n * lineage={{\n * lineageGraph: data.lineageGraph,\n * envInfo: data.envInfo,\n * onRefetchLineageGraph: refetch,\n * }}\n * runActions={{\n * onRunAction: handleRunAction,\n * onShowRunId: handleShowRun,\n * }}\n * checks={{\n * checks: checksData,\n * onCreateCheck: createCheck,\n * onUpdateCheck: updateCheck,\n * }}\n * features={{\n * enableLineage: true,\n * enableRunActions: true,\n * enableChecks: true,\n * }}\n * >\n * <MyApp />\n * </RecceProvider>\n * ```\n */\nexport function RecceProvider({\n children,\n api,\n theme = \"system\",\n routing,\n queryClient: queryClientConfig,\n runActions,\n lineage,\n checks,\n query,\n features = {},\n}: RecceProviderProps) {\n // Extract primitive values to stabilize dependency and prevent unnecessary QueryClient recreation\n const staleTime = queryClientConfig?.staleTime;\n const gcTime = queryClientConfig?.gcTime;\n\n const queryClient = useMemo(\n () =>\n createDefaultQueryClient({\n staleTime,\n gcTime,\n }),\n [staleTime, gcTime],\n );\n\n // Default all features to true unless explicitly disabled\n const {\n enableInstance = true,\n enableIdleTimeout = true,\n enableLineage = true,\n enableRunActions = true,\n enableChecks = true,\n enableQuery = true,\n } = features;\n\n // Foundation layer: QueryClient → API → Theme → Routing\n let tree = children;\n\n // Wrap with Routing (innermost of foundation layer)\n tree = <RoutingProvider config={routing}>{tree}</RoutingProvider>;\n\n // Wrap with Theme\n tree = <ThemeProvider defaultMode={theme}>{tree}</ThemeProvider>;\n\n // Wrap with API\n tree = <ApiProvider config={api}>{tree}</ApiProvider>;\n\n // Wrap with QueryClient (outermost of foundation layer)\n tree = <QueryClientProvider client={queryClient}>{tree}</QueryClientProvider>;\n\n // Data layer: Instance → Idle → Lineage → RunActions\n // These are wrapped in reverse order (bottom-up)\n\n // UI layer: Query (innermost)\n if (enableQuery) {\n tree = (\n <QueryProvider\n sql={query?.sql}\n isExecuting={query?.isExecuting}\n error={query?.error}\n baseResult={query?.baseResult}\n currentResult={query?.currentResult}\n onSqlChange={query?.onSqlChange}\n onExecute={query?.onExecute}\n onCancel={query?.onCancel}\n >\n {tree}\n </QueryProvider>\n );\n }\n\n // UI layer: Checks\n if (enableChecks) {\n tree = (\n <CheckProvider\n checks={checks?.checks}\n isLoading={checks?.isLoading}\n error={checks?.error}\n selectedCheckId={checks?.selectedCheckId}\n onSelectCheck={checks?.onSelectCheck}\n onCreateCheck={checks?.onCreateCheck}\n onUpdateCheck={checks?.onUpdateCheck}\n onDeleteCheck={checks?.onDeleteCheck}\n onReorderChecks={checks?.onReorderChecks}\n refetchChecks={checks?.refetchChecks}\n >\n {tree}\n </CheckProvider>\n );\n }\n\n // Data layer: RunActions\n if (enableRunActions) {\n tree = (\n <RecceActionProvider\n onRunAction={runActions?.onRunAction}\n onShowRunId={runActions?.onShowRunId}\n initialRunId={runActions?.initialRunId}\n initialHistoryOpen={runActions?.initialHistoryOpen}\n >\n {tree}\n </RecceActionProvider>\n );\n }\n\n // Data layer: Lineage\n if (enableLineage) {\n tree = (\n <LineageGraphProvider\n lineageGraph={lineage?.lineageGraph}\n envInfo={lineage?.envInfo}\n reviewMode={lineage?.reviewMode}\n cloudMode={lineage?.cloudMode}\n fileMode={lineage?.fileMode}\n fileName={lineage?.fileName}\n isDemoSite={lineage?.isDemoSite}\n isCodespace={lineage?.isCodespace}\n isLoading={lineage?.isLoading}\n error={lineage?.error}\n supportTasks={lineage?.supportTasks}\n onRefetchLineageGraph={lineage?.onRefetchLineageGraph}\n runsAggregated={lineage?.runsAggregated}\n onRefetchRunsAggregated={lineage?.onRefetchRunsAggregated}\n >\n {tree}\n </LineageGraphProvider>\n );\n }\n\n // Data layer: Idle (depends on Instance for idle timeout config)\n if (enableIdleTimeout) {\n tree = <IdleTimeoutProvider>{tree}</IdleTimeoutProvider>;\n }\n\n // Data layer: Instance (outermost, provides feature toggles for all others)\n if (enableInstance) {\n tree = <RecceInstanceInfoProvider>{tree}</RecceInstanceInfoProvider>;\n }\n\n return tree;\n}\n\nexport type { RecceProviderProps };\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Divider from \"@mui/material/Divider\";\nimport Stack from \"@mui/material/Stack\";\nimport type { ActionState } from \"../../../contexts/lineage/types\";\n\n/**\n * Props for the ActionControl component.\n */\nexport interface ActionControlProps {\n /**\n * The current action state containing progress, status, and mode information\n */\n actionState: ActionState;\n\n /**\n * Callback invoked when the user clicks the Cancel button\n */\n onCancel: () => void;\n\n /**\n * Callback invoked when the user clicks the Close button (after action completes)\n */\n onClose: () => void;\n}\n\n/**\n * ActionControl Component\n *\n * Displays progress information and control buttons for batch operations\n * on lineage graph nodes. Shows different UI based on action status:\n * - Running/Canceling: Shows progress with Cancel button\n * - Completed/Canceled: Shows progress with Close button\n *\n * This is a props-based component that doesn't use context directly.\n * For context-aware usage, wrap this component with context consumers.\n *\n * @example Basic usage\n * ```tsx\n * import { ActionControl } from '@datarecce/ui/components/lineage';\n *\n * function MyComponent() {\n * const [actionState, setActionState] = useState<ActionState>({\n * mode: 'per_node',\n * status: 'running',\n * completed: 5,\n * total: 10,\n * actions: {},\n * });\n *\n * return (\n * <ActionControl\n * actionState={actionState}\n * onCancel={() => setActionState(prev => ({ ...prev, status: 'canceling' }))}\n * onClose={() => console.log('closed')}\n * />\n * );\n * }\n * ```\n *\n * @example With context wrapper (OSS pattern)\n * ```tsx\n * import { ActionControl as BaseActionControl } from '@datarecce/ui/components/lineage';\n * import { useLineageViewContext } from './LineageViewContext';\n *\n * function ActionControl({ onClose }: { onClose: () => void }) {\n * const { cancel, actionState } = useLineageViewContext();\n * return <BaseActionControl actionState={actionState} onCancel={cancel} onClose={onClose} />;\n * }\n * ```\n */\nexport function ActionControl({\n actionState,\n onCancel,\n onClose,\n}: ActionControlProps) {\n /**\n * Calculate the progress message based on action mode and status\n */\n const getProgressMessage = () => {\n if (actionState.mode === \"per_node\") {\n return `${actionState.completed} / ${actionState.total}`;\n }\n // multi_nodes mode\n if (actionState.currentRun?.progress?.percentage) {\n return `${actionState.currentRun.progress.percentage * 100}%`;\n }\n if (actionState.status === \"completed\") {\n return \"100%\";\n }\n return \"0%\";\n };\n\n const isActionInProgress =\n actionState.status === \"running\" || actionState.status === \"canceling\";\n\n return (\n <Box sx={{ bgcolor: \"background.paper\", borderRadius: 1, boxShadow: 6 }}>\n <Stack\n direction=\"row\"\n divider={<Divider orientation=\"vertical\" flexItem />}\n spacing={2}\n sx={{ p: \"5px 15px\", mt: 2 }}\n >\n <Box sx={{ fontSize: \"10pt\" }}>\n Progress: {getProgressMessage()}{\" \"}\n {actionState.status === \"canceled\" ? \" (canceled)\" : \"\"}\n </Box>\n\n {isActionInProgress ? (\n <Button\n size=\"small\"\n variant=\"outlined\"\n onClick={onCancel}\n disabled={actionState.status === \"canceling\"}\n >\n {actionState.status === \"canceling\" ? \"Canceling\" : \"Cancel\"}\n </Button>\n ) : (\n <Stack direction=\"row\">\n <Button size=\"small\" variant=\"outlined\" onClick={onClose}>\n Close\n </Button>\n </Stack>\n )}\n </Stack>\n </Box>\n );\n}\n","\"use client\";\n\n/**\n * @file ColumnLevelLineageControl.tsx\n * @description Control panel for Column-Level Lineage (CLL) and Impact Radius features.\n *\n * This component provides:\n * - Impact Radius button to analyze downstream impact of changes\n * - Mode message panel showing current CLL context\n * - Loading and error states for CLL operations\n * - Reset button to exit CLL mode\n *\n * @example\n * ```tsx\n * <ColumnLevelLineageControl\n * action={cllMutation}\n * interactive={true}\n * viewOptions={viewOptions}\n * lineageGraph={lineageGraph}\n * singleEnvMode={false}\n * onShowCll={(params) => showColumnLevelLineage(params)}\n * onResetCll={() => resetColumnLevelLineage()}\n * onCenterNode={(nodeId) => centerNode(nodeId)}\n * />\n * ```\n */\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Link from \"@mui/material/Link\";\nimport MuiPopover from \"@mui/material/Popover\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport type { UseMutationResult } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport { FaRegDotCircle } from \"react-icons/fa\";\nimport { PiInfo, PiX } from \"react-icons/pi\";\nimport type { CllInput, ColumnLineageData } from \"../../../api/cll\";\nimport type { LineageDiffViewOptions } from \"../../../api/lineagecheck\";\nimport type { LineageGraph } from \"../../../contexts/lineage/types\";\nimport { useIsDark } from \"../../../hooks/useIsDark\";\n\n/**\n * Props for the ColumnLevelLineageControl component.\n */\nexport interface ColumnLevelLineageControlProps {\n /**\n * Mutation result for CLL operations.\n * Used to track loading, error, and success states.\n */\n action: UseMutationResult<ColumnLineageData, Error, CllInput>;\n\n /**\n * Whether the view is interactive (allows user actions).\n * When false, buttons are disabled.\n */\n interactive: boolean;\n\n /**\n * Current view options including column_level_lineage settings.\n */\n viewOptions: LineageDiffViewOptions;\n\n /**\n * The lineage graph data containing nodes and catalog metadata.\n * Used to determine node names and catalog availability.\n */\n lineageGraph?: LineageGraph;\n\n /**\n * Whether in single environment mode.\n * When true, the Impact Radius button is hidden.\n */\n singleEnvMode?: boolean;\n\n /**\n * Whether change analysis (Impact Radius) is available.\n * When false, the Impact Radius button is disabled with a tooltip.\n */\n changeAnalysisAvailable?: boolean;\n\n /**\n * Callback to show column-level lineage.\n * Called with CLL parameters when Impact Radius button is clicked.\n */\n onShowCll: (params?: CllInput) => Promise<void>;\n\n /**\n * Callback to reset column-level lineage view.\n */\n onResetCll: () => Promise<void>;\n\n /**\n * Callback to center the view on a specific node.\n */\n onCenterNode: (nodeId: string) => void;\n\n /**\n * Callback to set change analysis mode on/off.\n */\n setChangeAnalysisMode?: (active: boolean) => void;\n}\n\n/**\n * Internal component to display mode-specific messages.\n * Shows what CLL context is currently active.\n */\nconst ModeMessage = ({\n lineageGraph,\n cllInput,\n onCenterNode,\n}: {\n lineageGraph?: LineageGraph;\n cllInput?: CllInput;\n onCenterNode: (nodeId: string) => void;\n}) => {\n const isDark = useIsDark();\n\n const codeBlockSx = {\n cursor: \"pointer\",\n fontFamily: \"monospace\",\n bgcolor: isDark ? \"grey.700\" : \"grey.100\",\n px: 0.5,\n borderRadius: 0.5,\n };\n\n if (!lineageGraph) {\n return <></>;\n }\n\n if (!cllInput) {\n return <>Default View</>;\n }\n\n if (cllInput.node_id === undefined) {\n return (\n <Typography component=\"span\">\n Impact Radius for All Changed Models\n </Typography>\n );\n }\n\n const nodeName =\n cllInput.node_id in lineageGraph.nodes\n ? lineageGraph.nodes[cllInput.node_id].data.name\n : cllInput.node_id;\n\n if (!cllInput.column) {\n const nodeId = cllInput.node_id;\n\n return (\n <>\n <Typography component=\"span\" sx={{ mr: \"5px\" }}>\n Impact Radius for\n </Typography>\n <Box\n component=\"code\"\n onClick={() => {\n onCenterNode(nodeId);\n }}\n sx={codeBlockSx}\n >\n {nodeName}\n </Box>\n </>\n );\n }\n const nodeId = `${cllInput.node_id}_${cllInput.column}`;\n return (\n <>\n <Typography component=\"span\" sx={{ mr: \"5px\" }}>\n Column Lineage for{\" \"}\n </Typography>\n <Box\n component=\"code\"\n onClick={() => {\n onCenterNode(nodeId);\n }}\n sx={codeBlockSx}\n >\n {nodeName}.{cllInput.column}\n </Box>\n </>\n );\n};\n\n/**\n * Control panel for Column-Level Lineage (CLL) and Impact Radius features.\n *\n * Features:\n * - **Impact Radius Button**: Triggers analysis of downstream impact for changed models\n * - **Mode Message Panel**: Shows current CLL context (node, column, or all changes)\n * - **Loading State**: Displays spinner during CLL operations\n * - **Error State**: Shows error indicator with tooltip for failure details\n * - **Reset Button**: Allows exiting CLL mode to return to default view\n *\n * The component is designed for dependency injection, receiving all callbacks\n * and state as props to enable reuse across different contexts.\n */\nexport const ColumnLevelLineageControl = ({\n action,\n interactive,\n viewOptions,\n lineageGraph,\n singleEnvMode = false,\n changeAnalysisAvailable = true,\n onShowCll,\n onResetCll,\n onCenterNode,\n setChangeAnalysisMode,\n}: ColumnLevelLineageControlProps) => {\n const cllInput = viewOptions.column_level_lineage;\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n\n return (\n <Stack direction=\"row\" spacing=\"5px\">\n {!singleEnvMode && (\n <Box sx={{ borderRadius: 1, boxShadow: 3 }}>\n <MuiTooltip\n enterDelay={50}\n title={\n !changeAnalysisAvailable\n ? \"Requires warehouse connection\"\n : noCatalogCurrent\n ? \"Please provide catalog.json to enable Impact Radius\"\n : \"\"\n }\n placement=\"top\"\n >\n <span>\n <Button\n size=\"small\"\n variant=\"outlined\"\n color=\"neutral\"\n sx={{\n whiteSpace: \"nowrap\",\n display: \"inline-flex\",\n bgcolor: \"background.paper\",\n }}\n disabled={\n !interactive || noCatalogCurrent || !changeAnalysisAvailable\n }\n startIcon={<FaRegDotCircle />}\n onClick={() => {\n setChangeAnalysisMode?.(true);\n void onShowCll({\n no_upstream: true,\n change_analysis: true,\n });\n }}\n >\n Impact Radius\n </Button>\n </span>\n </MuiTooltip>\n </Box>\n )}\n {cllInput && (\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{\n borderRadius: 1,\n boxShadow: 3,\n border: \"1px solid\",\n borderColor: \"divider\",\n bgcolor: \"background.paper\",\n fontSize: \"0.8rem\",\n p: \"0 0.625rem\",\n }}\n >\n <ModeMessage\n lineageGraph={lineageGraph}\n cllInput={cllInput}\n onCenterNode={onCenterNode}\n />\n {action.isError && (\n <MuiTooltip\n title={`Error: ${action.error.message}`}\n placement=\"bottom\"\n >\n <Typography\n component=\"span\"\n sx={{\n color: \"error.main\",\n ml: \"2px\",\n display: \"inline-flex\",\n alignItems: \"center\",\n }}\n >\n <Box\n component={PiInfo}\n sx={{ color: \"error.main\", fontSize: \"14px\" }}\n />\n </Typography>\n </MuiTooltip>\n )}\n\n {action.isPending ? (\n <CircularProgress size={12} sx={{ ml: \"2px\" }} />\n ) : (\n <IconButton\n size=\"small\"\n sx={{ ml: \"2px\" }}\n aria-label=\"Reset Column Level Lineage\"\n onClick={() => {\n void onResetCll();\n }}\n >\n <PiX size=\"10px\" />\n </IconButton>\n )}\n </Stack>\n )}\n </Stack>\n );\n};\n","\"use client\";\n\n/**\n * @file ActionControlOss.tsx\n * @description OSS wrapper around @datarecce/ui ActionControl\n */\n\nimport { useLineageViewContextSafe } from \"../../contexts\";\nimport { ActionControl as BaseActionControl } from \"./controls\";\n\n/**\n * Props for the ActionControl wrapper component.\n */\nexport interface ActionControlOssProps {\n /**\n * Callback invoked when the user clicks the Close button (after action completes)\n */\n onClose: () => void;\n}\n\n/**\n * ActionControl Component (Wrapper)\n *\n * Wraps the @datarecce/ui ActionControl component with context from\n * LineageViewContext.\n */\nexport function ActionControlOss({ onClose }: ActionControlOssProps) {\n const { cancel, actionState } = useLineageViewContextSafe();\n\n return (\n <BaseActionControl\n actionState={actionState}\n onCancel={cancel}\n onClose={onClose}\n />\n );\n}\n","\"use client\";\n\n/**\n * @file ColumnLevelLineageControlOss.tsx\n * @description OSS wrapper around @datarecce/ui ColumnLevelLineageControl\n *\n * This wrapper:\n * 1. Connects the @datarecce/ui component to specific contexts\n * 2. Provides LineageViewContext callbacks as props\n * 3. Fetches server flags for single_env_onboarding\n */\n\nimport type { UseMutationResult } from \"@tanstack/react-query\";\nimport type { CllInput, ColumnLineageData } from \"../../api\";\nimport {\n useLineageGraphContext,\n useLineageViewContextSafe,\n useRecceServerFlag,\n} from \"../../contexts\";\nimport { ColumnLevelLineageControl as BaseColumnLevelLineageControl } from \"./controls\";\n\n/**\n * OSS wrapper for ColumnLevelLineageControl.\n *\n * Connects the @datarecce/ui component to:\n * - LineageViewContext for CLL operations\n * - LineageGraphContext for graph data\n * - Server flags for environment mode\n */\nexport const ColumnLevelLineageControlOss = ({\n action,\n}: {\n action: UseMutationResult<ColumnLineageData, Error, CllInput>;\n}) => {\n const {\n showColumnLevelLineage,\n resetColumnLevelLineage,\n interactive,\n viewOptions,\n centerNode,\n setChangeAnalysisMode,\n } = useLineageViewContextSafe();\n const { data: flagData } = useRecceServerFlag();\n const singleEnv = flagData?.single_env_onboarding ?? false;\n const { lineageGraph, isActionAvailable } = useLineageGraphContext();\n\n return (\n <BaseColumnLevelLineageControl\n action={action}\n interactive={interactive}\n viewOptions={viewOptions}\n lineageGraph={lineageGraph}\n singleEnvMode={singleEnv}\n changeAnalysisAvailable={isActionAvailable(\"change_analysis\")}\n onShowCll={showColumnLevelLineage}\n onResetCll={() => resetColumnLevelLineage()}\n onCenterNode={centerNode}\n setChangeAnalysisMode={setChangeAnalysisMode}\n />\n );\n};\n","\"use client\";\n\n/**\n * @file GraphColumnNodeOss.tsx\n * @description OSS wrapper for UI package LineageColumnNode component\n *\n * This component wraps the @datarecce/ui LineageColumnNode with OSS-specific\n * context integration. It extracts state from LineageViewContext and passes\n * it as props to the presentation component.\n *\n * Migration: Phase 3 of lineage component migration plan\n */\n\nimport type { NodeProps } from \"@xyflow/react\";\nimport { useStore } from \"@xyflow/react\";\nimport { type MouseEvent, memo } from \"react\";\nimport type { LineageGraphColumnNode } from \"../..\";\nimport { useLineageViewContextSafe } from \"../../contexts\";\nimport { useThemeColors } from \"../../hooks\";\nimport { LineageColumnNode, type LineageColumnNodeData } from \"./columns\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport type GraphColumnNodeProps = NodeProps<LineageGraphColumnNode>;\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * GraphColumnNode - OSS wrapper for UI package LineageColumnNode\n *\n * This component integrates LineageViewContext with the pure presentation\n * LineageColumnNode from @datarecce/ui.\n */\nfunction GraphColumnNodeComponent(nodeProps: GraphColumnNodeProps) {\n const { id: columnNodeId, data } = nodeProps;\n const { id: nodeId } = data.node;\n const { column, type, transformationType, changeStatus } = data;\n\n // Get zoom level for content visibility\n const showContent = useStore((s) => s.transform[2] > 0.3);\n\n // Get theme colors\n const { isDark } = useThemeColors();\n\n // Get context values\n const {\n viewOptions,\n showContextMenu,\n isNodeHighlighted,\n isNodeShowingChangeAnalysis,\n } = useLineageViewContextSafe();\n\n // Computed state\n const selectedNode = viewOptions.column_level_lineage?.node_id;\n const selectedColumn = viewOptions.column_level_lineage?.column;\n const isFocused = column === selectedColumn && nodeId === selectedNode;\n const isHighlighted = isNodeHighlighted(columnNodeId);\n const isShowingChangeAnalysis = isNodeShowingChangeAnalysis(nodeId);\n\n // Build LineageColumnNodeData\n const columnData: LineageColumnNodeData = {\n column,\n type,\n nodeId,\n transformationType:\n transformationType as LineageColumnNodeData[\"transformationType\"],\n changeStatus: changeStatus as LineageColumnNodeData[\"changeStatus\"],\n isHighlighted,\n isFocused,\n };\n\n // Callbacks\n const handleContextMenu = (event: MouseEvent, _columnId: string) => {\n showContextMenu(event, nodeProps as unknown as LineageGraphColumnNode);\n };\n\n return (\n <LineageColumnNode\n id={columnNodeId}\n data={columnData}\n showContent={showContent}\n showChangeAnalysis={isShowingChangeAnalysis}\n isDark={isDark}\n onContextMenu={handleContextMenu}\n />\n );\n}\n\nexport const GraphColumnNode = memo(GraphColumnNodeComponent);\nGraphColumnNode.displayName = \"GraphColumnNode\";\n","\"use client\";\n\n/**\n * @file GraphEdge.tsx\n * @description Graph edge component with dependency injection for highlighting\n *\n * This component renders a bezier edge in a React Flow graph with support for:\n * - Change status styling (added, removed edges)\n * - Highlighting via dependency injection\n * - Style customization\n *\n * The component uses dependency injection for context-dependent behavior,\n * allowing the OSS wrapper to inject its own highlighting logic.\n */\n\nimport { BaseEdge, type EdgeProps, getBezierPath } from \"@xyflow/react\";\nimport { memo } from \"react\";\nimport type { LineageGraphEdge } from \"../../../contexts/lineage/types\";\nimport { type ChangeStatus, getIconForChangeStatus } from \"../styles\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/**\n * Data structure for graph edge - re-exported from LineageGraphEdge for convenience\n */\nexport type GraphEdgeData = LineageGraphEdge[\"data\"];\n\n/**\n * Edge type for React Flow - uses LineageGraphEdge from contexts\n */\nexport type GraphEdgeType = LineageGraphEdge;\n\n/**\n * Props for the GraphEdge component with dependency injection\n */\nexport interface GraphEdgeProps extends EdgeProps<LineageGraphEdge> {\n /**\n * Dependency injection: function to determine if an edge is highlighted.\n * When not provided, the edge is always highlighted (fully visible).\n *\n * @param source - Source node ID\n * @param target - Target node ID\n * @returns Whether the edge should be highlighted\n *\n * @example\n * ```tsx\n * // Inject highlighting from context\n * const { isEdgeHighlighted } = useLineageViewContext();\n * <GraphEdge {...props} isEdgeHighlighted={isEdgeHighlighted} />\n * ```\n */\n isEdgeHighlighted?: (source: string, target: string) => boolean;\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * Graph edge component for lineage visualization\n *\n * Renders a bezier edge with:\n * - Color based on change status (green for added, red for removed)\n * - Dashed line for changed edges\n * - Dimmed appearance for non-highlighted edges\n *\n * @param props - Edge props including source/target coordinates and data\n * @returns Rendered SVG edge element\n *\n * @example\n * ```tsx\n * // Basic usage (always highlighted)\n * <GraphEdge {...edgeProps} />\n *\n * // With dependency injection for highlighting\n * <GraphEdge\n * {...edgeProps}\n * isEdgeHighlighted={(source, target) => highlightedEdges.has(`${source}-${target}`)}\n * />\n * ```\n */\nfunction GraphEdgeComponent(props: GraphEdgeProps) {\n const {\n source,\n target,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n style: styleOverride = {},\n markerEnd,\n data,\n isEdgeHighlighted,\n } = props;\n\n const style: React.CSSProperties = {\n ...styleOverride,\n };\n\n // Apply change status styling\n if (data?.changeStatus) {\n // Cast to ChangeStatus from styles module (added, removed, modified, unchanged)\n const statusStyle = getIconForChangeStatus(\n data.changeStatus as ChangeStatus,\n );\n style.stroke = statusStyle.hexColor;\n style.strokeDasharray = \"5\";\n }\n\n // Apply highlighting filter via dependency injection\n // Default to highlighted (true) if no function provided\n const isHighlighted = isEdgeHighlighted\n ? isEdgeHighlighted(source, target)\n : true;\n\n if (!isHighlighted) {\n style.filter = \"opacity(0.2) grayscale(50%)\";\n }\n\n const [edgePath] = getBezierPath({\n sourceX,\n sourceY,\n sourcePosition,\n targetX,\n targetY,\n targetPosition,\n });\n\n return (\n <>\n <BaseEdge\n path={edgePath}\n markerEnd={markerEnd}\n style={{ ...style, ...styleOverride }}\n />\n </>\n );\n}\n\n/**\n * Memoized GraphEdge component for performance optimization\n */\nexport const GraphEdge = memo(GraphEdgeComponent);\nGraphEdge.displayName = \"GraphEdge\";\n","\"use client\";\n\n/**\n * @file GraphEdgeOss.tsx\n * @description wrapper for GraphEdge that injects LineageViewContext dependencies\n *\n * This thin wrapper imports the core GraphEdge component from @datarecce/ui\n * and injects dependencies:\n * - isEdgeHighlighted from LineageViewContext\n */\n\nimport type { EdgeProps } from \"@xyflow/react\";\nimport type { LineageGraphEdge } from \"../..\";\nimport { useLineageViewContextSafe } from \"../../contexts\";\nimport { GraphEdge as GraphEdgeBase } from \"./edges\";\n\nimport \"../../styles\";\n\ntype GraphEdgeProps = EdgeProps<LineageGraphEdge>;\n\n/**\n * OSS GraphEdge component that wraps the UI package's GraphEdge\n * with context-based dependency injection.\n *\n * Injects:\n * - isEdgeHighlighted: from LineageViewContext for context-aware highlighting\n */\nexport default function GraphEdgeOss(props: GraphEdgeProps) {\n const { isEdgeHighlighted } = useLineageViewContextSafe();\n\n return <GraphEdgeBase {...props} isEdgeHighlighted={isEdgeHighlighted} />;\n}\n","\"use client\";\n\n/**\n * @file ActionTag.tsx\n * @description Pure presentation component for displaying action status in lineage nodes\n *\n * This component displays the status of an action (pending, running, skipped, error, or result).\n * It is designed to be used with lineage graph nodes and receives all data via props.\n *\n * Source: Simplified from OSS js/src/components/lineage/ActionTag.tsx\n */\n\nimport Box from \"@mui/material/Box\";\nimport Chip from \"@mui/material/Chip\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Stack from \"@mui/material/Stack\";\nimport Tooltip from \"@mui/material/Tooltip\";\nimport { memo } from \"react\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/**\n * Action status values\n */\nexport type ActionStatus =\n | \"pending\"\n | \"running\"\n | \"skipped\"\n | \"success\"\n | \"error\";\n\n/**\n * Progress information for running actions\n */\nexport interface ActionProgress {\n /** Progress percentage (0-1) */\n percentage?: number;\n /** Current step description */\n message?: string;\n}\n\n/**\n * Value diff result summary\n */\nexport interface ValueDiffResult {\n /** Number of mismatched columns */\n mismatchedColumns: number;\n /** Total number of columns compared */\n totalColumns: number;\n}\n\n/**\n * Row count diff result summary\n */\nexport interface RowCountDiffResult {\n /** Base row count (null if not available) */\n base: number | null;\n /** Current row count (null if not available) */\n current: number | null;\n}\n\n/**\n * Props for ActionTag component\n */\nexport interface ActionTagProps {\n /** Current status of the action */\n status: ActionStatus;\n /** Skip reason if status is 'skipped' */\n skipReason?: string;\n /** Error message if status is 'error' */\n errorMessage?: string;\n /** Progress info if status is 'running' */\n progress?: ActionProgress;\n /** Value diff result if this is a value diff action */\n valueDiffResult?: ValueDiffResult;\n /** Row count diff result if this is a row count diff action */\n rowCountDiffResult?: RowCountDiffResult;\n /** Run ID to display as fallback */\n runId?: string;\n /** Test ID for testing */\n \"data-testid\"?: string;\n}\n\n// =============================================================================\n// ICON COMPONENTS\n// =============================================================================\n\n/**\n * Info icon for tooltips\n */\nconst InfoIcon = () => (\n <svg\n stroke=\"currentColor\"\n fill=\"currentColor\"\n strokeWidth=\"0\"\n viewBox=\"0 0 256 256\"\n height=\"1em\"\n width=\"1em\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z\" />\n </svg>\n);\n\n/**\n * Warning icon for error states\n */\nconst WarningIcon = () => (\n <svg\n stroke=\"currentColor\"\n fill=\"currentColor\"\n strokeWidth=\"0\"\n viewBox=\"0 0 256 256\"\n height=\"1em\"\n width=\"1em\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path d=\"M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z\" />\n </svg>\n);\n\n// =============================================================================\n// HELPER COMPONENTS\n// =============================================================================\n\n/**\n * Skipped status display\n */\nfunction SkippedTag({ skipReason }: { skipReason?: string }) {\n return (\n <Chip\n size=\"small\"\n label={\n <Stack\n direction=\"row\"\n sx={{\n fontSize: \"10pt\",\n color: \"grey.500\",\n alignItems: \"center\",\n gap: \"3px\",\n }}\n >\n <Box>Skipped</Box>\n {skipReason && (\n <Tooltip title={skipReason}>\n <Box component=\"span\" sx={{ display: \"flex\" }}>\n <InfoIcon />\n </Box>\n </Tooltip>\n )}\n </Stack>\n }\n sx={{ bgcolor: \"grey.100\" }}\n />\n );\n}\n\n/**\n * Error status display\n */\nfunction ErrorTag({ errorMessage }: { errorMessage?: string }) {\n return (\n <Stack\n direction=\"row\"\n sx={{ fontSize: \"10pt\", color: \"gray\", alignItems: \"center\" }}\n >\n <Box>Error</Box>\n {errorMessage && (\n <Tooltip title={errorMessage}>\n <Box component=\"span\" sx={{ display: \"flex\" }}>\n <WarningIcon />\n </Box>\n </Tooltip>\n )}\n </Stack>\n );\n}\n\n/**\n * Value diff result display\n */\nfunction ValueDiffTag({ result }: { result: ValueDiffResult }) {\n const { mismatchedColumns } = result;\n const hasIssues = mismatchedColumns > 0;\n\n return (\n <Chip\n size=\"small\"\n sx={{\n bgcolor: hasIssues ? \"error.light\" : \"success.light\",\n }}\n label={\n <Stack\n direction=\"row\"\n sx={{\n fontSize: \"10pt\",\n color: hasIssues ? \"error.main\" : \"success.main\",\n alignItems: \"center\",\n gap: \"3px\",\n }}\n >\n {hasIssues\n ? `${mismatchedColumns} columns mismatched`\n : \"All columns match\"}\n </Stack>\n }\n />\n );\n}\n\n/**\n * Row count diff result display\n */\nfunction RowCountDiffTag({ result }: { result: RowCountDiffResult }) {\n const { base, current } = result;\n const baseLabel = base === null ? \"N/A\" : base.toLocaleString();\n const currentLabel = current === null ? \"N/A\" : current.toLocaleString();\n\n // Determine change direction\n let changeIndicator = \"\";\n let changeColor = \"grey.500\";\n\n if (base !== null && current !== null) {\n if (current > base) {\n changeIndicator = \"↑\";\n changeColor = \"success.main\";\n } else if (current < base) {\n changeIndicator = \"↓\";\n changeColor = \"error.main\";\n } else {\n changeIndicator = \"=\";\n }\n }\n\n return (\n <Chip\n size=\"small\"\n sx={{ bgcolor: \"grey.100\" }}\n label={\n <Stack\n direction=\"row\"\n sx={{\n fontSize: \"10pt\",\n alignItems: \"center\",\n gap: \"3px\",\n }}\n >\n <Box>{baseLabel}</Box>\n <Box>→</Box>\n <Box>{currentLabel}</Box>\n {changeIndicator && (\n <Box component=\"span\" sx={{ color: changeColor, ml: 0.5 }}>\n {changeIndicator}\n </Box>\n )}\n </Stack>\n }\n />\n );\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * ActionTag - Pure presentation component for action status\n *\n * Displays the current status of an action with appropriate visual feedback:\n * - Pending: Spinner\n * - Running: Progress indicator (indeterminate or percentage)\n * - Skipped: Chip with optional reason tooltip\n * - Error: Error text with optional message tooltip\n * - Success with value diff: Match/mismatch summary\n * - Success with row count diff: Count comparison\n *\n * @example\n * ```tsx\n * // Pending action\n * <ActionTag status=\"pending\" />\n *\n * // Running with progress\n * <ActionTag status=\"running\" progress={{ percentage: 0.5 }} />\n *\n * // Skipped with reason\n * <ActionTag status=\"skipped\" skipReason=\"No changes detected\" />\n *\n * // Value diff result\n * <ActionTag\n * status=\"success\"\n * valueDiffResult={{ mismatchedColumns: 2, totalColumns: 10 }}\n * />\n * ```\n */\nfunction ActionTagComponent({\n status,\n skipReason,\n errorMessage,\n progress,\n valueDiffResult,\n rowCountDiffResult,\n runId,\n \"data-testid\": testId,\n}: ActionTagProps) {\n // Pending state\n if (status === \"pending\") {\n return (\n <CircularProgress size={16} data-testid={testId} data-status=\"pending\" />\n );\n }\n\n // Skipped state\n if (status === \"skipped\") {\n return (\n <Box data-testid={testId} data-status=\"skipped\">\n <SkippedTag skipReason={skipReason} />\n </Box>\n );\n }\n\n // Running state\n if (status === \"running\") {\n if (progress?.percentage === undefined) {\n return (\n <CircularProgress\n size={16}\n data-testid={testId}\n data-status=\"running\"\n />\n );\n }\n return (\n <CircularProgress\n variant=\"determinate\"\n value={progress.percentage * 100}\n size={16}\n data-testid={testId}\n data-status=\"running\"\n />\n );\n }\n\n // Error state\n if (status === \"error\") {\n return (\n <Box data-testid={testId} data-status=\"error\">\n <ErrorTag errorMessage={errorMessage} />\n </Box>\n );\n }\n\n // Success state with value diff result\n if (valueDiffResult) {\n return (\n <Box data-testid={testId} data-status=\"success\">\n <ValueDiffTag result={valueDiffResult} />\n </Box>\n );\n }\n\n // Success state with row count diff result\n if (rowCountDiffResult) {\n return (\n <Box data-testid={testId} data-status=\"success\">\n <RowCountDiffTag result={rowCountDiffResult} />\n </Box>\n );\n }\n\n // Fallback: show run ID\n return (\n <Box data-testid={testId} data-status=\"success\">\n {runId || \"Complete\"}\n </Box>\n );\n}\n\nexport const ActionTag = memo(ActionTagComponent);\nActionTag.displayName = \"ActionTag\";\n","/**\n * TimelineEvent - Renders a single event in the check timeline.\n *\n * Handles different event types:\n * - check_created: Shows creation message\n * - comment: Shows user comment with edit/delete options\n * - approval_change: Shows approval status change\n * - description_change: Shows description update\n * - name_change: Shows name update\n * - preset_applied: Shows preset application\n */\n\n\"use client\";\n\nimport MuiAvatar from \"@mui/material/Avatar\";\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Popover from \"@mui/material/Popover\";\nimport Stack from \"@mui/material/Stack\";\nimport TextField from \"@mui/material/TextField\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { formatDistanceToNow } from \"date-fns\";\nimport { type MouseEvent, useState } from \"react\";\nimport {\n PiBookmarkSimple,\n PiChatText,\n PiCheckCircle,\n PiCircle,\n PiNotePencil,\n PiPencilSimple,\n PiPlusCircle,\n PiTrashSimple,\n} from \"react-icons/pi\";\nimport { type CheckEvent, getEventIconType } from \"../../../api\";\nimport { useAvatar } from \"../../../hooks/useAvatar\";\nimport { useIsDark } from \"../../../hooks/useIsDark\";\nimport { MarkdownContent } from \"../../../primitives\";\n\ninterface TimelineEventProps {\n event: CheckEvent;\n currentUserId?: string;\n onEdit?: (eventId: string, content: string) => Promise<void>;\n onDelete?: (eventId: string) => Promise<void>;\n}\n\nfunction EventIcon({ event }: { event: CheckEvent }) {\n const iconType = getEventIconType(event);\n\n const iconMap = {\n create: PiPlusCircle,\n comment: PiChatText,\n approve: PiCheckCircle,\n unapprove: PiCircle,\n edit: PiNotePencil,\n preset: PiBookmarkSimple,\n };\n\n const colorMap: Record<string, string> = {\n create: \"primary.main\",\n comment: \"grey.500\",\n approve: \"success.main\",\n unapprove: \"grey.400\",\n edit: \"warning.main\",\n preset: \"secondary.main\",\n };\n\n const IconComponent = iconMap[iconType];\n const color = colorMap[iconType];\n\n return <Box component={IconComponent} sx={{ color, fontSize: 16 }} />;\n}\n\nfunction UserAvatar({ event }: { event: CheckEvent }) {\n const { actor } = event;\n const { avatarUrl } = useAvatar({ userId: actor.user_id });\n\n const displayName = actor.fullname || actor.login || \"User\";\n const initials = displayName.charAt(0).toUpperCase();\n\n return (\n <MuiAvatar\n src={avatarUrl || undefined}\n sx={{ width: 24, height: 24, fontSize: \"0.75rem\" }}\n >\n {initials}\n </MuiAvatar>\n );\n}\n\nfunction StateChangeEvent({ event }: { event: CheckEvent }) {\n const { actor } = event;\n const actorName = actor.fullname || actor.login || \"Someone\";\n const relativeTime = formatDistanceToNow(new Date(event.created_at), {\n addSuffix: true,\n });\n\n let message = \"\";\n switch (event.event_type) {\n case \"check_created\":\n message = \"created this check\";\n break;\n case \"approval_change\":\n message =\n event.new_value === \"true\"\n ? \"approved this check\"\n : \"unapproved this check\";\n break;\n case \"description_change\":\n message = \"updated the description\";\n break;\n case \"name_change\":\n message = \"renamed this check\";\n break;\n case \"preset_applied\":\n message = \"applied a preset\";\n break;\n default:\n message = \"made a change\";\n }\n\n return (\n <Box sx={{ display: \"flex\", gap: 1, alignItems: \"flex-start\", py: 1 }}>\n <Box sx={{ pt: \"2px\" }}>\n <EventIcon event={event} />\n </Box>\n <Box sx={{ flex: 1 }}>\n <Stack\n direction=\"row\"\n spacing={0.5}\n flexWrap=\"wrap\"\n alignItems=\"center\"\n >\n <UserAvatar event={event} />\n <Typography variant=\"body2\" fontWeight=\"500\">\n {actorName}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {message}\n </Typography>\n <Typography variant=\"caption\" color=\"text.disabled\">\n {relativeTime}\n </Typography>\n </Stack>\n </Box>\n </Box>\n );\n}\n\nfunction CommentEvent({\n event,\n currentUserId,\n onEdit,\n onDelete,\n}: {\n event: CheckEvent;\n currentUserId?: string;\n onEdit?: (eventId: string, content: string) => Promise<void>;\n onDelete?: (eventId: string) => Promise<void>;\n}) {\n const isDark = useIsDark();\n const [isEditing, setIsEditing] = useState(false);\n const [editContent, setEditContent] = useState(event.content || \"\");\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [deleteAnchorEl, setDeleteAnchorEl] = useState<HTMLElement | null>(\n null,\n );\n const isDeletePopoverOpen = Boolean(deleteAnchorEl);\n\n const { actor } = event;\n const actorName = actor.fullname || actor.login || \"Someone\";\n const relativeTime = formatDistanceToNow(new Date(event.created_at), {\n addSuffix: true,\n });\n const isAuthor =\n currentUserId && String(actor.user_id) === String(currentUserId);\n\n const handleStartEdit = () => {\n setEditContent(event.content || \"\");\n setIsEditing(true);\n };\n\n const handleCancelEdit = () => {\n setEditContent(event.content || \"\");\n setIsEditing(false);\n };\n\n const handleSaveEdit = async () => {\n const trimmed = editContent.trim();\n if (!trimmed || trimmed === event.content) {\n handleCancelEdit();\n return;\n }\n\n if (onEdit) {\n setIsSubmitting(true);\n try {\n await onEdit(event.id, trimmed);\n setIsEditing(false);\n } finally {\n setIsSubmitting(false);\n }\n }\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === \"Escape\") {\n handleCancelEdit();\n } else if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n handleSaveEdit();\n }\n };\n\n const handleDeleteClick = (event: MouseEvent<HTMLButtonElement>) => {\n setDeleteAnchorEl(event.currentTarget);\n };\n\n const handleDeleteClose = () => {\n setDeleteAnchorEl(null);\n };\n\n const handleDelete = async () => {\n if (onDelete) {\n setIsDeleting(true);\n try {\n await onDelete(event.id);\n handleDeleteClose();\n } finally {\n setIsDeleting(false);\n }\n }\n };\n\n if (event.is_deleted) {\n return (\n <Box sx={{ display: \"flex\", gap: 1, alignItems: \"center\", py: 1 }}>\n <Box sx={{ pt: \"2px\", display: \"flex\", alignItems: \"center\" }}>\n <EventIcon event={event} />\n </Box>\n <Box sx={{ display: \"flex\", flex: 1, alignItems: \"center\" }}>\n <Typography variant=\"body2\" color=\"text.disabled\" fontStyle=\"italic\">\n Comment deleted\n </Typography>\n </Box>\n </Box>\n );\n }\n\n return (\n <Box sx={{ display: \"flex\", gap: 1, alignItems: \"flex-start\", py: 1 }}>\n <Box sx={{ pt: \"2px\" }}>\n <EventIcon event={event} />\n </Box>\n <Box sx={{ flex: 1 }}>\n <Stack\n direction=\"row\"\n spacing={0.5}\n sx={{ mb: 0.5 }}\n flexWrap=\"wrap\"\n alignItems=\"center\"\n >\n <UserAvatar event={event} />\n <Typography variant=\"body2\" fontWeight=\"500\">\n {actorName}\n {isAuthor && (\n <Typography\n component=\"span\"\n variant=\"body2\"\n color=\"text.secondary\"\n >\n {\" \"}\n (Author)\n </Typography>\n )}\n </Typography>\n <Typography variant=\"caption\" color=\"text.disabled\">\n {relativeTime}\n </Typography>\n {event.is_edited && (\n <Typography variant=\"caption\" color=\"text.disabled\">\n (edited)\n </Typography>\n )}\n </Stack>\n\n {isEditing ? (\n // Edit mode\n <Box>\n <TextField\n value={editContent}\n onChange={(e) => setEditContent(e.target.value)}\n onKeyDown={handleKeyDown}\n size=\"small\"\n multiline\n minRows={3}\n fullWidth\n disabled={isSubmitting}\n autoFocus\n sx={{\n \"& .MuiOutlinedInput-root\": {\n bgcolor: \"background.paper\",\n \"&:focus-within\": {\n borderColor: \"primary.main\",\n },\n },\n }}\n />\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ mt: 1 }}\n justifyContent=\"flex-end\"\n >\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={handleCancelEdit}\n disabled={isSubmitting}\n >\n Cancel\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n onClick={handleSaveEdit}\n disabled={!editContent.trim() || isSubmitting}\n >\n {isSubmitting ? \"Saving...\" : \"Save\"}\n </Button>\n </Stack>\n </Box>\n ) : (\n // View mode\n <Box\n sx={{\n bgcolor: isDark ? \"grey.800\" : \"grey.50\",\n borderRadius: 1,\n p: 1,\n border: \"1px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.200\",\n position: \"relative\",\n \"&:hover .comment-actions\": {\n opacity: 1,\n },\n }}\n >\n <MarkdownContent content={event.content || \"\"} fontSize=\"sm\" />\n\n {/* Edit/Delete buttons - only visible to author on hover */}\n {isAuthor && (onEdit || onDelete) && (\n <Stack\n className=\"comment-actions\"\n direction=\"row\"\n spacing={0}\n sx={{\n position: \"absolute\",\n top: 4,\n right: 4,\n opacity: 0,\n transition: \"opacity 0.2s\",\n }}\n >\n {onEdit && (\n <MuiTooltip title=\"Edit comment\">\n <IconButton\n aria-label=\"Edit comment\"\n size=\"small\"\n onClick={handleStartEdit}\n >\n <PiPencilSimple />\n </IconButton>\n </MuiTooltip>\n )}\n {onDelete && (\n <>\n <MuiTooltip title=\"Delete comment\">\n <IconButton\n aria-label=\"Delete comment\"\n size=\"small\"\n color=\"error\"\n onClick={handleDeleteClick}\n >\n <PiTrashSimple />\n </IconButton>\n </MuiTooltip>\n <Popover\n open={isDeletePopoverOpen}\n anchorEl={deleteAnchorEl}\n onClose={handleDeleteClose}\n anchorOrigin={{\n vertical: \"bottom\",\n horizontal: \"center\",\n }}\n transformOrigin={{\n vertical: \"top\",\n horizontal: \"center\",\n }}\n >\n <Box sx={{ p: 2 }}>\n <Typography variant=\"body2\" sx={{ mb: 2 }}>\n Delete this comment?\n </Typography>\n <Stack\n direction=\"row\"\n spacing={1}\n justifyContent=\"flex-end\"\n >\n <Button\n size=\"small\"\n variant=\"text\"\n onClick={handleDeleteClose}\n disabled={isDeleting}\n >\n Cancel\n </Button>\n <Button\n size=\"small\"\n variant=\"contained\"\n color=\"error\"\n onClick={handleDelete}\n disabled={isDeleting}\n >\n {isDeleting ? \"Deleting...\" : \"Delete\"}\n </Button>\n </Stack>\n </Box>\n </Popover>\n </>\n )}\n </Stack>\n )}\n </Box>\n )}\n </Box>\n </Box>\n );\n}\n\nexport function TimelineEventOss({\n event,\n currentUserId,\n onEdit,\n onDelete,\n}: TimelineEventProps) {\n if (event.event_type === \"comment\") {\n return (\n <CommentEvent\n event={event}\n currentUserId={currentUserId}\n onEdit={onEdit}\n onDelete={onDelete}\n />\n );\n }\n\n return <StateChangeEvent event={event} />;\n}\n","/**\n * CheckTimeline - Main timeline/conversation panel for a check.\n *\n * Displays a chronological list of events (comments, state changes)\n * and provides an input for adding new comments.\n *\n * This component is only rendered when connected to Recce Cloud.\n */\n\n\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Divider from \"@mui/material/Divider\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { cacheKeys } from \"../../../api\";\nimport { useApiConfig, useCheckEvents, useIsDark } from \"../../../hooks\";\nimport { fetchUser } from \"../../../lib/api/user\";\nimport { CommentInput } from \"../../../primitives\";\nimport { TimelineEventOss as TimelineEvent } from \"./TimelineEventOss\";\n\ninterface CheckTimelineProps {\n checkId: string;\n}\n\nexport function CheckTimelineOss({ checkId }: CheckTimelineProps) {\n const isDark = useIsDark();\n const { apiClient } = useApiConfig();\n const {\n events,\n isLoading,\n error,\n createComment,\n isCreatingComment,\n updateComment,\n deleteComment,\n } = useCheckEvents(checkId);\n\n // Get current user for determining edit/delete permissions\n const { data: currentUser } = useQuery({\n queryKey: cacheKeys.user(),\n queryFn: () => fetchUser(apiClient),\n retry: false,\n });\n\n const handleCreateComment = (content: string) => {\n createComment(content);\n };\n\n const handleEditComment = async (eventId: string, content: string) => {\n await updateComment({ eventId, content });\n };\n\n const handleDeleteComment = async (eventId: string) => {\n await deleteComment(eventId);\n };\n\n if (isLoading) {\n return (\n <Box\n sx={{\n height: \"100%\",\n p: 4,\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <CircularProgress size={20} />\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box\n sx={{\n height: \"100%\",\n p: 4,\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <Typography sx={{ fontSize: \"0.875rem\", color: \"error.main\" }}>\n Failed to load timeline\n </Typography>\n </Box>\n );\n }\n\n return (\n <Stack\n sx={{\n height: \"100%\",\n alignItems: \"stretch\",\n borderLeft: \"1px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.200\",\n }}\n spacing={0}\n >\n {/* Header */}\n <Box\n sx={{\n px: 3,\n py: 2,\n borderBottom: \"1px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.200\",\n }}\n >\n <Typography variant=\"subtitle2\" sx={{ fontWeight: 500 }}>\n Activity\n </Typography>\n </Box>\n\n {/* Events List - Scrollable */}\n <Box sx={{ flex: 1, overflowY: \"auto\", px: 3, py: 2 }}>\n {events.length === 0 ? (\n <Typography sx={{ fontSize: \"0.875rem\", color: \"grey.500\" }}>\n No activity yet\n </Typography>\n ) : (\n <Stack sx={{ alignItems: \"stretch\" }} spacing={0}>\n {events.map((event, index) => (\n <Box key={event.id}>\n <TimelineEvent\n event={event}\n currentUserId={currentUser?.id}\n onEdit={handleEditComment}\n onDelete={handleDeleteComment}\n />\n {index < events.length - 1 && (\n <Divider\n sx={{ borderColor: isDark ? \"grey.700\" : \"grey.100\" }}\n />\n )}\n </Box>\n ))}\n </Stack>\n )}\n </Box>\n\n {/* Comment Input - Fixed at bottom */}\n <Box\n sx={{\n px: 3,\n py: 3,\n borderTop: \"1px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.200\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n }}\n >\n <CommentInput\n onSubmit={handleCreateComment}\n isSubmitting={isCreatingComment}\n />\n </Box>\n </Stack>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\n\nexport interface SquareIconProps {\n /** The background color of the square icon */\n color: string;\n}\n\n/**\n * A small square icon used in chart legends\n */\nexport function SquareIcon({ color }: SquareIconProps) {\n return (\n <Box\n component=\"span\"\n sx={{\n display: \"inline-block\",\n width: \"10px\",\n height: \"10px\",\n bgcolor: color,\n mr: 1,\n borderRadius: \"4px\",\n }}\n />\n );\n}\n","\"use client\";\n\nimport Box, { type BoxProps } from \"@mui/material/Box\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMemo } from \"react\";\nimport { PiInfo } from \"react-icons/pi\";\nimport type { NodeColumnData } from \"../../api\";\nimport { useLineageGraphContext } from \"../../contexts\";\nimport { DropdownValuesInput } from \"../ui/DropdownValuesInput\";\n\nexport interface QueryFormProps extends BoxProps {\n defaultPrimaryKeys: string[] | undefined;\n onPrimaryKeysChange: (primaryKeys: string[]) => void;\n}\n\nexport const QueryForm = ({\n defaultPrimaryKeys,\n onPrimaryKeysChange,\n ...props\n}: QueryFormProps) => {\n const { lineageGraph, isActionAvailable } = useLineageGraphContext();\n\n const labelInfo =\n \"Provide a primary key to perform query diff in data warehouse and only return changed rows.\";\n\n const availableColumns = useMemo(() => {\n if (!lineageGraph) {\n return [];\n }\n const columnSet = new Set<string>();\n for (const modelName in lineageGraph.nodes) {\n const model = lineageGraph.nodes[modelName];\n const combinedColumns: Record<string, NodeColumnData | undefined> = {\n ...(model.data.data.base?.columns ?? {}),\n ...(model.data.data.current?.columns ?? {}),\n };\n\n Object.entries(combinedColumns).forEach(([columnName, col]) => {\n if (col?.unique) {\n columnSet.add(columnName);\n }\n });\n }\n return Array.from(columnSet).sort();\n }, [lineageGraph]);\n\n return (\n <Box sx={{ display: \"flex\" }} {...props}>\n <Stack spacing={0} sx={{ m: \"0 0.5rem\" }}>\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography\n component=\"label\"\n sx={{ fontSize: \"0.625rem\", color: \"text.secondary\" }}\n >\n Diff with Primary Key(s) (suggested)\n </Typography>\n <MuiTooltip title={labelInfo} placement=\"bottom-end\">\n <Box\n component=\"span\"\n sx={{ display: \"flex\", color: \"grey.600\", cursor: \"help\" }}\n >\n <PiInfo fontSize=\"0.75rem\" />\n </Box>\n </MuiTooltip>\n </Stack>\n <DropdownValuesInput\n className=\"no-track-pii-safe\"\n unitName=\"key\"\n defaultValues={defaultPrimaryKeys}\n suggestionList={availableColumns}\n onValuesChange={onPrimaryKeysChange}\n size=\"2xs\"\n width={\"240px\"}\n placeholder=\"Select or type to add keys\"\n disabled={!isActionAvailable(\"query_diff_with_primary_key\")}\n />\n </Stack>\n </Box>\n );\n};\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { RiTerminalBoxLine } from \"react-icons/ri\";\nimport { useRecceInstanceInfo } from \"../../contexts\";\nimport { getSettingsUrl } from \"../../utils\";\n\nexport interface SetupConnectionGuideProps {\n /** URL for the support calendar booking (e.g., \"https://cal.com/team/recce/chat\") */\n supportCalendarUrl?: string;\n}\n\n/**\n * SetupConnectionGuide - displays guidance when data warehouse connection is not configured\n *\n * This component shows a guide to help users connect to a data warehouse\n * when query functions are disabled due to missing connection.\n */\nexport function SetupConnectionGuide({\n supportCalendarUrl = \"https://cal.com/team/recce/chat\",\n}: SetupConnectionGuideProps) {\n const { data: instanceInfo } = useRecceInstanceInfo();\n\n return (\n <div className=\"flex flex-1 h-full min-h-0 m-2 p-4 bg-blue-50 rounded-lg shadow-md justify-center\">\n <div className=\"w-4/5 flex flex-col overflow-y-auto gap-6 px-8 pb-8\">\n <Stack alignItems=\"center\" spacing={2}>\n <Box\n sx={{\n p: 1,\n bgcolor: \"background.paper\",\n borderRadius: \"50%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: 2,\n }}\n >\n <Box\n component={RiTerminalBoxLine}\n sx={{ fontSize: 28, color: \"iochmara.500\" }}\n />\n </Box>\n <Typography variant=\"h5\" sx={{ mt: 2 }}>\n Wait, there's more!\n </Typography>\n <Typography sx={{ fontSize: \"1rem\", textAlign: \"center\" }}>\n Query functions disabled without a{\" \"}\n <Typography component=\"span\" sx={{ fontWeight: \"bold\" }}>\n data warehouse connection\n </Typography>\n </Typography>\n </Stack>\n <Stack sx={{ width: \"50%\", mt: 3, mx: \"auto\" }}>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n size=\"large\"\n onClick={() => {\n window.open(\n getSettingsUrl(instanceInfo, supportCalendarUrl),\n \"_blank\",\n );\n }}\n >\n Connect to Data Warehouse\n </Button>\n </Stack>\n </div>\n </div>\n );\n}\n\nexport default SetupConnectionGuide;\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiSwitch from \"@mui/material/Switch\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport { useMemo } from \"react\";\nimport { PiInfoFill } from \"react-icons/pi\";\nimport {\n type QueryParams,\n type SubmitOptions,\n submitQuery,\n submitQueryBase,\n submitQueryDiff,\n waitRun,\n} from \"../../api\";\nimport { HistoryToggle } from \"../../components\";\nimport { SetupConnectionPopover } from \"../../components/app\";\nimport { BaseEnvironmentSetupGuide } from \"../../components/lineage\";\nimport {\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport {\n defaultSqlQuery,\n useApiConfig,\n useRecceQueryContext,\n} from \"../../hooks\";\nimport { RECCE_SUPPORT_CALENDAR_URL } from \"../../lib/const\";\nimport { QueryForm } from \"./QueryForm\";\nimport { SetupConnectionGuide } from \"./SetupConnectionGuide\";\nimport SqlEditor, { DualSqlEditor } from \"./SqlEditor\";\n\nconst QueryModeToggle = () => {\n const { isCustomQueries, setCustomQueries, sqlQuery, setBaseSqlQuery } =\n useRecceQueryContext();\n const handleToggle = () => {\n if (!isCustomQueries && setBaseSqlQuery) setBaseSqlQuery(sqlQuery);\n setCustomQueries(!isCustomQueries);\n };\n const customQueriesDescription =\n \"Custom queries allow you to use two SQL queries to compare results between current and base environments.\";\n return (\n <Box>\n <Stack\n direction=\"row\"\n spacing={0.5}\n alignItems=\"center\"\n fontSize=\"0.75rem\"\n >\n <Typography variant=\"body2\" sx={{ fontSize: \"0.75rem\" }}>\n Custom Queries\n </Typography>\n <MuiTooltip title={customQueriesDescription}>\n <Box component=\"span\" sx={{ display: \"flex\", color: \"grey.600\" }}>\n <PiInfoFill fontSize=\"1rem\" />\n </Box>\n </MuiTooltip>\n </Stack>\n <MuiSwitch\n size=\"small\"\n checked={isCustomQueries}\n onChange={handleToggle}\n color=\"primary\"\n />\n </Box>\n );\n};\n\nexport const QueryPageOss = () => {\n const {\n sqlQuery: _sqlQuery,\n baseSqlQuery,\n setSqlQuery,\n setBaseSqlQuery,\n primaryKeys,\n setPrimaryKeys,\n isCustomQueries,\n } = useRecceQueryContext();\n const { lineageGraph, envInfo } = useLineageGraphContext();\n const { featureToggles, singleEnv } = useRecceInstanceContext();\n\n let sqlQuery = _sqlQuery;\n if (envInfo?.adapterType === \"sqlmesh\" && _sqlQuery === defaultSqlQuery) {\n sqlQuery = \"select * from db.mymodel\";\n }\n\n if (featureToggles.mode === \"read only\") {\n sqlQuery = `--- Would like to do query here? Book a demo with us at ${RECCE_SUPPORT_CALENDAR_URL}\\n${sqlQuery}`;\n }\n\n const { showRunId } = useRecceActionContext();\n const { apiClient } = useApiConfig();\n const queryFn = async (type: \"query\" | \"query_base\" | \"query_diff\") => {\n function queryFactory(type: string) {\n switch (type) {\n case \"query\":\n return submitQuery;\n case \"query_base\":\n return submitQueryBase;\n case \"query_diff\":\n return submitQueryDiff;\n default:\n throw new Error(`Unknown query type: ${type}`);\n }\n }\n const sqlTemplate = type === \"query_base\" ? (baseSqlQuery ?? \"\") : sqlQuery;\n const runFn = queryFactory(type);\n const params: QueryParams = { sql_template: sqlTemplate };\n const options: SubmitOptions = { nowait: true };\n\n if (type === \"query_diff\") {\n params.primary_keys = primaryKeys;\n if (isCustomQueries) params.base_sql_template = baseSqlQuery;\n }\n const { run_id } = await runFn(params, options, apiClient);\n\n showRunId(run_id);\n\n return await waitRun(run_id, undefined, apiClient);\n };\n\n const { mutate: runQuery, isPending } = useMutation({\n mutationFn: queryFn,\n });\n\n const currentSchema = useMemo(() => {\n const initialValue = \"N/A\";\n // find the most common schema from the current lineage graph\n const countMap: Record<string, number> = {};\n if (!lineageGraph) {\n return initialValue;\n }\n\n for (const key in lineageGraph.nodes) {\n const schema = lineageGraph.nodes[key].data.data.current?.schema;\n if (schema) {\n countMap[schema] = (countMap[schema] || 0) + 1;\n }\n }\n // Find the most common value\n return Object.keys(countMap).reduce((mostCommon, current) => {\n if (countMap[current] > (countMap[mostCommon] || 0)) {\n return current;\n }\n return mostCommon;\n }, initialValue);\n }, [lineageGraph]);\n\n if (singleEnv || featureToggles.mode === \"metadata only\") {\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n <Box\n sx={{\n display: \"flex\",\n justifyContent: \"right\",\n alignItems: \"center\",\n padding: \"4pt 8pt\",\n gap: \"5px\",\n height: \"54px\",\n borderBottom: \"1px solid\",\n borderColor: \"divider\",\n }}\n >\n <HistoryToggle />\n <Box sx={{ flexGrow: 1 }} />\n {singleEnv ? (\n <MuiTooltip\n title=\"Please configure the base environment before running the diff\"\n placement=\"left\"\n >\n <span>\n <Button\n variant=\"contained\"\n disabled\n size=\"small\"\n sx={{ fontSize: \"14px\", mt: \"16px\" }}\n >\n Run Diff\n </Button>\n </span>\n </MuiTooltip>\n ) : (\n <SetupConnectionPopover\n display={featureToggles.mode === \"metadata only\"}\n >\n <Button\n variant=\"contained\"\n disabled\n size=\"small\"\n sx={{ fontSize: \"14px\", mt: \"16px\" }}\n >\n Run Diff\n </Button>\n </SetupConnectionPopover>\n )}\n </Box>\n <DualSqlEditor\n value={sqlQuery}\n onChange={setSqlQuery}\n onRun={() => {\n runQuery(\"query\");\n }}\n labels={[\"base (production)\", `current (${currentSchema})`]}\n SetupGuide={\n featureToggles.mode === \"metadata only\" ? (\n <SetupConnectionGuide />\n ) : (\n <BaseEnvironmentSetupGuide />\n )\n }\n />\n </Box>\n );\n }\n\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n <Box\n sx={{\n display: \"flex\",\n justifyContent: \"right\",\n alignItems: \"flex-end\",\n padding: \"4pt 8pt\",\n gap: \"5px\",\n height: \"54px\",\n borderBottom: \"1px solid\",\n borderColor: \"divider\",\n flex: \"0 0 54px\",\n }}\n >\n <HistoryToggle />\n <QueryModeToggle />\n <Box sx={{ flexGrow: 1 }} />\n <QueryForm\n defaultPrimaryKeys={primaryKeys}\n onPrimaryKeysChange={setPrimaryKeys}\n />\n <Button\n variant=\"contained\"\n onClick={() => {\n runQuery(\"query_diff\");\n }}\n disabled={isPending || featureToggles.disableDatabaseQuery}\n size=\"small\"\n >\n Run Diff\n </Button>\n </Box>\n\n <Box sx={{ width: \"100%\", flex: 1, overflowY: \"auto\" }}>\n {isCustomQueries ? (\n <DualSqlEditor\n value={sqlQuery}\n baseValue={baseSqlQuery}\n onChange={setSqlQuery}\n onChangeBase={setBaseSqlQuery}\n onRun={() => {\n runQuery(\"query\");\n }}\n onRunBase={() => {\n runQuery(\"query_base\");\n }}\n onRunDiff={() => {\n runQuery(\"query_diff\");\n }}\n />\n ) : (\n <SqlEditor\n value={sqlQuery}\n onChange={setSqlQuery}\n onRun={() => {\n runQuery(\"query\");\n }}\n onRunDiff={() => {\n runQuery(\"query_diff\");\n }}\n />\n )}\n </Box>\n </Box>\n );\n};\n","\"use client\";\n\n/**\n * @file GraphNodeOss.tsx\n * @description OSS wrapper for UI package LineageNode component\n *\n * This component wraps the @datarecce/ui LineageNode with OSS-specific\n * context integration. It extracts state from LineageViewContext and\n * LineageGraphContext and passes it as props to the presentation component.\n *\n * Migration: Phase 4 of lineage component migration plan\n *\n * OSS-specific functionality injected:\n * - Run type icons from registry (schema_diff, row_count_diff)\n * - ActionTag with OSS run result parsing\n * - NodeRunsAggregated with schema change detection\n */\n\nimport Box from \"@mui/material/Box\";\nimport Chip from \"@mui/material/Chip\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport { type NodeProps, useStore } from \"@xyflow/react\";\nimport { memo } from \"react\";\nimport type { LineageGraphNode } from \"../..\";\nimport { COLUMN_HEIGHT, isSchemaChanged } from \"../..\";\nimport { isRowCountDiffRun, type RowCountDiff } from \"../../api\";\nimport {\n useLineageGraphContext,\n useLineageViewContextSafe,\n} from \"../../contexts\";\nimport { useThemeColors } from \"../../hooks\";\nimport { deltaPercentageString } from \"../../utils\";\nimport { findByRunType } from \"../run\";\nimport {\n ActionTag,\n type ChangeCategory,\n LineageNode,\n type NodeChangeStatus,\n type SelectMode,\n} from \"./nodes\";\nimport { getIconForChangeStatus } from \"./styles\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\nexport type GraphNodeProps = NodeProps<LineageGraphNode>;\n\n// =============================================================================\n// HELPER COMPONENTS\n// =============================================================================\n\n/**\n * Row count diff tag component with OSS icon injection\n */\nfunction RowCountDiffTag({ rowCount }: { rowCount: RowCountDiff }) {\n const base = rowCount.base;\n const current = rowCount.curr;\n const baseLabel = rowCount.base === null ? \"N/A\" : `${rowCount.base} Rows`;\n const currentLabel = rowCount.curr === null ? \"N/A\" : `${rowCount.curr} Rows`;\n\n let tagLabel: string;\n let chipColor: \"default\" | \"success\" | \"error\";\n\n if (base === null && current === null) {\n tagLabel = \"Failed to load\";\n chipColor = \"default\";\n } else if (base === null || current === null) {\n tagLabel = `${baseLabel} -> ${currentLabel}`;\n chipColor = base === null ? \"success\" : \"error\";\n } else if (base === current) {\n tagLabel = \"=\";\n chipColor = \"default\";\n } else {\n tagLabel = `${deltaPercentageString(base, current)} Rows`;\n chipColor = base < current ? \"success\" : \"error\";\n }\n\n const RowCountIcon = findByRunType(\"row_count_diff\").icon;\n\n return (\n <Chip\n size=\"small\"\n color={chipColor}\n icon={RowCountIcon ? <RowCountIcon /> : undefined}\n label={tagLabel}\n sx={{ height: 20, fontSize: \"0.7rem\" }}\n />\n );\n}\n\n/**\n * Node runs aggregated display component with OSS-specific icons\n * Shows schema diff indicator and row count diff for models\n */\nfunction NodeRunsAggregatedDisplay({\n id,\n inverted,\n}: {\n id: string;\n inverted: boolean;\n}) {\n const { lineageGraph, runsAggregated } = useLineageGraphContext();\n const { text, isDark } = useThemeColors();\n const runs = runsAggregated?.[id];\n const node = lineageGraph?.nodes[id];\n\n if (!runs && !node) {\n return null;\n }\n\n let schemaChanged: boolean | undefined;\n if (node?.data.data.base && node.data.data.current) {\n const baseColumns = node.data.data.base.columns;\n const currColumns = node.data.data.current.columns;\n schemaChanged = isSchemaChanged(baseColumns, currColumns);\n }\n\n let rowCountChanged: boolean | undefined;\n if (runs?.row_count_diff) {\n const rowCountDiff = runs.row_count_diff;\n const result = rowCountDiff.result as RowCountDiff;\n rowCountChanged = result.curr !== result.base;\n }\n\n const colorChanged = inverted\n ? text.inverted\n : getIconForChangeStatus(\"modified\").color;\n const colorUnchanged = inverted\n ? text.secondary\n : isDark\n ? \"grey.700\"\n : \"grey.100\";\n\n const SchemaDiffIcon = findByRunType(\"schema_diff\").icon;\n\n return (\n <Box sx={{ display: \"flex\", flex: 1, alignItems: \"center\" }}>\n {schemaChanged !== undefined && (\n <MuiTooltip\n title={`Schema (${schemaChanged ? \"changed\" : \"no change\"})`}\n enterDelay={500}\n >\n <Box sx={{ height: 16 }}>\n {SchemaDiffIcon && (\n <Box\n component={SchemaDiffIcon}\n sx={{ color: schemaChanged ? colorChanged : colorUnchanged }}\n />\n )}\n </Box>\n </MuiTooltip>\n )}\n <Box sx={{ flexGrow: 1 }} />\n {runs?.row_count_diff && rowCountChanged !== undefined && (\n <MuiTooltip\n title={`Row count (${rowCountChanged ? \"changed\" : \"=\"})`}\n enterDelay={500}\n >\n <Box>\n <RowCountDiffTag\n rowCount={runs.row_count_diff.result as RowCountDiff}\n />\n </Box>\n </MuiTooltip>\n )}\n </Box>\n );\n}\n\n/**\n * Action tag display component - bridges OSS run data to UI ActionTag\n * Parses OSS-specific run results and renders using UI package ActionTag\n */\nfunction ActionTagDisplay({\n nodeId,\n nodeName,\n}: {\n nodeId: string;\n nodeName: string;\n}) {\n const { getNodeAction } = useLineageViewContextSafe();\n const action = getNodeAction(nodeId);\n\n if (!action) {\n return null;\n }\n\n const { status, skipReason, run } = action;\n\n // Map OSS action status to UI ActionTag props\n if (status === \"pending\") {\n return <ActionTag status=\"pending\" />;\n }\n\n if (status === \"skipped\") {\n return <ActionTag status=\"skipped\" skipReason={skipReason} />;\n }\n\n if (!run) {\n return <ActionTag status=\"pending\" />;\n }\n\n const { error, run_id, progress } = run;\n\n if (status === \"running\") {\n return (\n <ActionTag\n status=\"running\"\n progress={{ percentage: progress?.percentage }}\n />\n );\n }\n\n if (error) {\n return <ActionTag status=\"error\" errorMessage={error} />;\n }\n\n // Value diff result - parse OSS format to UI format\n if (run.type === \"value_diff\" && run.result) {\n const r = run.result as { data: { data: unknown[][] } };\n let mismatched = 0;\n const totalColumns = r.data.data.length;\n\n for (const c of r.data.data) {\n if ((c[2] as number) < 1) {\n mismatched++;\n }\n }\n\n return (\n <ActionTag\n status=\"success\"\n valueDiffResult={{ mismatchedColumns: mismatched, totalColumns }}\n />\n );\n }\n\n // Row count diff result - use OSS RowCountDiffTag with icon\n if (isRowCountDiffRun(run) && run.result) {\n const result = run.result;\n const nodeResult = result[nodeName];\n if (nodeResult) {\n return <RowCountDiffTag rowCount={nodeResult} />;\n }\n }\n\n // Row count result\n if (run.type === \"row_count\" && run.result) {\n const result = run.result as Record<string, { curr: number | null }>;\n const nodeResult = result[nodeName];\n if (nodeResult?.curr !== undefined && nodeResult.curr !== null) {\n return (\n <Chip\n size=\"small\"\n label={`${nodeResult.curr.toLocaleString()} Rows`}\n sx={{ height: 20, fontSize: \"0.7rem\" }}\n />\n );\n }\n }\n\n return <ActionTag status=\"success\" runId={run_id} />;\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * GraphNode - OSS wrapper for UI package LineageNode\n *\n * This component integrates LineageViewContext and LineageGraphContext\n * with the pure presentation LineageNode from @datarecce/ui.\n */\nfunction GraphNodeComponent(nodeProps: GraphNodeProps) {\n const { data } = nodeProps;\n const { id, resourceType, changeStatus, name } = data;\n\n // Get zoom level for content visibility\n const showContent = useStore((s) => s.transform[2] > 0.3);\n\n // Get theme colors\n const { isDark } = useThemeColors();\n\n // Get context values\n const {\n interactive,\n selectNode,\n selectMode,\n focusedNode,\n getNodeAction,\n getNodeColumnSet,\n isNodeHighlighted,\n isNodeSelected,\n isNodeShowingChangeAnalysis,\n showContextMenu,\n viewOptions,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n } = useLineageViewContextSafe();\n const { isActionAvailable } = useLineageGraphContext();\n\n // Computed state\n const changeCategory = cll?.current.nodes[id]\n ?.change_category as ChangeCategory;\n const isHighlighted = isNodeHighlighted(id);\n const isSelected = isNodeSelected(id);\n const isFocusedByImpactRadius =\n viewOptions.column_level_lineage?.node_id === id &&\n viewOptions.column_level_lineage.column === undefined;\n const isFocused = focusedNode?.id === id || isFocusedByImpactRadius;\n const isShowingChangeAnalysis = isNodeShowingChangeAnalysis(id);\n const columnSet = getNodeColumnSet(data.id);\n const action =\n selectMode === \"action_result\" ? getNodeAction(data.id) : undefined;\n\n // Map to UI package types\n const nodeChangeStatus: NodeChangeStatus | undefined = changeStatus as\n | NodeChangeStatus\n | undefined;\n const nodeSelectMode: SelectMode = selectMode as SelectMode;\n\n // Create action tag if in action_result mode\n const actionTag =\n selectMode === \"action_result\" && action ? (\n <ActionTagDisplay nodeId={id} nodeName={name} />\n ) : undefined;\n\n // Create runs aggregated tag if model and not in action_result mode\n const runsAggregatedTag =\n selectMode !== \"action_result\" && data.resourceType === \"model\" ? (\n <NodeRunsAggregatedDisplay\n id={data.id}\n inverted={selectMode === \"selecting\" && isSelected}\n />\n ) : undefined;\n\n // Callbacks\n const handleSelect = (nodeId: string) => {\n selectNode(nodeId);\n };\n\n const handleContextMenu = (event: React.MouseEvent, _nodeId: string) => {\n showContextMenu(event, nodeProps as unknown as LineageGraphNode);\n };\n\n const handleShowImpactRadius = (nodeId: string) => {\n setChangeAnalysisMode(true);\n void showColumnLevelLineage({\n node_id: nodeId,\n change_analysis: true,\n no_upstream: true,\n });\n };\n\n return (\n <LineageNode\n id={id}\n data={{\n label: name,\n changeStatus: nodeChangeStatus,\n resourceType,\n }}\n // Interactive props\n interactive={interactive}\n selectMode={nodeSelectMode}\n isNodeSelected={isSelected}\n isFocused={isFocused}\n isHighlighted={isHighlighted}\n showContent={showContent}\n // Action display props\n actionTag={actionTag}\n showChangeAnalysis={isShowingChangeAnalysis}\n changeCategory={changeCategory}\n runsAggregatedTag={runsAggregatedTag}\n // Layout props\n hasParents={Object.keys(data.parents).length > 0}\n hasChildren={Object.keys(data.children).length > 0}\n columnCount={columnSet.size}\n columnHeight={COLUMN_HEIGHT}\n // Theme props\n isDark={isDark}\n // Callbacks\n onSelect={handleSelect}\n onContextMenu={handleContextMenu}\n onShowImpactRadius={\n changeStatus === \"modified\" && isActionAvailable(\"change_analysis\")\n ? handleShowImpactRadius\n : undefined\n }\n />\n );\n}\n\nexport const GraphNode = memo(GraphNodeComponent);\nGraphNode.displayName = \"GraphNode\";\n","import type { LineageGraphNode } from \"../../../contexts/lineage/types\";\nimport { colors } from \"../../../theme\";\nimport { GraphColumnNode } from \"../GraphColumnNodeOss\";\nimport GraphEdge from \"../GraphEdgeOss\";\nimport { GraphNode } from \"../GraphNodeOss\";\nimport { getIconForChangeStatus } from \"../styles\";\n\n/**\n * Node types configuration for ReactFlow.\n * Maps custom node type names to their React components.\n */\nexport const nodeTypes = {\n lineageGraphNode: GraphNode,\n lineageGraphColumnNode: GraphColumnNode,\n} as const;\n\n/**\n * Edge types configuration for ReactFlow.\n * Maps custom edge type names to their React components.\n */\nexport const edgeTypes = {\n lineageGraphEdge: GraphEdge,\n} as const;\n\n/**\n * Initial empty nodes array for ReactFlow initialization.\n */\nexport const initialNodes: LineageGraphNode[] = [];\n\n/**\n * Get the color for a node based on its change status.\n * Used by MiniMap for node coloring.\n *\n * @param node - The lineage graph node\n * @returns Hex color string\n */\nexport const getNodeColor = (node: LineageGraphNode): string => {\n return node.data.changeStatus\n ? getIconForChangeStatus(node.data.changeStatus).hexColor\n : colors.neutral[400];\n};\n","\"use client\";\n\n/**\n * @file LineageViewContextMenu.tsx\n * @description Context menu components for lineage graph nodes with dependency injection.\n *\n * These components provide right-click context menus for model nodes and column nodes\n * in the lineage visualization. They support:\n * - Query generation and navigation\n * - Row count, profile, value diff, and histogram diff actions\n * - Node selection (parent/child nodes)\n * - Column-level lineage (impact radius)\n *\n * The components use dependency injection for:\n * - Action execution (runAction callback)\n * - Navigation (onNavigate callback)\n * - Analytics tracking (onTrack callback)\n * - Histogram diff support checking (supportsHistogramDiff callback)\n * - Run type metadata (findByRunType callback)\n *\n * @example\n * ```tsx\n * <LineageViewContextMenu\n * x={100}\n * y={200}\n * node={selectedNode}\n * isOpen={true}\n * onClose={() => setMenuOpen(false)}\n * deps={{\n * runAction: (type, params, options) => executeAction(type, params, options),\n * onNavigate: (path) => router.push(path),\n * onTrack: (event, props) => analytics.track(event, props),\n * supportsHistogramDiff: (columnType) => checkHistogramSupport(columnType),\n * findByRunType: (type) => getRunTypeMetadata(type),\n * }}\n * />\n * ```\n */\n\nimport Box from \"@mui/material/Box\";\nimport Divider from \"@mui/material/Divider\";\nimport Menu from \"@mui/material/Menu\";\nimport MenuItem from \"@mui/material/MenuItem\";\nimport { type ReactNode, useState } from \"react\";\nimport { BiArrowFromBottom, BiArrowToBottom } from \"react-icons/bi\";\nimport { FaRegDotCircle } from \"react-icons/fa\";\nimport type { CllInput } from \"../../../api/cll\";\nimport type { SubmitRunTrackProps } from \"../../../api/runs\";\nimport {\n isLineageGraphColumnNode,\n isLineageGraphNode,\n type LineageGraphColumnNode,\n type LineageGraphNode,\n type LineageGraphNodes,\n} from \"../../../contexts/lineage/types\";\nimport { formatSelectColumns } from \"../../../utils/formatSelect\";\nimport type { IconComponent } from \"../../run/types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Tracking event types for context menu actions\n */\nexport type ContextMenuTrackEvent = \"explore_action\" | \"lineage_selection\";\n\n/**\n * Properties for explore action tracking\n */\nexport interface ExploreActionTrackProps {\n action: string;\n source: string;\n node_count: number;\n}\n\n/**\n * Properties for lineage selection tracking\n */\nexport interface LineageSelectionTrackProps {\n action: string;\n}\n\n/**\n * Run type metadata returned by findByRunType\n */\nexport interface RunTypeMetadata {\n title: string;\n icon: IconComponent;\n}\n\n/**\n * Dependency injection props for context menu components.\n * These allow the consumer to inject OSS-specific behavior.\n */\nexport interface LineageViewContextMenuDeps {\n /**\n * Execute a run action (e.g., row_count_diff, profile_diff).\n * @param type - The run type to execute\n * @param params - Parameters for the run\n * @param options - Options including showForm and trackProps\n */\n runAction?: (\n type: string,\n params: Record<string, unknown>,\n options: { showForm: boolean; trackProps: SubmitRunTrackProps },\n ) => void;\n\n /**\n * Navigate to a path in the application.\n * @param path - The path to navigate to (e.g., \"/query\")\n */\n onNavigate?: (path: string) => void;\n\n /**\n * Track analytics events.\n * @param event - The event type\n * @param props - Event properties\n */\n onTrack?: (\n event: ContextMenuTrackEvent,\n props: ExploreActionTrackProps | LineageSelectionTrackProps,\n ) => void;\n\n /**\n * Check if histogram diff is supported for a column type.\n * @param columnType - The column data type\n * @returns true if histogram diff is supported\n */\n supportsHistogramDiff?: (columnType: string) => boolean;\n\n /**\n * Get metadata for a run type (title, icon).\n * @param type - The run type\n * @returns Run type metadata or undefined\n */\n findByRunType?: (type: string) => RunTypeMetadata | undefined;\n\n /**\n * Set the SQL query in the query context.\n * @param query - The SQL query string\n */\n setSqlQuery?: (query: string) => void;\n\n /**\n * Set the primary keys in the query context.\n * @param keys - Array of primary key column names\n */\n setPrimaryKeys?: (keys: string[] | undefined) => void;\n\n /**\n * Get the primary key for a model.\n * @param modelName - The model name\n * @returns The primary key column name or undefined\n */\n getPrimaryKey?: (modelName: string) => string | undefined;\n\n /**\n * Wrapper component for disabled menu items (e.g., SetupConnectionPopover).\n * Rendered when mode is \"metadata only\".\n */\n DisabledItemWrapper?: React.ComponentType<{\n display: boolean;\n children: ReactNode;\n }>;\n}\n\n/**\n * Context menu view options passed from LineageViewContext\n */\nexport interface ContextMenuViewOptions {\n selectMode?: \"selecting\" | \"action_result\" | undefined;\n cll?: unknown;\n showColumnLevelLineage?: (params: CllInput) => Promise<void>;\n setChangeAnalysisMode?: (active: boolean) => void;\n selectParentNodes?: (nodeId: string, degree?: number) => void;\n selectChildNodes?: (nodeId: string, degree?: number) => void;\n getNodeColumnSet?: (nodeId: string) => Set<string>;\n}\n\n/**\n * Feature toggles that affect context menu behavior\n */\nexport interface ContextMenuFeatureToggles {\n disableDatabaseQuery?: boolean;\n disableViewActionDropdown?: boolean;\n mode?: string;\n}\n\n/**\n * Server flags that affect context menu behavior\n */\nexport interface ContextMenuServerFlags {\n single_env_onboarding?: boolean;\n}\n\n/**\n * Props for the main LineageViewContextMenu component\n */\nexport interface LineageViewContextMenuProps {\n x: number;\n y: number;\n node?: LineageGraphNodes;\n isOpen: boolean;\n onClose: () => void;\n deps?: LineageViewContextMenuDeps;\n viewOptions?: ContextMenuViewOptions;\n featureToggles?: ContextMenuFeatureToggles;\n serverFlags?: ContextMenuServerFlags;\n noCatalogCurrent?: boolean;\n isActionAvailable?: (actionName: string) => boolean;\n}\n\n/**\n * Props for ModelNodeContextMenu\n */\nexport interface ModelNodeContextMenuProps {\n x: number;\n y: number;\n node?: LineageGraphNode;\n isOpen: boolean;\n onClose: () => void;\n deps?: LineageViewContextMenuDeps;\n viewOptions?: ContextMenuViewOptions;\n featureToggles?: ContextMenuFeatureToggles;\n serverFlags?: ContextMenuServerFlags;\n noCatalogCurrent?: boolean;\n isActionAvailable?: (actionName: string) => boolean;\n}\n\n/**\n * Props for ColumnNodeContextMenu\n */\nexport interface ColumnNodeContextMenuProps {\n x: number;\n y: number;\n node?: LineageGraphColumnNode;\n isOpen: boolean;\n onClose: () => void;\n deps?: LineageViewContextMenuDeps;\n featureToggles?: ContextMenuFeatureToggles;\n serverFlags?: ContextMenuServerFlags;\n isActionAvailable?: (actionName: string) => boolean;\n}\n\n// ============================================================================\n// Internal Types\n// ============================================================================\n\ninterface ContextMenuItem {\n label?: string;\n itemIcon?: ReactNode;\n action?: () => void;\n isDisabled?: boolean;\n isSeparator?: boolean;\n}\n\ninterface ContextMenuProps {\n menuItems: ContextMenuItem[];\n open: boolean;\n onClose: () => void;\n x: number;\n y: number;\n featureToggles?: ContextMenuFeatureToggles;\n DisabledItemWrapper?: React.ComponentType<{\n display: boolean;\n children: ReactNode;\n }>;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * Explore action constants\n */\nexport const EXPLORE_ACTION = {\n ROW_COUNT: \"row_count\",\n ROW_COUNT_DIFF: \"row_count_diff\",\n PROFILE: \"profile\",\n PROFILE_DIFF: \"profile_diff\",\n VALUE_DIFF: \"value_diff\",\n HISTOGRAM_DIFF: \"histogram_diff\",\n TOP_K_DIFF: \"top_k_diff\",\n} as const;\n\n/**\n * Explore source constants\n */\nexport const EXPLORE_SOURCE = {\n LINEAGE_VIEW_CONTEXT_MENU: \"lineage_view_context_menu\",\n} as const;\n\n/**\n * Lineage selection action constants\n */\nexport const LINEAGE_SELECTION_ACTION = {\n SELECT_PARENT_NODES: \"select_parent_nodes\",\n SELECT_CHILD_NODES: \"select_child_nodes\",\n SELECT_ALL_UPSTREAM: \"select_all_upstream\",\n SELECT_ALL_DOWNSTREAM: \"select_all_downstream\",\n} as const;\n\n// ============================================================================\n// Internal Components\n// ============================================================================\n\n/**\n * Base context menu component that renders the MUI Menu with items.\n */\nconst ContextMenu = ({\n menuItems,\n open,\n onClose,\n x,\n y,\n featureToggles,\n DisabledItemWrapper,\n}: ContextMenuProps) => {\n // Default wrapper that just renders children\n const Wrapper =\n DisabledItemWrapper ??\n (({ children }: { children: ReactNode }) => <>{children}</>);\n const isMetadataOnlyMode = featureToggles?.mode === \"metadata only\";\n\n return (\n <Menu\n open={open}\n onClose={onClose}\n anchorReference=\"anchorPosition\"\n anchorPosition={{ top: y, left: x }}\n slotProps={{\n paper: {\n sx: { fontSize: \"0.85rem\", width: \"250px\" },\n },\n }}\n >\n {menuItems.length === 0 ? (\n <MenuItem disabled key=\"no action\">\n No action available\n </MenuItem>\n ) : (\n menuItems.map(\n ({ isSeparator, label, isDisabled, action, itemIcon }) => {\n if (isSeparator) {\n return <Divider key={label} />;\n }\n\n const menuItem = (\n <MenuItem\n key={label}\n disabled={isDisabled}\n onClick={() => {\n if (action) {\n action();\n }\n onClose();\n }}\n >\n {itemIcon} {label}\n </MenuItem>\n );\n\n // Wrap disabled items with DisabledItemWrapper if provided\n if (isDisabled && DisabledItemWrapper) {\n return (\n <Wrapper display={isMetadataOnlyMode} key={label}>\n {menuItem}\n </Wrapper>\n );\n }\n\n return menuItem;\n },\n )\n )}\n </Menu>\n );\n};\n\n// ============================================================================\n// Public Components\n// ============================================================================\n\n/**\n * Context menu for model/node right-click actions.\n *\n * Shows menu items for:\n * - Show Impact Radius (for modified nodes)\n * - Query / Query Related Columns / Query Modified Columns\n * - Row Count / Row Count Diff\n * - Profile / Profile Diff\n * - Value Diff\n * - Select Parent/Child Nodes\n */\nexport const ModelNodeContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n deps = {},\n viewOptions = {},\n featureToggles = {},\n serverFlags = {},\n noCatalogCurrent = false,\n isActionAvailable = () => true,\n}: ModelNodeContextMenuProps) => {\n const menuItems: ContextMenuItem[] = [];\n\n const {\n runAction,\n onNavigate,\n onTrack,\n findByRunType,\n setSqlQuery,\n setPrimaryKeys,\n getPrimaryKey,\n DisabledItemWrapper,\n } = deps;\n\n const {\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n } = viewOptions;\n\n const singleEnv = serverFlags.single_env_onboarding ?? false;\n const isQueryDisabled = featureToggles.disableDatabaseQuery ?? false;\n\n if (!node?.data) {\n return <></>;\n }\n\n const modelNode = node.data;\n const resourceType = modelNode.resourceType;\n const columns = getNodeColumnSet ? Array.from(getNodeColumnSet(node.id)) : [];\n const trackProps: SubmitRunTrackProps = {\n source: \"lineage_model_node\",\n };\n const changeStatus = modelNode.changeStatus;\n const primaryKey = getPrimaryKey?.(modelNode.name);\n\n // Show Impact Radius for modified nodes\n if (changeStatus === \"modified\") {\n menuItems.push({\n label: \"Show Impact Radius\",\n itemIcon: <FaRegDotCircle />,\n isDisabled: noCatalogCurrent || !isActionAvailable(\"change_analysis\"),\n action: () => {\n setChangeAnalysisMode?.(true);\n void showColumnLevelLineage?.({\n node_id: node.id,\n change_analysis: true,\n no_upstream: true,\n });\n },\n });\n }\n\n // Query actions for model/seed/snapshot resource types\n if (\n !selectMode &&\n resourceType &&\n [\"model\", \"seed\", \"snapshot\"].includes(resourceType)\n ) {\n if (menuItems.length > 0) {\n menuItems.push({\n label: \"select group one\",\n isSeparator: true,\n });\n }\n\n // Query action\n const queryRunType = singleEnv ? \"query\" : \"query_diff\";\n const queryRun = findByRunType?.(queryRunType);\n const baseColumns = Object.keys(modelNode.data.base?.columns ?? {});\n const currentColumns = Object.keys(modelNode.data.current?.columns ?? {});\n const formattedColumns = formatSelectColumns(baseColumns, currentColumns);\n let query = `select * from {{ ref(\"${modelNode.name}\") }}`;\n if (formattedColumns.length) {\n query = `select \\n ${formattedColumns.join(\"\\n \")}\\nfrom {{ ref(\"${modelNode.name}\") }}`;\n }\n\n if (queryRun) {\n menuItems.push({\n label: \"Query\",\n itemIcon: (\n <Box component={queryRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n setSqlQuery?.(query);\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys?.(\n primaryKey !== undefined ? [primaryKey] : undefined,\n );\n }\n onNavigate?.(\"/query\");\n },\n });\n }\n\n // Query Related Columns (when CLL is active)\n if (columns.length > 0 && queryRun) {\n if (cll !== undefined) {\n const allColumns = new Set<string>();\n if (primaryKey) {\n allColumns.add(primaryKey);\n }\n columns.forEach((column) => {\n allColumns.add(column);\n });\n\n menuItems.push({\n label: \"Query Related Columns\",\n itemIcon: (\n <Box component={queryRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n const relatedQuery = `select \\n ${Array.from(allColumns).join(\",\\n \")}\\nfrom {{ ref(\"${modelNode.name}\") }}`;\n setSqlQuery?.(relatedQuery);\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys?.(\n primaryKey !== undefined ? [primaryKey] : undefined,\n );\n }\n onNavigate?.(\"/query\");\n },\n });\n } else {\n // Query Modified Columns\n const changedColumns = Object.entries(modelNode.change?.columns ?? {})\n .filter(([, value]) => value === \"modified\")\n .map(([key]) => key);\n if (changedColumns.length > 0) {\n const allColumns = new Set<string>();\n if (primaryKey) {\n allColumns.add(primaryKey);\n }\n changedColumns.forEach((column) => {\n allColumns.add(column);\n });\n\n menuItems.push({\n label: \"Query Modified Columns\",\n itemIcon: (\n <Box component={queryRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n const modifiedQuery = `select \\n ${Array.from(allColumns).join(\",\\n \")}\\nfrom {{ ref(\"${modelNode.name}\") }}`;\n setSqlQuery?.(modifiedQuery);\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys?.(\n primaryKey !== undefined ? [primaryKey] : undefined,\n );\n }\n onNavigate?.(\"/query\");\n },\n });\n }\n }\n }\n\n // Row Count / Row Count Diff\n const rowCountRunType = singleEnv ? \"row_count\" : \"row_count_diff\";\n const rowCountRun = findByRunType?.(rowCountRunType);\n if (rowCountRun) {\n menuItems.push({\n label: rowCountRun.title,\n itemIcon: (\n <Box component={rowCountRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n onTrack?.(\"explore_action\", {\n action: singleEnv\n ? EXPLORE_ACTION.ROW_COUNT\n : EXPLORE_ACTION.ROW_COUNT_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n rowCountRunType,\n { node_names: [modelNode.name] },\n { showForm: false, trackProps },\n );\n },\n });\n }\n\n // Profile / Profile Diff\n const profileRunType = singleEnv ? \"profile\" : \"profile_diff\";\n const profileRun = findByRunType?.(profileRunType);\n if (profileRun) {\n menuItems.push({\n label: profileRun.title,\n itemIcon: (\n <Box component={profileRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n const profileColumns = getNodeColumnSet\n ? Array.from(getNodeColumnSet(node.id))\n : [];\n onTrack?.(\"explore_action\", {\n action: singleEnv\n ? EXPLORE_ACTION.PROFILE\n : EXPLORE_ACTION.PROFILE_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n profileRunType,\n { model: modelNode.name, columns: profileColumns },\n { showForm: true, trackProps },\n );\n },\n });\n }\n\n // Value Diff (multi-env only)\n if (!singleEnv) {\n const valueDiffRun = findByRunType?.(\"value_diff\");\n if (valueDiffRun) {\n menuItems.push({\n label: valueDiffRun.title,\n itemIcon: (\n <Box\n component={valueDiffRun.icon}\n sx={{ display: \"inline-flex\" }}\n />\n ),\n isDisabled: isQueryDisabled,\n action: () => {\n const valueDiffColumns = getNodeColumnSet\n ? Array.from(getNodeColumnSet(node.id))\n : [];\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.VALUE_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"value_diff\",\n { model: modelNode.name, columns: valueDiffColumns },\n { showForm: true, trackProps },\n );\n },\n });\n }\n }\n }\n\n // Select Parent/Child Nodes (multi-env only)\n if (!singleEnv) {\n if (menuItems.length > 0) {\n menuItems.push({\n label: \"select group two\",\n isSeparator: true,\n });\n }\n menuItems.push({\n label: \"Select Parent Nodes\",\n itemIcon: <BiArrowFromBottom />,\n action: () => {\n onTrack?.(\"lineage_selection\", {\n action: LINEAGE_SELECTION_ACTION.SELECT_PARENT_NODES,\n });\n selectParentNodes?.(node.id, 1);\n },\n });\n menuItems.push({\n label: \"Select Child Nodes\",\n itemIcon: <BiArrowToBottom />,\n action: () => {\n onTrack?.(\"lineage_selection\", {\n action: LINEAGE_SELECTION_ACTION.SELECT_CHILD_NODES,\n });\n selectChildNodes?.(node.id, 1);\n },\n });\n menuItems.push({\n label: \"Select All Upstream Nodes\",\n itemIcon: <BiArrowFromBottom />,\n action: () => {\n onTrack?.(\"lineage_selection\", {\n action: LINEAGE_SELECTION_ACTION.SELECT_ALL_UPSTREAM,\n });\n selectParentNodes?.(node.id);\n },\n });\n menuItems.push({\n label: \"Select All Downstream Nodes\",\n itemIcon: <BiArrowToBottom />,\n action: () => {\n onTrack?.(\"lineage_selection\", {\n action: LINEAGE_SELECTION_ACTION.SELECT_ALL_DOWNSTREAM,\n });\n selectChildNodes?.(node.id);\n },\n });\n }\n\n return (\n <ContextMenu\n x={x}\n y={y}\n menuItems={menuItems}\n open={isOpen}\n onClose={onClose}\n featureToggles={featureToggles}\n DisabledItemWrapper={DisabledItemWrapper}\n />\n );\n};\n\n/**\n * Context menu for column node right-click actions.\n *\n * Shows menu items for:\n * - Profile / Profile Diff\n * - Histogram Diff\n * - Top-K Diff\n * - Value Diff\n */\nexport const ColumnNodeContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n deps = {},\n featureToggles = {},\n serverFlags = {},\n isActionAvailable = () => true,\n}: ColumnNodeContextMenuProps) => {\n const menuItems: ContextMenuItem[] = [];\n\n const {\n runAction,\n onTrack,\n findByRunType,\n supportsHistogramDiff: checkHistogramSupport,\n DisabledItemWrapper,\n } = deps;\n\n const singleEnv = serverFlags.single_env_onboarding ?? false;\n const isQueryDisabled = featureToggles.disableDatabaseQuery ?? false;\n\n if (node?.data === undefined) {\n return <></>;\n }\n\n const columnNode = node.data;\n const modelNode = columnNode.node;\n const column = columnNode.column;\n const columnType = columnNode.type;\n const trackProps: SubmitRunTrackProps = {\n source: \"lineage_column_node\",\n };\n\n const handleProfileDiff = () => {\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.PROFILE_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"profile_diff\",\n { model: modelNode.name, columns: [column] },\n { showForm: false, trackProps },\n );\n };\n\n const handleHistogramDiff = () => {\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.HISTOGRAM_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"histogram_diff\",\n { model: modelNode.name, column_name: column, column_type: columnType },\n { showForm: false, trackProps },\n );\n };\n\n const handleTopkDiff = () => {\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.TOP_K_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"top_k_diff\",\n { model: modelNode.name, column_name: column, k: 50 },\n { showForm: false, trackProps },\n );\n };\n\n const handleValueDiff = () => {\n onTrack?.(\"explore_action\", {\n action: EXPLORE_ACTION.VALUE_DIFF,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: 1,\n });\n runAction?.(\n \"value_diff\",\n { model: modelNode.name, columns: [column] },\n { showForm: true, trackProps },\n );\n };\n\n const addedOrRemoved =\n modelNode.data.base?.columns?.[column] === undefined ||\n modelNode.data.current?.columns?.[column] === undefined;\n\n // Profile / Profile Diff\n const profileRunType = singleEnv ? \"profile\" : \"profile_diff\";\n const profileRun = findByRunType?.(profileRunType);\n if (profileRun) {\n menuItems.push({\n label: profileRun.title,\n itemIcon: (\n <Box component={profileRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n action: handleProfileDiff,\n isDisabled:\n addedOrRemoved || !isActionAvailable(\"profile_diff\") || isQueryDisabled,\n });\n }\n\n // Histogram Diff, Top-K Diff, Value Diff (multi-env only)\n if (!singleEnv) {\n const histogramRun = findByRunType?.(\"histogram_diff\");\n if (histogramRun) {\n const histogramSupported = checkHistogramSupport?.(columnType) ?? true;\n menuItems.push({\n label: histogramRun.title,\n itemIcon: (\n <Box component={histogramRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n action: handleHistogramDiff,\n isDisabled: addedOrRemoved || !histogramSupported || isQueryDisabled,\n });\n }\n\n const topKRun = findByRunType?.(\"top_k_diff\");\n if (topKRun) {\n menuItems.push({\n label: topKRun.title,\n itemIcon: (\n <Box component={topKRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n action: handleTopkDiff,\n isDisabled: addedOrRemoved || isQueryDisabled,\n });\n }\n\n const valueDiffRun = findByRunType?.(\"value_diff\");\n if (valueDiffRun) {\n menuItems.push({\n label: valueDiffRun.title,\n itemIcon: (\n <Box component={valueDiffRun.icon} sx={{ display: \"inline-flex\" }} />\n ),\n action: handleValueDiff,\n isDisabled: addedOrRemoved || isQueryDisabled,\n });\n }\n }\n\n return (\n <ContextMenu\n x={x}\n y={y}\n menuItems={menuItems}\n open={isOpen}\n onClose={onClose}\n featureToggles={featureToggles}\n DisabledItemWrapper={DisabledItemWrapper}\n />\n );\n};\n\n/**\n * Main context menu component that delegates to ModelNodeContextMenu\n * or ColumnNodeContextMenu based on node type.\n *\n * @example\n * ```tsx\n * <LineageViewContextMenu\n * x={event.clientX}\n * y={event.clientY}\n * node={selectedNode}\n * isOpen={menuOpen}\n * onClose={() => setMenuOpen(false)}\n * deps={contextMenuDeps}\n * viewOptions={lineageViewOptions}\n * featureToggles={featureToggles}\n * serverFlags={serverFlags}\n * noCatalogCurrent={!catalog?.current}\n * isActionAvailable={isActionAvailable}\n * />\n * ```\n */\nexport const LineageViewContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n deps = {},\n viewOptions = {},\n featureToggles = {},\n serverFlags = {},\n noCatalogCurrent = false,\n isActionAvailable = () => true,\n}: LineageViewContextMenuProps) => {\n if (featureToggles.disableViewActionDropdown) {\n return (\n <ContextMenu\n menuItems={[]}\n open={isOpen}\n onClose={onClose}\n x={x}\n y={y}\n featureToggles={featureToggles}\n DisabledItemWrapper={deps.DisabledItemWrapper}\n />\n );\n }\n\n if (node && isLineageGraphNode(node)) {\n return (\n <ModelNodeContextMenu\n x={x}\n y={y}\n isOpen={isOpen}\n onClose={onClose}\n node={node}\n deps={deps}\n viewOptions={viewOptions}\n featureToggles={featureToggles}\n serverFlags={serverFlags}\n noCatalogCurrent={noCatalogCurrent}\n isActionAvailable={isActionAvailable}\n />\n );\n }\n\n if (node && isLineageGraphColumnNode(node)) {\n return (\n <ColumnNodeContextMenu\n x={x}\n y={y}\n isOpen={isOpen}\n onClose={onClose}\n node={node}\n deps={deps}\n featureToggles={featureToggles}\n serverFlags={serverFlags}\n isActionAvailable={isActionAvailable}\n />\n );\n }\n\n return null;\n};\n\n/**\n * Hook to manage context menu state.\n * Returns props for LineageViewContextMenu and methods to show/close the menu.\n *\n * @example\n * ```tsx\n * const { props, showContextMenu, closeContextMenu } = useLineageViewContextMenu();\n *\n * const handleNodeContextMenu = (event: React.MouseEvent, node: LineageGraphNodes) => {\n * event.preventDefault();\n * showContextMenu(event.clientX, event.clientY, node);\n * };\n *\n * return (\n * <>\n * <LineageCanvas onNodeContextMenu={handleNodeContextMenu} />\n * <LineageViewContextMenu {...props} deps={deps} />\n * </>\n * );\n * ```\n */\nexport const useLineageViewContextMenu = () => {\n const [open, setOpen] = useState(false);\n const onOpen = () => setOpen(true);\n const onClose = () => setOpen(false);\n const [position, setPosition] = useState<{ x: number; y: number }>({\n x: 0,\n y: 0,\n });\n const [node, setNode] = useState<LineageGraphNodes>();\n\n const showContextMenu = (x: number, y: number, node: LineageGraphNodes) => {\n setPosition({ x, y });\n setNode(node);\n onOpen();\n };\n\n const closeContextMenu = () => {\n setPosition({ x: 0, y: 0 });\n setNode(undefined);\n onClose();\n };\n\n const props: Pick<\n LineageViewContextMenuProps,\n \"x\" | \"y\" | \"node\" | \"isOpen\" | \"onClose\"\n > = {\n x: position.x,\n y: position.y,\n node,\n isOpen: open,\n onClose,\n };\n\n return {\n props,\n showContextMenu,\n closeContextMenu,\n };\n};\n","import {\n IGNORE_SCREENSHOT_CLASS,\n useClipBoardToast,\n useCopyToClipboard,\n useThemeColors,\n} from \"../../../hooks\";\nimport { colors } from \"../../../theme\";\n\n/**\n * Hook that provides clipboard functionality for the lineage view.\n * Wraps useCopyToClipboard with lineage-specific configuration.\n *\n * @returns Object containing copyToClipboard function, ImageDownloadModal component, and ref\n */\nexport const useLineageCopyToClipboard = () => {\n const { isDark } = useThemeColors();\n const { successToast, failToast } = useClipBoardToast();\n\n return useCopyToClipboard({\n renderLibrary: \"html-to-image\",\n imageType: \"png\",\n shadowEffect: true,\n backgroundColor: isDark ? colors.neutral[900] : colors.neutral[50],\n ignoreElements: (element: Element) => {\n try {\n return element.classList.contains(IGNORE_SCREENSHOT_CLASS);\n } catch {\n if (element.className) {\n return element.className.includes(IGNORE_SCREENSHOT_CLASS);\n }\n return false;\n }\n },\n onSuccess: () => {\n successToast(\"Copied the Lineage View as an image to clipboard\");\n },\n onError: (error) => {\n console.error(\"Error taking screenshot\", error);\n failToast(\"Failed to copy image to clipboard\", error);\n },\n });\n};\n","import { useRouter } from \"next/navigation\";\nimport { useCallback } from \"react\";\nimport type { Check } from \"../../../api\";\nimport { useRouteConfig } from \"../../../contexts\";\n\n/**\n * Hook that provides navigation to a check's detail page.\n *\n * Uses the app router to navigate to /checks/?id={check_id}\n *\n * @returns A function that navigates to the given check's detail page\n */\nexport const useNavToCheck = () => {\n const router = useRouter();\n const { basePath } = useRouteConfig();\n\n return useCallback(\n (check: Check) => {\n if (check.check_id) {\n router.push(`${basePath}/checks/?id=${check.check_id}`);\n }\n },\n [router.push, basePath],\n );\n};\n","import type { RefObject } from \"react\";\nimport { useEffect, useRef } from \"react\";\n\n/**\n * Hook that observes element resize and calls a handler when the size changes\n * significantly (more than 10px difference in width or height).\n *\n * Used to trigger layout updates when the lineage view container is resized.\n *\n * @param ref - RefObject pointing to the HTML element to observe\n * @param handler - Callback function to invoke when resize is detected\n */\nexport const useResizeObserver = (\n ref: RefObject<HTMLElement | null>,\n handler: () => void,\n) => {\n const size = useRef({\n width: 0,\n height: 0,\n });\n\n useEffect(() => {\n const target = ref.current;\n const handleResize = (entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const newWidth = entry.contentRect.width;\n const newHeight = entry.contentRect.height;\n\n if (\n Math.abs(newHeight - size.current.height) > 10 ||\n Math.abs(newWidth - size.current.width) > 10\n ) {\n if (\n size.current.height > 0 &&\n newHeight > 0 &&\n size.current.width > 0 &&\n newWidth > 0\n ) {\n handler();\n }\n }\n size.current = {\n width: newWidth,\n height: newHeight,\n };\n }\n };\n\n const resizeObserver = new ResizeObserver(handleResize);\n\n if (target) {\n resizeObserver.observe(target);\n }\n\n return () => {\n if (target) {\n resizeObserver.unobserve(target);\n }\n };\n }, [handler, ref]);\n};\n","import { useCallback } from \"react\";\nimport type { LineageGraphNodes } from \"../../../contexts/lineage/types\";\nimport { isLineageGraphNode } from \"../../../contexts/lineage/types\";\nimport {\n type LineageViewRenderProps,\n trackLineageViewRender,\n} from \"../../../lib/api/track\";\n\n/**\n * Hook that provides a function to track lineage view render events.\n * Calculates node statistics and sends tracking data.\n *\n * @returns A memoized callback function for tracking lineage renders\n */\nexport const useTrackLineageRender = () => {\n return useCallback(\n (\n nodes: LineageGraphNodes[],\n currentViewMode: string,\n impactRadiusEnabled: boolean,\n cllColumnActive: boolean,\n rightSidebarOpen: boolean,\n ) => {\n const lineageGraphNodesOnly = nodes.filter(isLineageGraphNode);\n const grouped = Object.groupBy(\n lineageGraphNodesOnly,\n (node) => node.data.changeStatus ?? \"unchanged\",\n );\n // Prefix status counts with \"nodes_\"\n const statusCounts = Object.fromEntries(\n Object.entries(grouped).map(([status, nodes]) => [\n `nodes_${status}`,\n nodes?.length ?? 0,\n ]),\n );\n const trackingData = {\n node_count: lineageGraphNodesOnly.length,\n view_mode: currentViewMode,\n impact_radius_enabled: impactRadiusEnabled,\n right_sidebar_open: rightSidebarOpen,\n ...statusCounts,\n } as LineageViewRenderProps;\n // Only include cll_column_active when a column is being viewed\n if (cllColumnActive) {\n trackingData.cll_column_active = true;\n }\n trackLineageViewRender(trackingData);\n },\n [],\n );\n};\n","/**\n * @file useMultiNodesActionOss.ts\n * @description OSS wrapper for useMultiNodesAction hook.\n *\n * This wraps the base hook and adds Amplitude tracking.\n *\n * @see useMultiNodesAction for the base implementation\n */\n\nimport type { LineageGraphNode } from \"../index\";\nimport {\n EXPLORE_ACTION,\n EXPLORE_SOURCE,\n trackExploreAction,\n} from \"../lib/api/track\";\nimport {\n type MultiNodesActionCallbacks,\n type MultiNodesActionTrackProps,\n useMultiNodesAction as useMultiNodesActionBase,\n} from \"./useMultiNodesAction\";\n\n/**\n * Maps the generic action type to OSS-specific tracking constants.\n * This allows the base hook to use generic action names while\n * OSS uses its specific Amplitude tracking events.\n */\nconst actionTypeToExploreAction = {\n row_count: EXPLORE_ACTION.ROW_COUNT,\n row_count_diff: EXPLORE_ACTION.ROW_COUNT_DIFF,\n value_diff: EXPLORE_ACTION.VALUE_DIFF,\n} as const;\n\n/**\n * Tracking callback implementation for OSS.\n * Translates generic action tracking to Amplitude events.\n */\nconst handleTrackAction = (props: MultiNodesActionTrackProps) => {\n const exploreAction = actionTypeToExploreAction[props.action];\n if (exploreAction) {\n trackExploreAction({\n action: exploreAction,\n source: EXPLORE_SOURCE.LINEAGE_VIEW_TOP_BAR,\n node_count: props.node_count,\n });\n }\n};\n\n/**\n * OSS wrapper for useMultiNodesAction that provides Amplitude tracking.\n *\n * @param nodes - Array of lineage graph nodes to operate on\n * @param callbacks - Lifecycle callbacks for action execution\n * @returns Object containing action state and operation methods\n */\nexport const useMultiNodesActionOss = (\n nodes: LineageGraphNode[],\n callbacks: MultiNodesActionCallbacks,\n) => {\n return useMultiNodesActionBase(nodes, {\n ...callbacks,\n onTrackAction: handleTrackAction,\n trackingSource: EXPLORE_SOURCE.LINEAGE_VIEW_TOP_BAR,\n });\n};\n","\"use client\";\n\n/**\n * @file useValueDiffAlertDialogOss.tsx\n * @description OSS wrapper for useValueDiffAlertDialog that adds tracking.\n *\n * This is a thin wrapper around the base hook that injects\n * tracking callbacks for analytics.\n */\n\nimport {\n EXPLORE_ACTION,\n EXPLORE_FORM_EVENT,\n trackExploreActionForm,\n} from \"../lib/api/track\";\nimport { useValueDiffAlertDialog as useBaseDialog } from \"./useValueDiffAlertDialog\";\n\n/**\n * Hook for displaying a value diff confirmation dialog with tracking.\n *\n * This wrapper adds tracking callbacks to the base hook.\n */\nfunction useValueDiffAlertDialogOss() {\n return useBaseDialog({\n onConfirm: () =>\n trackExploreActionForm({\n action: EXPLORE_ACTION.VALUE_DIFF,\n event: EXPLORE_FORM_EVENT.EXECUTE,\n }),\n onCancel: () =>\n trackExploreActionForm({\n action: EXPLORE_ACTION.VALUE_DIFF,\n event: EXPLORE_FORM_EVENT.CANCEL,\n }),\n });\n}\n\nexport default useValueDiffAlertDialogOss;\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport IconButton from \"@mui/material/IconButton\";\nimport React, { useState } from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport { SESSION_STORAGE_KEYS } from \"../../api\";\n\nexport interface NotificationProps {\n notification?: React.ReactNode;\n type: \"info\" | \"success\" | \"warning\" | \"error\";\n}\n\nexport function LineageViewNotification({\n notification,\n type,\n}: NotificationProps) {\n const notificationKey = SESSION_STORAGE_KEYS.lineageNotificationDismissed;\n\n // Initialize state from sessionStorage (lazy initialization)\n const [visible, setVisible] = useState(() => {\n const dismissed = sessionStorage.getItem(notificationKey);\n return dismissed !== \"true\";\n });\n\n if (notification === null || !visible) {\n return null;\n }\n\n const bgColor = {\n info: \"iochmara.light\",\n success: \"success.light\",\n warning: \"warning.light\",\n error: \"error.light\",\n }[type];\n\n return (\n <Box\n sx={{\n width: \"100%\",\n display: \"flex\",\n flexDirection: \"row\",\n p: \"5px 10px\",\n gap: \"5px\",\n alignItems: \"flex-start\",\n borderRadius: 1,\n boxShadow: 4,\n border: \"1px solid\",\n borderColor: \"neutral.light\",\n bgcolor: bgColor,\n }}\n >\n {notification}\n <Box sx={{ flex: 1 }} />\n <IconButton\n size=\"small\"\n onClick={() => {\n sessionStorage.setItem(notificationKey, \"true\");\n setVisible(false);\n }}\n >\n <IoClose />\n </IconButton>\n </Box>\n );\n}\n","\"use client\";\n\n/**\n * @file LineageViewContextMenuOss.tsx\n * @description Thin wrapper that imports from @datarecce/ui and injects OSS-specific implementations.\n *\n * This file serves as the integration layer between the @datarecce/ui library and the OSS application.\n * It injects:\n * - Amplitude analytics tracking\n * - Application routing\n * - Query context management\n * - Run type registry\n * - Histogram diff support checking\n */\n\nimport { useRouter } from \"next/navigation\";\nimport type {\n LineageGraphColumnNode,\n LineageGraphNode,\n LineageGraphNodes,\n} from \"../..\";\nimport {\n useLineageGraphContext,\n useLineageViewContextSafe,\n useRecceActionContext,\n useRecceInstanceContext,\n useRecceServerFlag,\n useRouteConfig,\n} from \"../../contexts\";\nimport { useModelColumns, useRecceQueryContext } from \"../../hooks\";\nimport { trackExploreAction, trackLineageSelection } from \"../../lib/api/track\";\nimport { SetupConnectionPopover } from \"../app\";\nimport { supportsHistogramDiff } from \"../histogram\";\nimport { findByRunType } from \"../run\";\nimport {\n ColumnNodeContextMenu as BaseColumnNodeContextMenu,\n LineageViewContextMenu as BaseLineageViewContextMenu,\n ModelNodeContextMenu as BaseModelNodeContextMenu,\n useLineageViewContextMenu as baseUseLineageViewContextMenu,\n EXPLORE_ACTION,\n EXPLORE_SOURCE,\n LINEAGE_SELECTION_ACTION,\n type LineageViewContextMenuDeps,\n} from \"./contextmenu\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface LineageViewContextMenuProps<T> {\n x: number;\n y: number;\n node?: T;\n isOpen: boolean;\n onClose: () => void;\n}\n\n// ============================================================================\n// Internal Hooks\n// ============================================================================\n\n/**\n * Hook to create the dependency injection props for context menu components.\n * Wires up OSS-specific implementations for tracking, navigation, etc.\n */\nconst useContextMenuDeps = (modelName?: string): LineageViewContextMenuDeps => {\n const { runAction } = useRecceActionContext();\n const { setSqlQuery, setPrimaryKeys } = useRecceQueryContext();\n const router = useRouter();\n const { primaryKey } = useModelColumns(modelName);\n const { basePath } = useRouteConfig();\n\n return {\n runAction: (type, params, options) => {\n runAction(type, params as Parameters<typeof runAction>[1], options);\n },\n onNavigate: (path) => {\n router.push(`${basePath}${path}`);\n },\n onTrack: (event, props) => {\n if (event === \"explore_action\") {\n trackExploreAction({\n action: (props as { action: string })\n .action as (typeof EXPLORE_ACTION)[keyof typeof EXPLORE_ACTION],\n source: EXPLORE_SOURCE.LINEAGE_VIEW_CONTEXT_MENU,\n node_count: (props as { node_count: number }).node_count,\n });\n } else if (event === \"lineage_selection\") {\n trackLineageSelection({\n action: (props as { action: string })\n .action as (typeof LINEAGE_SELECTION_ACTION)[keyof typeof LINEAGE_SELECTION_ACTION],\n });\n }\n },\n supportsHistogramDiff,\n findByRunType: (type) => {\n const entry = findByRunType(type as Parameters<typeof findByRunType>[0]);\n return entry ? { title: entry.title, icon: entry.icon } : undefined;\n },\n setSqlQuery,\n setPrimaryKeys,\n getPrimaryKey: () => primaryKey,\n // Note: SetupConnectionPopover has a more restrictive children type (ReactElement vs ReactNode)\n // but it works correctly in practice since we always pass MenuItem elements\n DisabledItemWrapper:\n SetupConnectionPopover as LineageViewContextMenuDeps[\"DisabledItemWrapper\"],\n };\n};\n\n// ============================================================================\n// Exported Components\n// ============================================================================\n\n/**\n * OSS wrapper for ModelNodeContextMenu.\n * Injects OSS-specific dependencies and context.\n */\nexport const ModelNodeContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n}: LineageViewContextMenuProps<LineageGraphNode>) => {\n const {\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n } = useLineageViewContextSafe();\n const { featureToggles } = useRecceInstanceContext();\n const { isActionAvailable, lineageGraph } = useLineageGraphContext();\n const { data: flag } = useRecceServerFlag();\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n\n const deps = useContextMenuDeps(node?.data?.name);\n\n return (\n <BaseModelNodeContextMenu\n x={x}\n y={y}\n node={node}\n isOpen={isOpen}\n onClose={onClose}\n deps={deps}\n viewOptions={{\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n }}\n featureToggles={{\n disableDatabaseQuery: featureToggles.disableDatabaseQuery,\n mode: featureToggles.mode ?? undefined,\n }}\n serverFlags={{\n single_env_onboarding: flag?.single_env_onboarding,\n }}\n noCatalogCurrent={noCatalogCurrent}\n isActionAvailable={isActionAvailable}\n />\n );\n};\n\n/**\n * OSS wrapper for ColumnNodeContextMenu.\n * Injects OSS-specific dependencies and context.\n */\nexport const ColumnNodeContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n}: LineageViewContextMenuProps<LineageGraphColumnNode>) => {\n const { featureToggles } = useRecceInstanceContext();\n const { isActionAvailable } = useLineageGraphContext();\n const { data: flag } = useRecceServerFlag();\n\n const deps = useContextMenuDeps(node?.data?.node?.name);\n\n return (\n <BaseColumnNodeContextMenu\n x={x}\n y={y}\n node={node}\n isOpen={isOpen}\n onClose={onClose}\n deps={deps}\n featureToggles={{\n disableDatabaseQuery: featureToggles.disableDatabaseQuery,\n mode: featureToggles.mode ?? undefined,\n }}\n serverFlags={{\n single_env_onboarding: flag?.single_env_onboarding,\n }}\n isActionAvailable={isActionAvailable}\n />\n );\n};\n\n/**\n * OSS wrapper for LineageViewContextMenu.\n * Injects OSS-specific dependencies and context.\n */\nexport const LineageViewContextMenu = ({\n isOpen,\n onClose,\n x,\n y,\n node,\n}: LineageViewContextMenuProps<LineageGraphNodes>) => {\n const {\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n } = useLineageViewContextSafe();\n const { featureToggles } = useRecceInstanceContext();\n const { isActionAvailable, lineageGraph } = useLineageGraphContext();\n const { data: flag } = useRecceServerFlag();\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n\n // Get model name from either model node or column node\n const modelName =\n node?.type === \"lineageGraphNode\"\n ? (node as LineageGraphNode).data?.name\n : node?.type === \"lineageGraphColumnNode\"\n ? (node as LineageGraphColumnNode).data?.node?.name\n : undefined;\n\n const deps = useContextMenuDeps(modelName);\n\n return (\n <BaseLineageViewContextMenu\n x={x}\n y={y}\n node={node}\n isOpen={isOpen}\n onClose={onClose}\n deps={deps}\n viewOptions={{\n selectMode,\n cll,\n showColumnLevelLineage,\n setChangeAnalysisMode,\n selectParentNodes,\n selectChildNodes,\n getNodeColumnSet,\n }}\n featureToggles={{\n disableDatabaseQuery: featureToggles.disableDatabaseQuery,\n disableViewActionDropdown: featureToggles.disableViewActionDropdown,\n mode: featureToggles.mode ?? undefined,\n }}\n serverFlags={{\n single_env_onboarding: flag?.single_env_onboarding,\n }}\n noCatalogCurrent={noCatalogCurrent}\n isActionAvailable={isActionAvailable}\n />\n );\n};\n\n/**\n * Re-export the hook from @datarecce/ui.\n * This hook doesn't need any OSS-specific modifications.\n */\nexport const useLineageViewContextMenu = baseUseLineageViewContextMenu;\n","import dagre from \"@dagrejs/dagre\";\nimport { Position } from \"@xyflow/react\";\nimport {\n COLUMN_HEIGHT,\n type LineageGraph,\n type LineageGraphColumnNode,\n type LineageGraphEdge,\n type LineageGraphNode,\n type LineageGraphNodes,\n layoutWithDagre,\n type NodeColumnSetMap,\n} from \"../..\";\nimport type { ColumnLineageData } from \"../../api\";\n\n/**\n * Convert a LineageGraph to React Flow nodes and edges with column-level lineage support\n *\n * This OSS-specific function extends the basic toReactFlow functionality with:\n * - Column-level lineage (CLL) visualization\n * - Dynamic node heights based on column count\n * - Column nodes as child nodes within parent model nodes\n *\n * @param lineageGraph - The lineage graph to convert\n * @param options - Conversion options\n * @param options.selectedNodes - Optional filter for which nodes to include\n * @param options.cll - Column-level lineage data for adding column nodes\n * @param options.existingPositions - Map of node IDs to their existing positions. If provided, nodes will preserve their positions and layout will be skipped if all nodes have positions.\n * @returns Tuple of [nodes, edges, nodeColumnSetMap] where nodeColumnSetMap tracks columns per node\n *\n * @example\n * ```tsx\n * const [nodes, edges, columnSetMap] = toReactFlow(lineageGraph, {\n * selectedNodes: [\"model.project.orders\"],\n * cll: columnLineageData,\n * });\n * ```\n */\nexport function toReactFlow(\n lineageGraph: LineageGraph,\n options?: {\n selectedNodes?: string[];\n cll?: ColumnLineageData;\n existingPositions?: Map<string, { x: number; y: number }>;\n },\n): [LineageGraphNodes[], LineageGraphEdge[], NodeColumnSetMap] {\n const nodes: LineageGraphNodes[] = [];\n const edges: LineageGraphEdge[] = [];\n const { selectedNodes, cll, existingPositions } = options ?? {};\n\n const nodeColumnSetMap: NodeColumnSetMap = {};\n\n function getWeight(from?: string) {\n if (from === \"base\") {\n return 0;\n } else if (from === \"current\") {\n return 2;\n } else {\n return 1;\n }\n }\n\n function compareFn(\n a: LineageGraphNode | LineageGraphEdge,\n b: LineageGraphNode | LineageGraphEdge,\n ) {\n const weightA = getWeight(a.data?.from);\n const weightB = getWeight(b.data?.from);\n\n if (weightA < weightB) {\n return -1;\n } else if (weightA > weightB) {\n return 1;\n }\n return 0;\n }\n\n const filterSet =\n selectedNodes !== undefined ? new Set(selectedNodes) : undefined;\n const sortedNodes = Object.values(lineageGraph.nodes).sort(compareFn);\n for (const node of sortedNodes) {\n if (filterSet && !filterSet.has(node.id)) {\n continue;\n }\n\n // add column nodes\n const nodeColumnSet = new Set<string>();\n let columnIndex = 0;\n if (cll) {\n const maybeCurrent = cll.current as unknown as\n | ColumnLineageData[\"current\"]\n | undefined;\n const parentMap = maybeCurrent?.parent_map[node.id] ?? new Set<string>();\n\n for (const parentKey of parentMap) {\n const source = parentKey;\n const target = node.id;\n\n edges.push({\n id: `m2c_${source}_${target}`,\n source,\n target,\n style: {\n zIndex: 9999,\n },\n });\n }\n\n for (const columnName of Object.keys(\n node.data.data.current?.columns ?? {},\n )) {\n const columnKey = `${node.id}_${columnName}`;\n const maybeCurrent = cll.current as unknown as\n | ColumnLineageData[\"current\"]\n | undefined;\n const column = maybeCurrent?.columns[columnKey];\n const parentMap =\n maybeCurrent?.parent_map[columnKey] ?? new Set<string>();\n\n if (column == null) {\n continue;\n }\n\n nodes.push({\n id: columnKey,\n position: { x: 10, y: 70 + columnIndex * COLUMN_HEIGHT },\n parentId: node.id,\n extent: \"parent\",\n draggable: false,\n className: \"no-track-pii-safe\",\n data: {\n node: node.data,\n column: column.name,\n type: column.type,\n transformationType: column.transformation_type,\n changeStatus: column.change_status,\n },\n style: {\n zIndex: 9999,\n },\n type: \"lineageGraphColumnNode\",\n targetPosition: Position.Left,\n sourcePosition: Position.Right,\n } as LineageGraphColumnNode);\n\n for (const parentColumn of parentMap) {\n const source = parentColumn;\n const target = columnKey;\n\n edges.push({\n id: `${source}_${target}`,\n source,\n target,\n style: {\n zIndex: 9999,\n },\n });\n }\n\n columnIndex++;\n nodeColumnSet.add(column.name);\n }\n }\n\n nodeColumnSetMap[node.id] = nodeColumnSet;\n\n let height = 60;\n if (columnIndex > 0) {\n height += 20 + columnIndex * COLUMN_HEIGHT;\n }\n\n const existingPosition = existingPositions?.get(node.id);\n nodes.unshift({\n id: node.id,\n position: existingPosition ?? { x: 0, y: 0 },\n width: 300,\n height: height,\n className: \"no-track-pii-safe\",\n data: {\n ...node.data,\n },\n type: \"lineageGraphNode\",\n targetPosition: Position.Left,\n sourcePosition: Position.Right,\n style: {\n width: 300,\n height: height,\n },\n } as LineageGraphNode);\n }\n\n const sortedEdges = Object.values(lineageGraph.edges).sort(compareFn);\n for (const edge of sortedEdges) {\n if (\n filterSet &&\n (!filterSet.has(edge.source) || !filterSet.has(edge.target))\n ) {\n continue;\n }\n\n edges.push({\n id: edge.id,\n type: \"lineageGraphEdge\",\n source: edge.source,\n target: edge.target,\n data: {\n ...edge.data,\n },\n } as LineageGraphEdge);\n }\n\n // Only run layout if any parent node is missing a position\n const needsLayout = nodes.some(\n (node) =>\n node.type === \"lineageGraphNode\" && !existingPositions?.has(node.id),\n );\n\n if (needsLayout) {\n layout(nodes, edges);\n }\n\n return [nodes, edges, nodeColumnSetMap];\n}\n\n/**\n * Apply dagre layout to lineage graph nodes and edges\n *\n * This is a thin wrapper around layoutWithDagre from @datarecce/ui\n * that provides the dagre library instance.\n *\n * @param nodes - Array of lineage graph nodes\n * @param edges - Array of lineage graph edges\n * @param direction - Layout direction (\"LR\" for left-to-right, \"TB\" for top-to-bottom)\n */\nexport const layout = (\n nodes: LineageGraphNodes[],\n edges: LineageGraphEdge[],\n direction = \"LR\",\n): void => {\n layoutWithDagre(dagre, nodes, edges, direction);\n};\n","import MuiAlert from \"@mui/material/Alert\";\nimport Box from \"@mui/material/Box\";\nimport \"./style.css\";\nimport type {\n CellClickedEvent,\n GridApi,\n GridReadyEvent,\n RowClassParams,\n} from \"ag-grid-community\";\nimport {\n forwardRef,\n Ref,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport type { NodeData } from \"../../api\";\nimport { useLineageGraphContext, useLineageViewContext } from \"../../contexts\";\nimport { trackColumnLevelLineage } from \"../../lib/api/track\";\nimport type {\n SchemaDiffRow,\n SchemaRow,\n} from \"../../lib/dataGrid/generators/toSchemaDataGrid\";\nimport {\n type DataGridHandle,\n EmptyRowsRenderer,\n ScreenshotDataGrid,\n} from \"../../primitives\";\nimport { createDataGridFromData } from \"../ui/dataGrid\";\n\nexport function SchemaLegend() {\n return (\n <Box\n sx={{\n display: \"flex\",\n gap: 2,\n px: 1,\n py: 0.5,\n fontSize: \"0.75rem\",\n color: \"text.secondary\",\n }}\n >\n <span>\n <span className=\"schema-change-badge schema-change-badge-added\">+</span>{\" \"}\n added\n </span>\n <span>\n <span className=\"schema-change-badge schema-change-badge-removed\">\n -\n </span>{\" \"}\n removed\n </span>\n <span>\n <span className=\"schema-change-badge schema-change-badge-changed\">\n ~\n </span>{\" \"}\n changed\n </span>\n </Box>\n );\n}\n\ninterface SchemaViewProps {\n base?: NodeData;\n current?: NodeData;\n enableScreenshot?: boolean;\n showMenu?: boolean;\n /** Per-column change status from breaking change analysis */\n columnChanges?: Record<string, \"added\" | \"removed\" | \"modified\"> | null;\n /** Callback when user clicks a definition-changed badge to view SQL diff */\n onViewCode?: () => void;\n}\n\nfunction PrivateSingleEnvSchemaView(\n { current, showMenu = true }: { current?: NodeData; showMenu?: boolean },\n ref: Ref<DataGridHandle>,\n) {\n const lineageViewContext = useLineageViewContext();\n const [gridApi, setGridApi] = useState<GridApi<SchemaRow> | null>(null);\n const [cllRunningMap, setCllRunningMap] = useState<Map<string, boolean>>(\n new Map(),\n );\n const { columns, rows } = useMemo(() => {\n return createDataGridFromData(\n { type: \"schema_single\", columns: current?.columns },\n { node: current, cllRunningMap, showMenu },\n );\n }, [current, cllRunningMap, showMenu]);\n\n const { lineageGraph, isActionAvailable } = useLineageGraphContext();\n const changeAnalysisAvailable = isActionAvailable(\"change_analysis\");\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n let catalogMissingMessage = undefined;\n if (noCatalogCurrent) {\n catalogMissingMessage =\n \"catalog.json not found. Run `recce debug` to troubleshoot.\";\n }\n\n const noSchemaCurrent = current && current.columns === undefined;\n let schemaMissingMessage = undefined;\n if (noSchemaCurrent) {\n schemaMissingMessage =\n \"Node schema not found in catalog.json. Please regenerate your catalog.json to update.\";\n }\n\n const handleViewCll = async (columnName: string) => {\n if (!changeAnalysisAvailable) return;\n trackColumnLevelLineage({ action: \"view\", source: \"schema_column\" });\n setCllRunningMap((prev) => new Map(prev).set(columnName, true));\n const modelId = current?.id;\n if (modelId) {\n await lineageViewContext?.showColumnLevelLineage({\n node_id: modelId,\n column: columnName,\n });\n }\n setCllRunningMap((prev) => new Map(prev).set(columnName, false));\n };\n\n const getRowId = useCallback(\n (params: { data: SchemaRow }) => {\n const modelId = current?.id;\n return `${modelId}-${params.data.name}`;\n },\n [current?.id],\n );\n\n const cll = lineageViewContext?.viewOptions.column_level_lineage;\n const selectedRowId = cll ? `${cll.node_id}-${cll.column}` : null;\n\n // Update row selection when cll changes\n useEffect(() => {\n if (!gridApi) return;\n gridApi.deselectAll();\n if (selectedRowId) {\n const rowNode = gridApi.getRowNode(selectedRowId);\n if (rowNode) {\n rowNode.setSelected(true);\n }\n }\n }, [gridApi, selectedRowId]);\n\n const handleGridReady = useCallback((event: GridReadyEvent<SchemaRow>) => {\n setGridApi(event.api);\n }, []);\n\n const getRowClass = (_params: RowClassParams<SchemaRow>) => {\n if (lineageViewContext !== undefined && changeAnalysisAvailable) {\n return \"row-normal row-selectable\";\n }\n return \"row-normal\";\n };\n\n const handleCellClicked = async (event: CellClickedEvent<SchemaRow>) => {\n // Skip if clicking on the menu button\n const target = event.event?.target as HTMLElement | undefined;\n if (target?.closest(\".row-context-menu\")) {\n return;\n }\n const row = event.data;\n if (row) {\n await handleViewCll(row.name);\n }\n };\n\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n {catalogMissingMessage ? (\n <MuiAlert severity=\"warning\" sx={{ fontSize: \"12px\", p: 1 }}>\n {catalogMissingMessage}\n </MuiAlert>\n ) : schemaMissingMessage ? (\n <MuiAlert severity=\"warning\" sx={{ fontSize: \"12px\", p: 1 }}>\n {schemaMissingMessage}\n </MuiAlert>\n ) : (\n <></>\n )}\n\n {rows.length > 0 && (\n <ScreenshotDataGrid\n style={{\n blockSize: \"auto\",\n maxHeight: \"100%\",\n overflow: \"auto\",\n fontSize: \"10pt\",\n borderWidth: 1,\n }}\n columns={columns}\n rows={rows}\n renderers={{ noRowsFallback: <EmptyRowsRenderer /> }}\n ref={ref}\n getRowId={getRowId}\n getRowClass={getRowClass}\n onCellClicked={handleCellClicked}\n onGridReady={handleGridReady}\n rowSelection={{ mode: \"singleRow\", checkboxes: false }}\n containerClassName=\"no-track-pii-safe\"\n rowClassName=\"no-track-pii-safe\"\n />\n )}\n </Box>\n );\n}\n\nexport function PrivateSchemaView(\n {\n base,\n current,\n showMenu = true,\n columnChanges,\n onViewCode,\n }: SchemaViewProps,\n ref: Ref<DataGridHandle>,\n) {\n const lineageViewContext = useLineageViewContext();\n const [gridApi, setGridApi] = useState<GridApi<SchemaDiffRow> | null>(null);\n const [cllRunningMap, setCllRunningMap] = useState<Map<string, boolean>>(\n new Map(),\n );\n const { columns, rows } = useMemo(() => {\n const resourceType = current?.resource_type ?? base?.resource_type;\n const node =\n resourceType &&\n [\"model\", \"seed\", \"snapshot\", \"source\"].includes(resourceType)\n ? (current ?? base)\n : undefined;\n\n return createDataGridFromData(\n { type: \"schema_diff\", base: base?.columns, current: current?.columns },\n { node, cllRunningMap, showMenu, columnChanges, onViewCode },\n );\n }, [base, current, cllRunningMap, showMenu, columnChanges, onViewCode]);\n\n const { lineageGraph, isActionAvailable } = useLineageGraphContext();\n const changeAnalysisAvailable = isActionAvailable(\"change_analysis\");\n const noCatalogBase = !lineageGraph?.catalogMetadata.base;\n const noCatalogCurrent = !lineageGraph?.catalogMetadata.current;\n let catalogMissingMessage = undefined;\n if (noCatalogBase && noCatalogCurrent) {\n catalogMissingMessage =\n \"catalog.json not found on both environments. Run `recce debug` to troubleshoot.\";\n } else if (noCatalogBase) {\n catalogMissingMessage =\n \"catalog.json not found on base environment. Run `recce debug` to troubleshoot.\";\n } else if (noCatalogCurrent) {\n catalogMissingMessage =\n \"catalog.json not found on current environment. Run `recce debug` to troubleshoot.\";\n }\n\n const noSchemaBase = base && base.columns === undefined;\n const noSchemaCurrent = current && current.columns === undefined;\n let schemaMissingMessage = undefined;\n if (noSchemaBase && noSchemaCurrent) {\n schemaMissingMessage =\n \"Node schema not found in catalog.json on both environments. Please regenerate your catalog.json to update.\";\n } else if (noSchemaCurrent) {\n schemaMissingMessage =\n \"Node schema not found in catalog.json on current environment. Please regenerate your catalog.json to update.\";\n } else if (noSchemaBase) {\n schemaMissingMessage =\n \"Node schema not found in catalog.json on base environment. Please regenerate your catalog.json to update.\";\n }\n\n const handleViewCll = async (columnName: string) => {\n if (!changeAnalysisAvailable) return;\n trackColumnLevelLineage({ action: \"view\", source: \"schema_column\" });\n setCllRunningMap((prev) => new Map(prev).set(columnName, true));\n const modelId = current?.id ?? base?.id;\n if (modelId) {\n await lineageViewContext?.showColumnLevelLineage({\n node_id: modelId,\n column: columnName,\n });\n }\n setCllRunningMap((prev) => new Map(prev).set(columnName, false));\n };\n\n const getRowId = useCallback(\n (params: { data: SchemaDiffRow }) => {\n const modelId = current?.id ?? base?.id;\n return `${modelId}-${params.data.name}`;\n },\n [current?.id, base?.id],\n );\n\n const cll = lineageViewContext?.viewOptions.column_level_lineage;\n const selectedRowId = cll ? `${cll.node_id}-${cll.column}` : null;\n\n // Update row selection when cll changes\n useEffect(() => {\n if (!gridApi) return;\n gridApi.deselectAll();\n if (selectedRowId) {\n const rowNode = gridApi.getRowNode(selectedRowId);\n if (rowNode) {\n rowNode.setSelected(true);\n }\n }\n }, [gridApi, selectedRowId]);\n\n const handleGridReady = useCallback(\n (event: GridReadyEvent<SchemaDiffRow>) => {\n setGridApi(event.api);\n },\n [],\n );\n\n const getRowClass = (params: RowClassParams<SchemaDiffRow>) => {\n const row = params.data;\n if (!row) return \"row-normal\";\n\n let className: string;\n if (row.baseIndex === undefined) {\n className = \"row-added\";\n } else if (row.currentIndex === undefined) {\n return \"row-removed\"; // removed column isn't selectable\n } else if (\n row.baseType !== row.currentType ||\n row.reordered === true ||\n row.definitionChanged === true\n ) {\n // Any change (structural or definition-only) gets the changed row background\n className = \"row-changed\";\n } else {\n className = \"row-normal\";\n }\n if (lineageViewContext !== undefined && changeAnalysisAvailable) {\n className += \" row-selectable\";\n }\n return className;\n };\n\n const handleCellClicked = async (event: CellClickedEvent<SchemaDiffRow>) => {\n // Skip if clicking on the menu button\n const target = event.event?.target as HTMLElement | undefined;\n if (target?.closest(\".row-context-menu\")) {\n return;\n }\n const row = event.data;\n if (!row) return;\n // Removed columns aren't clickable\n if (row.baseIndex !== undefined && row.currentIndex === undefined) {\n return;\n }\n await handleViewCll(row.name);\n };\n\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n {catalogMissingMessage ? (\n <MuiAlert severity=\"warning\" sx={{ fontSize: \"12px\", p: 1 }}>\n {catalogMissingMessage}\n </MuiAlert>\n ) : schemaMissingMessage ? (\n <MuiAlert severity=\"warning\" sx={{ fontSize: \"12px\", p: 1 }}>\n {schemaMissingMessage}\n </MuiAlert>\n ) : (\n <></>\n )}\n\n <SchemaLegend />\n {rows.length > 0 && (\n <ScreenshotDataGrid\n style={{\n blockSize: \"auto\",\n maxHeight: \"100%\",\n overflow: \"auto\",\n fontSize: \"0.8rem\",\n borderWidth: 1,\n }}\n columns={columns}\n rows={rows}\n rowHeight={35}\n renderers={{ noRowsFallback: <EmptyRowsRenderer /> }}\n className=\"rdg-light no-track-pii-safe\"\n ref={ref}\n getRowId={getRowId}\n getRowClass={getRowClass}\n onCellClicked={handleCellClicked}\n onGridReady={handleGridReady}\n rowSelection={{ mode: \"singleRow\", checkboxes: false }}\n containerClassName=\"no-track-pii-safe\"\n rowClassName=\"no-track-pii-safe\"\n />\n )}\n </Box>\n );\n}\n\nexport const SchemaView = forwardRef(PrivateSchemaView);\nexport const SingleEnvSchemaView = forwardRef(PrivateSingleEnvSchemaView);\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport IconButton from \"@mui/material/IconButton\";\nimport type { ComponentType } from \"react\";\nimport { useState } from \"react\";\nimport { FaExpandArrowsAlt } from \"react-icons/fa\";\nimport { IoClose } from \"react-icons/io5\";\n\n/**\n * Props for a code editor component.\n * Used for dependency injection to avoid coupling to a specific editor implementation.\n */\nexport interface CodeEditorProps {\n value: string;\n language?: string;\n readOnly?: boolean;\n lineNumbers?: boolean;\n wordWrap?: boolean;\n theme?: \"light\" | \"dark\";\n fontSize?: number;\n height?: string;\n className?: string;\n}\n\n/**\n * Props for a diff editor component.\n * Used for dependency injection to avoid coupling to a specific editor implementation.\n */\nexport interface DiffEditorProps {\n original: string;\n modified: string;\n language?: string;\n readOnly?: boolean;\n lineNumbers?: boolean;\n sideBySide?: boolean;\n theme?: \"light\" | \"dark\";\n height?: string;\n className?: string;\n}\n\n/**\n * Node data structure representing model information for SQL display.\n * Fields are optional to accommodate various node data structures from the API.\n */\nexport interface NodeSqlViewNodeData {\n resourceType?: string;\n data: {\n base?: { raw_code?: string; name?: string };\n current?: { raw_code?: string; name?: string };\n };\n name?: string;\n}\n\n/**\n * Props for the NodeSqlView component.\n *\n * This component uses dependency injection for editor components to avoid\n * coupling to specific editor implementations like CodeMirror.\n *\n * Editor component props use `ComponentType<any>` to allow implementations\n * with more specific prop types (e.g., language as a union type vs string).\n * The component internally passes the correct props matching the interfaces above.\n */\nexport interface NodeSqlViewProps {\n /**\n * Node containing model data with SQL code.\n */\n node: {\n data: NodeSqlViewNodeData;\n };\n /**\n * Whether the environment is single-env mode (no diff view).\n */\n isSingleEnv: boolean;\n /**\n * Code editor component for single-env mode.\n * Should accept props matching CodeEditorProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n CodeEditor: ComponentType<any>;\n /**\n * Diff editor component for comparing base vs current.\n * Should accept props matching DiffEditorProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n DiffEditor: ComponentType<any>;\n /**\n * Whether dark mode is enabled.\n */\n isDark?: boolean;\n}\n\n/**\n * Displays SQL code for a lineage node with an expandable dialog.\n *\n * In single-env mode, shows just the code. Otherwise, shows a diff view\n * comparing base and current versions.\n *\n * Editor components are injected as props to allow the consuming application\n * to provide its own editor implementations (e.g., CodeMirror, Monaco).\n */\nexport const NodeSqlView = ({\n node,\n isSingleEnv,\n CodeEditor,\n DiffEditor,\n isDark = false,\n}: NodeSqlViewProps) => {\n const [isOpen, setIsOpen] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n\n if (\n node.data.resourceType !== \"model\" &&\n node.data.resourceType !== \"snapshot\"\n ) {\n return \"Not available\";\n }\n\n const original = node.data.data.base?.raw_code;\n const modified = node.data.data.current?.raw_code;\n const modelName =\n node.data.data.base?.name ?? node.data.data.current?.name ?? \"\";\n\n return (\n <Box\n className=\"no-track-pii-safe\"\n sx={{ position: \"relative\", height: \"100%\" }}\n onMouseEnter={() => {\n setIsHovered(true);\n }}\n onMouseLeave={() => {\n setIsHovered(false);\n }}\n >\n {isSingleEnv ? (\n <CodeEditor\n language=\"sql\"\n value={original ?? \"\"}\n readOnly={true}\n lineNumbers={true}\n wordWrap={false}\n theme={isDark ? \"dark\" : \"light\"}\n />\n ) : (\n <DiffEditor\n original={original ?? \"\"}\n modified={modified ?? \"\"}\n language=\"sql\"\n readOnly={true}\n lineNumbers={true}\n sideBySide={false} // Inline diff mode\n theme={isDark ? \"dark\" : \"light\"}\n height=\"100%\"\n />\n )}\n <IconButton\n onClick={() => setIsOpen(true)}\n size=\"medium\"\n aria-label=\"Expand\"\n sx={{\n position: \"absolute\",\n top: \"5px\",\n right: \"20px\",\n opacity: isHovered ? 0.5 : 0.1,\n transition: \"opacity 0.3s ease-in-out\",\n }}\n >\n <FaExpandArrowsAlt />\n </IconButton>\n <MuiDialog\n open={isOpen}\n onClose={() => setIsOpen(false)}\n maxWidth=\"sm\"\n fullWidth\n slotProps={{\n paper: { sx: { height: \"75%\", overflowY: \"auto\" } },\n }}\n >\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n {isSingleEnv ? (\n <>\n <code>{modelName}</code> Model Code\n </>\n ) : (\n <>\n <code>{modelName}</code> Model Code Diff\n </>\n )}\n <Box sx={{ flex: 1 }} />\n <IconButton size=\"small\" onClick={() => setIsOpen(false)}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent>\n {isSingleEnv ? (\n <CodeEditor\n language=\"sql\"\n value={original ?? \"\"}\n fontSize={16}\n readOnly={true}\n lineNumbers={true}\n wordWrap={false}\n theme={isDark ? \"dark\" : \"light\"}\n />\n ) : (\n <DiffEditor\n original={original ?? \"\"}\n modified={modified ?? \"\"}\n language=\"sql\"\n theme={isDark ? \"dark\" : \"light\"}\n className=\"text-base\"\n />\n )}\n </DialogContent>\n </MuiDialog>\n </Box>\n );\n};\n","\"use client\";\n\nimport type { LineageGraphNode } from \"../..\";\nimport { useRecceServerFlag } from \"../../contexts\";\nimport { useIsDark } from \"../../hooks\";\nimport { CodeEditor, DiffEditor } from \"../../primitives\";\nimport { NodeSqlView as BaseNodeSqlView } from \"./NodeSqlView\";\n\ninterface NodeSqlViewProps {\n node: LineageGraphNode;\n}\n\n/**\n * wrapper for NodeSqlView that injects CodeMirror-based editors.\n *\n * This wrapper:\n * 1. Handles loading state from useRecceServerFlag\n * 2. Injects editor components (CodeEditor, DiffEditor)\n * 3. Provides dark mode detection via useIsDark hook\n *\n * The underlying BaseNodeSqlView from @datarecce/ui is framework-agnostic\n * and accepts editor components as props for dependency injection.\n */\nexport const NodeSqlViewOss = ({ node }: NodeSqlViewProps) => {\n const { data: flags, isLoading } = useRecceServerFlag();\n const isDark = useIsDark();\n\n if (isLoading) {\n return <></>;\n }\n\n return (\n <BaseNodeSqlView\n node={node}\n isSingleEnv={flags?.single_env_onboarding ?? false}\n CodeEditor={CodeEditor}\n DiffEditor={DiffEditor}\n isDark={isDark}\n />\n );\n};\n","/**\n * @file tagStyles.ts\n * @description Shared styling utilities for lineage node tags\n *\n * Provides reusable styles for tag components that display information\n * in lineage graph nodes (resource type, row count, etc.).\n *\n * Source: Extracted from OSS js/src/components/lineage/NodeTag.tsx\n */\n\nimport type { SxProps, Theme } from \"@mui/material/styles\";\n\n/**\n * Get root styles for tag components\n *\n * Creates a pill-shaped container with theme-aware colors.\n *\n * @param isDark - Whether dark mode is active\n * @returns SxProps for the tag container\n *\n * @example\n * ```tsx\n * const isDark = useIsDark();\n * <Box sx={getTagRootSx(isDark)}>Tag content</Box>\n * ```\n */\nexport const getTagRootSx = (isDark: boolean): SxProps<Theme> => ({\n display: \"inline-flex\",\n alignItems: \"center\",\n borderRadius: 16,\n px: 1,\n py: 0.25,\n fontSize: \"0.75rem\",\n bgcolor: isDark ? \"grey.700\" : \"grey.100\",\n color: isDark ? \"grey.100\" : \"inherit\",\n});\n\n/**\n * Styles for the leading element (icon) in a tag\n *\n * Provides consistent spacing and alignment for icons at the start of tags.\n *\n * @example\n * ```tsx\n * <Box sx={tagStartElementSx}>\n * <MyIcon />\n * </Box>\n * ```\n */\nexport const tagStartElementSx: SxProps<Theme> = {\n mr: 0.5,\n display: \"flex\",\n alignItems: \"center\",\n};\n","\"use client\";\n\n/**\n * @file ResourceTypeTag.tsx\n * @description Pure presentation component for displaying resource type with icon\n *\n * Shows the resource type (model, source, seed, etc.) as a tag with an icon.\n * Uses theme-aware styling for light/dark mode support.\n *\n * Source: Migrated from OSS js/src/components/lineage/NodeTag.tsx\n */\n\nimport Box from \"@mui/material/Box\";\nimport Tooltip from \"@mui/material/Tooltip\";\nimport { memo } from \"react\";\nimport { useIsDark } from \"../../../hooks/useIsDark\";\nimport { getIconForResourceType } from \"../styles\";\nimport { getTagRootSx, tagStartElementSx } from \"./tagStyles\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\n/**\n * Data required for ResourceTypeTag\n */\nexport interface ResourceTypeTagData {\n /** The resource type to display (model, source, seed, snapshot, etc.) */\n resourceType?: string;\n}\n\n/**\n * Props for ResourceTypeTag component\n */\nexport interface ResourceTypeTagProps {\n /** Node data containing the resource type */\n data: ResourceTypeTagData;\n /** Test ID for testing */\n \"data-testid\"?: string;\n}\n\n// =============================================================================\n// COMPONENT\n// =============================================================================\n\n/**\n * ResourceTypeTag - Displays the resource type with an icon\n *\n * Shows a pill-shaped tag with an icon representing the resource type\n * (model, source, seed, snapshot, metric, exposure, semantic_model).\n *\n * @example\n * ```tsx\n * // Basic usage\n * <ResourceTypeTag data={{ resourceType: \"model\" }} />\n *\n * // With snapshot type\n * <ResourceTypeTag data={{ resourceType: \"snapshot\" }} />\n * ```\n */\nfunction ResourceTypeTagComponent({\n data,\n \"data-testid\": testId,\n}: ResourceTypeTagProps) {\n const isDark = useIsDark();\n const { icon: ResourceTypeIcon } = getIconForResourceType(data.resourceType);\n\n return (\n <Tooltip arrow title=\"Type of resource\">\n <Box component=\"span\" sx={getTagRootSx(isDark)} data-testid={testId}>\n {ResourceTypeIcon && (\n <Box component=\"span\" sx={tagStartElementSx}>\n <ResourceTypeIcon />\n </Box>\n )}\n {data.resourceType}\n </Box>\n </Tooltip>\n );\n}\n\nexport const ResourceTypeTag = memo(ResourceTypeTagComponent);\nResourceTypeTag.displayName = \"ResourceTypeTag\";\n","/**\n * @file NodeTag.tsx\n * @description Tag components for lineage graph nodes\n *\n * Includes tag components that rely on app-level dependencies:\n * - RowCountDiffTag: Shows row count comparison between base and current\n * - RowCountTag: Shows current row count (single env mode)\n */\n\nimport Box from \"@mui/material/Box\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Skeleton from \"@mui/material/Skeleton\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { FiArrowRight } from \"react-icons/fi\";\nimport { PiRepeat } from \"react-icons/pi\";\nimport { RiArrowDownSFill, RiArrowUpSFill, RiSwapLine } from \"react-icons/ri\";\nimport type { RowCount, RowCountDiff } from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport type { LineageGraphNode } from \"../../contexts/lineage/types\";\nimport { useIsDark } from \"../../hooks\";\nimport { deltaPercentageString } from \"../../utils\";\nimport { SetupConnectionPopover } from \"../app\";\nimport { findByRunType } from \"../run\";\nimport { getTagRootSx, tagStartElementSx } from \"./tags\";\n\n// =============================================================================\n// INTERNAL COMPONENTS\n// =============================================================================\n\nfunction _RowCountByRate({ rowCount }: { rowCount: RowCountDiff }) {\n const base = rowCount.base;\n const current = rowCount.curr;\n const baseLabel = rowCount.base === null ? \"N/A\" : `${rowCount.base} rows`;\n const currentLabel = rowCount.curr === null ? \"N/A\" : `${rowCount.curr} rows`;\n\n if (base === null && current === null) {\n return <> Failed to load</>;\n }\n if (base === null || current === null) {\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography variant=\"body2\" component=\"span\">\n {baseLabel}\n </Typography>\n <FiArrowRight />\n <Typography variant=\"body2\" component=\"span\">\n {currentLabel}\n </Typography>\n </Stack>\n );\n }\n if (base === current) {\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography variant=\"body2\" component=\"span\">\n {currentLabel}\n </Typography>\n <Box component=\"span\" sx={{ color: \"grey.500\", display: \"flex\" }}>\n <RiSwapLine />\n </Box>\n <Typography variant=\"body2\" component=\"span\" sx={{ color: \"grey.500\" }}>\n No Change\n </Typography>\n </Stack>\n );\n }\n if (base < current) {\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography variant=\"body2\" component=\"span\">\n {currentLabel}\n </Typography>\n <Box component=\"span\" sx={{ color: \"success.main\", display: \"flex\" }}>\n <RiArrowUpSFill />\n </Box>\n <Typography\n variant=\"body2\"\n component=\"span\"\n sx={{ color: \"success.main\" }}\n >\n {deltaPercentageString(base, current)}\n </Typography>\n </Stack>\n );\n }\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Typography variant=\"body2\" component=\"span\">\n {currentLabel}\n </Typography>\n <Box component=\"span\" sx={{ color: \"error.main\", display: \"flex\" }}>\n <RiArrowDownSFill />\n </Box>\n <Typography variant=\"body2\" component=\"span\" sx={{ color: \"error.main\" }}>\n {deltaPercentageString(base, current)}\n </Typography>\n </Stack>\n );\n}\n\n// =============================================================================\n// EXPORTED COMPONENTS\n// =============================================================================\n\nexport interface RowCountDiffTagProps {\n node: LineageGraphNode;\n rowCount?: RowCountDiff;\n onRefresh?: () => void;\n isFetching?: boolean;\n error?: Error | null;\n}\n\n/**\n * RowCountDiffTag - Shows row count comparison between base and current\n *\n * This component is app-specific because it requires:\n * - SetupConnectionPopover for database connection prompts\n * - findByRunType for getting run type icons from registry\n * - runsAggregated context for cached row count data\n */\nexport function RowCountDiffTag({\n rowCount: fetchedRowCount,\n node,\n onRefresh,\n isFetching,\n}: RowCountDiffTagProps) {\n const isDark = useIsDark();\n const { featureToggles } = useRecceInstanceContext();\n const { runsAggregated } = useLineageGraphContext();\n const lastRowCount: RowCountDiff | undefined = runsAggregated?.[node.id]\n ?.row_count_diff.result as RowCountDiff | undefined;\n const RunTypeIcon = findByRunType(\"row_count_diff\").icon;\n\n // Calculate during render instead of effect\n const rowCount = fetchedRowCount ?? lastRowCount;\n const rowsToShow = rowCount;\n const label = rowCount\n ? `${rowCount.base ?? \"N/A\"} -> ${rowCount.curr ?? \"N/A\"} rows`\n : \"\";\n\n return (\n <MuiTooltip title={label}>\n <SetupConnectionPopover display={featureToggles.mode === \"metadata only\"}>\n <Box\n component=\"span\"\n sx={{\n ...getTagRootSx(isDark),\n gap: 0.5,\n }}\n >\n <RunTypeIcon />\n {rowsToShow != null || isFetching ? (\n isFetching ? (\n <Skeleton width={30} height={16} />\n ) : rowsToShow != null ? (\n <_RowCountByRate rowCount={rowsToShow} />\n ) : (\n <Typography variant=\"caption\">row count</Typography>\n )\n ) : (\n <Typography variant=\"caption\">row count</Typography>\n )}\n {onRefresh && (\n <IconButton\n aria-label=\"Query Row Count\"\n size=\"small\"\n onClick={onRefresh}\n disabled={featureToggles.disableDatabaseQuery}\n sx={{ p: 0, ml: 0.5 }}\n >\n <PiRepeat size={12} />\n </IconButton>\n )}\n </Box>\n </SetupConnectionPopover>\n </MuiTooltip>\n );\n}\n\nexport interface RowCountTagProps {\n node: LineageGraphNode;\n rowCount?: RowCount;\n onRefresh?: () => void;\n isFetching?: boolean;\n error?: Error | null;\n}\n\n/**\n * RowCountTag - Shows current row count (single environment mode)\n *\n * This component is app-specific because it requires:\n * - findByRunType for getting run type icons from registry\n * - runsAggregated context for cached row count data\n */\nexport function RowCountTag({\n rowCount: fetchedRowCount,\n node,\n onRefresh,\n isFetching,\n}: RowCountTagProps) {\n const isDark = useIsDark();\n const { runsAggregated } = useLineageGraphContext();\n const lastRowCount: RowCountDiff | undefined = runsAggregated?.[node.id]\n ?.row_count.result as RowCountDiff | undefined;\n\n const RunTypeIcon = findByRunType(\"row_count\").icon;\n\n let label;\n const rowCount = fetchedRowCount ?? lastRowCount;\n if (rowCount) {\n const rows = rowCount.curr ?? \"N/A\";\n label = `${rows} rows`;\n }\n\n return (\n <Box component=\"span\" sx={getTagRootSx(isDark)}>\n <Box component=\"span\" sx={tagStartElementSx}>\n <RunTypeIcon />\n </Box>\n {rowCount || isFetching ? (\n isFetching ? (\n <Skeleton width={30} height={16} />\n ) : (\n <Typography variant=\"caption\">{label}</Typography>\n )\n ) : (\n <Typography variant=\"caption\">row count</Typography>\n )}\n {onRefresh && (\n <IconButton\n aria-label=\"Query Row Count\"\n size=\"small\"\n onClick={onRefresh}\n disabled={node.data.from === \"base\"}\n sx={{ p: 0, ml: 0.5 }}\n >\n <PiRepeat size={12} />\n </IconButton>\n )}\n </Box>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport Tab from \"@mui/material/Tab\";\nimport Tabs from \"@mui/material/Tabs\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport {\n type ComponentType,\n type ReactElement,\n type ReactNode,\n useState,\n} from \"react\";\nimport { IoClose } from \"react-icons/io5\";\n\nimport type { NodeColumnData } from \"../../api/info\";\nimport { DisableTooltipMessages } from \"../../constants\";\nimport { isSchemaChanged } from \"../../utils/schemaDiff\";\nimport type { NodeSqlViewProps } from \"./NodeSqlView\";\n\n// =============================================================================\n// TYPE DEFINITIONS\n// =============================================================================\n\n/**\n * Node data structure for NodeView.\n * Represents the data needed to display node details.\n */\nexport interface NodeViewNodeData {\n id: string;\n data: {\n name: string;\n resourceType?: string;\n changeStatus?: string;\n from?: string;\n data: {\n base?: {\n raw_code?: string;\n name?: string;\n columns?: Record<string, NodeColumnData | undefined>;\n };\n current?: {\n raw_code?: string;\n name?: string;\n columns?: Record<string, NodeColumnData | undefined>;\n };\n };\n change?: {\n category: string;\n columns: Record<string, \"added\" | \"removed\" | \"modified\"> | null;\n };\n };\n}\n\n/**\n * Run type icon configuration for dependency injection.\n * Maps run type names to their icon components.\n */\nexport interface RunTypeIconMap {\n query?: ComponentType<{ fontSize?: string }>;\n row_count?: ComponentType<{ fontSize?: string }>;\n row_count_diff?: ComponentType<{ fontSize?: string }>;\n profile?: ComponentType<{ fontSize?: string }>;\n profile_diff?: ComponentType<{ fontSize?: string }>;\n query_diff?: ComponentType<{ fontSize?: string }>;\n value_diff?: ComponentType<{ fontSize?: string }>;\n top_k_diff?: ComponentType<{ fontSize?: string }>;\n histogram_diff?: ComponentType<{ fontSize?: string }>;\n schema_diff?: ComponentType<{ fontSize?: string }>;\n sandbox?: ComponentType<{ fontSize?: string }>;\n}\n\n/**\n * Callbacks for action button clicks.\n * Used for dependency injection of action handlers.\n */\nexport interface NodeViewActionCallbacks {\n /** Called when Query button is clicked */\n onQueryClick?: () => void;\n /** Called when Row Count button is clicked */\n onRowCountClick?: () => void;\n /** Called when Row Count Diff button is clicked */\n onRowCountDiffClick?: () => void;\n /** Called when Profile button is clicked */\n onProfileClick?: () => void;\n /** Called when Profile Diff button is clicked */\n onProfileDiffClick?: () => void;\n /** Called when Query Diff button is clicked */\n onQueryDiffClick?: () => void;\n /** Called when Value Diff button is clicked */\n onValueDiffClick?: () => void;\n /** Called when Top-K Diff button is clicked */\n onTopKDiffClick?: () => void;\n /** Called when Histogram Diff button is clicked */\n onHistogramDiffClick?: () => void;\n /** Called when Add Schema Diff button is clicked */\n onAddSchemaDiffClick?: () => void;\n /** Called when Sandbox button is clicked */\n onSandboxClick?: () => void;\n}\n\n/**\n * Props for NodeView component.\n *\n * Uses dependency injection for:\n * - Schema view components (for different consumers)\n * - SQL view component (NodeSqlView)\n * - Action button icons\n * - Notification components\n * - Connection popover wrapper\n *\n * Note: Injected component types use `any` to allow consumers to provide\n * components with more specific prop types than the base interface requires.\n * The consumer is responsible for ensuring type compatibility.\n */\nexport interface NodeViewProps {\n /** The node to display */\n node: NodeViewNodeData;\n /** Callback when close button is clicked */\n onCloseNode: () => void;\n /** Whether in single environment mode */\n isSingleEnv: boolean;\n /** Feature toggles for conditional UI */\n featureToggles?: {\n mode?: string | null;\n disableDatabaseQuery?: boolean;\n };\n\n // =========================================================================\n // DEPENDENCY INJECTION: Components\n // =========================================================================\n // Using ComponentType<any> for flexibility - consumers provide their own types\n\n /**\n * Schema view component for diff mode.\n * Should accept: { base?: NodeData, current?: NodeData }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n SchemaView?: ComponentType<any>;\n /**\n * Schema view component for single env mode.\n * Should accept: { current?: NodeData }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n SingleEnvSchemaView?: ComponentType<any>;\n /**\n * Node SQL view component.\n * Should accept: { node: LineageGraphNode }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n NodeSqlView?: ComponentType<any>;\n /**\n * Row count diff tag component.\n * Should accept: { node: LineageGraphNode, onRefresh?: () => void }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n RowCountDiffTag?: ComponentType<any>;\n /**\n * Row count tag component (single env).\n * Should accept: { node: LineageGraphNode, onRefresh?: () => void }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n RowCountTag?: ComponentType<any>;\n /**\n * Resource type tag component.\n * Should accept: { node: LineageGraphNode }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n ResourceTypeTag?: ComponentType<any>;\n /**\n * Notification component for single env mode.\n * Should accept: { onClose: () => void, children: ReactNode }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n NotificationComponent?: ComponentType<any>;\n /**\n * Wrapper component for buttons that need connection popover.\n * Should accept: { display: boolean, children: ReactNode }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n ConnectionPopoverWrapper?: ComponentType<any>;\n /**\n * Sandbox dialog component.\n * Should accept: { isOpen: boolean, onClose: () => void, current?: NodeData }\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n SandboxDialog?: ComponentType<any>;\n\n // =========================================================================\n // DEPENDENCY INJECTION: Icons\n // =========================================================================\n\n /** Map of run type names to icon components */\n runTypeIcons?: RunTypeIconMap;\n\n // =========================================================================\n // DEPENDENCY INJECTION: Callbacks\n // =========================================================================\n\n /** Action callbacks for button clicks */\n actionCallbacks?: NodeViewActionCallbacks;\n /** Check if an action is available */\n isActionAvailable?: (runType: string) => boolean;\n}\n\n// =============================================================================\n// INTERNAL COMPONENTS\n// =============================================================================\n\ninterface TabPanelProps {\n children?: ReactNode;\n index: number;\n value: number;\n}\n\nfunction TabPanel({ children, value, index }: TabPanelProps) {\n return value === index ? <>{children}</> : null;\n}\n\n// =============================================================================\n// DEFAULT IMPLEMENTATIONS\n// =============================================================================\n\n/** Default icon placeholder when no icon is provided */\nconst DefaultIcon = () => <span />;\n\n/** Default connection wrapper that just renders children */\nconst DefaultConnectionWrapper = ({\n children,\n}: {\n display: boolean;\n children: ReactNode;\n}) => <>{children}</>;\n\n/** Default check for action availability - always available */\nconst defaultIsActionAvailable = () => true;\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\n/**\n * Gets the disable reason for an action button.\n */\nfunction getDisableReason(\n isAddedOrRemoved: boolean,\n runType: string,\n isActionAvailable: (runType: string) => boolean,\n): string {\n if (isAddedOrRemoved) {\n return DisableTooltipMessages.add_or_remove;\n }\n if (!isActionAvailable(runType)) {\n return \"This action is not supported yet.\";\n }\n return \"\";\n}\n\n// =============================================================================\n// SUB-COMPONENTS\n// =============================================================================\n\ninterface SingleEnvActionButtonsProps {\n node: NodeViewNodeData;\n actionCallbacks?: NodeViewActionCallbacks;\n runTypeIcons?: RunTypeIconMap;\n isActionAvailable: (runType: string) => boolean;\n}\n\nfunction SingleEnvActionButtons({\n node,\n actionCallbacks,\n runTypeIcons,\n isActionAvailable,\n}: SingleEnvActionButtonsProps) {\n const isAddedOrRemoved =\n node.data.changeStatus === \"added\" || node.data.changeStatus === \"removed\";\n\n const QueryIcon = runTypeIcons?.query ?? DefaultIcon;\n const RowCountIcon = runTypeIcons?.row_count ?? DefaultIcon;\n const ProfileIcon = runTypeIcons?.profile ?? DefaultIcon;\n\n return (\n <Stack direction=\"row\" alignItems=\"center\" flexWrap=\"wrap\" gap={1}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<QueryIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onQueryClick}\n sx={{ textTransform: \"none\" }}\n >\n Query\n </Button>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<RowCountIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onRowCountClick}\n sx={{ textTransform: \"none\" }}\n >\n Row Count\n </Button>\n <MuiTooltip\n title={getDisableReason(isAddedOrRemoved, \"profile\", isActionAvailable)}\n placement=\"top\"\n >\n <span>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<ProfileIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onProfileClick}\n disabled={isAddedOrRemoved}\n sx={{ textTransform: \"none\" }}\n >\n Profile\n </Button>\n </span>\n </MuiTooltip>\n </Stack>\n );\n}\n\ninterface ExploreHeaderButtonsProps {\n node: NodeViewNodeData;\n actionCallbacks?: NodeViewActionCallbacks;\n runTypeIcons?: RunTypeIconMap;\n featureToggles?: NodeViewProps[\"featureToggles\"];\n ConnectionPopoverWrapper: ComponentType<{\n display: boolean;\n children: ReactNode;\n }>;\n}\n\nfunction ExploreHeaderButtons({\n actionCallbacks,\n runTypeIcons,\n featureToggles,\n ConnectionPopoverWrapper,\n}: ExploreHeaderButtonsProps) {\n const metadataOnly = featureToggles?.mode === \"metadata only\";\n\n const SchemaDiffIcon = runTypeIcons?.schema_diff ?? DefaultIcon;\n const SandboxIcon = runTypeIcons?.sandbox ?? DefaultIcon;\n\n return (\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ mr: 1 }}\n flexWrap=\"wrap\"\n gap={1}\n >\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<SchemaDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onAddSchemaDiffClick}\n sx={{ textTransform: \"none\" }}\n >\n Add schema diff to checklist\n </Button>\n <ConnectionPopoverWrapper display={metadataOnly}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<SandboxIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onSandboxClick}\n disabled={featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Sandbox\n </Button>\n </ConnectionPopoverWrapper>\n </Stack>\n );\n}\n\ninterface DiffActionButtonsProps {\n node: NodeViewNodeData;\n actionCallbacks?: NodeViewActionCallbacks;\n runTypeIcons?: RunTypeIconMap;\n featureToggles?: NodeViewProps[\"featureToggles\"];\n isActionAvailable: (runType: string) => boolean;\n ConnectionPopoverWrapper: ComponentType<{\n display: boolean;\n children: ReactNode;\n }>;\n}\n\nfunction DiffActionButtons({\n node,\n actionCallbacks,\n runTypeIcons,\n featureToggles,\n isActionAvailable,\n ConnectionPopoverWrapper,\n}: DiffActionButtonsProps) {\n const metadataOnly = featureToggles?.mode === \"metadata only\";\n const isAddedOrRemoved =\n node.data.changeStatus === \"added\" || node.data.changeStatus === \"removed\";\n\n const QueryDiffIcon = runTypeIcons?.query_diff ?? DefaultIcon;\n const RowCountDiffIcon = runTypeIcons?.row_count_diff ?? DefaultIcon;\n const ProfileDiffIcon = runTypeIcons?.profile_diff ?? DefaultIcon;\n const ValueDiffIcon = runTypeIcons?.value_diff ?? DefaultIcon;\n const TopKDiffIcon = runTypeIcons?.top_k_diff ?? DefaultIcon;\n const HistogramDiffIcon = runTypeIcons?.histogram_diff ?? DefaultIcon;\n\n const wrapButton = (\n button: ReactElement<{\n ref?: React.Ref<HTMLElement>;\n [key: string]: unknown;\n }>,\n runType: string,\n ) => {\n if (metadataOnly) {\n return (\n <ConnectionPopoverWrapper display={true}>\n {button}\n </ConnectionPopoverWrapper>\n );\n }\n\n const tooltipContent = getDisableReason(\n isAddedOrRemoved,\n runType,\n isActionAvailable,\n );\n return (\n <MuiTooltip title={tooltipContent} placement=\"top\">\n <span>{button}</span>\n </MuiTooltip>\n );\n };\n\n return (\n <Stack direction=\"row\" alignItems=\"center\" flexWrap=\"wrap\" gap={2}>\n <Typography variant=\"caption\" fontWeight=\"bold\">\n Diff\n </Typography>\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n flexWrap=\"wrap\"\n gap={1}\n width=\"93%\"\n >\n <ConnectionPopoverWrapper display={metadataOnly}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<RowCountDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onRowCountDiffClick}\n disabled={featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Row Count\n </Button>\n </ConnectionPopoverWrapper>\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<ProfileDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onProfileDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Profile\n </Button>,\n \"profile_diff\",\n )}\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<ValueDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onValueDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Value\n </Button>,\n \"value_diff\",\n )}\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<TopKDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onTopKDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Top-K\n </Button>,\n \"top_k_diff\",\n )}\n {wrapButton(\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<HistogramDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onHistogramDiffClick}\n disabled={isAddedOrRemoved || featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Histogram\n </Button>,\n \"histogram_diff\",\n )}\n <ConnectionPopoverWrapper display={metadataOnly}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<QueryDiffIcon fontSize=\"small\" />}\n onClick={actionCallbacks?.onQueryDiffClick}\n disabled={featureToggles?.disableDatabaseQuery}\n sx={{ textTransform: \"none\" }}\n >\n Query\n </Button>\n </ConnectionPopoverWrapper>\n </Stack>\n </Stack>\n );\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * NodeView Component\n *\n * Displays detailed information about a lineage node including:\n * - Node name and metadata\n * - Action buttons for various operations (Query, Profile, Diff, etc.)\n * - Tabs for Columns and Code views\n *\n * Uses dependency injection for:\n * - Schema view components (different for OSS vs Cloud)\n * - Action button handlers\n * - Icon components from run registry\n * - Connection popover for database setup prompts\n *\n * @example\n * ```tsx\n * import { NodeView } from '@datarecce/ui/advanced';\n *\n * <NodeView\n * node={selectedNode}\n * onCloseNode={() => setSelectedNode(null)}\n * isSingleEnv={false}\n * SchemaView={MySchemaView}\n * NodeSqlView={MyNodeSqlView}\n * actionCallbacks={{\n * onQueryClick: handleQuery,\n * onProfileDiffClick: handleProfileDiff,\n * }}\n * />\n * ```\n */\nexport function NodeView({\n node,\n onCloseNode,\n isSingleEnv,\n featureToggles,\n // Injected components\n SchemaView,\n SingleEnvSchemaView,\n NodeSqlView,\n RowCountDiffTag,\n RowCountTag,\n ResourceTypeTag,\n NotificationComponent,\n ConnectionPopoverWrapper = DefaultConnectionWrapper,\n SandboxDialog,\n // Injected icons\n runTypeIcons,\n // Injected callbacks\n actionCallbacks,\n isActionAvailable = defaultIsActionAvailable,\n}: NodeViewProps) {\n const withColumns =\n node.data.resourceType === \"model\" ||\n node.data.resourceType === \"seed\" ||\n node.data.resourceType === \"source\" ||\n node.data.resourceType === \"snapshot\";\n\n const [isSandboxOpen, setIsSandboxOpen] = useState(false);\n const [isNotificationOpen, setIsNotificationOpen] = useState(true);\n const [tabValue, setTabValue] = useState(0);\n\n const { base, current } = node.data.data;\n const hasSchemaChanges =\n !isSingleEnv && isSchemaChanged(base?.columns, current?.columns) === true;\n const hasCodeChanges =\n !isSingleEnv &&\n base?.raw_code != null &&\n current?.raw_code != null &&\n base.raw_code !== current.raw_code;\n\n const isModelSeedOrSnapshot =\n node.data.resourceType === \"model\" ||\n node.data.resourceType === \"seed\" ||\n node.data.resourceType === \"snapshot\";\n\n // Extended callbacks that include sandbox open\n const extendedCallbacks: NodeViewActionCallbacks = {\n ...actionCallbacks,\n onSandboxClick: () => {\n actionCallbacks?.onSandboxClick?.();\n setIsSandboxOpen(true);\n },\n };\n\n return (\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n {/* Header row: name + close button */}\n <Stack direction=\"row\" alignItems=\"center\">\n <Box sx={{ flex: \"0 1 20%\", p: 2 }}>\n <Typography\n variant=\"subtitle1\"\n fontWeight={600}\n className=\"no-track-pii-safe\"\n >\n {node.data.name}\n </Typography>\n </Box>\n <Box sx={{ flexGrow: 1 }} />\n {!isSingleEnv && isModelSeedOrSnapshot && (\n <ExploreHeaderButtons\n node={node}\n actionCallbacks={extendedCallbacks}\n runTypeIcons={runTypeIcons}\n featureToggles={featureToggles}\n ConnectionPopoverWrapper={ConnectionPopoverWrapper}\n />\n )}\n <Box sx={{ flex: \"0 1 1%\" }}>\n <IconButton size=\"small\" onClick={onCloseNode}>\n <IoClose />\n </IconButton>\n </Box>\n </Stack>\n\n {/* Tags row: resource type, row count */}\n <Box sx={{ color: \"text.secondary\", pl: 2 }}>\n <Stack direction=\"row\" spacing={1}>\n {ResourceTypeTag && <ResourceTypeTag node={node} />}\n {isModelSeedOrSnapshot &&\n (isSingleEnv\n ? RowCountTag && (\n <RowCountTag\n node={node}\n onRefresh={actionCallbacks?.onRowCountClick}\n />\n )\n : RowCountDiffTag && (\n <RowCountDiffTag\n node={node}\n onRefresh={actionCallbacks?.onRowCountDiffClick}\n />\n ))}\n </Stack>\n </Box>\n\n {/* Action buttons row */}\n {isModelSeedOrSnapshot && (\n <Box sx={{ pl: 2, py: 1 }}>\n {isSingleEnv ? (\n <SingleEnvActionButtons\n node={node}\n actionCallbacks={actionCallbacks}\n runTypeIcons={runTypeIcons}\n isActionAvailable={isActionAvailable}\n />\n ) : (\n <DiffActionButtons\n node={node}\n actionCallbacks={extendedCallbacks}\n runTypeIcons={runTypeIcons}\n featureToggles={featureToggles}\n isActionAvailable={isActionAvailable}\n ConnectionPopoverWrapper={ConnectionPopoverWrapper}\n />\n )}\n </Box>\n )}\n\n {/* Content area: tabs for columns and code */}\n {withColumns && (\n <Box\n sx={{\n overflow: \"auto\",\n display: \"flex\",\n flexDirection: \"column\",\n flex: 1,\n minHeight: 0,\n }}\n >\n {/* Notification for single env mode */}\n {isSingleEnv && isNotificationOpen && NotificationComponent && (\n <Box sx={{ p: 1.5 }}>\n <NotificationComponent\n onClose={() => setIsNotificationOpen(false)}\n >\n <Typography variant=\"body2\">\n Enable the Recce Checklist and start adding checks for better\n data validation and review.\n </Typography>\n </NotificationComponent>\n </Box>\n )}\n\n {/* Tabs */}\n <Tabs\n value={tabValue}\n onChange={(_, newValue) => setTabValue(newValue)}\n sx={{ borderBottom: 1, borderColor: \"divider\" }}\n >\n <Tab\n label={\n <Box\n component=\"span\"\n sx={{ display: \"flex\", alignItems: \"center\", gap: 0.75 }}\n >\n Columns\n {hasSchemaChanges && (\n <Box\n component=\"span\"\n sx={{\n color: \"amber.main\",\n fontSize: \"0.5rem\",\n lineHeight: 1,\n }}\n >\n ●\n </Box>\n )}\n </Box>\n }\n />\n <Tab\n label={\n <Box\n component=\"span\"\n sx={{ display: \"flex\", alignItems: \"center\", gap: 0.75 }}\n >\n Code\n {hasCodeChanges && (\n <Box\n component=\"span\"\n sx={{\n color: \"amber.main\",\n fontSize: \"0.5rem\",\n lineHeight: 1,\n }}\n >\n ●\n </Box>\n )}\n </Box>\n }\n />\n </Tabs>\n\n {/* Tab panels */}\n <Box sx={{ overflow: \"auto\", height: \"calc(100% - 48px)\" }}>\n <TabPanel value={tabValue} index={0}>\n <Box sx={{ overflowY: \"auto\", height: \"100%\" }}>\n {isSingleEnv\n ? SingleEnvSchemaView && (\n <SingleEnvSchemaView current={node.data.data.current} />\n )\n : SchemaView && (\n <SchemaView\n base={node.data.data.base}\n current={node.data.data.current}\n columnChanges={node.data.change?.columns}\n onViewCode={() => setTabValue(1)}\n />\n )}\n </Box>\n </TabPanel>\n <TabPanel value={tabValue} index={1}>\n <Box sx={{ height: \"100%\" }}>\n {NodeSqlView && <NodeSqlView node={node} />}\n </Box>\n </TabPanel>\n </Box>\n </Box>\n )}\n\n {/* Sandbox dialog */}\n {SandboxDialog && (\n <SandboxDialog\n isOpen={isSandboxOpen}\n onClose={() => setIsSandboxOpen(false)}\n current={node.data.data.current}\n />\n )}\n </Box>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Chip from \"@mui/material/Chip\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport { alpha } from \"@mui/material/styles\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { format, formatDistanceToNow, parseISO } from \"date-fns\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { useState } from \"react\";\nimport { AiOutlineExperiment } from \"react-icons/ai\";\nimport { IoClose } from \"react-icons/io5\";\nimport { VscFeedback } from \"react-icons/vsc\";\nimport { useLineageGraphContext } from \"../../contexts/lineage\";\nimport { colors } from \"../../theme\";\nimport { VSplit } from \"../ui/Split\";\n\n/**\n * Props for a diff editor component used in the sandbox.\n * Supports editing the modified version while keeping original read-only.\n */\nexport interface SandboxDiffEditorProps {\n original: string;\n modified: string;\n language?: string;\n readOnly?: boolean;\n lineNumbers?: boolean;\n sideBySide?: boolean;\n theme?: \"light\" | \"dark\";\n height?: string;\n onModifiedChange?: (value: string) => void;\n}\n\n/**\n * Node data structure representing model information for the sandbox.\n */\nexport interface SandboxNodeData {\n id?: string;\n name?: string;\n raw_code?: string;\n}\n\n/**\n * Props for the QueryForm slot component.\n */\nexport interface SandboxQueryFormProps {\n defaultPrimaryKeys: string[];\n onPrimaryKeysChange: (primaryKeys: string[]) => void;\n}\n\n/**\n * Props for the RunResultPane slot component.\n */\nexport interface SandboxRunResultPaneProps {\n onClose: () => void;\n disableAddToChecklist?: boolean;\n}\n\n/**\n * Tracking event types for the sandbox.\n */\nexport interface SandboxTrackingCallbacks {\n onPreviewChange?: (params: {\n action: \"run\" | \"close\";\n node?: string;\n status?: \"success\" | \"failure\";\n }) => void;\n onFeedbackSubmit?: (params: {\n feedback: \"like\" | \"dislike\" | \"form\";\n node?: string;\n }) => void;\n onSingleEnvironment?: (params: {\n action: string;\n from: string;\n node?: string;\n }) => void;\n}\n\n/**\n * Props for the SandboxView component.\n *\n * This component uses dependency injection for editor and form components\n * to avoid coupling to specific implementations.\n */\nexport interface SandboxViewProps {\n /**\n * Whether the sandbox dialog is open.\n */\n isOpen: boolean;\n\n /**\n * Callback when the dialog is closed.\n */\n onClose: () => void;\n\n /**\n * Current node data containing model info and SQL code.\n */\n current?: SandboxNodeData;\n\n /**\n * Height of the dialog (not used in fullscreen mode).\n */\n height?: string;\n\n /**\n * Diff editor component for SQL editing.\n * Should accept props matching SandboxDiffEditorProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n DiffEditor: ComponentType<any>;\n\n /**\n * Query form component for primary key selection.\n * Should accept props matching SandboxQueryFormProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n QueryForm: ComponentType<any>;\n\n /**\n * Run result pane component for displaying query results.\n * Should accept props matching SandboxRunResultPaneProps interface.\n */\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n RunResultPane: ComponentType<any>;\n\n /**\n * Whether dark mode is enabled.\n */\n isDark?: boolean;\n\n /**\n * Primary keys for query diffing.\n */\n primaryKeys?: string[];\n\n /**\n * Callback when primary keys change.\n */\n onPrimaryKeysChange?: (keys: string[]) => void;\n\n /**\n * Whether a query is currently running.\n */\n isPending?: boolean;\n\n /**\n * Callback to run the query.\n */\n onRunQuery?: () => void;\n\n /**\n * Callback when run result pane opens (before running query).\n */\n onRunResultOpen?: () => void;\n\n /**\n * Callback when modified code changes.\n */\n onModifiedCodeChange?: (code: string) => void;\n\n /**\n * Callback to show feedback toast.\n */\n onShowFeedback?: () => void;\n\n /**\n * Tracking callbacks for analytics.\n */\n tracking?: SandboxTrackingCallbacks;\n\n /**\n * Logo image URL for the header.\n * @default \"/logo/recce-logo-white.png\"\n */\n logoUrl?: string;\n\n /**\n * Brand name to display in header.\n * @default \"RECCE\"\n */\n brandName?: string;\n}\n\n/**\n * Helper function to format timestamp for display.\n */\nfunction formatTimestamp(timestamp: string): string {\n const date = parseISO(timestamp);\n return format(date, \"yyyy-MM-dd'T'HH:mm:ss\");\n}\n\ninterface SandboxTopBarProps {\n current?: SandboxNodeData;\n primaryKeys: string[];\n onPrimaryKeysChange: (primaryKeys: string[]) => void;\n onRunResultOpen: () => void;\n runQuery: () => void;\n isPending: boolean;\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n QueryForm: ComponentType<any>;\n}\n\nfunction SandboxTopBar({\n current,\n primaryKeys,\n onPrimaryKeysChange,\n onRunResultOpen,\n runQuery,\n isPending,\n QueryForm,\n}: SandboxTopBarProps) {\n return (\n <Stack\n direction=\"row\"\n justifyContent=\"flex-end\"\n alignItems=\"center\"\n sx={{\n p: \"4pt 8pt\",\n gap: \"5px\",\n height: \"54px\",\n borderBottom: \"1px solid\",\n borderBottomColor: \"divider\",\n flex: \"0 0 54px\",\n }}\n >\n <Box>\n <Typography\n variant=\"h6\"\n component=\"h2\"\n sx={{ display: \"flex\", alignItems: \"center\", gap: \"5px\" }}\n >\n <Box component={AiOutlineExperiment} sx={{ fontSize: \"1.2em\" }} />\n Sandbox\n </Typography>\n <Typography sx={{ fontSize: \"0.75rem\", color: \"grey.500\" }}>\n Compare the run results based on the modified SQL code of model{\" \"}\n <b>{current?.name}</b>\n </Typography>\n </Box>\n <Box sx={{ flexGrow: 1 }} />\n <QueryForm\n defaultPrimaryKeys={primaryKeys}\n onPrimaryKeysChange={onPrimaryKeysChange}\n />\n <MuiTooltip title=\"Run diff to see the changes\">\n <Button\n size=\"small\"\n sx={{ mt: \"16px\", fontSize: \"14px\" }}\n onClick={() => {\n onRunResultOpen();\n runQuery();\n }}\n color=\"iochmara\"\n variant=\"contained\"\n disabled={isPending}\n >\n {isPending ? \"Running...\" : \"Run Diff\"}\n </Button>\n </MuiTooltip>\n </Stack>\n );\n}\n\ninterface SandboxEditorLabelsProps {\n currentModelID: string;\n height?: string;\n flex?: string;\n isDark?: boolean;\n}\n\nfunction SandboxEditorLabels({\n currentModelID,\n height = \"32px\",\n flex = \"0 0 auto\",\n isDark = false,\n}: SandboxEditorLabelsProps) {\n const { lineageGraph, envInfo } = useLineageGraphContext();\n const widthOfBar = \"50%\";\n const margin = \"0 16px\";\n\n const currentTime = formatTimestamp(\n envInfo?.dbt?.current?.generated_at ?? \"\",\n );\n const latestUpdateDistanceToNow = formatDistanceToNow(currentTime, {\n addSuffix: true,\n });\n let schema = \"N/A\";\n if (lineageGraph?.nodes[currentModelID]) {\n const value = lineageGraph.nodes[currentModelID];\n if (value.data.data.current?.schema) {\n schema = value.data.data.current.schema;\n }\n }\n\n return (\n <Stack\n direction=\"row\"\n sx={{\n gap: 0,\n height,\n flex,\n fontSize: \"14px\",\n alignItems: \"center\",\n m: 0,\n bgcolor: isDark\n ? alpha(colors.neutral[700], 0.5)\n : alpha(colors.neutral[100], 0.5),\n }}\n >\n <Stack sx={{ width: widthOfBar }}>\n <Typography sx={{ fontWeight: \"bold\", margin }}>\n ORIGINAL (Schema: {schema}, Last Updated: {latestUpdateDistanceToNow})\n </Typography>\n </Stack>\n <Stack sx={{ width: widthOfBar }}>\n <Typography sx={{ fontWeight: \"bold\", margin }}>\n SANDBOX EDITOR\n </Typography>\n </Stack>\n </Stack>\n );\n}\n\ninterface SqlPreviewProps {\n current?: SandboxNodeData;\n onChange: (value: string) => void;\n isDark?: boolean;\n // biome-ignore lint/suspicious/noExplicitAny: DI pattern requires flexible component types\n DiffEditor: ComponentType<any>;\n}\n\nfunction SqlPreview({\n current,\n onChange,\n isDark,\n DiffEditor,\n}: SqlPreviewProps) {\n return (\n <DiffEditor\n original={current?.raw_code ?? \"\"}\n modified={current?.raw_code ?? \"\"}\n language=\"sql\"\n readOnly={false}\n lineNumbers={true}\n sideBySide={true}\n theme={isDark ? \"dark\" : \"light\"}\n height=\"100%\"\n onModifiedChange={onChange}\n />\n );\n}\n\n/**\n * SandboxView Component\n *\n * A modal dialog for previewing and comparing SQL code changes in a sandbox environment.\n * Allows editing modified SQL and running diff queries against the data warehouse.\n *\n * Components are injected as props to allow the consuming application\n * to provide its own implementations (e.g., specific editors, form components).\n */\nexport function SandboxView({\n isOpen,\n onClose,\n current,\n DiffEditor,\n QueryForm,\n RunResultPane,\n isDark = false,\n primaryKeys = [],\n onPrimaryKeysChange,\n isPending = false,\n onRunQuery,\n onRunResultOpen,\n onModifiedCodeChange,\n onShowFeedback,\n tracking,\n logoUrl = \"/logo/recce-logo-white.png\",\n brandName = \"RECCE\",\n}: SandboxViewProps) {\n const [isRunResultOpen, setIsRunResultOpen] = useState(false);\n\n const handleModifiedCodeChange = (code: string) => {\n onModifiedCodeChange?.(code);\n };\n\n const handleRunResultOpen = () => {\n setIsRunResultOpen(true);\n onRunResultOpen?.();\n };\n\n const handleRunQuery = () => {\n onRunQuery?.();\n };\n\n const handleClose = () => {\n onClose();\n setIsRunResultOpen(false);\n tracking?.onPreviewChange?.({ action: \"close\", node: current?.name });\n };\n\n const handlePrimaryKeysChange = (keys: string[]) => {\n onPrimaryKeysChange?.(keys);\n };\n\n return (\n <MuiDialog\n open={isOpen}\n onClose={handleClose}\n maxWidth={false}\n fullWidth\n slotProps={{\n paper: {\n sx: {\n width: \"100%\",\n height: \"100%\",\n maxWidth: \"100%\",\n maxHeight: \"100%\",\n m: 0,\n },\n },\n }}\n >\n <Box\n sx={{\n height: \"40px\",\n bgcolor: \"cyan.600\",\n px: 0,\n py: 2,\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ height: \"100%\", gap: \"10px\" }}\n >\n <Box\n component=\"img\"\n sx={{ width: \"20px\", height: \"20px\", ml: \"18px\" }}\n src={logoUrl}\n alt=\"logo\"\n />\n <Typography\n variant=\"h6\"\n component=\"h1\"\n sx={{\n fontFamily: '\"Montserrat\", sans-serif',\n fontSize: \"1.125rem\",\n color: \"common.white\",\n }}\n >\n {brandName}\n </Typography>\n <Chip\n label=\"Experiment\"\n size=\"small\"\n variant=\"outlined\"\n sx={{\n fontSize: \"0.875rem\",\n color: \"common.white\",\n borderColor: \"rgba(255,255,255,0.5)\",\n }}\n />\n </Stack>\n <IconButton\n aria-label=\"close\"\n onClick={handleClose}\n sx={{\n position: \"absolute\",\n right: 8,\n top: 4,\n color: \"common.white\",\n }}\n >\n <IoClose />\n </IconButton>\n </Box>\n <DialogContent sx={{ p: 0 }}>\n {/* Constant props to avoid react-split destroy/recreate */}\n <VSplit\n sizes={isRunResultOpen ? [50, 50] : [100, 0]}\n minSize={0}\n gutterSize={5}\n className={isRunResultOpen ? undefined : \"split-gutter-hidden\"}\n style={{\n flex: \"1\",\n contain: \"size\",\n height: \"100%\",\n }}\n >\n <Stack sx={{ height: \"100%\", m: 0, p: 0 }}>\n <SandboxTopBar\n current={current}\n primaryKeys={primaryKeys}\n onPrimaryKeysChange={handlePrimaryKeysChange}\n onRunResultOpen={handleRunResultOpen}\n runQuery={handleRunQuery}\n isPending={isPending}\n QueryForm={QueryForm}\n />\n <SandboxEditorLabels\n height=\"32px\"\n flex=\"0 0 auto\"\n currentModelID={current?.id ?? \"\"}\n isDark={isDark}\n />\n <SqlPreview\n current={current}\n onChange={handleModifiedCodeChange}\n isDark={isDark}\n DiffEditor={DiffEditor}\n />\n </Stack>\n {isRunResultOpen ? (\n <RunResultPane\n onClose={() => setIsRunResultOpen(false)}\n disableAddToChecklist\n />\n ) : (\n <Box />\n )}\n </VSplit>\n </DialogContent>\n {/* Fixed position button */}\n <Box sx={{ position: \"fixed\", bottom: 16, right: 16, opacity: 0.5 }}>\n <MuiTooltip title=\"Give us feedback\">\n <IconButton\n aria-label=\"feedback\"\n size=\"medium\"\n onClick={() => {\n onShowFeedback?.();\n }}\n >\n <VscFeedback />\n </IconButton>\n </MuiTooltip>\n </Box>\n </MuiDialog>\n );\n}\n","import { useMutation } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport {\n LOCAL_STORAGE_KEYS,\n type QueryParams,\n type SubmitOptions,\n submitQueryDiff,\n waitRun,\n} from \"../../api\";\nimport { useRecceActionContext, useRecceServerFlag } from \"../../contexts\";\nimport {\n useApiConfig,\n useFeedbackCollectionToast,\n useGuideToast,\n useIsDark,\n useRecceQueryContext,\n} from \"../../hooks\";\nimport {\n trackPreviewChange,\n trackPreviewChangeFeedback,\n trackSingleEnvironment,\n} from \"../../lib/api/track\";\nimport { DiffEditor } from \"../../primitives\";\nimport { QueryForm } from \"../query\";\nimport { RunResultPaneOss as RunResultPane } from \"../run\";\nimport {\n SandboxView as BaseSandboxView,\n type SandboxNodeData,\n} from \"./SandboxView\";\n\ninterface SandboxViewProps {\n isOpen: boolean;\n onClose: () => void;\n current?: SandboxNodeData;\n height?: string;\n}\n\n/**\n * OSS wrapper for SandboxView that injects OSS-specific implementations.\n *\n * This wrapper:\n * 1. Handles query execution with React Query mutations\n * 2. Injects OSS-specific components (DiffEditor, QueryForm, RunResultPane)\n * 3. Provides OSS-specific tracking and feedback toasts\n * 4. Manages run state via useRecceActionContext\n *\n * The underlying BaseSandboxView from @datarecce/ui is framework-agnostic\n * and accepts components as props for dependency injection.\n */\nexport function SandboxViewOss({ isOpen, onClose, current }: SandboxViewProps) {\n const [modifiedCode, setModifiedCode] = useState<string>(\n current?.raw_code ?? \"\",\n );\n const [prevIsOpen, setPrevIsOpen] = useState(isOpen);\n const { showRunId, clearRunResult } = useRecceActionContext();\n const { primaryKeys, setPrimaryKeys } = useRecceQueryContext();\n const { data: flags, isLoading } = useRecceServerFlag();\n const { apiClient } = useApiConfig();\n const isDark = useIsDark();\n\n // Reset modifiedCode when modal opens\n if (isOpen !== prevIsOpen) {\n setPrevIsOpen(isOpen);\n if (isOpen) {\n setModifiedCode(current?.raw_code ?? \"\");\n }\n }\n\n const queryFn = async () => {\n const sqlTemplate = modifiedCode;\n const runFn = submitQueryDiff;\n const params: QueryParams = {\n current_model: current?.name ?? \"\",\n primary_keys: primaryKeys,\n sql_template: sqlTemplate,\n };\n const options: SubmitOptions = { nowait: true };\n\n const { run_id } = await runFn(params, options, apiClient);\n\n showRunId(run_id);\n\n return await waitRun(run_id, undefined, apiClient);\n };\n\n const { mutate: runQuery, isPending } = useMutation({\n mutationFn: queryFn,\n onSuccess(data) {\n if (data.error) {\n trackPreviewChange({\n action: \"run\",\n node: current?.name,\n status: \"failure\",\n });\n } else {\n trackPreviewChange({\n action: \"run\",\n node: current?.name,\n status: \"success\",\n });\n setTimeout(() => {\n feedbackToast();\n }, 1000);\n if (!isLoading && flags?.single_env_onboarding) {\n setTimeout(() => {\n prepareEnvToast();\n }, 2000);\n }\n }\n },\n });\n\n const { feedbackToast, closeToast } = useFeedbackCollectionToast({\n feedbackId: LOCAL_STORAGE_KEYS.previewChangeFeedbackID,\n description: \"Enjoy preview change?\",\n\n onFeedbackSubmit: (feedback: string) => {\n switch (feedback) {\n case \"like\":\n trackPreviewChangeFeedback({ feedback: \"like\", node: current?.name });\n break;\n case \"dislike\":\n trackPreviewChangeFeedback({\n feedback: \"dislike\",\n node: current?.name,\n });\n break;\n case \"link\":\n trackPreviewChangeFeedback({ feedback: \"form\", node: current?.name });\n break;\n default:\n console.log(\"Not support feedback type\");\n }\n },\n externalLink:\n \"https://docs.google.com/forms/d/e/1FAIpQLSd7Lei7Ijwo7MinWaI0K6rzZi_21gV1BKetmiNEX254kDziDA/viewform?usp=header\",\n externalLinkText: \"Give us feedback\",\n });\n\n const { guideToast: prepareEnvToast, closeGuideToast } = useGuideToast({\n guideId: LOCAL_STORAGE_KEYS.prepareEnvGuideID,\n description: \"Want to compare data changes with production data?\",\n externalLink: \"https://docs.reccehq.com/get-started/#prepare-dbt-artifacts\",\n externalLinkText: \"Learn how.\",\n onExternalLinkClick: () => {\n trackSingleEnvironment({\n action: \"external_link\",\n from: \"preview_changes\",\n node: current?.name,\n });\n },\n });\n\n const handleClose = () => {\n onClose();\n clearRunResult();\n closeToast();\n closeGuideToast();\n };\n\n return (\n <BaseSandboxView\n isOpen={isOpen}\n onClose={handleClose}\n current={current}\n DiffEditor={DiffEditor}\n QueryForm={QueryForm}\n RunResultPane={RunResultPane}\n isDark={isDark}\n primaryKeys={primaryKeys ?? []}\n onPrimaryKeysChange={setPrimaryKeys}\n isPending={isPending}\n onRunQuery={runQuery}\n onModifiedCodeChange={setModifiedCode}\n onShowFeedback={() => feedbackToast(true)}\n tracking={{\n onPreviewChange: trackPreviewChange,\n }}\n />\n );\n}\n","\"use client\";\n\n/**\n * @file NodeViewOss.tsx\n * @description wrapper for NodeView that injects dependencies.\n *\n * This wrapper:\n * 1. Provides OSS-specific schema view components\n * 2. Injects action callbacks that integrate with OSS contexts\n * 3. Provides run type icons from the OSS registry\n * 4. Handles navigation and tracking\n */\n\nimport Typography from \"@mui/material/Typography\";\nimport { useRouter } from \"next/navigation\";\nimport { useMemo } from \"react\";\nimport type { LineageGraphNode } from \"../..\";\nimport { createSchemaDiffCheck } from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n useRouteConfig,\n} from \"../../contexts\";\nimport {\n useApiConfig,\n useModelColumns,\n useRecceQueryContext,\n} from \"../../hooks\";\nimport {\n EXPLORE_ACTION,\n EXPLORE_SOURCE,\n trackExploreAction,\n trackPreviewChange,\n} from \"../../lib/api/track\";\nimport { formatSelectColumns } from \"../../utils\";\nimport { SetupConnectionPopover } from \"../app\";\nimport { LearnHowLink, RecceNotification } from \"../onboarding-guide\";\nimport { findByRunType } from \"../run\";\nimport { SchemaView, SingleEnvSchemaView } from \"../schema\";\nimport { NodeSqlViewOss } from \"./NodeSqlViewOss\";\nimport { RowCountDiffTag, RowCountTag } from \"./NodeTag\";\nimport {\n NodeView as BaseNodeView,\n type NodeViewActionCallbacks,\n type RunTypeIconMap,\n} from \"./NodeView\";\nimport { SandboxViewOss } from \"./SandboxViewOss\";\nimport { ResourceTypeTag as ResourceTypeTagBase } from \"./tags\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface NodeViewProps {\n node: LineageGraphNode;\n onCloseNode: () => void;\n}\n\nconst ResourceTypeTag = ({ node }: { node: LineageGraphNode }) => (\n <ResourceTypeTagBase data={{ resourceType: node.data.resourceType }} />\n);\n\n// =============================================================================\n// OSS-SPECIFIC WRAPPER COMPONENTS\n// =============================================================================\n\n/**\n * Notification component that includes the LearnHowLink.\n */\nfunction OssNotificationComponent({ onClose }: { onClose: () => void }) {\n return (\n <RecceNotification onClose={onClose} align=\"flex-start\">\n <Typography variant=\"body2\">\n Enable the Recce Checklist and start adding checks for better data\n validation and review.\n <br />\n <LearnHowLink />\n </Typography>\n </RecceNotification>\n );\n}\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\n/**\n * OSS wrapper for NodeView that injects OSS-specific dependencies.\n *\n * This wrapper provides:\n * - OSS-specific schema view components\n * - Action callbacks that integrate with OSS contexts (tracking, navigation)\n * - Run type icons from the OSS registry\n * - Connection popover wrapper for database setup prompts\n * - Sandbox dialog component\n */\nexport function NodeViewOss({ node, onCloseNode }: NodeViewProps) {\n const router = useRouter();\n const { runAction } = useRecceActionContext();\n const { isActionAvailable, envInfo } = useLineageGraphContext();\n const { singleEnv: isSingleEnvOnboarding, featureToggles } =\n useRecceInstanceContext();\n const { setSqlQuery, setPrimaryKeys } = useRecceQueryContext();\n const { primaryKey } = useModelColumns(node.data.name);\n const { apiClient } = useApiConfig();\n const { basePath } = useRouteConfig();\n\n // Build run type icons map from OSS registry\n const runTypeIcons: RunTypeIconMap = useMemo(\n () => ({\n query: findByRunType(\"query\").icon,\n row_count: findByRunType(\"row_count\").icon,\n row_count_diff: findByRunType(\"row_count_diff\").icon,\n profile: findByRunType(\"profile\").icon,\n profile_diff: findByRunType(\"profile_diff\").icon,\n query_diff: findByRunType(\"query_diff\").icon,\n value_diff: findByRunType(\"value_diff\").icon,\n top_k_diff: findByRunType(\"top_k_diff\").icon,\n histogram_diff: findByRunType(\"histogram_diff\").icon,\n schema_diff: findByRunType(\"schema_diff\").icon,\n sandbox: findByRunType(\"sandbox\").icon,\n }),\n [],\n );\n\n // Build query string for this node\n const baseColumns = Object.keys(node.data.data.base?.columns ?? {});\n const currentColumns = Object.keys(node.data.data.current?.columns ?? {});\n const formattedColumns = formatSelectColumns(baseColumns, currentColumns);\n const query = useMemo(() => {\n if (formattedColumns.length) {\n return `select \\n ${formattedColumns.join(\"\\n \")}\\nfrom {{ ref(\"${node.data.name}\") }}`;\n }\n return `select * from {{ ref(\"${node.data.name}\") }}`;\n }, [formattedColumns, node.data.name]);\n\n // Action callbacks for the base component\n const actionCallbacks: NodeViewActionCallbacks = useMemo(\n () => ({\n onQueryClick: () => {\n if (envInfo?.adapterType === \"dbt\") {\n setSqlQuery(query);\n } else if (envInfo?.adapterType === \"sqlmesh\") {\n setSqlQuery(`select * from ${node.data.name}`);\n }\n router.push(`${basePath}/query`);\n },\n\n onRowCountClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.ROW_COUNT,\n source: EXPLORE_SOURCE.SCHEMA_ROW_COUNT_BUTTON,\n node_count: 1,\n });\n runAction(\n \"row_count\",\n { node_names: [node.data.name] },\n { showForm: false, showLast: false },\n );\n },\n\n onRowCountDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.ROW_COUNT_DIFF,\n source: EXPLORE_SOURCE.SCHEMA_ROW_COUNT_BUTTON,\n node_count: 1,\n });\n runAction(\n \"row_count_diff\",\n { node_names: [node.data.name] },\n { showForm: false, showLast: false },\n );\n },\n\n onProfileClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.PROFILE,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_SINGLE_ENV,\n node_count: 1,\n });\n runAction(\n \"profile\",\n { model: node.data.name },\n { showForm: true, showLast: false },\n );\n },\n\n onProfileDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.PROFILE_DIFF,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,\n node_count: 1,\n });\n runAction(\n \"profile_diff\",\n { model: node.data.name },\n { showForm: true, showLast: false },\n );\n },\n\n onQueryDiffClick: () => {\n if (envInfo?.adapterType === \"dbt\") {\n setSqlQuery(query);\n } else if (envInfo?.adapterType === \"sqlmesh\") {\n setSqlQuery(`select * from ${node.data.name}`);\n }\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys(primaryKey !== undefined ? [primaryKey] : undefined);\n }\n router.push(`${basePath}/query`);\n },\n\n onValueDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.VALUE_DIFF,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,\n node_count: 1,\n });\n runAction(\n \"value_diff\",\n { model: node.data.name },\n { showForm: true, showLast: false },\n );\n },\n\n onTopKDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.TOP_K_DIFF,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,\n node_count: 1,\n });\n runAction(\n \"top_k_diff\",\n { model: node.data.name, column_name: \"\", k: 50 },\n { showForm: true },\n );\n },\n\n onHistogramDiffClick: () => {\n trackExploreAction({\n action: EXPLORE_ACTION.HISTOGRAM_DIFF,\n source: EXPLORE_SOURCE.NODE_SIDEBAR_MULTI_ENV,\n node_count: 1,\n });\n runAction(\n \"histogram_diff\",\n { model: node.data.name, column_name: \"\", column_type: \"\" },\n { showForm: true },\n );\n },\n\n onAddSchemaDiffClick: async () => {\n const check = await createSchemaDiffCheck(\n { node_id: node.id },\n apiClient,\n );\n router.push(`${basePath}/checks/?id=${check.check_id}`);\n },\n\n onSandboxClick: () => {\n if (isActionAvailable(\"query_diff_with_primary_key\")) {\n setPrimaryKeys(primaryKey !== undefined ? [primaryKey] : undefined);\n }\n trackPreviewChange({\n action: \"explore\",\n node: node.data.name,\n });\n },\n }),\n [\n node,\n query,\n envInfo,\n setSqlQuery,\n runAction,\n isActionAvailable,\n setPrimaryKeys,\n primaryKey,\n apiClient,\n router.push,\n basePath,\n ],\n );\n\n return (\n <BaseNodeView\n node={node}\n onCloseNode={onCloseNode}\n isSingleEnv={isSingleEnvOnboarding ?? false}\n featureToggles={featureToggles}\n // Schema components\n SchemaView={SchemaView}\n SingleEnvSchemaView={SingleEnvSchemaView}\n NodeSqlView={NodeSqlViewOss}\n // Tag components\n ResourceTypeTag={ResourceTypeTag}\n RowCountDiffTag={RowCountDiffTag}\n RowCountTag={RowCountTag}\n // Notification for single env\n NotificationComponent={OssNotificationComponent}\n // Connection popover wrapper\n ConnectionPopoverWrapper={SetupConnectionPopover}\n // Sandbox dialog\n SandboxDialog={SandboxViewOss}\n // Icons\n runTypeIcons={runTypeIcons}\n // Callbacks\n actionCallbacks={actionCallbacks}\n isActionAvailable={isActionAvailable}\n />\n );\n}\n","import type { ColumnLineageData } from \"../../api/cll\";\nimport type { LineageDiffData } from \"../../api/info\";\n\n/**\n * Extract change analysis data from a CLL response and merge it into\n * an existing LineageDiffData object.\n *\n * This replaces the old pattern of refetching the entire lineage after\n * a CLL call with change_analysis: true. Instead, we patch the cached\n * diff directly so React Query triggers a re-render.\n */\nexport function patchLineageDiffFromCll(\n existingDiff: LineageDiffData,\n cllData: ColumnLineageData,\n): LineageDiffData {\n const patch: LineageDiffData = { ...existingDiff };\n\n for (const [nodeId, cllNode] of Object.entries(cllData.current.nodes)) {\n if (!cllNode.change_status) {\n continue;\n }\n\n // Build column change map from CLL node's columns\n let columns: Record<string, \"added\" | \"removed\" | \"modified\"> | null = null;\n if (cllNode.columns) {\n const columnChanges: Record<string, \"added\" | \"removed\" | \"modified\"> =\n {};\n let hasChanges = false;\n for (const col of Object.values(cllNode.columns)) {\n if (col.change_status) {\n columnChanges[col.name] = col.change_status;\n hasChanges = true;\n }\n }\n if (hasChanges) {\n columns = columnChanges;\n }\n }\n\n patch[nodeId] = {\n change_status: cllNode.change_status,\n change: cllNode.change_category\n ? { category: cllNode.change_category, columns }\n : null,\n };\n }\n\n return patch;\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { PiInfo } from \"react-icons/pi\";\n\nimport type { RecceFeatureToggles } from \"../../contexts/instance\";\n\n/**\n * Props for the SetupConnectionBanner component.\n *\n * Uses dependency injection for context dependencies to enable reuse\n * across different applications (OSS Recce, Recce Cloud).\n */\nexport interface SetupConnectionBannerProps {\n /**\n * Feature toggles from RecceInstanceContext.\n * Used to determine if banner should be shown (metadata only mode).\n */\n featureToggles: RecceFeatureToggles;\n\n /**\n * URL to open when user clicks \"Connect to Data Warehouse\" button.\n * Injected because URL generation varies by application context.\n */\n settingsUrl: string;\n}\n\n/**\n * Banner component that prompts users to set up a data warehouse connection.\n *\n * Displays when the Recce instance is in \"metadata only\" mode, indicating\n * that query functions are disabled without a data warehouse connection.\n *\n * @example\n * ```tsx\n * import { SetupConnectionBanner } from \"@datarecce/ui\";\n *\n * function MyComponent() {\n * const { featureToggles } = useRecceInstanceContext();\n * const { data: instanceInfo } = useRecceInstanceInfo();\n * const settingsUrl = getSettingsUrl(instanceInfo);\n *\n * return (\n * <SetupConnectionBanner\n * featureToggles={featureToggles}\n * settingsUrl={settingsUrl}\n * />\n * );\n * }\n * ```\n */\nexport function SetupConnectionBanner({\n featureToggles,\n settingsUrl,\n}: SetupConnectionBannerProps) {\n if (featureToggles.mode !== \"metadata only\") {\n return null;\n }\n\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n width: \"100%\",\n px: 1,\n py: 0.25,\n bgcolor: \"cyan.50\",\n }}\n >\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ flex: 1, fontSize: \"0.875rem\", color: \"cyan.600\" }}\n spacing={1}\n >\n <Box component={PiInfo} />\n <Typography sx={{ fontSize: \"inherit\", color: \"inherit\" }}>\n Query functions disabled without a data warehouse connection.\n </Typography>\n <Button\n sx={{ bgcolor: \"iochmara.400\" }}\n size=\"small\"\n variant=\"contained\"\n onClick={() => {\n window.open(settingsUrl, \"_blank\");\n }}\n >\n Connect to Data Warehouse\n </Button>\n </Stack>\n </Box>\n );\n}\n\nexport default SetupConnectionBanner;\n","\"use client\";\n\nimport { useRecceInstanceContext, useRecceInstanceInfo } from \"../../contexts\";\nimport { RECCE_SUPPORT_CALENDAR_URL } from \"../../lib/const\";\nimport { getSettingsUrl } from \"../../utils\";\nimport {\n SetupConnectionBanner as BaseSetupConnectionBanner,\n type SetupConnectionBannerProps as BaseSetupConnectionBannerProps,\n} from \"./SetupConnectionBanner\";\n\n/**\n * Props for the OSS SetupConnectionBanner wrapper.\n * Extends the base props but makes injected dependencies optional\n * since they're provided by OSS-specific contexts.\n */\nexport type SetupConnectionBannerOssProps =\n Partial<BaseSetupConnectionBannerProps>;\n\n/**\n * OSS wrapper for SetupConnectionBanner.\n *\n * Provides OSS-specific context integration by automatically:\n * - Fetching feature toggles from RecceInstanceContext\n * - Generating settings URL from instance info\n */\nexport default function SetupConnectionBannerOss(\n props: SetupConnectionBannerOssProps,\n) {\n const { featureToggles } = useRecceInstanceContext();\n const { data: instanceInfo } = useRecceInstanceInfo();\n\n return (\n <BaseSetupConnectionBanner\n featureToggles={props.featureToggles ?? featureToggles}\n settingsUrl={\n props.settingsUrl ??\n getSettingsUrl(instanceInfo, RECCE_SUPPORT_CALENDAR_URL)\n }\n />\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Link from \"@mui/material/Link\";\nimport List from \"@mui/material/List\";\nimport ListItem from \"@mui/material/ListItem\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { RiMindMap, RiTerminalBoxLine } from \"react-icons/ri\";\n\n// =============================================================================\n// TYPE DEFINITIONS\n// =============================================================================\n\n/**\n * Props for BaseEnvironmentSetupGuide component.\n * Uses dependency injection for external URLs to support different consumers.\n */\nexport interface BaseEnvironmentSetupGuideProps {\n /**\n * URL to navigate to when \"Start Now\" button is clicked.\n * @default \"https://docs.reccehq.com/get-started/#prepare-dbt-artifacts\"\n */\n docsUrl?: string;\n /**\n * Callback when the \"Start Now\" button is clicked.\n * If provided, will be called instead of opening the URL.\n */\n onStartClick?: () => void;\n}\n\n/**\n * Props for BaseEnvironmentSetupNotification component.\n * Uses dependency injection for external URLs to support different consumers.\n */\nexport interface BaseEnvironmentSetupNotificationProps {\n /**\n * URL for the documentation link.\n * @default \"https://docs.reccehq.com/configure-diff/\"\n */\n docsUrl?: string;\n}\n\n// =============================================================================\n// COMPONENTS\n// =============================================================================\n\n/**\n * BaseEnvironmentSetupGuide Component\n *\n * Full-page guide displayed when Recce is running in single environment mode\n * (limited functionality mode). Explains the benefits of setting up a base\n * environment and provides a call-to-action to configure it.\n *\n * @example\n * ```tsx\n * import { BaseEnvironmentSetupGuide } from '@datarecce/ui/components/lineage';\n *\n * // Default usage with standard docs URL\n * <BaseEnvironmentSetupGuide />\n *\n * // Custom docs URL for different consumer\n * <BaseEnvironmentSetupGuide\n * docsUrl=\"https://cloud.reccehq.com/docs/setup\"\n * />\n *\n * // Custom click handler\n * <BaseEnvironmentSetupGuide\n * onStartClick={() => navigateToSetup()}\n * />\n * ```\n */\nexport function BaseEnvironmentSetupGuide({\n docsUrl = \"https://docs.reccehq.com/get-started/#prepare-dbt-artifacts\",\n onStartClick,\n}: BaseEnvironmentSetupGuideProps = {}) {\n const handleStartClick = () => {\n if (onStartClick) {\n onStartClick();\n } else {\n window.open(docsUrl, \"_blank\");\n }\n };\n\n return (\n <Stack\n sx={{\n flex: 1,\n height: \"100%\",\n minHeight: 0,\n m: 1,\n p: 2,\n bgcolor: \"iochmara.50\",\n borderRadius: 2,\n boxShadow: 2,\n justifyContent: \"center\",\n }}\n >\n <Stack\n sx={{ width: \"80%\", overflowY: \"auto\", gap: 3, px: 4, pb: 4 }}\n alignSelf=\"center\"\n >\n <Stack alignItems=\"center\" spacing={2}>\n <Box\n sx={{\n p: 1,\n bgcolor: \"background.paper\",\n borderRadius: \"50%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: 2,\n }}\n >\n <Box\n component={RiTerminalBoxLine}\n sx={{ fontSize: 28, color: \"iochmara.500\" }}\n />\n </Box>\n <Typography variant=\"h5\" sx={{ mt: 2 }}>\n Wait, there's more!\n </Typography>\n <Typography sx={{ textAlign: \"center\" }}>\n Recce is currently running in{\" \"}\n <Typography component=\"span\" sx={{ fontWeight: \"bold\" }}>\n limited functionality mode\n </Typography>{\" \"}\n so you can run queries but{\" \"}\n <Typography component=\"span\" sx={{ fontWeight: \"bold\" }}>\n can't diff the results yet!\n </Typography>\n </Typography>\n </Stack>\n <Stack spacing={1}>\n <Typography>\n To unlock the full power of Recce, set up a base environment of dbt\n artifacts for comparison.\n </Typography>\n <Typography>Once configured, you'll be able to:</Typography>\n <List sx={{ listStyleType: \"disc\", pl: 2 }}>\n <ListItem sx={{ display: \"list-item\", p: 0 }}>\n <Typography>Run statistical data diffs</Typography>\n </ListItem>\n <ListItem sx={{ display: \"list-item\", p: 0 }}>\n <Typography>Run query diffs</Typography>\n </ListItem>\n <ListItem sx={{ display: \"list-item\", p: 0 }}>\n <Typography>Save checks to your Recce Checklist</Typography>\n </ListItem>\n <ListItem sx={{ display: \"list-item\", p: 0 }}>\n <Typography>...and more!</Typography>\n </ListItem>\n </List>\n <Typography>\n Take the next step toward better data impact assessment.\n </Typography>\n </Stack>\n <Stack sx={{ width: \"100%\", mt: 3 }}>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n size=\"large\"\n onClick={handleStartClick}\n >\n Start Now\n </Button>\n </Stack>\n </Stack>\n </Stack>\n );\n}\n\n/**\n * BaseEnvironmentSetupNotification Component\n *\n * Compact notification/banner component displayed in the lineage view sidebar\n * when in single environment mode. Provides quick guidance on how to set up\n * full environment comparison.\n *\n * @example\n * ```tsx\n * import { BaseEnvironmentSetupNotification } from '@datarecce/ui/components/lineage';\n *\n * // Default usage with standard docs URL\n * <BaseEnvironmentSetupNotification />\n *\n * // Custom docs URL for different consumer\n * <BaseEnvironmentSetupNotification\n * docsUrl=\"https://cloud.reccehq.com/docs/configure\"\n * />\n * ```\n */\nexport function BaseEnvironmentSetupNotification({\n docsUrl = \"https://docs.reccehq.com/configure-diff/\",\n}: BaseEnvironmentSetupNotificationProps = {}) {\n return (\n <Stack direction=\"row\" spacing=\"10px\" alignItems=\"flex-start\">\n <Box\n component={RiMindMap}\n sx={{ color: \"iochmara.main\", fontSize: 20 }}\n />\n <Stack spacing=\"5px\">\n <Typography sx={{ fontWeight: 700 }}>\n Single Environment Mode{\" \"}\n <Typography\n component=\"span\"\n sx={{ color: \"error.main\", fontWeight: 600 }}\n >\n Limited Functionality\n </Typography>\n </Typography>\n\n <Typography sx={{ fontSize: \"0.875rem\" }}>\n Single Environment Mode allows you to explore your dbt project but\n won't show data comparisons between environments.\n </Typography>\n <Typography sx={{ fontSize: \"0.875rem\" }}>\n To set up full environment comparison:\n </Typography>\n <List sx={{ pl: 2 }}>\n <ListItem sx={{ p: 0, display: \"list-item\" }}>\n <Typography sx={{ fontSize: \"0.875rem\" }}>\n Run `recce debug` for setup assistance\n </Typography>\n </ListItem>\n <ListItem sx={{ p: 0, display: \"list-item\" }}>\n <Typography sx={{ fontSize: \"0.875rem\" }}>\n Visit{\" \"}\n <Link\n sx={{ color: \"primary.main\", fontWeight: 500 }}\n target=\"_blank\"\n href={docsUrl}\n >\n docs\n </Link>{\" \"}\n for configuration details\n </Typography>\n </ListItem>\n </List>\n </Stack>\n </Stack>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Stack from \"@mui/material/Stack\";\nimport type { LineageDiffViewOptions } from \"../../../api\";\n\n/**\n * Loading state component for LineageView.\n * Displays a centered spinner while lineage data is loading.\n */\nexport function LineageViewLoading() {\n return (\n <Box\n sx={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <CircularProgress size={48} />\n </Box>\n );\n}\n\n/**\n * Props for LineageViewError component.\n */\nexport interface LineageViewErrorProps {\n /** The error message to display */\n error: string;\n /** Callback to retry loading the lineage data */\n onRetry?: () => void;\n}\n\n/**\n * Error state component for LineageView.\n * Displays error message with a retry button.\n */\nexport function LineageViewError({ error, onRetry }: LineageViewErrorProps) {\n return (\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Stack alignItems=\"center\" spacing={1}>\n <Box>\n Failed to load lineage data. This could be because the server has been\n terminated or there is a network error.\n </Box>\n <Box>[Reason: {error}]</Box>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n onClick={() => {\n if (onRetry) {\n onRetry();\n }\n }}\n >\n Retry\n </Button>\n </Stack>\n </Box>\n );\n}\n\n/**\n * Props for LineageViewNoChanges component.\n */\nexport interface LineageViewNoChangesProps {\n /** Current view options */\n viewOptions: LineageDiffViewOptions;\n /** Callback to change view options */\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n}\n\n/**\n * Empty state component when no changes are detected in changed_models view mode.\n * Provides a button to switch to \"all\" view mode.\n */\nexport function LineageViewNoChanges({\n viewOptions,\n onViewOptionsChanged,\n}: LineageViewNoChangesProps) {\n return (\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Stack alignItems=\"center\" spacing={1}>\n <>No change detected</>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n onClick={async () => {\n await onViewOptionsChanged({\n ...viewOptions,\n view_mode: \"all\",\n });\n }}\n >\n Show all nodes\n </Button>\n </Stack>\n </Box>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport type { ReactNode } from \"react\";\nimport { VscHistory } from \"react-icons/vsc\";\nimport { useRecceActionContext } from \"../../contexts\";\nimport { trackHistoryAction } from \"../../lib/api/track\";\n\nexport function HistoryToggle(): ReactNode {\n const { isHistoryOpen, showHistory } = useRecceActionContext();\n\n if (isHistoryOpen) {\n return;\n }\n\n return (\n <Box>\n <Box sx={{ fontSize: \"8pt\" }}>History</Box>\n\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<VscHistory />}\n onClick={() => {\n trackHistoryAction({ name: isHistoryOpen ? \"hide\" : \"show\" });\n showHistory();\n }}\n >\n Show\n </Button>\n </Box>\n );\n}\n","\"use client\";\n\n/**\n * @file LineageViewTopBar.tsx\n * @description Core LineageViewTopBar component with dependency injection.\n *\n * This component provides the top toolbar for the lineage view with filtering,\n * mode selection, package filtering, and action menus. It uses dependency injection\n * for consumer-specific features like routing, tracking, and run type icons.\n *\n * @example\n * ```tsx\n * import { LineageViewTopBar } from '@datarecce/ui/components/lineage';\n *\n * <LineageViewTopBar\n * viewOptions={viewOptions}\n * onViewOptionsChanged={handleViewOptionsChanged}\n * lineageGraph={lineageGraph}\n * featureToggles={featureToggles}\n * serverFlags={serverFlags}\n * selectedNodes={selectedNodes}\n * focusedNode={focusedNode}\n * onDeselect={handleDeselect}\n * onRunRowCount={handleRunRowCount}\n * onRunRowCountDiff={handleRunRowCountDiff}\n * onRunValueDiff={handleRunValueDiff}\n * onAddLineageDiffCheck={handleAddLineageDiffCheck}\n * onAddSchemaDiffCheck={handleAddSchemaDiffCheck}\n * runTypeIcons={runTypeIcons}\n * historyToggleSlot={<HistoryToggle />}\n * setupConnectionPopoverSlot={SetupConnectionPopover}\n * />\n * ```\n */\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Checkbox from \"@mui/material/Checkbox\";\nimport Divider from \"@mui/material/Divider\";\nimport FormControlLabel from \"@mui/material/FormControlLabel\";\nimport ListItemIcon from \"@mui/material/ListItemIcon\";\nimport ListItemText from \"@mui/material/ListItemText\";\nimport ListSubheader from \"@mui/material/ListSubheader\";\nimport Menu from \"@mui/material/Menu\";\nimport MenuItem from \"@mui/material/MenuItem\";\nimport Radio from \"@mui/material/Radio\";\nimport RadioGroup from \"@mui/material/RadioGroup\";\nimport Stack from \"@mui/material/Stack\";\nimport TextField from \"@mui/material/TextField\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport {\n type ComponentType,\n type CSSProperties,\n type MouseEvent,\n type ReactNode,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { FiPackage } from \"react-icons/fi\";\nimport { PiCaretDown } from \"react-icons/pi\";\nimport type { RecceServerFlags } from \"../../../api/flag\";\nimport type { LineageDiffViewOptions } from \"../../../api/lineagecheck\";\nimport type { RecceFeatureToggles } from \"../../../contexts/instance/types\";\nimport type {\n LineageGraph,\n LineageGraphNode,\n} from \"../../../contexts/lineage/types\";\nimport { useIsDark } from \"../../../hooks/useIsDark\";\nimport type { IconComponent } from \"../../run/types\";\nimport { getIconForResourceType } from \"../styles\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Icons for run types used in action menus.\n * Consumers inject their own icons based on their run type registry.\n */\nexport interface RunTypeIcons {\n /** Icon for row_count_diff action */\n rowCountDiff: IconComponent;\n /** Icon for value_diff action */\n valueDiff: IconComponent;\n /** Icon for lineage_diff action */\n lineageDiff: IconComponent;\n /** Icon for schema_diff action */\n schemaDiff: IconComponent;\n}\n\n/**\n * Props for the SetupConnectionPopover slot component.\n */\nexport interface SetupConnectionPopoverSlotProps {\n /** Whether to display the popover */\n display: boolean;\n /** Child element to wrap */\n children: ReactNode;\n}\n\n/**\n * Props for the LineageViewTopBar component.\n */\nexport interface LineageViewTopBarProps {\n // View options and state\n /** Current view options for filtering and display mode */\n viewOptions: LineageDiffViewOptions;\n /** Callback when view options change */\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n /** The lineage graph data */\n lineageGraph?: LineageGraph;\n /** Feature toggles controlling what is enabled */\n featureToggles: RecceFeatureToggles;\n /** Server-side feature flags */\n serverFlags?: RecceServerFlags;\n\n // Selection state\n /** Currently focused node (hovered/highlighted) */\n focusedNode?: LineageGraphNode;\n /** Currently selected nodes for batch operations */\n selectedNodes: LineageGraphNode[];\n /** Callback to clear selection */\n onDeselect: () => void;\n\n // Action callbacks\n /** Callback for running row count (single env mode) */\n onRunRowCount?: () => Promise<void>;\n /** Callback for running row count diff */\n onRunRowCountDiff?: () => Promise<void>;\n /** Callback for running value diff */\n onRunValueDiff?: () => Promise<void>;\n /** Callback for adding a lineage diff check */\n onAddLineageDiffCheck?: (\n viewMode?: LineageDiffViewOptions[\"view_mode\"],\n ) => void;\n /** Callback for adding a schema diff check */\n onAddSchemaDiffCheck?: () => void;\n\n // Dependency injection slots\n /** Icons for run types in action menus */\n runTypeIcons: RunTypeIcons;\n /** Slot for history toggle component */\n historyToggleSlot?: ReactNode;\n /** Component for setup connection popover wrapper */\n SetupConnectionPopoverSlot?: ComponentType<SetupConnectionPopoverSlotProps>;\n}\n\n// ============================================================================\n// Internal Components\n// ============================================================================\n\nconst getCodeBlockSx = (isDark: boolean) => ({\n fontSize: \"8pt\",\n bgcolor: isDark ? \"grey.700\" : \"grey.100\",\n px: 0.5,\n borderRadius: 1,\n});\n\nconst SelectFilterTooltip = () => {\n const isDark = useIsDark();\n const codeBlockSx = getCodeBlockSx(isDark);\n return (\n <Stack alignItems=\"flex-start\" spacing={0}>\n <Typography fontSize=\"10pt\" color=\"text.secondary\" pb={1}>\n Select nodes by dbt node selector syntax\n </Typography>\n <Typography fontSize=\"8pt\">\n <Box component=\"code\" sx={codeBlockSx}>\n model_name\n </Box>{\" \"}\n Select a node\n </Typography>\n <Typography fontSize=\"8pt\">\n <Box component=\"code\" sx={codeBlockSx}>\n model_name+\n </Box>{\" \"}\n Select downstream nodes\n </Typography>\n <Typography fontSize=\"8pt\">\n <Box component=\"code\" sx={codeBlockSx}>\n +model_name\n </Box>{\" \"}\n Select upstream nodes\n </Typography>\n <Typography fontSize=\"8pt\">\n <Box component=\"code\" sx={codeBlockSx}>\n model*\n </Box>{\" \"}\n Select by wildcard\n </Typography>\n </Stack>\n );\n};\n\ninterface ViewModeSelectMenuProps {\n isDisabled: boolean;\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n}\n\nconst ViewModeSelectMenu = ({\n isDisabled,\n viewOptions,\n onViewOptionsChanged,\n}: ViewModeSelectMenuProps) => {\n const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);\n const open = Boolean(anchorEl);\n\n const viewMode = viewOptions.view_mode ?? \"changed_models\";\n const label = viewMode === \"changed_models\" ? \"Changed Models\" : \"All\";\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n const handleSelect = (newViewMode: LineageDiffViewOptions[\"view_mode\"]) => {\n onViewOptionsChanged({\n ...viewOptions,\n view_mode: newViewMode,\n });\n handleClose();\n };\n\n const ModelIcon = getIconForResourceType(\"model\").icon;\n\n return (\n <>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n onClick={handleClick}\n disabled={isDisabled}\n startIcon={ModelIcon && <ModelIcon />}\n endIcon={<PiCaretDown />}\n sx={{ minWidth: 100, textTransform: \"none\", fontSize: \"0.75rem\" }}\n >\n {label}\n </Button>\n <Menu anchorEl={anchorEl} open={open} onClose={handleClose}>\n <ListSubheader sx={{ lineHeight: \"32px\", bgcolor: \"transparent\" }}>\n mode\n </ListSubheader>\n <RadioGroup value={viewMode}>\n <MenuItem onClick={() => handleSelect(\"changed_models\")}>\n <FormControlLabel\n value=\"changed_models\"\n control={<Radio size=\"small\" sx={{ py: 0 }} />}\n label=\"Changed Models\"\n sx={{ m: 0 }}\n />\n </MenuItem>\n <MenuItem onClick={() => handleSelect(\"all\")}>\n <FormControlLabel\n value=\"all\"\n control={<Radio size=\"small\" sx={{ py: 0 }} />}\n label=\"All\"\n sx={{ m: 0 }}\n />\n </MenuItem>\n </RadioGroup>\n </Menu>\n </>\n );\n};\n\ninterface PackageSelectMenuProps {\n isDisabled: boolean;\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n lineageGraph?: LineageGraph;\n}\n\nconst PackageSelectMenu = ({\n isDisabled,\n viewOptions,\n onViewOptionsChanged,\n lineageGraph,\n}: PackageSelectMenuProps) => {\n const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);\n const open = Boolean(anchorEl);\n\n // get unique package names\n const available = new Set<string>();\n const nodes = Object.values(lineageGraph?.nodes ?? {});\n for (const node of nodes) {\n if (node.data.packageName) {\n available.add(node.data.packageName);\n }\n }\n\n const projectName = lineageGraph?.manifestMetadata.current?.project_name;\n\n const selected = viewOptions.packages\n ? new Set(viewOptions.packages)\n : projectName\n ? new Set([projectName])\n : available;\n const isSelectAll = selected.size === available.size;\n const isSelectNone = selected.size === 0;\n const label =\n selected.size === 1\n ? Array.from(selected)[0]\n : isSelectAll\n ? \"All Packages\"\n : isSelectNone\n ? \"No Package\"\n : `${selected.size} Packages`;\n\n const handleClick = (event: MouseEvent<HTMLButtonElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n const handleSelectAll = () => {\n if (isSelectAll) {\n onViewOptionsChanged({\n ...viewOptions,\n packages: [],\n });\n } else {\n onViewOptionsChanged({\n ...viewOptions,\n packages: Array.from(available),\n });\n }\n };\n\n const handleSelect = (pkg: string) => {\n const newSelected = new Set(selected);\n if (newSelected.has(pkg)) {\n newSelected.delete(pkg);\n } else {\n newSelected.add(pkg);\n }\n onViewOptionsChanged({\n ...viewOptions,\n packages: Array.from(newSelected),\n });\n };\n\n return (\n <>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n onClick={handleClick}\n disabled={isDisabled}\n startIcon={<FiPackage />}\n endIcon={<PiCaretDown />}\n sx={{ minWidth: 100, textTransform: \"none\", fontSize: \"0.75rem\" }}\n >\n {label}\n </Button>\n <Menu anchorEl={anchorEl} open={open} onClose={handleClose}>\n <ListSubheader sx={{ lineHeight: \"32px\", bgcolor: \"transparent\" }}>\n Select Packages\n </ListSubheader>\n <MenuItem onClick={handleSelectAll}>\n <Checkbox\n checked={isSelectAll}\n indeterminate={!isSelectAll && !isSelectNone}\n size=\"small\"\n sx={{ py: 0 }}\n />\n <ListItemText>Select All</ListItemText>\n </MenuItem>\n\n <Divider />\n\n {Array.from(available).map((pkg) => (\n <MenuItem key={pkg} onClick={() => handleSelect(pkg)}>\n <Checkbox checked={selected.has(pkg)} size=\"small\" sx={{ py: 0 }} />\n <ListItemText className=\"no-track-pii-safe\">{pkg}</ListItemText>\n </MenuItem>\n ))}\n </Menu>\n </>\n );\n};\n\ninterface NodeSelectionInputProps {\n value: string;\n onChange: (value: string) => void;\n isDisabled?: boolean;\n tooltipComponent?: ReactNode;\n showTooltip?: boolean;\n}\n\nconst NodeSelectionInput = ({\n value,\n onChange,\n isDisabled,\n tooltipComponent,\n showTooltip = true,\n}: NodeSelectionInputProps) => {\n const [inputValue, setInputValue] = useState(value);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (inputRef.current) {\n inputRef.current.value = value;\n }\n }, [value]);\n\n return (\n <MuiTooltip\n title={showTooltip ? tooltipComponent : \"\"}\n placement=\"bottom-start\"\n slotProps={{\n tooltip: {\n sx: {\n width: \"18.75rem\",\n p: 2,\n boxShadow: 3,\n border: 1,\n borderRadius: 1,\n color: \"text.primary\",\n bgcolor: \"background.paper\",\n },\n },\n }}\n >\n <TextField\n inputRef={inputRef}\n size=\"small\"\n placeholder=\"with selectors\"\n disabled={isDisabled}\n value={inputValue}\n onChange={(event) => {\n setInputValue(event.target.value);\n }}\n onKeyUp={(event) => {\n if (event.key === \"Enter\") {\n onChange(inputValue);\n } else if (event.key === \"Escape\") {\n event.preventDefault();\n setInputValue(value);\n if (inputRef.current) {\n inputRef.current.blur();\n }\n }\n }}\n onBlur={() => {\n setInputValue(value);\n }}\n sx={{\n \"& .MuiInputBase-root\": {\n width: \"18.75rem\",\n height: 24,\n fontSize: \"0.75rem\",\n },\n \"& .MuiInputBase-input\": {\n py: 0.5,\n px: 1,\n },\n }}\n />\n </MuiTooltip>\n );\n};\n\ninterface SelectFilterProps {\n isDisabled: boolean;\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n showTooltip?: boolean;\n}\n\nconst SelectFilter = ({\n isDisabled,\n viewOptions,\n onViewOptionsChanged,\n showTooltip,\n}: SelectFilterProps) => {\n return (\n <NodeSelectionInput\n isDisabled={isDisabled}\n value={viewOptions.select ?? \"\"}\n onChange={(value) => {\n onViewOptionsChanged({\n ...viewOptions,\n select: value ? value : undefined,\n });\n }}\n tooltipComponent={<SelectFilterTooltip />}\n showTooltip={showTooltip}\n />\n );\n};\n\ninterface ExcludeFilterProps {\n isDisabled: boolean;\n viewOptions: LineageDiffViewOptions;\n onViewOptionsChanged: (options: LineageDiffViewOptions) => void;\n}\n\nconst ExcludeFilter = ({\n isDisabled,\n viewOptions,\n onViewOptionsChanged,\n}: ExcludeFilterProps) => {\n return (\n <NodeSelectionInput\n isDisabled={isDisabled}\n value={viewOptions.exclude ?? \"\"}\n onChange={(value) => {\n onViewOptionsChanged({\n ...viewOptions,\n exclude: value ? value : undefined,\n });\n }}\n />\n );\n};\n\ninterface ControlItemProps {\n label?: string;\n children: ReactNode;\n style?: CSSProperties;\n}\n\nconst ControlItem = ({ label, children, style }: ControlItemProps) => {\n return (\n <Box style={style} sx={{ maxWidth: 300 }}>\n <Typography fontSize=\"8pt\">\n {(label ?? \"\").trim() || <> </>}\n </Typography>\n {children}\n </Box>\n );\n};\n\n// ============================================================================\n// Default Setup Connection Popover (passthrough)\n// ============================================================================\n\nconst DefaultSetupConnectionPopover = ({\n children,\n}: SetupConnectionPopoverSlotProps) => {\n return <>{children}</>;\n};\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * LineageViewTopBar Component\n *\n * Top toolbar for the lineage view providing:\n * - View mode selection (Changed Models vs All)\n * - Package filtering\n * - Node selector filters (Select, Exclude)\n * - Actions menu for diff operations and checklist additions\n * - Multi-node selection controls\n *\n * This component uses dependency injection for consumer-specific features\n * like history toggle, setup connection popover, and run type icons.\n */\nexport const LineageViewTopBar = ({\n viewOptions,\n onViewOptionsChanged,\n lineageGraph,\n featureToggles,\n serverFlags,\n focusedNode,\n selectedNodes,\n onDeselect,\n onRunRowCount,\n onRunRowCountDiff,\n onRunValueDiff,\n onAddLineageDiffCheck,\n onAddSchemaDiffCheck,\n runTypeIcons,\n historyToggleSlot,\n SetupConnectionPopoverSlot = DefaultSetupConnectionPopover,\n}: LineageViewTopBarProps) => {\n const isSingleEnvOnboarding = serverFlags?.single_env_onboarding;\n\n const [actionsAnchorEl, setActionsAnchorEl] = useState<HTMLElement | null>(\n null,\n );\n const actionsOpen = Boolean(actionsAnchorEl);\n\n const isMultiSelect = selectedNodes.length > 0;\n const isFilterDisabled = isMultiSelect;\n\n const handleActionsClick = (event: MouseEvent<HTMLButtonElement>) => {\n setActionsAnchorEl(event.currentTarget);\n };\n\n const handleActionsClose = () => {\n setActionsAnchorEl(null);\n };\n\n // Get icons from dependency injection\n const RowCountDiffIcon = runTypeIcons.rowCountDiff;\n const ValueDiffIcon = runTypeIcons.valueDiff;\n const LineageDiffIcon = runTypeIcons.lineageDiff;\n const SchemaDiffIcon = runTypeIcons.schemaDiff;\n\n return (\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n borderBottom={1}\n borderColor=\"neutral.light\"\n sx={{ width: \"100%\", p: \"4pt 8pt\", gap: \"0.5rem\" }}\n >\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ flex: 1, gap: \"0.5rem\" }}\n >\n {historyToggleSlot}\n <ControlItem label=\"Mode\" style={{ flexShrink: 1 }}>\n <ViewModeSelectMenu\n isDisabled={isFilterDisabled}\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n />\n </ControlItem>\n <ControlItem label=\"Package\" style={{ flexShrink: 1 }}>\n <PackageSelectMenu\n isDisabled={isFilterDisabled}\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n lineageGraph={lineageGraph}\n />\n </ControlItem>\n <ControlItem label=\"Select\" style={{ flexShrink: 1 }}>\n <SelectFilter\n isDisabled={isFilterDisabled}\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n showTooltip={isSingleEnvOnboarding}\n />\n </ControlItem>\n <ControlItem label=\"Exclude\" style={{ flexShrink: 1 }}>\n <ExcludeFilter\n isDisabled={isFilterDisabled}\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n />\n </ControlItem>\n <Box sx={{ flexGrow: 1 }} />\n {isMultiSelect && (\n <>\n <ControlItem label=\"\" style={{ flexShrink: 0 }}>\n <Typography fontSize=\"9pt\" color=\"text.secondary\">\n {selectedNodes.length > 1\n ? `${selectedNodes.length} nodes selected`\n : `${selectedNodes.length} node selected`}\n </Typography>\n </ControlItem>\n\n <ControlItem label=\"\">\n <Button\n variant=\"outlined\"\n color=\"neutral\"\n size=\"xsmall\"\n onClick={() => {\n onDeselect();\n }}\n sx={{ textTransform: \"none\", fontSize: \"9pt\" }}\n >\n Deselect\n </Button>\n </ControlItem>\n {isSingleEnvOnboarding && (\n <ControlItem label=\"Explore\">\n <Box sx={{ display: \"inline-flex\" }}>\n <Button\n size=\"xsmall\"\n color=\"neutral\"\n variant=\"outlined\"\n onClick={handleActionsClick}\n endIcon={<PiCaretDown />}\n sx={{ textTransform: \"none\", fontSize: \"0.75rem\" }}\n >\n Actions\n </Button>\n <Menu\n anchorEl={actionsAnchorEl}\n open={actionsOpen}\n onClose={handleActionsClose}\n anchorOrigin={{ vertical: \"bottom\", horizontal: \"right\" }}\n transformOrigin={{ vertical: \"top\", horizontal: \"right\" }}\n >\n <MenuItem\n disabled={featureToggles.disableDatabaseQuery}\n onClick={async () => {\n await onRunRowCount?.();\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <RowCountDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Row Count</ListItemText>\n </MenuItem>\n </Menu>\n </Box>\n </ControlItem>\n )}\n </>\n )}\n {!isSingleEnvOnboarding && (\n <ControlItem label=\"Explore\">\n <Box sx={{ display: \"inline-flex\" }}>\n <Button\n size=\"xsmall\"\n color=\"neutral\"\n variant=\"outlined\"\n disabled={featureToggles.disableViewActionDropdown}\n onClick={handleActionsClick}\n endIcon={<PiCaretDown />}\n sx={{ textTransform: \"none\", fontSize: \"0.75rem\" }}\n >\n Actions\n </Button>\n <Menu\n anchorEl={actionsAnchorEl}\n open={actionsOpen}\n onClose={handleActionsClose}\n anchorOrigin={{ vertical: \"bottom\", horizontal: \"right\" }}\n transformOrigin={{ vertical: \"top\", horizontal: \"right\" }}\n >\n <ListSubheader\n sx={{ lineHeight: \"32px\", bgcolor: \"transparent\" }}\n >\n Diff\n </ListSubheader>\n <SetupConnectionPopoverSlot\n display={featureToggles.mode === \"metadata only\"}\n >\n <MenuItem\n disabled={featureToggles.disableDatabaseQuery}\n onClick={async () => {\n await onRunRowCountDiff?.();\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <RowCountDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Row Count Diff</ListItemText>\n </MenuItem>\n </SetupConnectionPopoverSlot>\n <SetupConnectionPopoverSlot\n display={featureToggles.mode === \"metadata only\"}\n >\n <MenuItem\n disabled={featureToggles.disableDatabaseQuery}\n onClick={async () => {\n await onRunValueDiff?.();\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <ValueDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Value Diff</ListItemText>\n </MenuItem>\n </SetupConnectionPopoverSlot>\n\n <Divider />\n\n <ListSubheader\n sx={{ lineHeight: \"32px\", bgcolor: \"transparent\" }}\n >\n Add to Checklist\n </ListSubheader>\n <MenuItem\n onClick={() => {\n onAddLineageDiffCheck?.(viewOptions.view_mode);\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <LineageDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Lineage Diff</ListItemText>\n </MenuItem>\n <MenuItem\n onClick={() => {\n onAddSchemaDiffCheck?.();\n handleActionsClose();\n }}\n >\n <ListItemIcon>\n <SchemaDiffIcon fontSize=\"small\" />\n </ListItemIcon>\n <ListItemText>Schema Diff</ListItemText>\n </MenuItem>\n </Menu>\n </Box>\n </ControlItem>\n )}\n </Stack>\n </Stack>\n );\n};\n","\"use client\";\n\n/**\n * @file LineageViewTopBarOss.tsx\n * @description OSS wrapper for the LineageViewTopBar component.\n *\n * This thin wrapper imports the core component from @datarecce/ui and injects\n * OSS-specific implementations including:\n * - Run type icons from the local registry\n * - HistoryToggle component\n * - SetupConnectionPopover component\n * - Context hooks for state management\n */\n\nimport type { ReactElement, Ref } from \"react\";\nimport {\n useLineageGraphContext,\n useLineageViewContextSafe,\n useRecceInstanceContext,\n useRecceServerFlag,\n} from \"../../../contexts\";\nimport { SetupConnectionPopover } from \"../../app\";\nimport { findByRunType } from \"../../run\";\nimport { HistoryToggle } from \"../../shared\";\nimport {\n LineageViewTopBar as LineageViewTopBarCore,\n type SetupConnectionPopoverSlotProps,\n} from \"./LineageViewTopBar\";\n\n/**\n * SetupConnectionPopover wrapper to adapt to the slot props interface.\n * We need to cast the children to match SetupConnectionPopover's expected type.\n */\nconst SetupConnectionPopoverSlot = ({\n display,\n children,\n}: SetupConnectionPopoverSlotProps) => {\n return (\n <SetupConnectionPopover display={display}>\n {\n children as ReactElement<{\n ref?: Ref<HTMLElement>;\n [key: string]: unknown;\n }>\n }\n </SetupConnectionPopover>\n );\n};\n\n/**\n * LineageViewTopBar component for OSS.\n *\n * Wraps the core @datarecce/ui component with OSS-specific implementations:\n * - Injects run type icons from the local registry\n * - Provides HistoryToggle slot\n * - Provides SetupConnectionPopover slot\n * - Connects to LineageViewContext for state\n */\nexport const LineageViewTopBarOss = () => {\n const {\n deselect,\n focusedNode,\n selectedNodes,\n viewOptions,\n onViewOptionsChanged,\n runRowCount,\n runRowCountDiff,\n runValueDiff,\n addLineageDiffCheck,\n addSchemaDiffCheck,\n } = useLineageViewContextSafe();\n\n const { lineageGraph } = useLineageGraphContext();\n const { featureToggles } = useRecceInstanceContext();\n const { data: flags } = useRecceServerFlag();\n const runTypeIcons = {\n rowCountDiff: findByRunType(\"row_count_diff\").icon,\n valueDiff: findByRunType(\"value_diff\").icon,\n lineageDiff: findByRunType(\"lineage_diff\").icon,\n schemaDiff: findByRunType(\"schema_diff\").icon,\n };\n\n return (\n <LineageViewTopBarCore\n viewOptions={viewOptions}\n onViewOptionsChanged={onViewOptionsChanged}\n lineageGraph={lineageGraph}\n featureToggles={featureToggles}\n serverFlags={flags}\n focusedNode={focusedNode}\n selectedNodes={selectedNodes}\n onDeselect={deselect}\n onRunRowCount={runRowCount}\n onRunRowCountDiff={runRowCountDiff}\n onRunValueDiff={runValueDiff}\n onAddLineageDiffCheck={addLineageDiffCheck}\n onAddSchemaDiffCheck={addSchemaDiffCheck}\n runTypeIcons={runTypeIcons}\n historyToggleSlot={<HistoryToggle />}\n SetupConnectionPopoverSlot={SetupConnectionPopoverSlot}\n />\n );\n};\n","\"use client\";\n\nimport type { Check, ServerInfoResult } from \"../../api\";\nimport {\n type CllInput,\n type ColumnLineageData,\n cacheKeys,\n createLineageDiffCheck,\n createSchemaDiffCheck,\n getCll,\n isHistogramDiffRun,\n isProfileDiffRun,\n isTopKDiffRun,\n isValueDiffDetailRun,\n isValueDiffRun,\n type LineageDiffViewOptions,\n select,\n} from \"../../api\";\nimport {\n LineageViewContext,\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport {\n isLineageGraphColumnNode,\n isLineageGraphNode,\n type LineageGraphColumnNode,\n type LineageGraphEdge,\n type LineageGraphNode,\n type LineageGraphNodes,\n type LineageViewContextType,\n} from \"../../contexts/lineage/types\";\nimport {\n type NodeColumnSetMap,\n selectDownstream,\n selectUpstream,\n union,\n} from \"../../contexts/lineage/utils\";\nimport {\n IGNORE_SCREENSHOT_CLASS,\n useApiConfig,\n useRun,\n useThemeColors,\n} from \"../../hooks\";\nimport { useMultiNodesActionOss as useMultiNodesAction } from \"../../hooks/useMultiNodesActionOss\";\nimport useValueDiffAlertDialog from \"../../hooks/useValueDiffAlertDialogOss\";\nimport {\n trackCopyToClipboard,\n trackMultiNodesAction,\n} from \"../../lib/api/track\";\nimport \"../../styles\";\nimport Box from \"@mui/material/Box\";\nimport Divider from \"@mui/material/Divider\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n Background,\n BackgroundVariant,\n ControlButton,\n Controls,\n getNodesBounds,\n MiniMap,\n Node,\n Panel,\n ReactFlow,\n useEdgesState,\n useNodesState,\n useReactFlow,\n} from \"@xyflow/react\";\nimport \"@xyflow/react/dist/style.css\";\nimport { AxiosError } from \"axios\";\nimport React, {\n forwardRef,\n Ref,\n useCallback,\n useEffect,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { FiCopy } from \"react-icons/fi\";\nimport { colors } from \"../../theme\";\nimport { LineageViewNotification } from \"../notifications\";\nimport { HSplit, toaster } from \"../ui\";\nimport { ActionControlOss } from \"./ActionControlOss\";\nimport { ColumnLevelLineageControlOss } from \"./ColumnLevelLineageControlOss\";\nimport { edgeTypes, getNodeColor, initialNodes, nodeTypes } from \"./config\";\nimport {\n useLineageCopyToClipboard,\n useNavToCheck,\n useResizeObserver,\n useTrackLineageRender,\n} from \"./hooks\";\nimport {\n LineageViewContextMenu,\n useLineageViewContextMenu,\n} from \"./LineageViewContextMenuOss\";\nimport { LineageLegend } from \"./legend\";\nimport { toReactFlow } from \"./lineage\";\nimport { NodeViewOss as NodeView } from \"./NodeViewOss\";\nimport { patchLineageDiffFromCll } from \"./patchLineageDiffFromCll\";\nimport SetupConnectionBanner from \"./SetupConnectionBannerOss\";\nimport { BaseEnvironmentSetupNotification } from \"./SingleEnvironmentQueryView\";\nimport {\n LineageViewError,\n LineageViewLoading,\n LineageViewNoChanges,\n} from \"./states\";\nimport { LineageViewTopBarOss as LineageViewTopBar } from \"./topbar/LineageViewTopBarOss\";\n\nexport interface LineageViewProps {\n viewOptions?: LineageDiffViewOptions;\n interactive?: boolean;\n weight?: number;\n height?: number;\n\n // to be removed\n // viewMode?: \"changed_models\" | \"all\"; // deprecated\n filterNodes?: (key: string, node: LineageGraphNode) => boolean;\n}\n\nexport interface LineageViewRef {\n copyToClipboard: () => void;\n}\n\nexport function PrivateLineageView(\n { interactive = false, ...props }: LineageViewProps,\n ref: Ref<LineageViewRef>,\n) {\n const { isDark } = useThemeColors();\n const { apiClient } = useApiConfig();\n const queryClient = useQueryClient();\n const reactFlow = useReactFlow();\n const refResize = useRef<HTMLDivElement>(null);\n const {\n copyToClipboard,\n ImageDownloadModal,\n ref: refReactFlow,\n } = useLineageCopyToClipboard();\n const [nodes, setNodes, onNodesChange] =\n useNodesState<LineageGraphNodes>(initialNodes);\n const [edges, setEdges, onEdgesChange] = useEdgesState<LineageGraphEdge>([]);\n\n const {\n lineageGraph,\n refetchLineageGraph,\n isLoading,\n error,\n refetchRunsAggregated,\n } = useLineageGraphContext();\n\n const { featureToggles, singleEnv } = useRecceInstanceContext();\n const { runId, showRunId, closeRunResult, runAction, isRunResultOpen } =\n useRecceActionContext();\n const { run } = useRun(runId);\n\n const [viewOptions, setViewOptions] = useState<LineageDiffViewOptions>({\n ...props.viewOptions,\n });\n\n const trackLineageRender = useTrackLineageRender();\n\n const cllHistory = useRef<(CllInput | undefined)[]>([]).current;\n\n const [cll, setCll] = useState<ColumnLineageData | undefined>(undefined);\n const [changeAnalysisMode, setChangeAnalysisMode] = useState(false);\n // Ref mirror of changeAnalysisMode for reading inside useLayoutEffect\n // without adding it as a dependency (avoids re-running layout on toggle).\n const changeAnalysisModeRef = useRef(false);\n changeAnalysisModeRef.current = changeAnalysisMode;\n const actionGetCll = useMutation({\n mutationFn: (input: CllInput) => getCll(input, apiClient),\n });\n // Guard against useLayoutEffect re-entry after cache patching.\n // When setQueryData patches lineage.diff, queryServerInfo.data changes,\n // lineageGraph recomputes via useMemo, and the effect re-fires. This ref\n // tells the effect to reuse the previous CLL result instead of re-calling\n // the API and re-patching (which would loop infinitely).\n const cllCachePatchRef = useRef<{\n pending: boolean;\n cllData?: ColumnLineageData;\n }>({ pending: false });\n const [nodeColumnSetMap, setNodeColumSetMap] = useState<NodeColumnSetMap>({});\n\n const findNodeByName = useCallback(\n (name: string) => {\n return nodes.filter(isLineageGraphNode).find((n) => n.data.name === name);\n },\n [nodes],\n );\n\n // Expose the function to the parent via the ref\n useImperativeHandle(ref, () => ({\n copyToClipboard,\n }));\n\n const isModelsChanged = useMemo(() => {\n return !!(lineageGraph && lineageGraph.modifiedSet.length > 0);\n }, [lineageGraph]);\n\n /**\n * View mode\n * - all: show all nodes\n * - changed_models: show only changed models\n */\n const viewMode = viewOptions.view_mode ?? \"changed_models\";\n const filteredNodeIds: string[] = useMemo(() => {\n return nodes\n .filter((node) => node.type === \"lineageGraphNode\")\n .map((node) => node.id);\n }, [nodes]);\n const filteredNodes = useMemo(() => {\n if (!lineageGraph) {\n return [];\n }\n\n return filteredNodeIds.map((nodeId) => lineageGraph.nodes[nodeId]);\n }, [lineageGraph, filteredNodeIds]);\n\n /**\n * Focused node: the node that is currently focused. Show the NodeView when a node is focused\n */\n const [focusedNodeId, setFocusedNodeId] = useState<string>();\n const focusedNode = focusedNodeId\n ? lineageGraph?.nodes[focusedNodeId]\n : undefined;\n\n /**\n * Select mode: the behavior of clicking on nodes\n * - (undefined): no selection\n * - selecting: selecting nodes\n * - action_result: take action on selected nodes\n */\n const [selectMode, setSelectMode] = useState<\"selecting\" | \"action_result\">();\n const [selectedNodeIds, setSelectedNodeIds] = useState<Set<string>>(\n new Set(),\n );\n const selectedNodes = useMemo(() => {\n if (!lineageGraph) {\n return [];\n }\n\n const nodeIds = Array.from(selectedNodeIds);\n return nodeIds.map((nodeId) => lineageGraph.nodes[nodeId]);\n }, [lineageGraph, selectedNodeIds]);\n const multiNodeAction = useMultiNodesAction(\n selectedNodes.length > 0 ? selectedNodes : filteredNodes,\n {\n onActionStarted: () => {\n setSelectMode(\"action_result\");\n },\n onActionNodeUpdated: (updated: LineageGraphNode) => {\n // trigger a re-render by updating the nodes\n setNodes((nodes) =>\n nodes.map((node) => {\n if (node.id === updated.id) {\n return {\n ...node,\n };\n }\n return node;\n }),\n );\n },\n onActionCompleted: () => {\n return void 0;\n },\n },\n );\n\n /**\n * Helper to extract node positions for preserving layout.\n *\n * IMPORTANT: We read positions from React Flow's internal store via getNodes()\n * rather than from React state. This ensures we capture the actual rendered\n * positions, which may differ from React state due to:\n * - fitView adjustments\n * - Node measuring/resizing\n * - Internal React Flow position updates\n *\n * This fixes where clicking between columns caused graph reflow\n * because React state positions were stale relative to what React Flow rendered.\n */\n const getNodePositions = useCallback(() => {\n const positions = new Map<string, { x: number; y: number }>();\n // Get nodes directly from React Flow's internal store, not React state\n const currentNodes = reactFlow.getNodes();\n for (const node of currentNodes) {\n // Only capture parent node positions, not column nodes\n if (!node.parentId && node.position) {\n positions.set(node.id, { x: node.position.x, y: node.position.y });\n }\n }\n return positions;\n }, [reactFlow]);\n\n /**\n * Highlighted nodes: the nodes that are highlighted. The behavior of highlighting depends on the select mode\n *\n * - Default: nodes in the impact radius, or all nodes if the impact radius is not available\n * - Focus: upstream/downstream nodes of the focused node\n * - Multi-select: nodes in the impact radius, or all nodes if the impact radius is not available\n * - Action Result: selected nodes\n */\n const highlighted = useMemo<Set<string>>(() => {\n if (!lineageGraph) {\n return new Set<string>();\n }\n\n let highlightedModels: Set<string> = new Set<string>();\n if (cll) {\n for (const [nodeId, node] of Object.entries(cll.current.nodes)) {\n if (node.impacted !== false) {\n highlightedModels.add(nodeId);\n }\n }\n for (const columnId of Object.keys(cll.current.columns)) {\n highlightedModels.add(columnId);\n }\n } else if (selectMode === \"action_result\") {\n const nodeIds = Object.keys(multiNodeAction.actionState.actions);\n highlightedModels = new Set(nodeIds);\n } else if (focusedNode) {\n highlightedModels = union(\n selectUpstream(lineageGraph, [focusedNode.id]),\n selectDownstream(lineageGraph, [focusedNode.id]),\n );\n } else if (isModelsChanged) {\n highlightedModels = selectDownstream(\n lineageGraph,\n lineageGraph.modifiedSet,\n );\n } else {\n highlightedModels = new Set(filteredNodeIds);\n }\n\n // Add columns in the highlighted models\n return new Set<string>(highlightedModels);\n }, [\n lineageGraph,\n cll,\n selectMode,\n focusedNode,\n isModelsChanged,\n multiNodeAction.actionState.actions,\n filteredNodeIds,\n ]);\n\n const lineageViewContextMenu = useLineageViewContextMenu();\n\n const closeContextMenu = () => {\n lineageViewContextMenu.closeContextMenu();\n };\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: Intentionally only run when lineageGraph changes (initial load/refetch).\n useLayoutEffect(() => {\n const t = async () => {\n let filteredNodeIds: string[] | undefined = undefined;\n\n if (!lineageGraph) {\n return;\n }\n\n if (viewOptions.node_ids) {\n filteredNodeIds = viewOptions.node_ids;\n } else {\n const packageName = lineageGraph.manifestMetadata.current?.project_name;\n const viewMode =\n viewOptions.view_mode ?? (isModelsChanged ? \"changed_models\" : \"all\");\n\n const newViewOptions: LineageDiffViewOptions = {\n view_mode: viewMode,\n packages: packageName ? [packageName] : undefined,\n ...props.viewOptions,\n };\n\n try {\n const result = await select(\n {\n select: newViewOptions.select,\n exclude: newViewOptions.exclude,\n packages: newViewOptions.packages,\n view_mode: newViewOptions.view_mode,\n },\n apiClient,\n );\n filteredNodeIds = result.nodes;\n } catch (_) {\n // fallback behavior\n newViewOptions.view_mode = \"all\";\n const result = await select(\n {\n select: newViewOptions.select,\n exclude: newViewOptions.exclude,\n packages: newViewOptions.packages,\n view_mode: newViewOptions.view_mode,\n },\n apiClient,\n );\n filteredNodeIds = result.nodes;\n }\n\n setViewOptions(newViewOptions);\n }\n\n let cll: ColumnLineageData | undefined;\n if (viewOptions.column_level_lineage) {\n if (cllCachePatchRef.current.pending) {\n // Effect re-fired because our setQueryData updated lineageGraph.\n // Reuse the previous CLL result; skip the API call and re-patch.\n cll = cllCachePatchRef.current.cllData;\n cllCachePatchRef.current = { pending: false };\n } else {\n const cllApiInput: CllInput = {\n ...viewOptions.column_level_lineage,\n change_analysis:\n viewOptions.column_level_lineage.change_analysis ??\n changeAnalysisModeRef.current,\n };\n try {\n cll = await actionGetCll.mutateAsync(cllApiInput);\n // Patch the lineage diff cache with change data from CLL\n const cllResult = cll;\n if (cllApiInput.change_analysis && cllResult) {\n cllCachePatchRef.current = { pending: true, cllData: cllResult };\n queryClient.setQueryData(\n cacheKeys.lineage(),\n (old: ServerInfoResult | undefined) => {\n if (!old) return old;\n return {\n ...old,\n lineage: {\n ...old.lineage,\n diff: patchLineageDiffFromCll(\n old.lineage.diff,\n cllResult,\n ),\n },\n };\n },\n );\n }\n } catch (e) {\n if (e instanceof AxiosError) {\n const e2 = e as AxiosError<{ detail?: string }>;\n toaster.create({\n title: \"Column Level Lineage error\",\n description: e2.response?.data.detail ?? e.message,\n type: \"error\",\n closable: true,\n });\n return;\n }\n }\n }\n } else {\n // CLL disabled — clear any pending guard so stale data isn't reused\n // if CLL is re-enabled after a toggle-off.\n cllCachePatchRef.current = { pending: false };\n }\n\n const [nodes, edges, nodeColumnSetMap] = await toReactFlow(lineageGraph, {\n selectedNodes: filteredNodeIds,\n cll: cll,\n });\n setNodes(nodes);\n setEdges(edges);\n setNodeColumSetMap(nodeColumnSetMap);\n setCll(cll);\n\n // Track lineage view render\n // Note: this call is intentionally duplicated in refreshLayout. The two\n // sites read changeAnalysisMode differently (ref here vs. state there)\n // due to the useLayoutEffect closure, so they can't share a helper\n // without reintroducing stale-closure risks.\n trackLineageRender(\n nodes,\n viewOptions.view_mode ?? \"changed_models\",\n changeAnalysisModeRef.current,\n !!viewOptions.column_level_lineage?.column,\n !!focusedNodeId || !!run,\n );\n };\n\n void t();\n // Intentionally only run when lineageGraph changes (initial load/refetch).\n // viewOptions changes are handled separately by handleViewOptionsChanged.\n // Other dependencies (setNodes, setEdges, actionGetCll) are stable.\n // changeAnalysisModeRef is a ref to avoid stale closure issues.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [lineageGraph]);\n\n const onNodeViewClosed = () => {\n setFocusedNodeId(undefined);\n };\n\n const centerNode = async (nodeId: string) => {\n let node = nodes.find((n) => n.id === nodeId);\n if (!node) {\n return;\n }\n\n if (node.parentId) {\n const parentId = node.parentId;\n node = nodes.find((n) => n.id === parentId) ?? node;\n }\n\n if (node.measured != null) {\n const { width, height } = node.measured;\n if (width && height) {\n const x = node.position.x + width / 2;\n const y = node.position.y + height / 2;\n const zoom = reactFlow.getZoom();\n\n await reactFlow.setCenter(x, y, { zoom, duration: 200 });\n }\n }\n };\n\n const navToCheck = useNavToCheck();\n\n useResizeObserver(refResize, async () => {\n if (selectMode !== \"selecting\") {\n if (!focusedNodeId) {\n await reactFlow.fitView({ nodes, duration: 200 });\n } else {\n await centerNode(focusedNodeId);\n }\n }\n });\n\n const showColumnLevelLineage = async (\n columnLevelLineage?: CllInput,\n previous = false,\n ) => {\n const previousColumnLevelLineage = viewOptions.column_level_lineage;\n\n // Clear change analysis mode when CLL is turned off entirely\n if (!columnLevelLineage) {\n setChangeAnalysisMode(false);\n }\n\n // Preserve positions when:\n // 1. CLL is being turned OFF (previous exists but new one is undefined)\n // 2. Switching between columns (both previous and new have CLL)\n // This prevents jarring graph reflow when the user clicks between columns\n const shouldPreservePositions = previousColumnLevelLineage !== undefined;\n\n await handleViewOptionsChanged(\n {\n ...viewOptions,\n column_level_lineage: columnLevelLineage,\n },\n false,\n shouldPreservePositions, // preserve positions when CLL was previously active\n );\n\n if (!previous) {\n cllHistory.push(previousColumnLevelLineage);\n }\n if (columnLevelLineage?.node_id) {\n setFocusedNodeId(columnLevelLineage.node_id);\n } else if (!columnLevelLineage) {\n // Only clear focus when CLL is turned off, not for Impact Radius\n setFocusedNodeId(undefined);\n }\n };\n\n const resetColumnLevelLineage = async (previous?: boolean) => {\n if (previous) {\n if (cllHistory.length === 0) {\n return;\n }\n const previousCll = cllHistory.pop();\n if (previousCll) {\n await showColumnLevelLineage(previousCll, true);\n } else {\n await showColumnLevelLineage(undefined, true);\n }\n } else {\n await showColumnLevelLineage(undefined, true);\n }\n };\n\n const onColumnNodeClick = (\n event: React.MouseEvent,\n node: LineageGraphColumnNode,\n ) => {\n if (selectMode) {\n return;\n }\n\n void showColumnLevelLineage({\n node_id: node.data.node.id,\n column: node.data.column,\n });\n };\n\n const onNodeClick = (event: React.MouseEvent, node: Node) => {\n if (!interactive) return;\n if (!lineageGraph) {\n return;\n }\n\n if (isLineageGraphColumnNode(node as LineageGraphNodes)) {\n onColumnNodeClick(event, node as LineageGraphColumnNode);\n return;\n }\n\n closeContextMenu();\n if (!selectMode) {\n setFocusedNodeId(node.id);\n } else if (selectMode === \"action_result\") {\n const action = multiNodeAction.actionState.actions[node.id];\n if (action.run?.run_id) {\n showRunId(action.run.run_id);\n }\n setFocusedNodeId(node.id);\n } else {\n const newSet = new Set(selectedNodeIds);\n if (selectedNodeIds.has(node.id)) {\n newSet.delete(node.id);\n } else {\n newSet.add(node.id);\n }\n setSelectedNodeIds(newSet);\n if (newSet.size === 0) {\n setSelectMode(undefined);\n }\n }\n };\n\n const refreshLayout = async (options: {\n viewOptions?: LineageDiffViewOptions;\n fitView?: boolean;\n preservePositions?: boolean;\n }) => {\n let { viewOptions: newViewOptions = viewOptions } = options;\n const { fitView, preservePositions = false } = options;\n\n let selectedNodes: string[] | undefined = undefined;\n\n if (!lineageGraph) {\n return;\n }\n\n const reselect =\n viewOptions.select !== newViewOptions.select ||\n viewOptions.exclude !== newViewOptions.exclude ||\n viewOptions.packages !== newViewOptions.packages ||\n viewOptions.view_mode !== newViewOptions.view_mode;\n\n if (reselect) {\n try {\n const result = await select(\n {\n select: newViewOptions.select,\n exclude: newViewOptions.exclude,\n packages: newViewOptions.packages,\n view_mode: newViewOptions.view_mode,\n },\n apiClient,\n );\n // focus to unfocus the model or column node\n newViewOptions = { ...newViewOptions, column_level_lineage: undefined };\n selectedNodes = result.nodes;\n } catch (e) {\n if (e instanceof AxiosError) {\n const e2 = e as AxiosError<{ detail?: string }>;\n toaster.create({\n title: \"Select node error\",\n description: e2.response?.data.detail ?? e.message,\n type: \"error\",\n closable: true,\n });\n }\n return;\n }\n setFocusedNodeId(undefined);\n } else {\n selectedNodes = nodes.map((n) => n.id);\n }\n\n let cll: ColumnLineageData | undefined;\n if (newViewOptions.column_level_lineage) {\n const cllApiInput: CllInput = {\n ...newViewOptions.column_level_lineage,\n change_analysis:\n newViewOptions.column_level_lineage.change_analysis ??\n changeAnalysisMode,\n };\n try {\n cll = await actionGetCll.mutateAsync(cllApiInput);\n // Patch the lineage diff cache with change data from CLL.\n // Also set the guard ref so the useLayoutEffect that re-fires\n // (due to lineageGraph recomputing) skips the redundant CLL call.\n const cllResult = cll;\n if (cllApiInput.change_analysis && cllResult) {\n cllCachePatchRef.current = { pending: true, cllData: cllResult };\n queryClient.setQueryData(\n cacheKeys.lineage(),\n (old: ServerInfoResult | undefined) => {\n if (!old) return old;\n return {\n ...old,\n lineage: {\n ...old.lineage,\n diff: patchLineageDiffFromCll(old.lineage.diff, cllResult),\n },\n };\n },\n );\n }\n } catch (e) {\n if (e instanceof AxiosError) {\n const e2 = e as AxiosError<{ detail?: string }>;\n toaster.create({\n title: \"Column Level Lineage error\",\n description: e2.response?.data.detail ?? e.message,\n type: \"error\",\n closable: true,\n });\n return;\n }\n }\n } else {\n // Clear change analysis mode when CLL is cleared by any path\n // (reselect, selectParentNodes, selectChildNodes, etc.)\n setChangeAnalysisMode(false);\n }\n\n // Capture positions if preservePositions is true\n let existingPositions: Map<string, { x: number; y: number }> | undefined;\n if (preservePositions) {\n existingPositions = getNodePositions();\n }\n\n const [newNodes, newEdges, newNodeColumnSetMap] = await toReactFlow(\n lineageGraph,\n {\n selectedNodes,\n cll,\n existingPositions,\n },\n );\n setNodes(newNodes);\n setEdges(newEdges);\n setNodeColumSetMap(newNodeColumnSetMap);\n setCll(cll);\n\n // Track lineage view render\n trackLineageRender(\n newNodes,\n newViewOptions.view_mode ?? \"changed_models\",\n changeAnalysisMode,\n !!newViewOptions.column_level_lineage?.column,\n !!focusedNodeId || !!run,\n );\n\n // Close the run result view if the run result node is not in the new nodes\n if (\n run &&\n (isTopKDiffRun(run) ||\n isProfileDiffRun(run) ||\n isHistogramDiffRun(run) ||\n isValueDiffRun(run) ||\n isValueDiffDetailRun(run))\n ) {\n if (run.params?.model && !findNodeByName(run.params.model)) {\n closeRunResult();\n }\n }\n\n if (fitView) {\n await new Promise((resolve) => setTimeout(resolve, 1));\n (() => {\n void reactFlow.fitView({ nodes: newNodes, duration: 200 });\n })();\n }\n };\n\n const handleViewOptionsChanged = async (\n newViewOptions: LineageDiffViewOptions,\n fitView = true,\n preservePositions = false,\n ) => {\n setViewOptions(newViewOptions);\n await refreshLayout({\n viewOptions: newViewOptions,\n fitView,\n preservePositions,\n });\n };\n\n const valueDiffAlertDialog = useValueDiffAlertDialog();\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: handleViewOptionsChanged and onNodeClick are intentionally omitted\n useEffect(() => {\n const runResultType = run?.type;\n if (!interactive) {\n // Skip the following logic if the view is not interactive\n return;\n }\n if (!isRunResultOpen) {\n // Skip the following logic if the run result is not open\n return;\n }\n if (\n !runResultType ||\n [\"query_diff\", \"query\", \"row_count\", \"row_count_diff\"].includes(\n runResultType,\n )\n ) {\n // Skip the following logic if the run result type is not related to a node\n return;\n }\n\n if (!selectMode) {\n // Skip the following logic if the select mode is not single\n let selectedRunModel = undefined;\n if (\n isTopKDiffRun(run) ||\n isProfileDiffRun(run) ||\n isHistogramDiffRun(run) ||\n isValueDiffRun(run) ||\n isValueDiffDetailRun(run)\n ) {\n selectedRunModel = run.params?.model;\n }\n\n // Create a mock MouseEvent\n const mockEvent = new MouseEvent(\"click\", {\n bubbles: true,\n cancelable: true,\n view: window,\n }) as unknown as React.MouseEvent;\n\n if (selectedRunModel) {\n // If the run result is related to a node, select the node to show NodeView\n const node = findNodeByName(selectedRunModel);\n if (!node) {\n // Cannot find the node in the current nodes, try to change the view mode to 'all'\n void handleViewOptionsChanged({\n ...viewOptions,\n view_mode: \"all\",\n });\n } else if (isLineageGraphNode(node) && focusedNode !== node.data.data) {\n // Only select the node if it is not already selected\n onNodeClick(mockEvent, node);\n }\n } else {\n // If the run result is not related to a node, close the NodeView\n onNodeViewClosed();\n }\n }\n // handleViewOptionsChanged and onNodeClick are intentionally omitted to prevent\n // unnecessary re-runs. These functions are called conditionally within the effect\n // and don't need to trigger the effect when they change.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n run,\n viewOptions,\n isRunResultOpen,\n selectMode,\n findNodeByName,\n focusedNode,\n interactive,\n ]);\n\n const selectParentNodes = (nodeId: string, degree = 1000) => {\n if (selectMode === \"action_result\" || lineageGraph === undefined) return;\n\n if (!selectMode) {\n setSelectMode(\"selecting\");\n multiNodeAction.reset();\n if (viewOptions.column_level_lineage) {\n void handleViewOptionsChanged({\n ...viewOptions,\n column_level_lineage: undefined,\n });\n }\n }\n\n const upstream = selectUpstream(lineageGraph, [nodeId], degree);\n setSelectedNodeIds(union(selectedNodeIds, upstream));\n };\n\n const selectChildNodes = (nodeId: string, degree = 1000) => {\n if (selectMode === \"action_result\" || lineageGraph === undefined) return;\n\n if (!selectMode) {\n setSelectMode(\"selecting\");\n multiNodeAction.reset();\n if (viewOptions.column_level_lineage) {\n void handleViewOptionsChanged({\n ...viewOptions,\n column_level_lineage: undefined,\n });\n }\n }\n\n const downstream = selectDownstream(lineageGraph, [nodeId], degree);\n setSelectedNodeIds(union(selectedNodeIds, downstream));\n };\n\n const onNodeContextMenu = (\n event: React.MouseEvent,\n node: LineageGraphNodes,\n ) => {\n if (!interactive) {\n return;\n }\n if (selectMode === \"action_result\") {\n return;\n }\n // Only show context menu when selectMode is action\n // Prevent native context menu from showing\n event.preventDefault();\n const reactFlowDiv = refReactFlow.current as unknown as HTMLDivElement;\n const pane = reactFlowDiv.getBoundingClientRect();\n const x = event.clientX - pane.left;\n const y = event.clientY - pane.top + reactFlowDiv.offsetTop;\n lineageViewContextMenu.showContextMenu(x, y, node);\n };\n\n const selectNode = (nodeId: string) => {\n if (!selectMode) {\n if (!lineageGraph) {\n return;\n }\n\n setSelectedNodeIds(new Set([nodeId]));\n setSelectMode(\"selecting\");\n setFocusedNodeId(undefined);\n multiNodeAction.reset();\n } else if (selectMode === \"selecting\") {\n const newSelectedNodeIds = new Set(selectedNodeIds);\n if (selectedNodeIds.has(nodeId)) {\n newSelectedNodeIds.delete(nodeId);\n } else {\n newSelectedNodeIds.add(nodeId);\n }\n\n setSelectedNodeIds(newSelectedNodeIds);\n if (newSelectedNodeIds.size === 0) {\n setSelectMode(undefined);\n }\n }\n };\n const deselect = () => {\n setSelectMode(undefined);\n setSelectedNodeIds(new Set());\n setFocusedNodeId(undefined);\n closeRunResult();\n refetchRunsAggregated?.();\n };\n\n const contextValue: LineageViewContextType = {\n interactive,\n nodes,\n focusedNode,\n selectedNodes,\n viewOptions,\n showContextMenu: onNodeContextMenu,\n onViewOptionsChanged: handleViewOptionsChanged,\n selectMode,\n selectNode,\n selectParentNodes,\n selectChildNodes,\n deselect,\n isNodeHighlighted: (nodeId: string) => highlighted.has(nodeId),\n isNodeSelected: (nodeId: string) => selectedNodeIds.has(nodeId),\n isEdgeHighlighted: (source, target) => {\n if (!cll) {\n return highlighted.has(source) && highlighted.has(target);\n } else {\n if (!(source in cll.current.parent_map)) {\n return false;\n }\n return target in cll.current.parent_map[source];\n }\n },\n isNodeShowingChangeAnalysis: (nodeId: string) => {\n if (!lineageGraph || !changeAnalysisMode) {\n return false;\n }\n\n const node =\n nodeId in lineageGraph.nodes ? lineageGraph.nodes[nodeId] : undefined;\n\n const cll = viewOptions.column_level_lineage;\n if (cll?.node_id && !cll.column) {\n return cll.node_id === nodeId && !!node?.data.changeStatus;\n }\n return !!node?.data.changeStatus;\n },\n changeAnalysisMode,\n setChangeAnalysisMode,\n getNodeAction: (nodeId: string) => {\n return multiNodeAction.actionState.actions[nodeId];\n },\n getNodeColumnSet: (nodeId: string) => {\n if (!(nodeId in nodeColumnSetMap)) {\n return new Set<string>();\n }\n\n return new Set(nodeColumnSetMap[nodeId]);\n },\n runRowCount: async () => {\n if (selectMode === \"selecting\") {\n await multiNodeAction.runRowCount();\n trackMultiNodesAction({ type: \"row_count\", selected: \"multi\" });\n } else if (focusedNode) {\n runAction(\n \"row_count\",\n { node_names: [focusedNode.data.name] },\n { showForm: false, showLast: false },\n );\n trackMultiNodesAction({ type: \"row_count\", selected: \"single\" });\n } else {\n runAction(\"row_count\", {\n select: viewOptions.select,\n exclude: viewOptions.exclude,\n packages: viewOptions.packages,\n view_mode: viewOptions.view_mode,\n });\n trackMultiNodesAction({ type: \"row_count\", selected: \"none\" });\n }\n },\n runRowCountDiff: async () => {\n if (selectMode === \"selecting\") {\n await multiNodeAction.runRowCountDiff();\n trackMultiNodesAction({ type: \"row_count_diff\", selected: \"multi\" });\n } else if (focusedNode) {\n runAction(\n \"row_count_diff\",\n { node_names: [focusedNode.data.name] },\n { showForm: false, showLast: false },\n );\n trackMultiNodesAction({ type: \"row_count_diff\", selected: \"single\" });\n } else {\n runAction(\"row_count_diff\", {\n select: viewOptions.select,\n exclude: viewOptions.exclude,\n packages: viewOptions.packages,\n view_mode: viewOptions.view_mode,\n });\n trackMultiNodesAction({ type: \"row_count_diff\", selected: \"none\" });\n }\n },\n runValueDiff: async () => {\n if (focusedNode) {\n runAction(\n \"value_diff\",\n {\n model: focusedNode.data.name,\n },\n { showForm: true, showLast: false },\n );\n trackMultiNodesAction({ type: \"value_diff\", selected: \"single\" });\n } else {\n const nodeCount =\n selectMode === \"selecting\"\n ? selectedNodes.length\n : filteredNodeIds.length;\n if (await valueDiffAlertDialog.confirm(nodeCount)) {\n await multiNodeAction.runValueDiff();\n trackMultiNodesAction({\n type: \"value_diff\",\n selected: selectMode === \"selecting\" ? \"multi\" : \"none\",\n });\n }\n }\n },\n addLineageDiffCheck: async () => {\n // create lineage diff check based on the current lineage view (ignoring focus/selection).\n const check = await createLineageDiffCheck(viewOptions, apiClient);\n // the mode is tracked for analytics purposes.\n let selectedMode: \"multi\" | \"single\" | \"none\";\n if (selectMode === \"selecting\") {\n selectedMode = \"multi\";\n } else if (focusedNode) {\n selectedMode = \"single\";\n } else {\n selectedMode = \"none\";\n }\n trackMultiNodesAction({ type: \"lineage_diff\", selected: selectedMode });\n\n if (check) {\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n navToCheck(check);\n }\n },\n addSchemaDiffCheck: async () => {\n let check: Check | undefined = undefined;\n\n if (selectMode === \"selecting\") {\n if (selectedNodes.length > 0) {\n check = await multiNodeAction.addSchemaDiffCheck();\n deselect();\n trackMultiNodesAction({ type: \"schema_diff\", selected: \"multi\" });\n }\n } else if (focusedNode) {\n check = await createSchemaDiffCheck(\n {\n node_id: focusedNode.id,\n },\n apiClient,\n );\n trackMultiNodesAction({ type: \"schema_diff\", selected: \"single\" });\n } else {\n check = await createSchemaDiffCheck(\n {\n select: viewOptions.select,\n exclude: viewOptions.exclude,\n packages: viewOptions.packages,\n view_mode: viewOptions.view_mode,\n },\n apiClient,\n );\n trackMultiNodesAction({ type: \"schema_diff\", selected: \"none\" });\n }\n\n if (check) {\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n navToCheck(check);\n }\n },\n cancel: multiNodeAction.cancel,\n actionState: multiNodeAction.actionState,\n\n // Column Level Lineage\n centerNode,\n cll,\n showColumnLevelLineage,\n resetColumnLevelLineage,\n };\n\n if (isLoading) {\n return <LineageViewLoading />;\n }\n\n if (error) {\n return <LineageViewError error={error} onRetry={refetchLineageGraph} />;\n }\n\n if (!lineageGraph || nodes == initialNodes) {\n return <></>;\n }\n\n if (viewMode === \"changed_models\" && !lineageGraph.modifiedSet.length) {\n return (\n <LineageViewNoChanges\n viewOptions={viewOptions}\n onViewOptionsChanged={handleViewOptionsChanged}\n />\n );\n }\n return (\n <LineageViewContext.Provider value={contextValue}>\n {/* Constant props to avoid react-split destroy/recreate.\n minSize={0} (was 400) lets users drag the panel smaller; the default\n snapOffset (30px) prevents it from reaching 0px in practice. */}\n <HSplit\n sizes={focusedNode ? [70, 30] : [100, 0]}\n minSize={0}\n gutterSize={5}\n className={focusedNode ? undefined : \"split-gutter-hidden\"}\n style={{ height: \"100%\", width: \"100%\" }}\n >\n <Stack\n ref={refResize}\n divider={<Divider sx={{ borderColor: \"grey.200\" }} />}\n spacing={0}\n sx={{ contain: \"strict\", position: \"relative\" }}\n >\n {interactive && (\n <>\n <LineageViewTopBar />\n {featureToggles.mode === \"metadata only\" && (\n <SetupConnectionBanner />\n )}\n </>\n )}\n <ReactFlow\n proOptions={{\n hideAttribution: true,\n }}\n nodeTypes={nodeTypes}\n edgeTypes={edgeTypes}\n nodes={nodes}\n edges={edges}\n onNodesChange={onNodesChange}\n onEdgesChange={onEdgesChange}\n onNodeClick={onNodeClick}\n onNodeContextMenu={onNodeContextMenu}\n onClick={closeContextMenu}\n onInit={async () => {\n if (isModelsChanged) {\n await reactFlow.fitView();\n } else {\n const bounds = getNodesBounds(nodes, {});\n await reactFlow.setCenter(\n bounds.x + bounds.width / 2,\n bounds.y + bounds.height / 2,\n {\n zoom: 1,\n },\n );\n }\n }}\n maxZoom={1}\n minZoom={0.1}\n nodesDraggable={interactive}\n ref={refReactFlow as unknown as Ref<HTMLDivElement>}\n colorMode={isDark ? \"dark\" : \"light\"}\n >\n <Background\n id=\"lineage-bg\"\n variant={BackgroundVariant.Dots}\n color={isDark ? colors.neutral[700] : colors.neutral[300]}\n gap={20}\n size={2}\n />\n <Controls\n showInteractive={false}\n position=\"top-right\"\n className={IGNORE_SCREENSHOT_CLASS}\n style={{\n backgroundColor: isDark ? colors.neutral[700] : undefined,\n borderColor: isDark ? colors.neutral[600] : undefined,\n }}\n >\n <ControlButton\n title=\"copy image\"\n onClick={async () => {\n await copyToClipboard();\n trackCopyToClipboard({\n type: viewMode,\n from: \"lineage_view\",\n });\n }}\n style={{\n backgroundColor: isDark ? colors.neutral[700] : undefined,\n color: isDark ? colors.neutral[200] : undefined,\n }}\n >\n <Box component={FiCopy} />\n </ControlButton>\n </Controls>\n <ImageDownloadModal />\n <Panel position=\"bottom-left\">\n <Stack spacing=\"5px\">\n {isModelsChanged && <LineageLegend variant=\"changeStatus\" />}\n {viewOptions.column_level_lineage && (\n <LineageLegend variant=\"transformation\" />\n )}\n </Stack>\n </Panel>\n <Panel position=\"top-center\">\n <LineageViewNotification\n notification={\n singleEnv ? <BaseEnvironmentSetupNotification /> : null\n }\n type={\"info\"}\n />\n </Panel>\n <Panel position=\"top-left\">\n <Stack spacing=\"5px\">\n <ColumnLevelLineageControlOss action={actionGetCll} />\n {nodes.length == 0 && (\n <Typography\n sx={{ fontSize: \"1.25rem\", color: \"grey\", opacity: 0.5 }}\n >\n No nodes\n </Typography>\n )}\n </Stack>\n </Panel>\n <MiniMap\n nodeColor={getNodeColor}\n nodeStrokeWidth={3}\n zoomable\n pannable\n bgColor={isDark ? colors.neutral[800] : undefined}\n maskColor={\n isDark ? `${colors.neutral[900]}99` : `${colors.neutral[100]}99`\n }\n />\n {selectMode === \"action_result\" && (\n <Panel\n position=\"bottom-center\"\n className={IGNORE_SCREENSHOT_CLASS}\n >\n <ActionControlOss\n onClose={() => {\n deselect();\n }}\n />\n </Panel>\n )}\n </ReactFlow>\n <LineageViewContextMenu {...lineageViewContextMenu.props} />\n </Stack>\n {focusedNode ? (\n <Box\n sx={{\n borderLeft: \"solid 1px\",\n borderColor: \"divider\",\n height: \"100%\",\n }}\n >\n <NodeView node={focusedNode} onCloseNode={onNodeViewClosed} />\n </Box>\n ) : (\n <Box></Box>\n )}\n </HSplit>\n {valueDiffAlertDialog.AlertDialog}\n </LineageViewContext.Provider>\n );\n}\n\nexport const LineageViewOss = forwardRef<LineageViewRef, LineageViewProps>(\n PrivateLineageView,\n);\n","\"use client\";\n\nimport { ReactFlowProvider } from \"@xyflow/react\";\nimport { LineageViewOss } from \"./LineageViewOss\";\n\nexport function LineagePageOss() {\n return (\n <ReactFlowProvider>\n <LineageViewOss interactive />\n </ReactFlowProvider>\n );\n}\n","import MuiAvatar from \"@mui/material/Avatar\";\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Divider from \"@mui/material/Divider\";\nimport ListItemIcon from \"@mui/material/ListItemIcon\";\nimport ListItemText from \"@mui/material/ListItemText\";\nimport Menu from \"@mui/material/Menu\";\nimport MenuItem from \"@mui/material/MenuItem\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { type MouseEvent, useState } from \"react\";\nimport { FaCloud, FaUser } from \"react-icons/fa\";\nimport { cacheKeys } from \"../../api\";\nimport { useApiConfig } from \"../../hooks\";\nimport { fetchGitHubAvatar, fetchUser } from \"../../lib/api/user\";\nimport {\n PUBLIC_CLOUD_WEB_URL,\n RECCE_SUPPORT_CALENDAR_URL,\n} from \"../../lib/const\";\n\nexport default function AvatarDropdown() {\n const { apiClient } = useApiConfig();\n const {\n data: user,\n isLoading,\n error,\n } = useQuery({\n queryKey: cacheKeys.user(),\n queryFn: () => fetchUser(apiClient),\n retry: false,\n });\n\n const { data: avatarUrl } = useQuery({\n queryKey: [\"github-avatar\", user?.id],\n queryFn: () => (user ? fetchGitHubAvatar(user.id) : Promise.resolve(null)),\n enabled: !!user?.id && user.login_type === \"github\",\n retry: false,\n staleTime: 5 * 60 * 1000, // Cache for 5 minutes\n });\n\n const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);\n const open = Boolean(anchorEl);\n\n const handleClick = (event: MouseEvent<HTMLElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n const showUserInfo = !isLoading && !error && user;\n\n // Get initials for avatar fallback\n const getInitials = (name?: string) => {\n if (!name) return \"U\";\n return name.charAt(0).toUpperCase();\n };\n\n return (\n <>\n {isLoading ? (\n <Box\n onClick={handleClick}\n sx={{\n width: 32,\n height: 32,\n borderRadius: \"50%\",\n bgcolor: \"background.paper\",\n color: \"primary.main\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n cursor: \"pointer\",\n }}\n >\n <CircularProgress size={16} />\n </Box>\n ) : (\n <MuiAvatar\n onClick={handleClick}\n src={avatarUrl || undefined}\n sx={{\n width: 28,\n height: 28,\n cursor: \"pointer\",\n outline: \"1px solid white\",\n fontSize: \"0.875rem\",\n }}\n >\n {getInitials(user?.login)}\n </MuiAvatar>\n )}\n <Menu\n anchorEl={anchorEl}\n open={open}\n onClose={handleClose}\n slotProps={{\n paper: {\n sx: {\n bgcolor: \"background.paper\",\n borderColor: \"divider\",\n boxShadow: 3,\n minWidth: 180,\n },\n },\n }}\n >\n <Box sx={{ px: 2, py: 1.5 }}>\n {isLoading && (\n <Box sx={{ display: \"flex\", alignItems: \"center\", gap: 1 }}>\n <Typography variant=\"body2\" color=\"text.primary\">\n Loading...\n </Typography>\n <CircularProgress size={16} />\n </Box>\n )}\n {error && (\n <Typography variant=\"caption\" color=\"error\">\n Failed to load user information\n </Typography>\n )}\n {showUserInfo && (\n <>\n <Typography variant=\"body2\" fontWeight=\"600\" color=\"text.primary\">\n {user.login}\n </Typography>\n {user.email && (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {user.email}\n </Typography>\n )}\n </>\n )}\n </Box>\n <Divider />\n <MenuItem\n onClick={() => {\n window.open(PUBLIC_CLOUD_WEB_URL, \"_blank\");\n handleClose();\n }}\n >\n <ListItemIcon>\n <FaCloud />\n </ListItemIcon>\n <ListItemText>Recce Cloud</ListItemText>\n </MenuItem>\n <MenuItem\n onClick={() => {\n window.open(RECCE_SUPPORT_CALENDAR_URL, \"_blank\");\n handleClose();\n }}\n >\n <ListItemIcon>\n <FaUser />\n </ListItemIcon>\n <ListItemText>Get live support</ListItemText>\n </MenuItem>\n </Menu>\n </>\n );\n}\n","\"use client\";\n\nimport IconButton from \"@mui/material/IconButton\";\nimport Tooltip from \"@mui/material/Tooltip\";\nimport { useTheme } from \"next-themes\";\nimport { useEffect, useState } from \"react\";\nimport { PiMoon, PiSun } from \"react-icons/pi\";\n\n/**\n * Display Mode Toggle - switches between light and dark themes\n *\n * Uses next-themes to persist the user's preference.\n * Defaults to system preference; explicit user choice is stored in localStorage.\n */\nexport const DisplayModeToggleOss = () => {\n const { setTheme, resolvedTheme } = useTheme();\n const [mounted, setMounted] = useState(false);\n\n // Avoid hydration mismatch by only rendering after mount\n useEffect(() => {\n setMounted(true);\n }, []);\n\n const toggleTheme = () => {\n setTheme(resolvedTheme === \"dark\" ? \"light\" : \"dark\");\n };\n\n // Don't render anything until mounted to avoid hydration mismatch\n if (!mounted) {\n return (\n <IconButton\n size=\"small\"\n sx={{\n color: \"rgba(255, 255, 255, 0.8)\",\n \"&:hover\": { bgcolor: \"rgba(255, 255, 255, 0.1)\" },\n }}\n disabled\n >\n <PiSun style={{ width: 18, height: 18 }} />\n </IconButton>\n );\n }\n\n const isDark = resolvedTheme === \"dark\";\n\n return (\n <Tooltip title={isDark ? \"Switch to light mode\" : \"Switch to dark mode\"}>\n <IconButton\n size=\"small\"\n onClick={toggleTheme}\n sx={{\n color: \"rgba(255, 255, 255, 0.8)\",\n \"&:hover\": { bgcolor: \"rgba(255, 255, 255, 0.1)\" },\n }}\n aria-label={isDark ? \"Switch to light mode\" : \"Switch to dark mode\"}\n >\n {isDark ? (\n <PiSun style={{ width: 18, height: 18 }} />\n ) : (\n <PiMoon style={{ width: 18, height: 18 }} />\n )}\n </IconButton>\n </Tooltip>\n );\n};\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport Divider from \"@mui/material/Divider\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Link from \"@mui/material/Link\";\nimport Stack from \"@mui/material/Stack\";\nimport Table from \"@mui/material/Table\";\nimport TableBody from \"@mui/material/TableBody\";\nimport TableCell from \"@mui/material/TableCell\";\nimport TableContainer from \"@mui/material/TableContainer\";\nimport TableHead from \"@mui/material/TableHead\";\nimport TableRow from \"@mui/material/TableRow\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { isEmpty } from \"lodash\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport { LuExternalLink } from \"react-icons/lu\";\nimport { PiInfo } from \"react-icons/pi\";\nimport {\n type EnvInfo as EnvInfoType,\n useLineageGraphContext,\n} from \"../../contexts\";\nimport {\n type EnvironmentConfigProps,\n trackEnvironmentConfig,\n} from \"../../lib/api/track\";\nimport { extractSchemas, formatTimestamp, formatTimeToNow } from \"../../utils\";\n\nfunction buildEnvironmentTrackingData(\n envInfo: EnvInfoType | undefined,\n reviewMode: boolean | undefined,\n baseSchemas: Set<string>,\n currentSchemas: Set<string>,\n): EnvironmentConfigProps {\n const git = envInfo?.git;\n const pr = envInfo?.pullRequest;\n const dbtBase = envInfo?.dbt?.base;\n const dbtCurrent = envInfo?.dbt?.current;\n\n const trackingData: EnvironmentConfigProps = {\n review_mode: reviewMode || false,\n adapter_type: envInfo?.adapterType || null,\n has_git_info: !isEmpty(git),\n has_pr_info: !isEmpty(pr),\n };\n\n // DBT-specific tracking\n if (envInfo?.adapterType === \"dbt\") {\n trackingData.base = {\n schema_count: baseSchemas.size,\n dbt_version: dbtBase?.dbt_version || null,\n timestamp: dbtBase?.generated_at || null,\n };\n trackingData.current = {\n schema_count: currentSchemas.size,\n dbt_version: dbtCurrent?.dbt_version || null,\n timestamp: dbtCurrent?.generated_at || null,\n };\n trackingData.schemas_match =\n baseSchemas.size === currentSchemas.size &&\n Array.from(baseSchemas).every((s) => currentSchemas.has(s));\n }\n\n // SQLMesh-specific tracking\n if (envInfo?.adapterType === \"sqlmesh\") {\n trackingData.base = {\n has_env: !!envInfo.sqlmesh?.base_env,\n };\n trackingData.current = {\n has_env: !!envInfo.sqlmesh?.current_env,\n };\n }\n\n return trackingData;\n}\n\nfunction renderInfoEntries(info: object): React.JSX.Element[] {\n if (Object.values(info).every((value) => value === null)) {\n return [\n <Box key={\"no info\"} sx={{ ml: \"10px\" }}>\n No information\n </Box>,\n ];\n }\n\n return Object.entries(info)\n .filter(\n ([key, value]) => key !== \"url\" && value !== null && value !== undefined,\n )\n .map(([key, value]) => (\n <li key={key} style={{ marginLeft: \"10px\" }}>\n {key}: {value}\n </li>\n ));\n}\n\nexport function EnvInfo() {\n const { envInfo, reviewMode, lineageGraph } = useLineageGraphContext();\n const [open, setOpen] = useState(false);\n const git = envInfo?.git;\n const pr = envInfo?.pullRequest;\n const reviewInfo = { ...git, ...pr };\n\n const dbtBase = envInfo?.dbt?.base;\n const dbtCurrent = envInfo?.dbt?.current;\n\n const baseTime = dbtBase?.generated_at\n ? formatTimestamp(dbtBase.generated_at)\n : \"\";\n const currentTime = dbtCurrent?.generated_at\n ? formatTimestamp(dbtCurrent.generated_at)\n : \"\";\n let baseRelativeTime = \"\";\n let currentRelativeTime = \"\";\n if (dbtBase) {\n baseRelativeTime = dbtBase.generated_at\n ? formatTimeToNow(dbtBase.generated_at)\n : \"\";\n }\n if (dbtCurrent) {\n currentRelativeTime = dbtCurrent.generated_at\n ? formatTimeToNow(dbtCurrent.generated_at)\n : \"\";\n }\n const [baseSchemas, currentSchemas] = extractSchemas(lineageGraph);\n\n // Track environment configuration once at startup\n const hasTrackedRef = useRef(false);\n useEffect(() => {\n if (!hasTrackedRef.current && envInfo) {\n hasTrackedRef.current = true;\n const trackingData = buildEnvironmentTrackingData(\n envInfo,\n reviewMode,\n baseSchemas,\n currentSchemas,\n );\n trackEnvironmentConfig(trackingData);\n }\n }, [envInfo, reviewMode, baseSchemas, currentSchemas]);\n\n const handleOpen = () => setOpen(true);\n const handleClose = () => setOpen(false);\n\n return (\n <>\n <MuiTooltip title=\"Environment Info\" placement=\"bottom-end\">\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n cursor: \"pointer\",\n \"&:hover\": {\n color: \"text.primary\",\n },\n }}\n onClick={handleOpen}\n >\n <Stack\n direction=\"column\"\n sx={{\n display: { xs: \"none\", lg: \"flex\" },\n fontSize: \"0.875rem\",\n }}\n >\n <Box sx={{ display: \"flex\", alignItems: \"baseline\", gap: 0.5 }}>\n <Typography\n component=\"span\"\n noWrap\n sx={{ color: \"warning.main\", maxWidth: 128 }}\n className=\"no-track-pii-safe\"\n >\n {Array.from(baseSchemas).join(\", \")}\n </Typography>\n <Typography component=\"span\" noWrap>\n ({baseRelativeTime})\n </Typography>\n </Box>\n <Box sx={{ display: \"flex\", alignItems: \"baseline\", gap: 0.5 }}>\n <Typography\n component=\"span\"\n noWrap\n sx={{ color: \"primary.main\", maxWidth: 128 }}\n className=\"no-track-pii-safe\"\n >\n {Array.from(currentSchemas).join(\", \")}\n </Typography>\n <Typography component=\"span\" noWrap>\n ({currentRelativeTime})\n </Typography>\n </Box>\n </Stack>\n <IconButton size=\"small\" aria-label=\"Environment Info\">\n <Box\n component={PiInfo}\n sx={{ fontSize: 16, verticalAlign: \"middle\" }}\n />\n </IconButton>\n </Box>\n </MuiTooltip>\n <MuiDialog open={open} onClose={handleClose} maxWidth=\"sm\" fullWidth>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n Environment Information\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent>\n <Stack direction=\"column\" spacing={1}>\n {reviewMode ? (\n <Stack direction=\"column\" spacing={0.5}>\n <Typography variant=\"h6\" sx={{ fontSize: \"1rem\" }}>\n Review Information\n </Typography>\n <ul style={{ margin: 0, paddingLeft: \"20px\" }}>\n {reviewInfo.url && (\n <li style={{ marginLeft: \"10px\" }}>\n url:{\" \"}\n <Link\n href={reviewInfo.url}\n target=\"_blank\"\n sx={{ color: \"primary.main\" }}\n >\n {reviewInfo.url} <LuExternalLink />\n </Link>\n </li>\n )}\n {!isEmpty(reviewInfo) && renderInfoEntries(reviewInfo)}\n </ul>\n </Stack>\n ) : (\n <Stack direction=\"column\" spacing={0.5}>\n <Typography variant=\"h6\" sx={{ fontSize: \"1rem\" }}>\n Dev Information\n </Typography>\n <ul style={{ margin: 0, paddingLeft: \"20px\" }}>\n {git && renderInfoEntries(git)}\n </ul>\n </Stack>\n )}\n <Divider />\n {envInfo?.adapterType === \"dbt\" && (\n <Stack direction=\"column\" spacing={0.5}>\n <Typography variant=\"h6\" sx={{ fontSize: \"1rem\" }}>\n DBT\n </Typography>\n <TableContainer\n sx={{ border: 1, borderColor: \"divider\", maxHeight: \"30rem\" }}\n >\n <Table size=\"small\" stickyHeader>\n <TableHead>\n <TableRow>\n <TableCell />\n <TableCell>base</TableCell>\n <TableCell>current</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n <TableRow>\n <TableCell>schema</TableCell>\n <TableCell className=\"no-track-pii-safe\">\n {Array.from(baseSchemas).map((item) => (\n <MuiTooltip\n key={item}\n title={item}\n placement=\"bottom\"\n >\n <div className=\"max-w-72 truncate\">{item}</div>\n </MuiTooltip>\n ))}\n </TableCell>\n <TableCell className=\"no-track-pii-safe\">\n {Array.from(currentSchemas).map((item) => (\n <MuiTooltip\n key={item}\n title={item}\n placement=\"bottom\"\n >\n <div className=\"max-w-72 truncate\">{item}</div>\n </MuiTooltip>\n ))}\n </TableCell>\n </TableRow>\n <TableRow>\n <TableCell>version</TableCell>\n <TableCell>{dbtBase?.dbt_version}</TableCell>\n <TableCell>{dbtCurrent?.dbt_version}</TableCell>\n </TableRow>\n <TableRow>\n <TableCell>timestamp</TableCell>\n <TableCell>{baseTime}</TableCell>\n <TableCell>{currentTime}</TableCell>\n </TableRow>\n </TableBody>\n </Table>\n </TableContainer>\n </Stack>\n )}\n {envInfo?.adapterType === \"sqlmesh\" && (\n <Stack direction=\"column\" spacing={0.5}>\n <Typography variant=\"h6\" sx={{ fontSize: \"1rem\" }}>\n SQLMesh\n </Typography>\n <TableContainer\n sx={{ border: 1, borderColor: \"divider\", maxHeight: \"30rem\" }}\n >\n <Table stickyHeader>\n <TableHead>\n <TableRow>\n <TableCell />\n <TableCell>base</TableCell>\n <TableCell>current</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n <TableRow>\n <TableCell>Environment</TableCell>\n <TableCell className=\"no-track-pii-safe\">\n {envInfo.sqlmesh?.base_env}\n </TableCell>\n <TableCell className=\"no-track-pii-safe\">\n {envInfo.sqlmesh?.current_env}\n </TableCell>\n </TableRow>\n </TableBody>\n </Table>\n </TableContainer>\n </Stack>\n )}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button color=\"iochmara\" onClick={handleClose}>\n Close\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Checkbox from \"@mui/material/Checkbox\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport FormControlLabel from \"@mui/material/FormControlLabel\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport TextField from \"@mui/material/TextField\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { isAxiosError } from \"axios\";\nimport React, { useEffect, useRef, useState } from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport { LuSave } from \"react-icons/lu\";\nimport { PiPencil } from \"react-icons/pi\";\nimport {\n cacheKeys,\n LOCAL_STORAGE_KEYS,\n rename,\n saveAs,\n useChecks,\n} from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks\";\nimport { formatRunDateTime } from \"../run\";\nimport { toaster } from \"../ui\";\n\nconst useRecceToast = () => {\n const toastSuccess = (message: string) => {\n toaster.create({\n description: message,\n type: \"success\",\n duration: 5000,\n closable: true,\n });\n };\n\n const toastError = (message: string, error?: Error) => {\n let errorMessage = message;\n if (error != null) {\n if (isAxiosError<{ detail?: string }>(error)) {\n errorMessage = `${message}. ${String(error.response?.data?.detail)}`;\n } else {\n errorMessage = `${message}. ${error}`;\n }\n }\n\n toaster.create({\n description: errorMessage,\n type: \"error\",\n duration: 5000,\n closable: true,\n });\n };\n\n return { toastSuccess, toastError };\n};\n\nconst useClosePrompt = (prompt: boolean) => {\n useEffect(() => {\n const handleBeforeUnload = (e: BeforeUnloadEvent) => {\n e.preventDefault();\n };\n\n if (prompt) {\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\n }\n\n return () => {\n if (prompt) {\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\n }\n };\n }, [prompt]);\n};\n\ninterface FilenameState {\n newFileName: string;\n errorMessage?: string;\n modified?: boolean;\n overwriteWithMethod?: \"save\" | \"rename\";\n bypass?: boolean;\n}\n\nexport const Filename = () => {\n const { featureToggles } = useRecceInstanceContext();\n const { fileName, cloudMode, isDemoSite, envInfo } = useLineageGraphContext();\n const { apiClient } = useApiConfig();\n const [modalOpen, setModalOpen] = useState(false);\n const [overwriteOpen, setOverwriteOpen] = useState(false);\n const isStateless = !fileName && !cloudMode && !isDemoSite;\n const { data: checks } = useChecks(isStateless);\n const hasNonPresetChecks =\n checks != undefined &&\n checks.filter((check) => !check.is_preset).length > 0;\n useClosePrompt(isStateless && hasNonPresetChecks);\n\n const [\n { newFileName, errorMessage, modified, overwriteWithMethod, bypass },\n setState,\n ] = useState<FilenameState>({\n newFileName: fileName ?? \"recce_state.json\",\n bypass: false,\n });\n\n const inputRef = useRef<HTMLInputElement>(null);\n const { toastSuccess, toastError } = useRecceToast();\n const queryClient = useQueryClient();\n\n const handleOpen = () => {\n setState({\n newFileName: fileName ?? \"recce_state.json\",\n modified: !fileName,\n bypass: false,\n });\n\n setModalOpen(true);\n };\n\n const handleModalClose = () => setModalOpen(false);\n const handleOverwriteClose = () => setOverwriteOpen(false);\n\n const handleAction = async (\n method: \"save\" | \"rename\",\n overwrite?: boolean,\n ) => {\n if (!newFileName) {\n return;\n }\n\n const bypassOverwrite =\n localStorage.getItem(LOCAL_STORAGE_KEYS.bypassSaveOverwrite) === \"true\";\n\n try {\n if (method === \"save\") {\n await saveAs(\n {\n filename: newFileName,\n overwrite: overwrite ?? bypassOverwrite,\n },\n apiClient,\n );\n } else {\n await rename(\n {\n filename: newFileName,\n overwrite: overwrite ?? bypassOverwrite,\n },\n apiClient,\n );\n }\n toastSuccess(\n method === \"save\"\n ? \"Save file successfully\"\n : \"Rename file successfully\",\n );\n await queryClient.invalidateQueries({ queryKey: cacheKeys.lineage() });\n if (bypass) {\n localStorage.setItem(LOCAL_STORAGE_KEYS.bypassSaveOverwrite, \"true\");\n }\n } catch (error: unknown) {\n if (isAxiosError(error)) {\n if (error.response?.status === 409) {\n setState((s) => ({\n ...s,\n overwriteWithMethod: method,\n }));\n\n setOverwriteOpen(true);\n return;\n }\n }\n toastError(\n method === \"save\" ? \"Save file failed\" : \"Rename file failed\",\n error as Error,\n );\n } finally {\n handleModalClose();\n }\n };\n\n const handleOvewriteBack = () => {\n handleOverwriteClose();\n setModalOpen(true);\n setState((s) => {\n return {\n ...s,\n overwriteWithMethod: undefined,\n };\n });\n };\n\n if (cloudMode || isDemoSite) {\n return <></>;\n }\n\n const titleNewInstance =\n \"New Instance\" + (hasNonPresetChecks ? \" (unsaved)\" : \"\");\n let titleReadOnlyState;\n if (featureToggles.disableSaveToFile && fileName) {\n const generatedAt = envInfo?.stateMetadata?.generated_at;\n const formattedDate = generatedAt\n ? formatRunDateTime(new Date(generatedAt))\n : null;\n titleReadOnlyState = formattedDate\n ? `${fileName} (${formattedDate})`\n : null;\n }\n\n return (\n <>\n <Stack direction=\"row\" alignItems=\"center\" justifyContent=\"center\">\n <Box sx={{ fontWeight: 600 }}>\n {titleReadOnlyState ?? fileName ?? titleNewInstance}\n </Box>\n {!featureToggles.disableSaveToFile && (\n <MuiTooltip\n title={fileName ? \"Change Filename\" : \"Save\"}\n enterDelay={1000}\n >\n <IconButton\n onClick={handleOpen}\n aria-label={fileName ? \"Change Filename\" : \"Save\"}\n size=\"small\"\n >\n <Box\n component={fileName ? PiPencil : LuSave}\n sx={{ fontSize: 16, verticalAlign: \"middle\" }}\n />\n </IconButton>\n </MuiTooltip>\n )}\n </Stack>\n <MuiDialog open={modalOpen} onClose={handleModalClose}>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n {fileName ? \"Change Filename\" : \"Save File\"}\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleModalClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent\n onKeyDown={(e) => {\n e.stopPropagation();\n }}\n >\n <TextField\n inputRef={inputRef}\n value={newFileName}\n label=\"File name\"\n placeholder=\"Enter filename\"\n error={!!errorMessage}\n helperText={errorMessage}\n fullWidth\n size=\"small\"\n sx={{ mt: 1 }}\n onChange={(e) => {\n const value = e.target.value;\n let newErrorMessage: string | undefined = undefined;\n\n if (!value) {\n newErrorMessage = \"Filename cannot be empty.\";\n } else if (!value.endsWith(\".json\")) {\n newErrorMessage = \"Filename must end with .json.\";\n } else if (!/^[a-zA-Z0-9 _-]+\\.json$/.test(value)) {\n newErrorMessage =\n \"Invalid filename. Only alphanumeric, space, _ and - are allowed.\";\n } else if (fileName && value === fileName) {\n newErrorMessage = \"Filename is the same as the current one.\";\n }\n\n setState((s) => {\n return {\n ...s,\n modified: true,\n newFileName: value,\n errorMessage: newErrorMessage,\n };\n });\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n if (errorMessage) {\n return;\n }\n\n if (!fileName) {\n void handleAction(\"save\");\n } else {\n void handleAction(\"rename\");\n }\n } else if (e.key === \"Escape\") {\n handleModalClose();\n }\n }}\n />\n </DialogContent>\n <DialogActions sx={{ gap: \"5px\" }}>\n <Button\n size=\"small\"\n color={fileName ? \"inherit\" : \"iochmara\"}\n variant=\"contained\"\n onClick={async () => {\n await handleAction(\"save\");\n }}\n disabled={!newFileName || !!errorMessage || !modified}\n >\n {fileName ? \"Save as New File\" : \"Confirm\"}\n </Button>\n {fileName && (\n <Button\n size=\"small\"\n color=\"iochmara\"\n variant=\"contained\"\n onClick={async () => {\n await handleAction(\"rename\");\n }}\n disabled={!newFileName || !!errorMessage || !modified}\n >\n Rename\n </Button>\n )}\n </DialogActions>\n </MuiDialog>\n <MuiDialog open={overwriteOpen} onClose={handleOverwriteClose}>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n Overwrite File?\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleOverwriteClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent\n sx={{\n borderTop: \"solid 1px\",\n borderBottom: \"solid 1px\",\n borderColor: \"divider\",\n }}\n onKeyDown={(e) => {\n e.stopPropagation();\n }}\n >\n <Typography sx={{ fontSize: \"12pt\" }}>\n {overwriteWithMethod === \"save\"\n ? \"Saving a file with this name will overwrite the existing file. Are you sure you wish to continue?\"\n : \"Renaming the file with this name will overwrite the existing file. Are you sure you wish to continue?\"}\n </Typography>\n\n <FormControlLabel\n control={\n <Checkbox\n size=\"small\"\n checked={bypass}\n onChange={(e) => {\n setState((s) => ({ ...s, bypass: e.target.checked }));\n }}\n />\n }\n label={\n <Typography sx={{ fontWeight: \"bold\", pt: \"8px\" }}>\n Don't show this again\n </Typography>\n }\n />\n </DialogContent>\n <DialogActions sx={{ gap: \"5px\" }}>\n <Button variant=\"outlined\" onClick={handleOvewriteBack} size=\"small\">\n Back\n </Button>\n <Button\n size=\"small\"\n color=\"iochmara\"\n variant=\"contained\"\n onClick={() => {\n if (!overwriteWithMethod) {\n return;\n }\n\n void handleAction(overwriteWithMethod, true);\n handleOverwriteClose();\n }}\n >\n Overwrite\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n};\n","\"use client\";\n\nimport Button from \"@mui/material/Button\";\nimport Dialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogContentText from \"@mui/material/DialogContentText\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport { useState } from \"react\";\nimport { MdWarningAmber } from \"react-icons/md\";\nimport { useRecceInstanceContext } from \"../../contexts\";\n\nconst SESSION_KEY = \"recce-python-deprecation-dismissed\";\n\nexport function PythonDeprecationModal() {\n const { pythonVersion } = useRecceInstanceContext();\n const [dismissed, setDismissed] = useState(\n () =>\n typeof window !== \"undefined\" &&\n sessionStorage.getItem(SESSION_KEY) === \"true\",\n );\n\n const shouldShow =\n !dismissed &&\n typeof pythonVersion === \"string\" &&\n pythonVersion.startsWith(\"3.9\");\n\n if (!shouldShow) {\n return null;\n }\n\n const handleDismiss = () => {\n sessionStorage.setItem(SESSION_KEY, \"true\");\n setDismissed(true);\n };\n\n return (\n <Dialog open onClose={handleDismiss}>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\", gap: 1 }}>\n <MdWarningAmber\n style={{ color: \"rgb(237 108 2)\", fontSize: \"1.5rem\" }}\n />\n Python 3.9 Deprecation Notice\n </DialogTitle>\n <DialogContent>\n <DialogContentText>\n Python 3.9 support will be removed in a future release of Recce.\n Please upgrade to Python 3.10 or later to continue receiving updates.\n </DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleDismiss} variant=\"contained\">\n Got it\n </Button>\n </DialogActions>\n </Dialog>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport IconButton from \"@mui/material/IconButton\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport { format } from \"date-fns\";\nimport saveAs from \"file-saver\";\nimport { PiExport } from \"react-icons/pi\";\nimport { exportState } from \"../../api\";\nimport { useRecceInstanceContext } from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks\";\nimport { trackStateAction } from \"../../lib/api/track\";\nimport { toaster } from \"../ui\";\n\nexport function StateExporter() {\n const { featureToggles } = useRecceInstanceContext();\n const { apiClient } = useApiConfig();\n\n const handleExport = async () => {\n try {\n const jsonData = await exportState(apiClient);\n const jsonString = JSON.stringify(jsonData, null, 2);\n const blob = new Blob([jsonString], { type: \"application/json\" });\n\n const now = new Date();\n const fileName = `recce-state-${format(now, \"yyyy-MM-dd-HH-mm-ss\")}.json`;\n\n saveAs(blob, fileName);\n } catch (error) {\n console.error(\"Export failed\", error);\n toaster.create({\n title: \"Export failed\",\n description: String(error),\n type: \"error\",\n duration: 5000,\n closable: true,\n });\n }\n };\n\n return (\n <MuiTooltip title=\"Export\">\n <IconButton\n size=\"small\"\n aria-label=\"Export state\"\n onClick={async () => {\n await handleExport();\n trackStateAction({ name: \"export\" });\n }}\n disabled={featureToggles.disableExportStateFile}\n >\n <Box\n component={PiExport}\n sx={{ verticalAlign: \"middle\", width: \"16px\", height: \"16px\" }}\n />\n </IconButton>\n </MuiTooltip>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useEffect, useState } from \"react\";\nimport { PiCheckCircle, PiCopy } from \"react-icons/pi\";\nimport { TbCloudUpload } from \"react-icons/tb\";\nimport { useCopyToClipboard, useInterval } from \"usehooks-ts\";\nimport { useRecceInstanceContext } from \"../../contexts\";\nimport { useClipBoardToast, useRecceShareStateContext } from \"../../hooks\";\nimport { trackShareState } from \"../../lib/api/track\";\nimport AuthModal from \"./AuthModal\";\n\nconst LOADING_MESSAGES = [\n \"Processing...\", // 0-30s\n \"Still processing, please wait...\", // 30-60s\n \"Almost there, thanks for your patience...\", // 60s+\n];\n\nexport function TopLevelShare() {\n const { successToast, failToast } = useClipBoardToast();\n const [, copyToClipboard] = useCopyToClipboard();\n const { authed } = useRecceInstanceContext();\n const { shareUrl, isLoading, error, handleShareClick } =\n useRecceShareStateContext();\n const [showModal, setShowModal] = useState(false);\n const [messageIndex, setMessageIndex] = useState(0);\n const [prevIsLoading, setPrevIsLoading] = useState(isLoading);\n\n // Reset message index when loading starts (during render)\n if (isLoading !== prevIsLoading) {\n setPrevIsLoading(isLoading);\n if (isLoading) {\n // Loading just started, reset to 0\n setMessageIndex(0);\n }\n }\n\n // Increment message index every 30 seconds while loading\n useInterval(\n () => {\n setMessageIndex((prev) =>\n Math.min(prev + 1, LOADING_MESSAGES.length - 1),\n );\n },\n isLoading ? 30000 : null,\n );\n\n // Show error toast when sharing fails\n useEffect(() => {\n if (error) {\n failToast(\"Failed to share state\", error);\n }\n }, [error, failToast]);\n\n const handleCopy = async () => {\n try {\n await copyToClipboard(String(shareUrl));\n successToast(\"Copied the link to clipboard\");\n } catch (error) {\n failToast(\"Failed to copy the link\", error);\n }\n };\n\n if (!authed) {\n return (\n <Stack direction=\"row\" sx={{ flex: 1, alignItems: \"center\" }}>\n <Button\n size=\"xsmall\"\n color=\"neutral\"\n variant=\"outlined\"\n onClick={() => {\n setShowModal(true);\n }}\n startIcon={<TbCloudUpload />}\n >\n Share\n </Button>\n {showModal && (\n <AuthModal\n parentOpen={showModal}\n handleParentClose={setShowModal}\n ignoreCookie\n variant=\"enable-share\"\n />\n )}\n </Stack>\n );\n }\n\n return (\n <Stack direction=\"row\" sx={{ flex: 1, alignItems: \"center\", gap: \"5px\" }}>\n <Button\n size=\"xsmall\"\n variant=\"outlined\"\n color=\"neutral\"\n startIcon={<TbCloudUpload />}\n endIcon={\n shareUrl ? (\n <Box component={PiCheckCircle} sx={{ color: \"success.main\" }} />\n ) : undefined\n }\n onClick={async () => {\n await handleShareClick();\n trackShareState({ name: \"create\" });\n }}\n disabled={isLoading}\n >\n {isLoading ? \"Sharing...\" : \"Share\"}\n </Button>\n {isLoading && (\n <Typography sx={{ fontSize: 14, color: \"grey.500\" }}>\n {LOADING_MESSAGES[messageIndex]}\n </Typography>\n )}\n <Stack direction=\"row\" spacing={0.5} alignItems=\"center\">\n {shareUrl && (\n <>\n <Box\n sx={{\n overflowX: \"auto\",\n whiteSpace: \"nowrap\",\n maxWidth: \"350px\",\n }}\n >\n <Typography sx={{ fontSize: 14 }}>{shareUrl}</Typography>\n </Box>\n <IconButton\n size=\"small\"\n aria-label=\"Copy the share URL\"\n onClick={async () => {\n await handleCopy();\n trackShareState({ name: \"copy\" });\n }}\n >\n <PiCopy />\n </IconButton>\n </>\n )}\n </Stack>\n </Stack>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport FormControlLabel from \"@mui/material/FormControlLabel\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Radio from \"@mui/material/Radio\";\nimport RadioGroup from \"@mui/material/RadioGroup\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport React, { useCallback, useState } from \"react\";\nimport { IoClose, IoSync } from \"react-icons/io5\";\nimport { PiInfo } from \"react-icons/pi\";\nimport {\n cacheKeys,\n isStateSyncing,\n type SyncStateInput,\n syncState,\n} from \"../../api\";\nimport { useRecceInstanceInfo, useRouteConfig } from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks\";\nimport { toaster } from \"../ui\";\n\nfunction isCheckDetailPage(href: string): boolean {\n const pattern =\n /^\\/checks\\/([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;\n return pattern.test(href);\n}\n\nexport function StateSpinner() {\n return (\n <MuiTooltip title=\"Syncing\">\n <Box sx={{ mx: \"10px\" }}>\n <CircularProgress size={20} />\n </Box>\n </MuiTooltip>\n );\n}\n\nexport function StateSynchronizer() {\n const [isSyncing, setSyncing] = useState(false);\n const queryClient = useQueryClient();\n const { apiClient } = useApiConfig();\n const router = useRouter();\n const pathname = usePathname();\n const { basePath } = useRouteConfig();\n const [open, setOpen] = useState(false);\n const [syncOption, setSyncOption] = useState<\n \"overwrite\" | \"revert\" | \"merge\" | \"\"\n >(\"\");\n const { data: instanceInfo } = useRecceInstanceInfo();\n\n const handleClose = () => setOpen(false);\n\n const handleSync = useCallback(\n async (input: SyncStateInput) => {\n setOpen(false);\n setSyncing(true);\n\n const response = await syncState(input, apiClient);\n if (response.status === \"conflict\") {\n setOpen(true);\n setSyncing(false);\n return;\n }\n\n while (await isStateSyncing(apiClient)) {\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n\n toaster.create({\n description: \"Sync Completed\",\n type: \"success\",\n duration: 5000,\n closable: true,\n });\n\n setSyncing(false);\n setSyncOption(\"\");\n\n await queryClient.invalidateQueries({ queryKey: cacheKeys.lineage() });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.runs() });\n\n if (isCheckDetailPage(pathname)) {\n router.push(`${basePath}/checks`);\n }\n },\n [queryClient, pathname, apiClient, router.push, basePath],\n );\n\n if (isSyncing) return <StateSpinner />;\n return (\n <>\n <MuiTooltip title=\"Sync with Cloud\">\n <IconButton\n size=\"small\"\n aria-label=\"Sync state\"\n onClick={() =>\n handleSync(instanceInfo?.session_id ? { method: \"merge\" } : {})\n }\n >\n <Box\n component={IoSync}\n sx={{ fontSize: 16, verticalAlign: \"middle\" }}\n />\n </IconButton>\n </MuiTooltip>\n <MuiDialog open={open} onClose={handleClose}>\n <DialogTitle\n sx={{ display: \"flex\", alignItems: \"center\", fontWeight: \"bold\" }}\n >\n Sync with Cloud\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent>\n <Typography>\n New changes have been detected in the cloud. Please choose a method\n to sync your state\n </Typography>\n <Box sx={{ mt: \"5px\" }}>\n <RadioGroup\n value={syncOption}\n onChange={(e) => {\n setSyncOption(\n e.target.value as \"merge\" | \"overwrite\" | \"revert\",\n );\n }}\n >\n <Stack direction=\"column\">\n {/* Merge */}\n <FormControlLabel\n value=\"merge\"\n control={<Radio />}\n label={\n <Stack direction=\"row\" alignItems=\"center\">\n Merge\n <MuiTooltip title=\"This will merge the local and remote states.\">\n <Box\n component={PiInfo}\n sx={{ ml: 2, cursor: \"pointer\" }}\n />\n </MuiTooltip>\n </Stack>\n }\n />\n\n {/* Overwrite */}\n <FormControlLabel\n value=\"overwrite\"\n control={<Radio />}\n label={\n <Stack direction=\"row\" alignItems=\"center\">\n Overwrite\n <MuiTooltip title=\"This will overwrite the remote state file with the local state.\">\n <Box\n component={PiInfo}\n sx={{ ml: 2, cursor: \"pointer\" }}\n />\n </MuiTooltip>\n </Stack>\n }\n />\n\n {/* Revert */}\n <FormControlLabel\n value=\"revert\"\n control={<Radio />}\n label={\n <Stack direction=\"row\" alignItems=\"center\">\n Revert\n <MuiTooltip title=\"This will discard local changes and revert to the cloud state.\">\n <Box\n component={PiInfo}\n sx={{ ml: 2, cursor: \"pointer\" }}\n />\n </MuiTooltip>\n </Stack>\n }\n />\n </Stack>\n </RadioGroup>\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleClose} sx={{ mr: 1 }}>\n Cancel\n </Button>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n onClick={() => handleSync({ method: syncOption || undefined })}\n disabled={!syncOption}\n >\n Sync\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n}\n","\"use client\";\n\n\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Tab from \"@mui/material/Tab\";\nimport MuiTabs from \"@mui/material/Tabs\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport NextLink from \"next/link\";\nimport { usePathname } from \"next/navigation\";\nimport React, { type ReactNode, useEffect, useMemo, useRef } from \"react\";\nimport { type Check, cacheKeys, listChecks } from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n useRecceServerFlag,\n} from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks/useApiConfig\";\nimport { trackNavigation } from \"../../lib/api/track\";\nimport { EnvInfo } from \"./EnvInfo\";\nimport { Filename } from \"./Filename\";\nimport { PythonDeprecationModal } from \"./PythonDeprecationModal\";\nimport { StateExporter } from \"./StateExporter\";\nimport { TopLevelShare } from \"./StateSharing\";\nimport { StateSynchronizer } from \"./StateSynchronizer\";\n\n/**\n * Route configuration for tabs\n */\nconst ROUTE_CONFIG = [\n { path: \"/lineage\", name: \"Lineage\" },\n { path: \"/query\", name: \"Query\" },\n { path: \"/checks\", name: \"Checklist\" },\n] as const;\n\ninterface TabBadgeProps<T> {\n queryKey: string[];\n fetchCallback: () => Promise<T>;\n selectCallback?: (data: T) => number;\n}\n\nfunction TabBadge<T>({\n queryKey,\n fetchCallback,\n selectCallback,\n}: TabBadgeProps<T>): ReactNode {\n const {\n data: count,\n isLoading,\n error,\n } = useQuery({\n queryKey: queryKey,\n queryFn: fetchCallback,\n select: selectCallback,\n });\n\n if (isLoading || error || count === 0) {\n return <></>;\n }\n\n return (\n <Box\n bgcolor=\"brand.main\"\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n p={1}\n borderRadius=\"100%\"\n color=\"white\"\n fontWeight={700}\n fontSize=\"0.75rem\"\n >\n <span>{count}</span>\n </Box>\n );\n}\n\nfunction ChecklistBadge(): ReactNode {\n const { apiClient } = useApiConfig();\n return (\n <TabBadge<Check[]>\n queryKey={cacheKeys.checks()}\n fetchCallback={() => listChecks(apiClient)}\n selectCallback={(checks: Check[]) => {\n return checks.filter((check) => !check.is_checked).length;\n }}\n />\n );\n}\n\n// NavBar component with Next.js Link navigation\nexport const NavBarOss = () => {\n const pathname = usePathname();\n const { isDemoSite, isLoading, cloudMode } = useLineageGraphContext();\n const { featureToggles } = useRecceInstanceContext();\n const { data: flag, isLoading: isFlagLoading } = useRecceServerFlag();\n\n // Track navigation changes with previous pathname\n const prevPathnameRef = useRef<string | null>(null);\n useEffect(() => {\n if (prevPathnameRef.current && prevPathnameRef.current !== pathname) {\n trackNavigation({ from: prevPathnameRef.current, to: pathname });\n }\n prevPathnameRef.current = pathname;\n }, [pathname]);\n\n // Get current tab value from pathname\n const currentTab = useMemo(() => {\n if (pathname.startsWith(\"/checks\")) return \"/checks\";\n if (pathname.startsWith(\"/query\")) return \"/query\";\n if (pathname.startsWith(\"/runs\")) return \"/runs\";\n return \"/lineage\";\n }, [pathname]);\n\n return (\n <Box sx={{ borderBottom: \"1px solid lightgray\", px: \"12px\" }}>\n {/* Grid layout outside Tabs so MUI Tabs can find tab children */}\n <Box\n sx={{\n display: \"grid\",\n gridTemplateColumns: \"1fr auto 1fr\",\n width: \"100%\",\n alignItems: \"center\",\n }}\n >\n {/* Left section: Tabs */}\n <MuiTabs\n value={currentTab}\n sx={{ borderBottom: \"none\", minHeight: \"auto\" }}\n >\n {ROUTE_CONFIG.map(({ path, name }) => {\n const disable = name === \"Query\" && flag?.single_env_onboarding;\n\n // Don't render hidden tabs\n if (disable) {\n return null;\n }\n\n if (name === \"Checklist\" && ChecklistBadge) {\n return (\n <Tab\n key={path}\n value={path}\n disabled={isLoading || isFlagLoading}\n sx={{\n p: 0,\n }}\n label={\n <Box\n sx={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}\n >\n <NextLink\n href={path}\n style={{\n textDecoration: \"none\",\n color: \"inherit\",\n padding: \"0.875rem 1.1875rem\",\n display: \"flex\",\n gap: 3,\n alignItems: \"center\",\n }}\n >\n {name} <ChecklistBadge />\n </NextLink>\n </Box>\n }\n />\n );\n }\n\n return (\n <Tab\n key={path}\n value={path}\n disabled={isLoading || isFlagLoading}\n sx={{\n p: 0,\n }}\n label={\n <Box\n sx={{ display: \"flex\", alignItems: \"center\", gap: \"4px\" }}\n >\n <NextLink\n href={path}\n style={{\n textDecoration: \"none\",\n color: \"inherit\",\n padding: \"0.875rem 1.1875rem\",\n }}\n >\n {name}\n </NextLink>\n </Box>\n }\n />\n );\n })}\n </MuiTabs>\n\n {/* Center section: Filename and TopLevelShare */}\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\",\n justifyContent: \"center\",\n }}\n >\n {!isLoading && !isDemoSite && <Filename />}\n {!isLoading &&\n !isDemoSite &&\n !flag?.single_env_onboarding &&\n !featureToggles.disableShare && <TopLevelShare />}\n </Box>\n\n {/* Right section: EnvInfo, StateSynchronizer, StateExporter */}\n {!isLoading && (\n <Box\n sx={{\n display: \"flex\",\n justifyContent: \"right\",\n alignItems: \"center\",\n mr: \"8px\",\n }}\n >\n <EnvInfo />\n {cloudMode && <StateSynchronizer />}\n <StateExporter />\n </Box>\n )}\n </Box>\n <PythonDeprecationModal />\n </Box>\n );\n};\n","\"use client\";\n\nimport Badge from \"@mui/material/Badge\";\nimport Box from \"@mui/material/Box\";\nimport { IoWarning } from \"react-icons/io5\";\nimport { useIdleTimeout } from \"../../contexts\";\nimport { formatDuration } from \"../../utils\";\n\n/**\n * Warning threshold in seconds - badge appears when remaining time is below this\n */\nconst WARNING_THRESHOLD_SECONDS = 60;\n\n/**\n * Badge component that displays idle timeout warning countdown\n * Only shows when remaining time is below the warning threshold\n * Styled as a warning indicator to draw user attention\n */\nexport function IdleTimeoutBadge() {\n const { remainingSeconds, isEnabled } = useIdleTimeout();\n\n // Don't render if idle timeout is not configured\n if (!isEnabled || remainingSeconds === null) {\n return null;\n }\n\n // Only show when below warning threshold\n if (remainingSeconds > WARNING_THRESHOLD_SECONDS) {\n return null;\n }\n\n return (\n <Badge\n color=\"warning\"\n variant=\"standard\"\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 1,\n fontSize: \"0.75rem\",\n mr: 2,\n }}\n >\n <Box component={IoWarning} sx={{ display: \"inline-flex\" }} />\n Idle timeout: {formatDuration(remainingSeconds, \"compact\")}\n </Badge>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Link from \"@mui/material/Link\";\nimport Typography from \"@mui/material/Typography\";\nimport React, { useEffect, useMemo } from \"react\";\nimport { useVersionNumber } from \"../../api\";\nimport { toaster } from \"../ui\";\n\nexport const RecceVersionBadgeOss = () => {\n const { version, latestVersion } = useVersionNumber();\n const versionFormatRegex = useMemo(\n () => new RegExp(\"^\\\\d+\\\\.\\\\d+\\\\.\\\\d+$\"),\n [],\n );\n\n useEffect(() => {\n if (versionFormatRegex.test(version) && version !== latestVersion) {\n const storageKey = \"recce-update-toast-shown\";\n const hasShownForThisVersion = sessionStorage.getItem(storageKey);\n if (hasShownForThisVersion) {\n return;\n }\n // Defer toast creation to next tick to avoid React's flushSync error\n // This prevents \"flushSync called from inside lifecycle method\" when\n // the toast library tries to immediately update DOM during render cycle\n setTimeout(() => {\n toaster.create({\n id: \"recce-update-available\", // Fixed ID prevents duplicates\n title: \"Update available\",\n description: (\n <span>\n A new version of Recce (v{latestVersion}) is available.\n <br />\n Please run{\" \"}\n <Box\n component=\"code\"\n sx={{\n bgcolor: \"grey.200\",\n px: 0.5,\n py: 0.25,\n borderRadius: 0.5,\n fontFamily: \"monospace\",\n fontSize: \"0.875em\",\n }}\n >\n pip install --upgrade recce\n </Box>{\" \"}\n to update Recce.\n <br />\n <Link\n sx={{\n color: \"primary.main\",\n fontWeight: \"bold\",\n \"&:hover\": { textDecoration: \"underline\" },\n }}\n href={`https://github.com/DataRecce/recce/releases/tag/v${latestVersion}`}\n target=\"_blank\"\n >\n Click here to view the detail of latest release\n </Link>\n </span>\n ),\n duration: 60 * 1000,\n closable: true,\n });\n sessionStorage.setItem(storageKey, \"true\");\n }, 0);\n }\n }, [version, latestVersion, versionFormatRegex]);\n\n if (!versionFormatRegex.test(version)) {\n // If the version is not in the format of x.y.z, don't apply\n return (\n <Typography\n component=\"span\"\n sx={{\n fontSize: \"sm\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n }}\n >\n {version}\n </Typography>\n );\n }\n\n // Link to the release page on GitHub if the version is in the format of x.y.z\n return (\n <Link\n href={`https://github.com/DataRecce/recce/releases/tag/v${version}`}\n sx={{\n \"&:hover\": { textDecoration: \"none\" },\n fontSize: \"sm\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n }}\n target=\"_blank\"\n >\n {version}\n </Link>\n );\n};\n","\"use client\";\n\nimport Badge from \"@mui/material/Badge\";\nimport Box from \"@mui/material/Box\";\nimport Link from \"@mui/material/Link\";\nimport Stack from \"@mui/material/Stack\";\nimport { useTheme } from \"@mui/material/styles\";\nimport Typography from \"@mui/material/Typography\";\nimport React, { useState } from \"react\";\nimport { IconType } from \"react-icons\";\nimport { FaGithub, FaQuestionCircle, FaSlack } from \"react-icons/fa\";\nimport { VscGitPullRequest } from \"react-icons/vsc\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n} from \"../../contexts\";\nimport { PUBLIC_CLOUD_WEB_URL } from \"../../lib/const\";\nimport { colors } from \"../../theme\";\nimport { IdleTimeoutBadge } from \"../timeout/IdleTimeoutBadge\";\nimport AuthModal from \"./AuthModal\";\nimport AvatarDropdown from \"./AvatarDropdown\";\nimport { DisplayModeToggleOss as DisplayModeToggle } from \"./DisplayModeToggleOss\";\nimport { RecceVersionBadgeOss as RecceVersionBadge } from \"./RecceVersionBadgeOss\";\n\ninterface LinkIconProps {\n icon: IconType;\n href: string;\n sx?: object;\n}\n\nfunction LinkIcon({ icon: IconComponent, href, sx, ...props }: LinkIconProps) {\n const theme = useTheme();\n return (\n <Link\n sx={{ height: \"20px\", color: \"common.white\", ...sx }}\n href={href}\n target=\"_blank\"\n {...props}\n >\n <IconComponent\n style={{ color: theme.palette.common.white, width: 20, height: 20 }}\n />\n </Link>\n );\n}\n\nexport const TopBarOss = () => {\n const { reviewMode, isDemoSite, envInfo, cloudMode } =\n useLineageGraphContext();\n const { featureToggles, authed } = useRecceInstanceContext();\n const { url: prURL, id: prID } = envInfo?.pullRequest ?? {};\n const demoPrId = prURL ? prURL.split(\"/\").pop() : null;\n const brandLink =\n cloudMode || authed ? PUBLIC_CLOUD_WEB_URL : \"https://reccehq.com/\";\n const [showModal, setShowModal] = useState(false);\n\n return (\n <Box\n sx={{\n display: \"flex\",\n gap: \"10px\",\n minHeight: \"40px\",\n alignItems: \"center\",\n bgcolor: colors.brand[400],\n }}\n >\n <Link\n href={brandLink}\n target=\"_blank\"\n sx={{ \"&:hover\": { textDecoration: \"none\" } }}\n >\n <Box sx={{ display: \"flex\", gap: \"10px\", alignItems: \"center\" }}>\n <Box\n component=\"img\"\n sx={{ width: 20, height: 20, ml: \"18px\" }}\n src=\"/logo/recce-logo-white.png\"\n alt=\"recce-logo-white\"\n />\n <Typography\n variant=\"h4\"\n sx={{\n fontFamily: '\"Montserrat\", sans-serif',\n color: \"common.white\",\n fontSize: \"1.25rem\",\n }}\n >\n RECCE\n </Typography>\n </Box>\n </Link>\n <DisplayModeToggle />\n <RecceVersionBadge />\n {(featureToggles.mode ?? reviewMode) && (\n <Badge\n sx={{\n fontSize: \"0.875rem\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n borderColor: \"rgba(255,255,255,0.8)\",\n }}\n >\n {featureToggles.mode ?? \"review mode\"}\n </Badge>\n )}\n {cloudMode && prID && (\n <Badge\n sx={{\n fontSize: \"0.875rem\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n borderColor: \"rgba(255,255,255,0.8)\",\n }}\n >\n <Stack direction=\"row\" spacing={1} alignItems=\"center\">\n <Box>cloud mode</Box>\n <Box\n sx={{\n borderLeft: \"1px solid rgba(255,255,255,0.8)\",\n pl: \"8px\",\n }}\n >\n <Link\n href={prURL}\n sx={{ \"&:hover\": { textDecoration: \"none\" } }}\n target=\"_blank\"\n >\n <VscGitPullRequest\n style={{\n color: \"rgba(255,255,255,0.8)\",\n width: 12,\n height: 12,\n marginRight: 2,\n display: \"inline\",\n verticalAlign: \"middle\",\n }}\n />\n <Typography\n component=\"span\"\n sx={{ color: \"rgba(255,255,255,0.8)\", display: \"inline\" }}\n >{`#${String(prID)}`}</Typography>\n </Link>\n </Box>\n </Stack>\n </Badge>\n )}\n {isDemoSite && prURL && demoPrId && (\n <Badge\n sx={{\n fontSize: \"0.875rem\",\n color: \"rgba(255,255,255,0.8)\",\n textTransform: \"uppercase\",\n borderWidth: 1,\n px: 1,\n borderRadius: 0.75,\n borderColor: \"rgba(255,255,255,0.8)\",\n }}\n >\n <Stack direction=\"row\" spacing={1} alignItems=\"center\">\n <Box>demo mode</Box>\n <Box\n sx={{\n borderLeft: \"1px solid rgba(255,255,255,0.8)\",\n pl: \"8px\",\n }}\n >\n <Link\n href={prURL}\n sx={{ \"&:hover\": { textDecoration: \"none\" } }}\n target=\"_blank\"\n >\n <VscGitPullRequest\n style={{\n color: \"rgba(255,255,255,0.8)\",\n width: 12,\n height: 12,\n marginRight: 2,\n display: \"inline\",\n verticalAlign: \"middle\",\n }}\n />\n <Typography\n component=\"span\"\n sx={{ color: \"rgba(255,255,255,0.8)\", display: \"inline\" }}\n >{`#${demoPrId}`}</Typography>\n </Link>\n </Box>\n </Stack>\n </Badge>\n )}\n <Box sx={{ flex: 1 }} />\n\n {(isDemoSite || featureToggles.mode === \"read only\") && (\n <>\n <LinkIcon icon={FaGithub} href=\"https://github.com/DataRecce/recce\" />\n <LinkIcon\n icon={FaSlack}\n href=\"https://getdbt.slack.com/archives/C05C28V7CPP\"\n />\n <LinkIcon\n sx={{ mr: 2 }}\n icon={FaQuestionCircle}\n href=\"https://docs.reccehq.com\"\n />\n </>\n )}\n {!isDemoSite && featureToggles.mode !== \"read only\" && (\n <>\n <IdleTimeoutBadge />\n {authed || cloudMode ? (\n <Box sx={{ mr: 2 }}>\n <AvatarDropdown />\n </Box>\n ) : (\n <>\n <Box\n component=\"button\"\n sx={{\n color: \"common.white\",\n fontSize: \"0.875rem\",\n fontWeight: 600,\n bgcolor: \"brand.700\",\n borderRadius: 1,\n px: 3,\n py: 1,\n mr: 2,\n cursor: \"pointer\",\n border: \"none\",\n }}\n onClick={() => {\n setShowModal(true);\n }}\n >\n Connect to Cloud\n </Box>\n {showModal && (\n <AuthModal\n parentOpen={showModal}\n handleParentClose={setShowModal}\n ignoreCookie\n variant=\"user-profile\"\n />\n )}\n </>\n )}\n </>\n )}\n </Box>\n );\n};\n","/**\n * MainLayout - Handles parallel route visibility and main app structure\n *\n * This component manages the visibility of the @lineage parallel route\n * while keeping it mounted to preserve React state (React Flow graph state, etc.)\n */\n\n\"use client\";\n\nimport \"@fontsource/montserrat/800.css\";\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport { usePathname } from \"next/navigation\";\nimport React, { type ReactNode, Suspense, useEffect } from \"react\";\nimport {\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n useRecceServerFlag,\n useRouteConfig,\n} from \"../../contexts\";\nimport { useIsDark } from \"../../hooks/useIsDark\";\nimport { trackInit } from \"../../lib/api/track\";\nimport { RunListOss, RunResultPaneOss as RunResultPane } from \"../run\";\nimport { HSplit, VSplit } from \"../ui\";\nimport AuthModal from \"./AuthModal\";\nimport { NavBarOss as NavBar } from \"./NavBarOss\";\nimport { TopBarOss as TopBar } from \"./TopBarOss\";\n\ninterface MainLayoutProps {\n children: ReactNode;\n /** Parallel route slot from @lineage */\n lineage: ReactNode;\n}\n\nfunction MainContentLoading(): ReactNode {\n return (\n <Box\n sx={{\n display: \"flex\",\n height: \"100%\",\n alignItems: \"center\",\n justifyContent: \"center\",\n contain: \"size\",\n }}\n >\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n <CircularProgress size={48} />\n </Box>\n </Box>\n );\n}\n\nexport function MainLayout({ children, lineage }: MainLayoutProps) {\n const pathname = usePathname();\n const { isDemoSite, isLoading, isCodespace } = useLineageGraphContext();\n const { featureToggles } = useRecceInstanceContext();\n\n // Determine if lineage route is active (handle trailing slashes)\n const normalizedPath = pathname.replace(/\\/$/, \"\") || \"/\";\n const isLineageRoute =\n normalizedPath === \"/lineage\" || normalizedPath === \"/\";\n\n useEffect(() => {\n trackInit();\n }, []);\n\n return (\n <Box\n sx={{\n display: \"flex\",\n flexDirection: \"column\",\n height: \"100vh\",\n overflow: \"hidden\",\n }}\n >\n <TopBar />\n <NavBar />\n <Main isLineageRoute={isLineageRoute} lineage={lineage}>\n {children}\n </Main>\n {!isLoading &&\n !isDemoSite &&\n !isCodespace &&\n featureToggles.mode === null && <AuthModal />}\n </Box>\n );\n}\n\n// Main content area with parallel route handling\ninterface MainProps {\n children: ReactNode;\n lineage: ReactNode;\n isLineageRoute: boolean;\n /** Disable internal history panel (RunListOss) - cloud uses its own */\n disableInternalHistory?: boolean;\n}\n\nexport function Main({\n children,\n lineage,\n isLineageRoute,\n disableInternalHistory = false,\n}: MainProps) {\n const { isRunResultOpen, isHistoryOpen, closeRunResult } =\n useRecceActionContext();\n const { basePath } = useRouteConfig();\n const { data: flag } = useRecceServerFlag();\n const pathname = usePathname();\n const isDark = useIsDark();\n\n const _isRunResultOpen =\n isRunResultOpen && !pathname.startsWith(`${basePath}/checks`);\n // When disableInternalHistory is true, always hide internal history panel\n // Cloud mode uses its own RunHistoryDrawer instead\n const _isHistoryOpen = disableInternalHistory\n ? false\n : isHistoryOpen && !pathname.startsWith(`${basePath}/checks`);\n\n // Keep gutterSize and minSize constant to avoid react-split's destroy/recreate\n // path (triggered when these props change). Only changing `sizes` uses the\n // simpler `setSizes()` code path, which avoids DOM measurement race conditions\n // that can cause the result pane to appear at the top of the page.\n return (\n <HSplit\n sizes={_isHistoryOpen ? [20, 80] : [0, 100]}\n minSize={0}\n gutterSize={5}\n className={_isHistoryOpen ? undefined : \"split-gutter-hidden\"}\n style={{ height: \"100%\" }}\n >\n {/* suppressHydrationWarning: react-split adds inline styles after mount */}\n <Box style={{ contain: \"size\" }} suppressHydrationWarning>\n {_isHistoryOpen && <RunListOss />}\n </Box>\n <VSplit\n sizes={_isRunResultOpen ? [50, 50] : [100, 0]}\n minSize={0}\n gutterSize={5}\n className={_isRunResultOpen ? undefined : \"split-gutter-hidden\"}\n style={{ flex: \"1\", contain: \"size\" }}\n >\n <Suspense fallback={<MainContentLoading />}>\n {/* suppressHydrationWarning: react-split adds inline styles (height, width)\n to children after mount, causing expected server/client mismatches */}\n <Box\n sx={{\n p: 0,\n contain: \"content\",\n height: \"100%\",\n position: \"relative\",\n }}\n suppressHydrationWarning\n >\n {/*\n * Lineage parallel route - always mounted but visibility controlled\n * This replaces the old RouteAlwaysMount pattern\n */}\n <Box\n sx={{\n display: isLineageRoute ? \"block\" : \"none\",\n height: \"100%\",\n position: isLineageRoute ? \"relative\" : \"absolute\",\n inset: 0,\n }}\n >\n {lineage}\n </Box>\n\n {/* Other route content */}\n {!isLineageRoute && children}\n </Box>\n </Suspense>\n {/* suppressHydrationWarning: react-split adds inline styles after mount */}\n <Box\n sx={{ height: \"100%\", bgcolor: isDark ? \"grey.900\" : \"grey.50\" }}\n suppressHydrationWarning\n >\n {_isRunResultOpen ? (\n <RunResultPane\n onClose={closeRunResult}\n isSingleEnvironment={!!flag?.single_env_onboarding}\n />\n ) : null}\n </Box>\n </VSplit>\n </HSplit>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Link from \"@mui/material/Link\";\nimport MuiPopover from \"@mui/material/Popover\";\nimport { ReactElement, useCallback, useRef, useState } from \"react\";\nimport { RECCE_SUPPORT_CALENDAR_URL } from \"../../lib/const\";\n\ninterface SetupConnectionPopoverProps {\n children: ReactElement<{\n ref?: React.Ref<HTMLElement>;\n [key: string]: unknown;\n }>;\n display: boolean;\n}\n\nexport default function SetupConnectionPopover({\n children,\n display,\n}: SetupConnectionPopoverProps) {\n const [hovered, setHovered] = useState(false);\n const timeoutRef = useRef<NodeJS.Timeout | null>(null);\n const anchorRef = useRef<HTMLDivElement | null>(null);\n\n const handleMouseEnter = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n setHovered(true);\n }, []);\n\n const handleMouseLeave = useCallback(() => {\n timeoutRef.current = setTimeout(() => {\n setHovered(false);\n }, 100);\n }, []);\n\n if (!display) {\n return children;\n }\n\n return (\n <>\n <Box\n ref={anchorRef}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n sx={{ display: \"contents\" }}\n >\n {children}\n </Box>\n <MuiPopover\n open={hovered}\n anchorEl={anchorRef.current}\n onClose={() => setHovered(false)}\n anchorOrigin={{\n vertical: \"bottom\",\n horizontal: \"left\",\n }}\n transformOrigin={{\n vertical: \"top\",\n horizontal: \"left\",\n }}\n disableAutoFocus\n disableEnforceFocus\n sx={{ pointerEvents: \"none\" }}\n slotProps={{\n paper: {\n onMouseEnter: handleMouseEnter,\n onMouseLeave: handleMouseLeave,\n sx: {\n bgcolor: \"grey.600\",\n color: \"white\",\n p: 1.5,\n pointerEvents: \"auto\",\n },\n },\n }}\n >\n Connect to a data warehouse to unlock Diff.{\" \"}\n <Link\n href={RECCE_SUPPORT_CALENDAR_URL}\n target=\"_blank\"\n sx={{ color: \"white\", textDecoration: \"underline\" }}\n >\n Learn more\n </Link>\n .\n </MuiPopover>\n </>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { usePathname, useRouter } from \"next/navigation\";\nimport React, { ChangeEvent, useCallback, useRef, useState } from \"react\";\nimport { FaFileImport } from \"react-icons/fa6\";\nimport { IoClose } from \"react-icons/io5\";\nimport { PiInfo } from \"react-icons/pi\";\nimport { cacheKeys, importState } from \"../../api\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n useRouteConfig,\n useRunsAggregated,\n} from \"../../contexts\";\nimport { useApiConfig, useIsDark } from \"../../hooks\";\nimport { trackStateAction } from \"../../lib/api/track\";\nimport { toaster } from \"../ui\";\n\nexport function StateImporter({ checksOnly = true }: { checksOnly?: boolean }) {\n const isDark = useIsDark();\n const { featureToggles } = useRecceInstanceContext();\n const queryClient = useQueryClient();\n const { apiClient } = useApiConfig();\n const hiddenFileInput = useRef<HTMLInputElement>(null);\n const cancelRef = useRef<HTMLButtonElement>(null);\n const [selectedFile, setSelectedFile] = useState<File | null>(null);\n const [open, setOpen] = useState(false);\n const router = useRouter();\n const pathname = usePathname();\n const { basePath } = useRouteConfig();\n const [, refetchRunsAggregated] = useRunsAggregated();\n\n const handleImport = useCallback(async () => {\n if (!selectedFile) {\n return;\n }\n\n try {\n const { runs, checks } = await importState(\n selectedFile,\n checksOnly,\n apiClient,\n );\n refetchRunsAggregated?.();\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.runs() });\n if (pathname.includes(\"/checks\")) {\n router.push(`${basePath}/checks`);\n }\n const description = checksOnly\n ? `${checks} checks imported successfully`\n : `${runs} runs and ${checks} checks imported successfully`;\n toaster.create({\n description: description,\n type: \"info\",\n duration: 5000,\n closable: true,\n });\n } catch (error) {\n console.error(\"Import failed\", error);\n toaster.create({\n title: \"Import failed\",\n description: String(error),\n type: \"error\",\n duration: 5000,\n closable: true,\n });\n }\n\n setOpen(false);\n }, [\n queryClient,\n selectedFile,\n pathname,\n refetchRunsAggregated,\n checksOnly,\n apiClient,\n router.push,\n basePath,\n ]);\n\n const handleClick = () => {\n if (hiddenFileInput.current) {\n hiddenFileInput.current.click();\n }\n };\n\n const handleFileSelect = (event: ChangeEvent<HTMLInputElement>) => {\n if (event.target.files?.length === 1) {\n setSelectedFile(event.target.files[0]);\n setOpen(true);\n }\n\n if (hiddenFileInput.current) {\n hiddenFileInput.current.value = \"\";\n }\n };\n\n const handleClose = () => setOpen(false);\n\n const warningSubject = checksOnly ? \"checks\" : \"runs and checks\";\n const { isDemoSite } = useLineageGraphContext();\n return (\n <>\n <MuiTooltip\n title={\n \"Import Checklist from State File\" +\n (isDemoSite ? \" (Disabled in the demo site)\" : \"\")\n }\n >\n <IconButton\n sx={{\n pt: \"6px\",\n color: isDark ? \"grey.300\" : \"grey.600\",\n \"&:hover\": { color: isDark ? \"grey.100\" : \"grey.800\" },\n fontSize: 20,\n }}\n aria-label=\"Import state\"\n onClick={() => {\n handleClick();\n trackStateAction({ name: \"import\" });\n }}\n disabled={featureToggles.disableImportStateFile || isDemoSite}\n >\n <FaFileImport />\n </IconButton>\n </MuiTooltip>\n <input\n type=\"file\"\n style={{ display: \"none\" }}\n ref={hiddenFileInput}\n onChange={handleFileSelect}\n />\n <MuiDialog\n open={open}\n onClose={handleClose}\n maxWidth=\"sm\"\n fullWidth\n aria-labelledby=\"import-dialog-title\"\n >\n <DialogTitle\n id=\"import-dialog-title\"\n sx={{ display: \"flex\", alignItems: \"center\", fontWeight: \"bold\" }}\n >\n Import state\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <DialogContent>\n <Stack\n direction=\"column\"\n spacing={1}\n sx={{ px: \"5px\", borderRadius: 1 }}\n >\n <Stack direction=\"row\" alignItems=\"center\" spacing={0.5}>\n <Box component={PiInfo} sx={{ color: \"error.main\" }} />\n <Typography\n component=\"span\"\n sx={{ fontWeight: 500, color: \"error.main\" }}\n >\n Caution!\n </Typography>\n </Stack>\n <Typography>\n The current {warningSubject} will be{\" \"}\n <Typography component=\"span\" sx={{ fontWeight: 600 }}>\n merged\n </Typography>{\" \"}\n with the imported state\n </Typography>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button ref={cancelRef} onClick={handleClose}>\n Cancel\n </Button>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n onClick={handleImport}\n sx={{ ml: \"5px\" }}\n >\n Import\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n}\n","import Box from \"@mui/material/Box\";\nimport { ReactFlowProvider } from \"@xyflow/react\";\nimport { forwardRef, Ref } from \"react\";\nimport type { Check, LineageDiffViewOptions } from \"../../api\";\nimport type { LineageViewRef } from \"../lineage/LineageViewOss\";\nimport { LineageViewOss as LineageView } from \"../lineage/LineageViewOss\";\n\nexport interface LineageDiffViewProps {\n check: Check;\n}\n\nfunction _LineageDiffView(\n { check }: LineageDiffViewProps,\n ref: Ref<LineageViewRef>,\n) {\n const viewOptions = {\n ...(check.params as Record<string, unknown>),\n ...(check.view_options as LineageDiffViewOptions),\n };\n\n return (\n <Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\" }}>\n <ReactFlowProvider>\n <LineageView viewOptions={viewOptions} interactive={false} ref={ref} />\n </ReactFlowProvider>\n </Box>\n );\n}\n\nexport const LineageDiffViewOss = forwardRef<\n LineageViewRef,\n LineageDiffViewProps\n>(_LineageDiffView);\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport List from \"@mui/material/List\";\nimport ListItem from \"@mui/material/ListItem\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport React, { forwardRef, useMemo, useState } from \"react\";\nimport type { IconType } from \"react-icons\";\nimport type { LineageGraphNode } from \"../..\";\nimport { HSplit, isSchemaChanged } from \"../..\";\nimport { type Check, cacheKeys, select } from \"../../api\";\nimport { useLineageGraphContext } from \"../../contexts\";\nimport { useApiConfig, useIsDark } from \"../../hooks\";\nimport type { DataGridHandle } from \"../../primitives\";\nimport {\n getIconForChangeStatus,\n getIconForResourceType,\n type IconComponent,\n} from \"../lineage\";\nimport { findByRunType } from \"../run\";\nimport { SchemaView } from \"../schema\";\n\ninterface SchemaDiffViewProps {\n check: Check;\n}\n\nexport interface SchemaDiffParams {\n node_id?: string | string[];\n select?: string;\n exclude?: string;\n view_mode?: \"all\" | \"changed_models\";\n packages?: string[];\n}\n\nconst NodelistItem = ({\n node,\n selected,\n onSelect,\n schemaChanged,\n isDark,\n}: {\n node: LineageGraphNode;\n selected: boolean;\n onSelect: (nodeId: string) => void;\n schemaChanged: boolean;\n isDark: boolean;\n}) => {\n const { icon } = getIconForResourceType(node.data.resourceType);\n const { base, current } = node.data.data;\n\n let statusIcon: IconComponent | IconType | undefined;\n let statusColor: string | undefined;\n\n if (schemaChanged) {\n statusIcon = findByRunType(\"schema_diff\").icon;\n statusColor = getIconForChangeStatus(\"modified\").color;\n } else if (!base && current) {\n statusIcon = getIconForChangeStatus(\"added\").icon;\n statusColor = getIconForChangeStatus(\"added\").color;\n } else if (base && !current) {\n statusIcon = getIconForChangeStatus(\"removed\").icon;\n statusColor = getIconForChangeStatus(\"removed\").color;\n }\n\n return (\n <ListItem disablePadding>\n <Box\n sx={{\n display: \"flex\",\n width: \"100%\",\n fontSize: \"10pt\",\n p: \"5px 8px\",\n cursor: \"pointer\",\n \"&:hover\": { bgcolor: isDark ? \"grey.700\" : \"grey.200\" },\n bgcolor: selected ? (isDark ? \"grey.800\" : \"grey.100\") : \"inherit\",\n alignItems: \"center\",\n gap: \"5px\",\n }}\n onClick={() => {\n onSelect(node.id);\n }}\n >\n {icon && <Box component={icon} />}\n <Box\n sx={{\n flex: 1,\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n }}\n >\n {node.data.name}\n </Box>\n\n {statusIcon && statusColor && (\n <Box component={statusIcon} sx={{ color: statusColor }} />\n )}\n </Box>\n </ListItem>\n );\n};\n\nexport function PrivateSchemaDiffView(\n { check }: SchemaDiffViewProps,\n ref: React.Ref<DataGridHandle>,\n) {\n const isDark = useIsDark();\n const { apiClient } = useApiConfig();\n const { lineageGraph } = useLineageGraphContext();\n const params = check.params as SchemaDiffParams;\n\n const queryKey = [...cacheKeys.check(check.check_id), \"select\"];\n\n const { isLoading, error, data } = useQuery({\n queryKey,\n queryFn: async () =>\n select(\n {\n select: params.select,\n exclude: params.exclude,\n packages: params.packages,\n view_mode: params.view_mode,\n },\n apiClient,\n ),\n refetchOnMount: true,\n enabled: !params.node_id,\n });\n\n const [nodes, changedNodes] = useMemo(() => {\n const selectedNodes: LineageGraphNode[] = [];\n const changedNodes: string[] = [];\n const addedNodes: string[] = [];\n const removedNodes: string[] = [];\n\n if (params.node_id) {\n const nodeIds =\n params.node_id instanceof Array ? params.node_id : [params.node_id];\n for (const nodeId of nodeIds) {\n const node = lineageGraph?.nodes[nodeId];\n if (node) {\n selectedNodes.push(node);\n }\n }\n } else {\n for (const nodeId of data?.nodes ?? []) {\n const node = lineageGraph?.nodes[nodeId];\n if (node) {\n selectedNodes.push(node);\n }\n }\n }\n\n // filter that the resourec_type is mode,seed, source, or snapshot\n const filteredNodes = selectedNodes.filter(\n (node) =>\n node.data.resourceType === \"model\" ||\n node.data.resourceType === \"seed\" ||\n node.data.resourceType === \"source\" ||\n node.data.resourceType === \"snapshot\",\n );\n\n for (const node of filteredNodes) {\n if (\n isSchemaChanged(\n node.data.data.base?.columns,\n node.data.data.current?.columns,\n )\n ) {\n changedNodes.push(node.id);\n } else if (!node.data.data.base && node.data.data.current) {\n addedNodes.push(node.id);\n } else if (node.data.data.base && !node.data.data.current) {\n removedNodes.push(node.id);\n }\n }\n function sortScore(node: LineageGraphNode) {\n if (changedNodes.includes(node.id)) {\n return 3;\n }\n if (addedNodes.includes(node.id)) {\n return 2;\n }\n if (removedNodes.includes(node.id)) {\n return 1;\n }\n return 0;\n }\n\n //sort the selectedNodes from schemaChange and node name\n filteredNodes.sort((a, b) => {\n const scoreA = sortScore(a);\n const scoreB = sortScore(b);\n if (scoreA !== scoreB) {\n return scoreB - scoreA;\n } else {\n return a.data.name.localeCompare(b.data.name);\n }\n });\n\n return [filteredNodes, changedNodes];\n }, [params.node_id, data?.nodes, lineageGraph]);\n\n const [selected, setSelected] = useState<number>(0);\n\n if (isLoading) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n height: \"100%\",\n }}\n >\n Loading...\n </Box>\n );\n } else if (error) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n height: \"100%\",\n }}\n className=\"no-track-pii-safe\"\n >\n Error: {error.message}\n </Box>\n );\n } else if (nodes.length == 0) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n height: \"100%\",\n }}\n >\n No nodes matched\n </Box>\n );\n } else if (selected < nodes.length) {\n const node = nodes[selected];\n return (\n <HSplit sizes={[80, 20]} minSize={30} style={{ height: \"100%\" }}>\n <SchemaView\n base={node.data.data.base}\n current={node.data.data.current}\n columnChanges={node.data.change?.columns}\n enableScreenshot={true}\n showMenu={false}\n ref={ref}\n />\n <List\n sx={{\n overflow: \"auto\",\n bgcolor: \"background.paper\",\n listStyle: \"none\",\n }}\n >\n {nodes.map((node, i) => (\n <NodelistItem\n key={node.id}\n node={node}\n schemaChanged={changedNodes.includes(node.id)}\n selected={i === selected}\n isDark={isDark}\n onSelect={() => {\n setSelected(i);\n }}\n />\n ))}\n </List>\n </HSplit>\n );\n }\n\n // TODO: handle the edge case where the node is not found\n return <></>;\n}\n\nexport const SchemaDiffView = forwardRef(PrivateSchemaDiffView);\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport Divider from \"@mui/material/Divider\";\nimport Grid from \"@mui/material/Grid\";\nimport IconButton from \"@mui/material/IconButton\";\nimport ListItemIcon from \"@mui/material/ListItemIcon\";\nimport ListItemText from \"@mui/material/ListItemText\";\nimport Menu from \"@mui/material/Menu\";\nimport MenuItem from \"@mui/material/MenuItem\";\nimport Stack from \"@mui/material/Stack\";\nimport { useTheme } from \"@mui/material/styles\";\nimport Tab from \"@mui/material/Tab\";\nimport Tabs from \"@mui/material/Tabs\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { stripIndents } from \"common-tags\";\nimport { formatDistanceToNow } from \"date-fns\";\nimport { useRouter } from \"next/navigation\";\nimport React, {\n type MouseEvent,\n ReactNode,\n Ref,\n useCallback,\n useRef,\n useState,\n} from \"react\";\nimport { FaBookmark } from \"react-icons/fa6\";\nimport { IoMdCodeWorking } from \"react-icons/io\";\nimport { IoBookmarksOutline, IoClose } from \"react-icons/io5\";\nimport { PiCheckCircle, PiCopy, PiRepeat, PiTrashFill } from \"react-icons/pi\";\nimport { TbEdit } from \"react-icons/tb\";\nimport { VscCircleLarge, VscKebabVertical } from \"react-icons/vsc\";\nimport {\n type QueryDiffParams,\n type QueryParams,\n type QueryRunParams,\n} from \"../../api/adhocQuery\";\nimport { cacheKeys } from \"../../api/cacheKeys\";\nimport {\n type Check,\n deleteCheck,\n getCheck,\n markAsPresetCheck,\n updateCheck,\n} from \"../../api/checks\";\nimport { cancelRun, submitRunFromCheck } from \"../../api/runs\";\nimport type { Run, RunParamTypes } from \"../../api/types\";\nimport {\n useLineageGraphContext,\n useRecceInstanceContext,\n useRouteConfig,\n} from \"../../contexts\";\nimport {\n useApiConfig,\n useClipBoardToast,\n useCopyToClipboardButton,\n useIsDark,\n useRecceCheckContext,\n useRecceQueryContext,\n useRun,\n} from \"../../hooks\";\nimport { trackCopyToClipboard } from \"../../lib/api/track\";\nimport {\n buildCheckDescription,\n buildCheckTitle,\n CheckBreadcrumb,\n CheckDescription,\n formatSqlAsMarkdown,\n generateCheckTemplate,\n isDisabledByNoResult,\n PresetCheckTemplateView,\n} from \"../../primitives\";\nimport { VSplit } from \"..\";\nimport { SetupConnectionPopover } from \"../app\";\nimport type { LineageViewRef } from \"../lineage/LineageViewOss\";\nimport { DualSqlEditor, SqlEditor } from \"../query\";\nimport {\n findByRunType,\n type IconComponent,\n RefTypes,\n RegistryEntry,\n RunViewOss,\n ViewOptionTypes,\n} from \"../run\";\nimport { toaster } from \"../ui\";\nimport { LineageDiffViewOss as LineageDiffView } from \"./LineageDiffViewOss\";\nimport { SchemaDiffView } from \"./SchemaDiffView\";\nimport { CheckTimelineOss as CheckTimeline } from \"./timeline/CheckTimelineOss\";\n\ninterface CheckDetailProps {\n checkId: string;\n refreshCheckList?: () => void;\n}\n\ntype TabValueList = \"result\" | \"query\";\n\nexport function CheckDetailOss({\n checkId,\n refreshCheckList,\n}: CheckDetailProps): ReactNode {\n const theme = useTheme();\n const isDark = useIsDark();\n const { apiClient } = useApiConfig();\n const { featureToggles, sessionId } = useRecceInstanceContext();\n const { setLatestSelectedCheckId } = useRecceCheckContext();\n const { cloudMode } = useLineageGraphContext();\n const queryClient = useQueryClient();\n const router = useRouter();\n const { basePath } = useRouteConfig();\n const { setSqlQuery, setBaseSqlQuery, setCustomQueries, setPrimaryKeys } =\n useRecceQueryContext();\n const { successToast, failToast } = useClipBoardToast();\n const [submittedRunId, setSubmittedRunId] = useState<string>();\n const [progress] = useState<Run[\"progress\"]>();\n const [isAborting, setAborting] = useState(false);\n const [isPresetCheckTemplateOpen, setIsPresetCheckTemplateOpen] =\n useState(false);\n const [menuAnchorEl, setMenuAnchorEl] = useState<HTMLElement | null>(null);\n const menuOpen = Boolean(menuAnchorEl);\n\n const {\n isLoading,\n error,\n data: check,\n } = useQuery({\n queryKey: cacheKeys.check(checkId),\n queryFn: async () => getCheck(checkId, apiClient),\n refetchOnMount: true,\n });\n\n const trackedRunId = submittedRunId ?? check?.last_run?.run_id;\n const { run, error: rerunError } = useRun(trackedRunId);\n const isRunning = submittedRunId\n ? !run || run.status === \"Running\"\n : run?.status === \"Running\";\n\n const runTypeEntry = check?.type ? findByRunType(check.type) : undefined;\n\n let RunResultView: RegistryEntry[\"RunResultView\"] | undefined;\n if (runTypeEntry) {\n RunResultView =\n runTypeEntry.RunResultView as RegistryEntry[\"RunResultView\"];\n }\n\n const isPresetCheck = check?.is_preset ?? false;\n\n const lineageViewRef = useRef<LineageViewRef>(null);\n\n const { mutate } = useMutation({\n mutationFn: (check: Partial<Check>) =>\n updateCheck(checkId, check, apiClient),\n onSuccess: async () => {\n await queryClient.invalidateQueries({\n queryKey: cacheKeys.check(checkId),\n });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n },\n });\n\n const { mutate: handleDelete } = useMutation({\n mutationFn: () => deleteCheck(checkId, apiClient),\n onSuccess: async () => {\n setLatestSelectedCheckId(\"\");\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n router.push(`${basePath}/checks`);\n },\n });\n\n const { mutate: handleMarkAsPresetCheck, isPending: isMarkingAsPreset } =\n useMutation({\n mutationFn: async () => {\n if (!check) {\n throw new Error(\"Check not found\");\n }\n\n return await markAsPresetCheck(checkId, apiClient);\n },\n onSuccess: async () => {\n successToast(\"Check marked as preset successfully\");\n // Invalidate queries to refresh the check data\n await queryClient.invalidateQueries({\n queryKey: cacheKeys.check(checkId),\n });\n },\n onError: (error) => {\n failToast(\"Failed to mark check as preset\", error);\n },\n });\n\n const handleRerun = useCallback(async () => {\n const type = check?.type;\n if (!type) {\n return;\n }\n\n const submittedRun = await submitRunFromCheck(\n checkId,\n { nowait: true },\n apiClient,\n );\n setSubmittedRunId(submittedRun.run_id);\n await queryClient.invalidateQueries({ queryKey: cacheKeys.check(checkId) });\n if (refreshCheckList) refreshCheckList(); // refresh the checklist to fetch correct last run status\n }, [check, checkId, queryClient, refreshCheckList, apiClient]);\n\n const handleCancel = useCallback(async () => {\n setAborting(true);\n if (!trackedRunId) {\n return;\n }\n\n return await cancelRun(trackedRunId, apiClient);\n }, [trackedRunId, apiClient]);\n\n const handleCopy = async () => {\n if (!check) {\n return;\n }\n\n // Cast to Check<RunParamTypes> since we know the check params are valid\n const markdown = buildMarkdown(check as Check<RunParamTypes>);\n // @see https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts\n if (!window.isSecureContext) {\n failToast(\n \"Failed to copy the check to clipboard\",\n new Error(\n \"Copy to clipboard is available only in secure contexts (HTTPS)\",\n ),\n );\n return;\n }\n\n try {\n await navigator.clipboard.writeText(markdown);\n successToast(\"Copied the check to the clipboard\");\n } catch (err) {\n failToast(\"Failed to copy the check to clipboard\", err);\n }\n };\n\n const handleApproveCheck = useCallback(() => {\n const isChecked = check?.is_checked;\n mutate({ is_checked: !isChecked });\n if (!isChecked) {\n toaster.create({\n title: \"Marked as approved\",\n type: \"success\",\n duration: 2000,\n });\n }\n }, [check?.is_checked, mutate]);\n\n const handelUpdateViewOptions = (viewOptions: ViewOptionTypes) => {\n mutate({ view_options: viewOptions });\n };\n\n const handleUpdateDescription = (description?: string) => {\n mutate({ description });\n };\n\n const handleMenuClick = (event: MouseEvent<HTMLButtonElement>) => {\n setMenuAnchorEl(event.currentTarget);\n };\n\n const handleMenuClose = () => {\n setMenuAnchorEl(null);\n };\n\n const handleOpenQuery = useCallback(() => {\n if (!check) return;\n\n const params = check.params as QueryParams | QueryDiffParams;\n const sqlTemplate = params?.sql_template || \"\";\n\n // Set current SQL\n setSqlQuery(sqlTemplate);\n\n // Handle query_diff with custom queries (dual mode)\n if (\"base_sql_template\" in params && params.base_sql_template) {\n setBaseSqlQuery(params.base_sql_template);\n setCustomQueries(true);\n } else {\n setBaseSqlQuery(\"\"); // Clear stale base SQL from previous checks\n setCustomQueries(false);\n }\n\n // Set primary keys if available, otherwise clear stale primary keys\n if (\"primary_keys\" in params && params.primary_keys) {\n setPrimaryKeys(params.primary_keys);\n } else {\n setPrimaryKeys(undefined);\n }\n\n // Navigate to query page\n router.push(`${basePath}/query`);\n }, [\n check,\n router,\n basePath,\n setSqlQuery,\n setBaseSqlQuery,\n setCustomQueries,\n setPrimaryKeys,\n ]);\n\n const [tabValue, setTabValue] = useState<TabValueList>(\"result\");\n const { ref, onCopyToClipboard, onMouseEnter, onMouseLeave } =\n useCopyToClipboardButton();\n\n // Calculate during render instead of effect\n const presetCheckTemplate = generateCheckTemplate({\n name: check?.name ?? \"\",\n description: check?.description ?? \"\",\n type: check?.type ?? \"\",\n params: check?.params as Record<string, unknown>,\n viewOptions: check?.view_options as Record<string, unknown>,\n });\n\n if (isLoading) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n Loading\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100%\",\n }}\n >\n Error: <span className=\"no-track-pii-safe\">{error.message}</span>\n </Box>\n );\n }\n\n if (!check) {\n return (\n <VSplit\n minSize={100}\n sizes={[30, 70]}\n style={{ height: \"100%\", width: \"100%\", maxHeight: \"100%\" }}\n >\n <Box\n style={{ contain: \"strict\" }}\n sx={{ display: \"flex\", flexDirection: \"column\" }}\n >\n <Box\n sx={{\n display: \"flex\",\n p: \"0px 16px\",\n alignItems: \"center\",\n height: 40,\n }}\n >\n <CheckBreadcrumb name=\"Check not found\" disabled />\n </Box>\n </Box>\n </VSplit>\n );\n }\n\n const relativeTime = run?.run_at\n ? formatDistanceToNow(new Date(run.run_at), { addSuffix: true })\n : null;\n\n // Get the icon for the check type\n const checkTypeIcon: IconComponent | undefined = runTypeEntry?.icon;\n\n return (\n <Grid\n spacing={0}\n container\n sx={{\n height: \"100%\",\n width: \"100%\",\n }}\n >\n <Grid size={{ xs: 12, md: cloudMode ? 9 : 12 }} sx={{ height: \"100%\" }}>\n <VSplit\n minSize={100}\n sizes={[40, 60]}\n style={{ height: \"100%\", width: \"100%\", maxHeight: \"100%\" }}\n >\n <Box\n sx={{\n height: \"100%\",\n contain: \"strict\",\n display: \"flex\",\n flexDirection: \"row\",\n }}\n >\n {/* Main content area - takes remaining space */}\n <Box\n sx={{\n flex: 1,\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n minWidth: 0,\n }}\n >\n {/* Title bar with icon, name, and actions */}\n <Box\n sx={{\n display: \"flex\",\n p: \"0px 16px\",\n alignItems: \"center\",\n height: 40,\n borderBottom: \"2px solid\",\n borderColor: isDark ? \"grey.700\" : \"grey.300\",\n }}\n >\n {/* Check type icon */}\n {checkTypeIcon && (\n <Box\n component={checkTypeIcon}\n sx={{ fontSize: 20, mr: 1, flexShrink: 0 }}\n />\n )}\n <CheckBreadcrumb\n name={check.name}\n onNameChange={(name) => {\n mutate({ name });\n }}\n />\n <Box sx={{ flexGrow: 1 }} />\n <Stack direction=\"row\" spacing={2} sx={{ mr: \"10px\" }}>\n {relativeTime && (\n <Box\n sx={{\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n fontSize: \"0.75rem\",\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n {relativeTime}\n </Box>\n )}\n\n {/* Preset label moved to the right */}\n {isPresetCheck && (\n <MuiTooltip title=\"This is a preset check\">\n <Box\n display=\"flex\"\n alignItems=\"center\"\n justifyContent=\"center\"\n >\n <FaBookmark\n size=\"1rem\"\n color={theme.palette.iochmara.dark}\n />\n </Box>\n </MuiTooltip>\n )}\n\n <IconButton size=\"small\" onClick={handleMenuClick}>\n <VscKebabVertical />\n </IconButton>\n <Menu\n anchorEl={menuAnchorEl}\n open={menuOpen}\n onClose={handleMenuClose}\n >\n {sessionId && (\n <MenuItem\n onClick={() => {\n handleMarkAsPresetCheck();\n handleMenuClose();\n }}\n disabled={isMarkingAsPreset || isPresetCheck}\n >\n <ListItemIcon>\n <IoBookmarksOutline />\n </ListItemIcon>\n <ListItemText>Mark as Preset Check</ListItemText>\n </MenuItem>\n )}\n <MenuItem\n onClick={() => {\n setIsPresetCheckTemplateOpen(true);\n handleMenuClose();\n }}\n >\n <ListItemIcon>\n <IoMdCodeWorking />\n </ListItemIcon>\n <ListItemText>Get Preset Check Template</ListItemText>\n </MenuItem>\n <MenuItem\n onClick={() => {\n handleCopy();\n handleMenuClose();\n }}\n >\n <ListItemIcon>\n <PiCopy />\n </ListItemIcon>\n <ListItemText>Copy Markdown</ListItemText>\n </MenuItem>\n <Divider />\n <MenuItem\n onClick={() => {\n handleDelete();\n handleMenuClose();\n }}\n disabled={featureToggles.disableUpdateChecklist}\n sx={{ color: \"error.main\" }}\n >\n <ListItemIcon sx={{ color: \"error.main\" }}>\n <PiTrashFill />\n </ListItemIcon>\n <ListItemText>Delete</ListItemText>\n </MenuItem>\n </Menu>\n\n <MuiTooltip\n title={\n isDisabledByNoResult({\n type: check.type,\n hasResult: !!run?.result,\n hasError: !!run?.error,\n })\n ? \"Run the check first\"\n : check.is_checked\n ? \"Remove approval\"\n : \"Mark as approved\"\n }\n placement=\"bottom-end\"\n >\n <Button\n size=\"small\"\n color={check.is_checked ? \"success\" : \"neutral\"}\n variant={check.is_checked ? \"contained\" : \"outlined\"}\n onClick={() => {\n handleApproveCheck();\n }}\n disabled={\n isDisabledByNoResult({\n type: check.type,\n hasResult: !!run?.result,\n hasError: !!run?.error,\n }) || featureToggles.disableUpdateChecklist\n }\n startIcon={\n check.is_checked ? (\n <PiCheckCircle />\n ) : (\n <VscCircleLarge\n style={{\n color: isDark\n ? theme.palette.grey[600]\n : theme.palette.grey[400],\n }}\n />\n )\n }\n sx={{ flex: \"0 0 auto\", textTransform: \"none\" }}\n >\n {check.is_checked ? \"Approved\" : \"Pending\"}\n </Button>\n </MuiTooltip>\n </Stack>\n </Box>\n\n {/* Description area */}\n <Box sx={{ flex: 1, p: \"8px 16px\", minHeight: 100 }}>\n <CheckDescription\n key={check.check_id}\n value={check.description}\n onChange={handleUpdateDescription}\n disabled={featureToggles.disableUpdateChecklist}\n />\n </Box>\n </Box>\n </Box>\n <Box style={{ contain: \"strict\" }}>\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n }}\n >\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n borderBottom: 1,\n borderColor: \"divider\",\n height: 50,\n }}\n >\n <Tabs\n value={tabValue}\n onChange={(_, newValue) =>\n setTabValue(newValue as TabValueList)\n }\n >\n <Tab\n label=\"Result\"\n value=\"result\"\n sx={{ fontSize: \"0.75rem\", textTransform: \"none\" }}\n />\n {(check.type === \"query\" || check.type === \"query_diff\") && (\n <Tab\n label=\"Query\"\n value=\"query\"\n sx={{ fontSize: \"0.75rem\", textTransform: \"none\" }}\n />\n )}\n </Tabs>\n <Box sx={{ flexGrow: 1 }} />\n <Stack direction=\"row\" spacing={1} sx={{ mr: \"10px\" }}>\n {(check.type === \"query\" ||\n check.type === \"query_base\" ||\n check.type === \"query_diff\") && (\n <MuiTooltip title=\"Open in Query tab\">\n <Button\n variant=\"outlined\"\n color=\"neutral\"\n size=\"small\"\n onClick={handleOpenQuery}\n disabled={featureToggles.disableDatabaseQuery}\n startIcon={<TbEdit />}\n sx={{ textTransform: \"none\" }}\n >\n Open Query\n </Button>\n </MuiTooltip>\n )}\n {RunResultView && (\n <MuiTooltip title=\"Rerun\">\n <Button\n variant=\"outlined\"\n color=\"neutral\"\n size=\"small\"\n onClick={() => handleRerun()}\n disabled={\n featureToggles.disableDatabaseQuery || isRunning\n }\n startIcon={<PiRepeat />}\n sx={{ textTransform: \"none\" }}\n >\n {isRunning ? \"Running...\" : \"Rerun\"}\n </Button>\n </MuiTooltip>\n )}\n <Button\n variant=\"outlined\"\n color=\"neutral\"\n disabled={\n isDisabledByNoResult({\n type: check.type,\n hasResult: !!run?.result,\n hasError: !!run?.error,\n }) || tabValue !== \"result\"\n }\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n size=\"small\"\n onClick={async () => {\n if (check.type === \"lineage_diff\") {\n lineageViewRef.current?.copyToClipboard();\n } else {\n await onCopyToClipboard();\n }\n trackCopyToClipboard({ type: check.type, from: \"check\" });\n }}\n startIcon={<PiCopy />}\n sx={{ textTransform: \"none\" }}\n >\n Copy to Clipboard\n </Button>\n </Stack>\n </Box>\n <Box sx={{ flex: 1, contain: \"strict\" }}>\n {tabValue === \"result\" && (\n <Box sx={{ width: \"100%\", height: \"100%\" }}>\n {RunResultView &&\n (check.last_run || trackedRunId ? (\n <RunViewOss\n ref={ref as unknown as Ref<RefTypes>}\n isRunning={isRunning}\n isAborting={isAborting}\n run={\n trackedRunId\n ? run\n : // Cast from library Run to OSS Run\n (check.last_run as Run | undefined)\n }\n error={rerunError}\n progress={progress}\n RunResultView={RunResultView}\n viewOptions={check.view_options as ViewOptionTypes}\n onViewOptionsChanged={handelUpdateViewOptions}\n onCancel={handleCancel}\n onExecuteRun={handleRerun}\n />\n ) : (\n <Box\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n bgcolor: isDark ? \"grey.900\" : \"grey.50\",\n height: \"100%\",\n }}\n >\n <Stack spacing={2} alignItems=\"center\">\n <Box>\n This action is part of the initial preset and has\n not been performed yet. Once performed, the result\n will be shown here.\n </Box>\n <SetupConnectionPopover\n display={featureToggles.mode === \"metadata only\"}\n >\n <Button\n onClick={handleRerun}\n variant=\"contained\"\n size=\"small\"\n disabled={featureToggles.disableDatabaseQuery}\n >\n Run Query\n </Button>\n </SetupConnectionPopover>\n </Stack>\n </Box>\n ))}\n {check.type === \"schema_diff\" && (\n <SchemaDiffView\n key={check.check_id}\n check={check}\n ref={ref}\n />\n )}\n {check.type === \"lineage_diff\" && (\n <LineageDiffView\n key={check.check_id}\n check={check}\n ref={lineageViewRef}\n />\n )}\n </Box>\n )}\n {tabValue === \"query\" &&\n (check.type === \"query\" ||\n check.type === \"query_diff\" ||\n check.type === \"query_base\") && (\n <Box sx={{ height: \"100%\", width: \"100%\" }}>\n {(check.params as QueryParams).base_sql_template ? (\n <DualSqlEditor\n value={\n (check.params as QueryDiffParams).sql_template || \"\"\n }\n baseValue={\n (check.params as QueryDiffParams)\n .base_sql_template ?? \"\"\n }\n options={{ readOnly: true }}\n />\n ) : (\n <SqlEditor\n value={\n (check.params as QueryRunParams).sql_template || \"\"\n }\n options={{ readOnly: true }}\n />\n )}\n </Box>\n )}\n </Box>\n </Box>\n </Box>\n <MuiDialog\n open={isPresetCheckTemplateOpen}\n onClose={() => setIsPresetCheckTemplateOpen(false)}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Preset Check Template</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle2\" fontWeight=\"bold\" sx={{ mb: 2 }}>\n Please{\" \"}\n <Typography\n component=\"span\"\n sx={{\n cursor: \"pointer\",\n \"&:hover\": { textDecoration: \"underline\" },\n color: \"primary.main\",\n }}\n onClick={async () => {\n await navigator.clipboard.writeText(presetCheckTemplate);\n successToast(\"Copied the template to the clipboard\");\n }}\n >\n copy\n </Typography>{\" \"}\n the following template and paste it into the{\" \"}\n <Box\n component=\"span\"\n sx={{ px: 0.5, bgcolor: \"error.light\", borderRadius: 0.5 }}\n >\n recce.yml\n </Box>{\" \"}\n file.\n </Typography>\n <PresetCheckTemplateView yamlTemplate={presetCheckTemplate} />\n </DialogContent>\n <DialogActions>\n <IconButton\n size=\"small\"\n onClick={() => setIsPresetCheckTemplateOpen(false)}\n sx={{ position: \"absolute\", top: 8, right: 8 }}\n >\n <IoClose />\n </IconButton>\n </DialogActions>\n </MuiDialog>\n </VSplit>\n </Grid>\n {/* Timeline/Activity panel - fixed 20% width, hidden on mobile */}\n {cloudMode && (\n <Grid\n size={3}\n sx={{\n height: \"100%\",\n overflow: \"hidden\",\n flexShrink: 0,\n display: { xs: \"none\", md: \"block\" },\n }}\n >\n <CheckTimeline checkId={checkId} />\n </Grid>\n )}\n </Grid>\n );\n}\n\nfunction buildMarkdown(check: Check<RunParamTypes>) {\n const title = buildCheckTitle({\n name: check.name,\n isChecked: check.is_checked,\n });\n return stripIndents`\n <details><summary>${title}</summary>\n\n ${buildBody(check)}\n\n </details>`;\n}\n\nfunction buildBody(check: Check<RunParamTypes>) {\n const description = buildCheckDescription({ description: check.description });\n if (check.type === \"query\" || check.type === \"query_diff\") {\n const params = check.params;\n const sqlTemplate =\n params && \"sql_template\" in params ? params.sql_template : \"\";\n return `${description}\\n\\n${formatSqlAsMarkdown({ sql: sqlTemplate })}`;\n }\n\n return description;\n}\n","\"use client\";\n\n/**\n * CheckEmptyStateOss - wrapper around CheckEmptyState primitive\n *\n * Adds business logic:\n * - API calls to create schema diff check\n * - Navigation after check creation\n * - Query cache invalidation\n */\n\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useRouter } from \"next/navigation\";\nimport { TbChecklist } from \"react-icons/tb\";\nimport { type Check, cacheKeys, createSchemaDiffCheck } from \"../../api\";\nimport { useRouteConfig } from \"../../contexts\";\nimport { useApiConfig } from \"../../hooks\";\nimport { CheckEmptyState as CheckEmptyStateUI } from \"../../primitives\";\n\nexport const CheckEmptyStateOss = () => {\n const queryClient = useQueryClient();\n const router = useRouter();\n const { apiClient } = useApiConfig();\n const { basePath } = useRouteConfig();\n\n const { mutate: createSchemaCheck, isPending } = useMutation({\n mutationFn: () =>\n createSchemaDiffCheck({ select: \"state:modified\" }, apiClient),\n onSuccess: async (check: Check) => {\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n router.push(`${basePath}/checks/?id=${check.check_id}`);\n },\n });\n\n const handleCreateSchemaCheck = () => {\n createSchemaCheck();\n };\n\n return (\n <CheckEmptyStateUI\n title=\"No checks yet\"\n description=\"Checks help you validate data quality and catch issues.\"\n icon={<TbChecklist size={48} />}\n actionText=\"Create Schema Diff Check\"\n onAction={handleCreateSchemaCheck}\n isLoading={isPending}\n helperText=\"The schema checks compare modified models between environments to detect changes, additions, or removals.\"\n />\n );\n};\n","\"use client\";\n\nimport {\n DragDropContext,\n Draggable,\n Droppable,\n type DropResult,\n} from \"@hello-pangea/dnd\";\nimport Box from \"@mui/material/Box\";\nimport Button from \"@mui/material/Button\";\nimport Checkbox from \"@mui/material/Checkbox\";\nimport MuiDialog from \"@mui/material/Dialog\";\nimport DialogActions from \"@mui/material/DialogActions\";\nimport DialogContent from \"@mui/material/DialogContent\";\nimport DialogTitle from \"@mui/material/DialogTitle\";\nimport Divider from \"@mui/material/Divider\";\nimport FormControlLabel from \"@mui/material/FormControlLabel\";\nimport IconButton from \"@mui/material/IconButton\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useState } from \"react\";\nimport { IoClose } from \"react-icons/io5\";\nimport { cacheKeys } from \"../../api/cacheKeys\";\nimport { type Check, updateCheck } from \"../../api/checks\";\nimport { useRecceInstanceContext } from \"../../contexts\";\nimport { useApiConfig, useRun } from \"../../hooks\";\nimport { toaster } from \"../ui\";\nimport {\n CheckCard,\n type CheckCardData,\n type CheckRunStatus,\n type CheckType,\n} from \"./CheckCard\";\nimport { isDisabledByNoResult } from \"./utils\";\n\n/**\n * Wrapper component that adapts Check data to CheckCard props.\n * Handles run data fetching and maps to UI primitive expectations.\n */\nconst ChecklistItem = ({\n check,\n selected,\n onSelect,\n onApprovalChange,\n}: {\n check: Check;\n selected: boolean;\n onSelect: (checkId: string) => void;\n onApprovalChange: (checkId: string, isApproved: boolean) => void;\n}) => {\n const { featureToggles } = useRecceInstanceContext();\n const trackedRunId = check.last_run?.run_id;\n const { run } = useRun(trackedRunId);\n\n const isNoResult = isDisabledByNoResult({\n type: check.type,\n hasResult: !!run?.result,\n hasError: !!run?.error,\n });\n\n const isApprovalDisabled =\n isNoResult || featureToggles.disableUpdateChecklist;\n\n // Map run status if available\n const getRunStatus = (): CheckRunStatus | undefined => {\n if (!run) return undefined;\n if (run.error) return \"error\";\n if (run.result) return \"success\";\n return undefined;\n };\n\n // Adapt Check to CheckCardData\n const checkCardData: CheckCardData = {\n id: check.check_id,\n name: check.name,\n type: check.type as CheckType,\n isApproved: check.is_checked,\n runStatus: getRunStatus(),\n isPreset: check.is_preset,\n };\n\n return (\n <CheckCard\n check={checkCardData}\n isSelected={selected}\n onClick={onSelect}\n onApprovalChange={onApprovalChange}\n disableApproval={isApprovalDisabled}\n disabledApprovalTooltip={isNoResult ? \"Run the check first\" : undefined}\n />\n );\n};\n\nexport const CheckListOss = ({\n checks,\n selectedItem,\n onCheckSelected,\n onChecksReordered,\n}: {\n checks: Check[];\n selectedItem: string | null;\n onCheckSelected: (checkId: string) => void;\n onChecksReordered: (source: number, destination: number) => void;\n}) => {\n const [bypassModal, setBypassModal] = useState(false);\n const [open, setOpen] = useState(false);\n const [pendingApprovalCheckId, setPendingApprovalCheckId] = useState<\n string | null\n >(null);\n const queryClient = useQueryClient();\n const { apiClient } = useApiConfig();\n\n // Mutation for updating check approval status\n const { mutate: updateApproval } = useMutation({\n mutationFn: ({\n checkId,\n isChecked,\n }: {\n checkId: string;\n isChecked: boolean;\n }) => updateCheck(checkId, { is_checked: isChecked }, apiClient),\n onSuccess: async (_, { checkId }) => {\n await queryClient.invalidateQueries({\n queryKey: cacheKeys.check(checkId),\n });\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n },\n });\n\n const onDragEnd = (result: DropResult) => {\n if (!result.destination) {\n return;\n }\n\n onChecksReordered(result.source.index, result.destination.index);\n };\n\n const handleOpen = () => setOpen(true);\n const handleClose = () => {\n setOpen(false);\n setPendingApprovalCheckId(null);\n };\n\n const showApprovedToast = () => {\n toaster.create({\n title: \"Marked as approved\",\n type: \"success\",\n duration: 2000,\n });\n };\n\n /**\n * Handle approval change from CheckCard.\n * If approving (true), show modal or bypass based on localStorage.\n * If unapproving (false), update directly.\n */\n const handleApprovalChange = (checkId: string, isApproved: boolean) => {\n if (!isApproved) {\n // Unapproving - update directly\n updateApproval({ checkId, isChecked: false });\n } else {\n // Approving - check for bypass\n const bypassMarkAsApprovedWarning = localStorage.getItem(\n \"bypassMarkAsApprovedWarning\",\n );\n if (bypassMarkAsApprovedWarning === \"true\") {\n updateApproval({ checkId, isChecked: true });\n showApprovedToast();\n } else {\n setPendingApprovalCheckId(checkId);\n handleOpen();\n }\n }\n };\n\n const handleMarkAsApprovedConfirmed = () => {\n if (pendingApprovalCheckId) {\n updateApproval({ checkId: pendingApprovalCheckId, isChecked: true });\n if (bypassModal) {\n localStorage.setItem(\"bypassMarkAsApprovedWarning\", \"true\");\n }\n showApprovedToast();\n handleClose();\n setPendingApprovalCheckId(null);\n }\n };\n\n return (\n <>\n <DragDropContext onDragEnd={onDragEnd}>\n <Droppable droppableId=\"checklist\">\n {(provided) => (\n <Stack\n {...provided.droppableProps}\n ref={provided.innerRef}\n className=\"no-track-pii-safe\"\n sx={{\n width: \"100%\",\n flex: 1,\n overflow: \"auto\",\n }}\n spacing={0}\n >\n {checks.map((check, index) => (\n <Draggable\n key={check.check_id}\n draggableId={check.check_id}\n index={index}\n >\n {(provided, snapshot) => {\n // see https://github.com/atlassian/react-beautiful-dnd/issues/1881#issuecomment-691237307\n // Create a new style object instead of mutating the read-only one\n let style = provided.draggableProps.style;\n if (snapshot.isDragging && style && \"left\" in style) {\n const offset = { x: 0, y: 80 };\n style = {\n ...style,\n left: (style.left as number) - offset.x,\n top: (style.top as number) - offset.y,\n };\n }\n\n return (\n <Box\n ref={provided.innerRef}\n {...provided.draggableProps}\n {...provided.dragHandleProps}\n style={style}\n sx={{ width: \"100%\" }}\n borderBottom=\"1px solid\"\n borderColor=\"divider\"\n >\n <ChecklistItem\n key={check.check_id}\n check={check}\n selected={check.check_id === selectedItem}\n onSelect={onCheckSelected}\n onApprovalChange={handleApprovalChange}\n />\n </Box>\n );\n }}\n </Draggable>\n ))}\n {provided.placeholder}\n </Stack>\n )}\n </Droppable>\n </DragDropContext>\n <MuiDialog open={open} onClose={handleClose} maxWidth=\"xs\" fullWidth>\n <DialogTitle sx={{ display: \"flex\", alignItems: \"center\" }}>\n Mark as Approved?\n <Box sx={{ flexGrow: 1 }} />\n <IconButton size=\"small\" onClick={handleClose}>\n <IoClose />\n </IconButton>\n </DialogTitle>\n <Divider />\n <DialogContent sx={{ fontSize: \"0.875rem\" }}>\n <Typography>\n Please ensure you have reviewed the contents of this check before\n marking it as approved.\n </Typography>\n <FormControlLabel\n control={\n <Checkbox\n checked={bypassModal}\n onChange={(e) => {\n setBypassModal(e.target.checked);\n }}\n size=\"small\"\n />\n }\n label={\n <Typography sx={{ fontWeight: \"bold\", pt: \"8px\" }}>\n Don't show this again\n </Typography>\n }\n />\n </DialogContent>\n <Divider />\n <DialogActions sx={{ gap: 0 }}>\n <Button variant=\"outlined\" size=\"small\" onClick={handleClose}>\n Cancel\n </Button>\n <Button\n color=\"iochmara\"\n variant=\"contained\"\n size=\"small\"\n onClick={handleMarkAsApprovedConfirmed}\n >\n Mark as approved\n </Button>\n </DialogActions>\n </MuiDialog>\n </>\n );\n};\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Divider from \"@mui/material/Divider\";\nimport Stack from \"@mui/material/Stack\";\nimport { useTheme } from \"@mui/material/styles\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useRouter, useSearchParams } from \"next/navigation\";\nimport React, {\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from \"react\";\nimport { cacheKeys, listChecks, reorderChecks } from \"../../api\";\nimport { useRouteConfig } from \"../../contexts\";\nimport { useApiConfig, useRecceCheckContext } from \"../../hooks\";\nimport { StateImporter } from \"../app\";\nimport { HSplit } from \"../ui\";\nimport { CheckDetailOss as CheckDetail } from \"./CheckDetailOss\";\nimport { CheckEmptyStateOss as CheckEmptyState } from \"./CheckEmptyStateOss\";\nimport { CheckListOss as CheckList } from \"./CheckListOss\";\n\nexport const CheckPageContentOss = (): ReactNode => {\n const theme = useTheme();\n const isDark = theme.palette.mode === \"dark\";\n const borderColor = isDark ? \"grey.700\" : \"grey.300\";\n const router = useRouter();\n const searchParams = useSearchParams();\n const checkId = searchParams.get(\"id\");\n const { latestSelectedCheckId, setLatestSelectedCheckId } =\n useRecceCheckContext();\n const queryClient = useQueryClient();\n const { apiClient } = useApiConfig();\n const { basePath } = useRouteConfig();\n const selectedItem = checkId;\n\n useEffect(() => {\n if (selectedItem) {\n setLatestSelectedCheckId(selectedItem);\n }\n }, [selectedItem, setLatestSelectedCheckId]);\n\n const {\n isLoading,\n error,\n data: checks,\n status,\n refetch: refetchCheckList,\n } = useQuery({\n queryKey: cacheKeys.checks(),\n queryFn: () => listChecks(apiClient),\n refetchOnMount: true,\n });\n\n const handleSelectItem = useCallback(\n (checkId: string) => {\n router.push(`${basePath}/checks/?id=${checkId}`);\n },\n [router.push, basePath],\n );\n\n const [orderedChecks, setOrderedChecks] = useState(checks ?? []);\n const [prevChecks, setPrevChecks] = useState(checks);\n\n // Sync orderedChecks with checks when checks data changes (during render)\n if (checks !== prevChecks) {\n setPrevChecks(checks);\n setOrderedChecks(checks ?? []);\n }\n\n const { mutate: changeChecksOrder } = useMutation({\n mutationFn: (order: { source: number; destination: number }) =>\n reorderChecks(order, apiClient),\n onSuccess: async () => {\n await queryClient.invalidateQueries({ queryKey: cacheKeys.checks() });\n },\n });\n\n const handleDragEnd = useCallback(\n (source: number, destination: number) => {\n const updatedItems = [...orderedChecks];\n const [reorderedItem] = updatedItems.splice(source, 1);\n updatedItems.splice(destination, 0, reorderedItem);\n\n changeChecksOrder({\n source,\n destination,\n });\n\n setOrderedChecks(updatedItems);\n },\n [orderedChecks, changeChecksOrder],\n );\n\n // Memoized validation to avoid duplicate checks.some() calls\n const isValidSelection = useMemo(\n () =>\n Boolean(\n selectedItem &&\n checks?.some((check) => check.check_id === selectedItem),\n ),\n [selectedItem, checks],\n );\n\n useEffect(() => {\n if (status !== \"success\" || checks.length === 0) {\n return;\n }\n\n if (!isValidSelection) {\n // No selection or invalid selection - redirect to a valid check\n // First try latestSelectedCheckId if it's valid\n const isValidLatestSelectedCheckId =\n latestSelectedCheckId &&\n checks.some((check) => check.check_id === latestSelectedCheckId);\n\n if (isValidLatestSelectedCheckId) {\n router.replace(`${basePath}/checks/?id=${latestSelectedCheckId}`);\n } else {\n // Fall back to the first check\n router.replace(`${basePath}/checks/?id=${checks[0].check_id}`);\n }\n }\n }, [\n status,\n isValidSelection,\n checks,\n latestSelectedCheckId, // Fall back to the first check\n router.replace,\n basePath,\n ]);\n\n if (isLoading) {\n return null;\n }\n\n if (error) {\n return (\n <Box>\n Error: <span className=\"no-track-pii-safe\">{error.message}</span>\n </Box>\n );\n }\n\n if (!checks?.length) {\n return (\n <HSplit style={{ height: \"100%\" }} minSize={50} sizes={[20, 80]}>\n <Box\n sx={{\n borderRight: \"1px solid\",\n borderRightColor: borderColor,\n height: \"100%\",\n }}\n style={{ contain: \"size\" }}\n >\n <Stack\n sx={{ height: \"100%\", alignItems: \"stretch\" }}\n style={{ contain: \"strict\" }}\n spacing={0}\n >\n <Box\n sx={{\n display: \"flex\",\n justifyContent: \"flex-end\",\n p: \"0px 10px\",\n }}\n >\n <StateImporter checksOnly />\n </Box>\n <Divider />\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <Box sx={{ textAlign: \"center\", color: \"grey.500\" }}>\n No checks\n </Box>\n </Box>\n </Stack>\n </Box>\n <Box>\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <CheckEmptyState />\n </Box>\n </Box>\n </HSplit>\n );\n }\n\n return (\n <HSplit style={{ height: \"100%\" }} minSize={50} sizes={[20, 80]}>\n <Box sx={{ height: \"100%\" }} style={{ contain: \"size\" }}>\n <Stack\n sx={{ height: \"100%\", alignItems: \"stretch\" }}\n style={{ contain: \"strict\" }}\n spacing={0}\n >\n <Box\n sx={{ display: \"flex\", justifyContent: \"flex-end\", p: \"0px 10px\" }}\n >\n <StateImporter checksOnly />\n </Box>\n <Divider />\n <CheckList\n checks={orderedChecks}\n selectedItem={selectedItem}\n onCheckSelected={handleSelectItem}\n onChecksReordered={handleDragEnd}\n />\n </Stack>\n </Box>\n <Box sx={{ height: \"100%\" }}>\n {/* isValidSelection already checks selectedItem, but TS needs explicit check for type narrowing */}\n {isValidSelection && selectedItem && (\n <CheckDetail\n key={selectedItem}\n checkId={selectedItem}\n refreshCheckList={refetchCheckList}\n />\n )}\n </Box>\n </HSplit>\n );\n};\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport { useTheme } from \"@mui/material/styles\";\nimport React, { ReactNode } from \"react\";\nimport { HSplit } from \"../ui\";\n\n/**\n * Loading fallback - shows minimal UI while search params are being read\n */\nexport const CheckPageLoadingOss = (): ReactNode => {\n const theme = useTheme();\n const isDark = theme.palette.mode === \"dark\";\n const borderColor = isDark ? \"grey.700\" : \"grey.300\";\n\n return (\n <HSplit style={{ height: \"100%\" }} minSize={50} sizes={[20, 80]}>\n <Box\n sx={{\n borderRight: \"1px solid\",\n borderRightColor: borderColor,\n height: \"100%\",\n }}\n style={{ contain: \"size\" }}\n >\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <CircularProgress size={20} />\n </Box>\n </Box>\n <Box>\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n }}\n >\n <CircularProgress size={20} />\n </Box>\n </Box>\n </HSplit>\n );\n};\n","/**\n * @file types.ts\n * @description Type definitions for change summary components\n *\n * These types define the various change statuses that can occur in a\n * lineage graph, including node-level changes (added, removed, modified)\n * and column-level changes.\n */\n\nimport type { LineageGraph } from \"../../types\";\n\n/**\n * Change status for nodes and columns in a lineage graph\n *\n * Node changes:\n * - added: New resource added to the graph\n * - removed: Resource removed from the graph\n * - modified: Resource code was modified\n *\n * Column changes:\n * - col_added: New column added to a node\n * - col_removed: Column removed from a node\n * - col_changed: Column type or definition changed\n *\n * Folder changes:\n * - folder_changed: Files in a folder were modified\n *\n * null: No change detected\n */\nexport type ChangeStatus =\n // node change (code change - user edit)\n | \"added\"\n | \"removed\"\n | \"modified\"\n // column change\n | \"col_added\"\n | \"col_removed\"\n | \"col_changed\"\n // folder change\n | \"folder_changed\"\n | null;\n\n/**\n * Labels for each change status\n * Format: [shortLabel, longLabel]\n */\nexport const NODE_CHANGE_STATUS_MSGS: Record<\n NonNullable<ChangeStatus>,\n [string, string]\n> = {\n added: [\"Model Added\", \"Added resource\"],\n removed: [\"Model Removed\", \"Removed resource\"],\n modified: [\"Model Modified\", \"Modified resource\"],\n col_added: [\"Column Added\", \"Added column\"],\n col_removed: [\"Column Removed\", \"Removed column\"],\n col_changed: [\"Column Modified\", \"Modified column\"],\n folder_changed: [\"Modified\", \"Modified folder\"],\n};\n\n/**\n * Props for the ChangeSummary component\n */\nexport interface ChangeSummaryProps {\n lineageGraph: LineageGraph;\n}\n\n/**\n * Result of calculating column changes between base and current\n */\nexport interface ColumnChangeResult {\n adds: number;\n removes: number;\n modifies: number;\n}\n\n/**\n * Result of calculating all changes in a lineage graph\n */\nexport interface ChangeSummaryResult {\n adds: number;\n removes: number;\n modifies: number;\n col_added: number;\n col_removed: number;\n col_changed: number;\n}\n","/**\n * @file utils.ts\n * @description Utility functions for change summary calculations\n *\n * These functions calculate change statistics across a lineage graph,\n * including node-level and column-level changes.\n */\n\nimport type { NodeData } from \"../../api/info\";\nimport { token } from \"../../theme/colors\";\nimport type { LineageGraph } from \"../../types\";\nimport {\n IconAdded,\n IconChanged,\n type IconComponent,\n IconModified,\n IconRemoved,\n} from \"../lineage/styles\";\nimport type {\n ChangeStatus,\n ChangeSummaryResult,\n ColumnChangeResult,\n} from \"./types\";\n\n/**\n * Get icon and color for a given change status\n *\n * Maps change statuses to visual indicators (icons and colors) for\n * consistent display across the UI.\n *\n * @param changeStatus - The change status to get icon for\n * @returns Object with color and icon component\n */\nexport function getIconForChangeStatus(changeStatus?: ChangeStatus): {\n color: string;\n icon: IconComponent | undefined;\n} {\n const greenColor = String(token(\"colors.green.solid\"));\n const redColor = String(token(\"colors.red.solid\"));\n const amberColor = String(token(\"colors.amber.emphasized\"));\n\n if (changeStatus === \"added\") {\n return { color: greenColor, icon: IconAdded };\n } else if (changeStatus === \"removed\") {\n return { color: redColor, icon: IconRemoved };\n } else if (changeStatus === \"modified\") {\n return { color: amberColor, icon: IconModified };\n } else if (changeStatus === \"col_added\") {\n return { color: greenColor, icon: IconAdded };\n } else if (changeStatus === \"col_removed\") {\n return { color: redColor, icon: IconRemoved };\n } else if (changeStatus === \"col_changed\") {\n return { color: amberColor, icon: IconModified };\n } else if (changeStatus === \"folder_changed\") {\n return { color: amberColor, icon: IconChanged };\n }\n\n return { color: \"inherit\", icon: undefined };\n}\n\n/**\n * Calculate column changes between base and current node data\n *\n * Compares the columns in base and current versions of a node to\n * determine how many columns were added, removed, or modified.\n *\n * @param base - Base version node data\n * @param current - Current version node data\n * @returns Column change counts\n */\nexport function calculateColumnChange(\n base: NodeData | undefined,\n current: NodeData | undefined,\n): ColumnChangeResult {\n let adds = 0;\n let removes = 0;\n let modifies = 0;\n if (!base && !current) return { adds, removes, modifies };\n\n // Add columns\n if (current) {\n Object.keys(current.columns ?? {}).forEach((col) => {\n if (!base?.columns?.[col]) adds++;\n });\n }\n\n // Remove columns\n if (base) {\n Object.keys(base.columns ?? {}).forEach((col) => {\n if (!current?.columns?.[col]) removes++;\n });\n }\n\n // Modify columns\n if (current && base) {\n Object.keys(current.columns ?? {}).forEach((col) => {\n if (base.columns && current.columns?.[col] && base.columns[col]) {\n if (base.columns[col].type !== current.columns[col].type) modifies++;\n }\n });\n }\n\n return { adds, removes, modifies };\n}\n\n/**\n * Calculate all changes in a lineage graph\n *\n * Iterates through all modified nodes in the lineage graph and\n * aggregates node-level and column-level changes.\n *\n * @param lineageGraph - The lineage graph to analyze\n * @returns Aggregated change counts\n */\nexport function calculateChangeSummary(\n lineageGraph: LineageGraph,\n): ChangeSummaryResult {\n const modifiedSet = lineageGraph.modifiedSet;\n let adds = 0;\n let removes = 0;\n let modifies = 0;\n let col_added = 0;\n let col_removed = 0;\n let col_changed = 0;\n\n modifiedSet.forEach((nodeId) => {\n if (lineageGraph.nodes[nodeId].data.changeStatus === \"added\") adds++;\n else if (lineageGraph.nodes[nodeId].data.changeStatus === \"removed\")\n removes++;\n else if (lineageGraph.nodes[nodeId].data.changeStatus === \"modified\")\n modifies++;\n\n const base = lineageGraph.nodes[nodeId].data.data.base;\n const current = lineageGraph.nodes[nodeId].data.data.current;\n const columnChange = calculateColumnChange(base, current);\n col_added += columnChange.adds;\n col_removed += columnChange.removes;\n col_changed += columnChange.modifies;\n });\n\n return { adds, removes, modifies, col_added, col_removed, col_changed };\n}\n","/**\n * @file ChangeSummary.tsx\n * @description Change summary component for displaying node and column changes\n *\n * This component displays an overview of changes detected in a lineage graph,\n * including:\n * - Code changes (added, removed, modified nodes)\n * - Column changes (added, removed, modified columns)\n *\n * Each change type is displayed with its corresponding icon and count.\n */\n\nimport Box from \"@mui/material/Box\";\nimport Grid from \"@mui/material/Grid\";\nimport Stack from \"@mui/material/Stack\";\nimport MuiTooltip from \"@mui/material/Tooltip\";\nimport Typography from \"@mui/material/Typography\";\nimport type { ReactNode } from \"react\";\nimport { FiInfo } from \"react-icons/fi\";\n\nimport type { ChangeStatus, ChangeSummaryProps } from \"./types\";\nimport { NODE_CHANGE_STATUS_MSGS } from \"./types\";\nimport { calculateChangeSummary, getIconForChangeStatus } from \"./utils\";\n\n/**\n * Summary text display with optional tooltip\n *\n * @internal\n */\nfunction SummaryText({\n name,\n value,\n tip,\n}: {\n name: ReactNode;\n value: ReactNode;\n tip?: ReactNode;\n}) {\n return (\n <Stack alignItems=\"stretch\">\n <Typography sx={{ fontSize: \"0.875rem\", color: \"grey.600\" }}>\n {name}\n {tip && (\n <MuiTooltip title={tip}>\n <Box sx={{ display: \"inline-block\" }}>\n <Box\n component={FiInfo}\n sx={{ mx: \"2px\", fontSize: 12, verticalAlign: \"middle\" }}\n />\n </Box>\n </MuiTooltip>\n )}\n </Typography>\n {value}\n </Stack>\n );\n}\n\n/**\n * Label for a change status count with icon\n *\n * @internal\n */\nfunction ChangeStatusCountLabel({\n changeStatus,\n value,\n}: {\n changeStatus: ChangeStatus;\n value: number;\n}) {\n const [label] = changeStatus ? NODE_CHANGE_STATUS_MSGS[changeStatus] : [\"\"];\n const { icon, color } = getIconForChangeStatus(changeStatus);\n\n return (\n <Stack alignItems=\"stretch\">\n <Stack\n direction=\"row\"\n alignItems=\"center\"\n sx={{ fontSize: \"0.875rem\", color: \"grey.600\" }}\n >\n {icon && (\n <Box component={icon} sx={{ mr: \"5px\", color, fontSize: \"1rem\" }} />\n )}\n {label}\n </Stack>\n <Typography sx={{ fontSize: \"0.875rem\" }}>{value}</Typography>\n </Stack>\n );\n}\n\n/**\n * ChangeSummary component\n *\n * Displays a summary of all changes in a lineage graph, split into two sections:\n * - Code Changes: Added, removed, and modified nodes\n * - Column Changes: Added, removed, and modified columns\n *\n * @example\n * ```tsx\n * <ChangeSummary lineageGraph={lineageGraph} />\n * ```\n */\nexport function ChangeSummary({ lineageGraph }: ChangeSummaryProps) {\n const { adds, removes, modifies, col_added, col_removed, col_changed } =\n calculateChangeSummary(lineageGraph);\n\n return (\n <Grid\n container\n sx={{\n mb: \"10px\",\n borderTop: \"1px solid\",\n borderColor: \"divider\",\n p: \"2.5vw\",\n }}\n >\n <Grid size={6} sx={{ borderColor: \"divider\" }}>\n <SummaryText\n name=\"Code Changes\"\n value={\n <Grid container sx={{ width: \"100%\" }}>\n <Grid size={4}>\n <ChangeStatusCountLabel changeStatus=\"added\" value={adds} />\n </Grid>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"removed\"\n value={removes}\n />\n </Grid>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"modified\"\n value={modifies}\n />\n </Grid>\n </Grid>\n }\n />\n </Grid>\n <Grid\n size={6}\n sx={{ borderLeft: \"1px solid\", borderLeftColor: \"divider\", pl: \"12px\" }}\n >\n <SummaryText\n name=\"Column Changes\"\n value={\n <Grid container sx={{ width: \"100%\" }}>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"col_added\"\n value={col_added}\n />\n </Grid>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"col_removed\"\n value={col_removed}\n />\n </Grid>\n <Grid size={4}>\n <ChangeStatusCountLabel\n changeStatus=\"col_changed\"\n value={col_changed}\n />\n </Grid>\n </Grid>\n }\n />\n </Grid>\n </Grid>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport Card from \"@mui/material/Card\";\nimport CardContent from \"@mui/material/CardContent\";\nimport CardHeader from \"@mui/material/CardHeader\";\nimport Stack from \"@mui/material/Stack\";\nimport Typography from \"@mui/material/Typography\";\nimport { useEffect, useState } from \"react\";\nimport type { LineageGraph, LineageGraphNode } from \"../../contexts\";\nimport { mergeKeysWithStatus } from \"../../utils\";\nimport { ResourceTypeTag, RowCountDiffTag } from \"../lineage\";\nimport { SchemaView } from \"../schema\";\n\ninterface SchemaDiffCardProps {\n title: string;\n node: LineageGraphNode;\n}\n\nfunction SchemaDiffCard({ node, ...props }: SchemaDiffCardProps) {\n return (\n <Card sx={{ maxWidth: 500 }}>\n <CardHeader\n title={\n <Typography sx={{ fontSize: 18, fontWeight: \"bold\" }}>\n {props.title}\n </Typography>\n }\n subheader={\n <Stack direction=\"row\" spacing=\"8px\" sx={{ p: \"16px\" }}>\n <ResourceTypeTag data={{ resourceType: node.data.resourceType }} />\n {node.data.resourceType === \"model\" && (\n <RowCountDiffTag node={node} />\n )}\n </Stack>\n }\n />\n <CardContent>\n <Box sx={{ display: \"flex\" }}>\n <SchemaView\n base={node.data.data.base}\n current={node.data.data.current}\n columnChanges={node.data.change?.columns}\n />\n </Box>\n </CardContent>\n </Card>\n );\n}\n\nfunction listChangedNodes(lineageGraph: LineageGraph) {\n const changedNodes: LineageGraphNode[] = [];\n const allNodes = lineageGraph.nodes;\n lineageGraph.modifiedSet.forEach((nodeId) => {\n const node = allNodes[nodeId];\n const columnDiffStatus = mergeKeysWithStatus(\n Object.keys(node.data.data.base?.columns ?? {}),\n Object.keys(node.data.data.current?.columns ?? {}),\n );\n const isSchemaChanged = !Object.values(columnDiffStatus).every(\n (el) => el === undefined,\n );\n // We only want to show nodes that have real schema changes.\n // It doesn't include added or deleted model.\n if (isSchemaChanged && node.data.data.base && node.data.data.current)\n changedNodes.push(node);\n });\n return changedNodes;\n}\n\nexport interface SchemaSummaryProps {\n lineageGraph: LineageGraph;\n}\n\nexport function SchemaSummary({ lineageGraph }: SchemaSummaryProps) {\n const [changedNodes, setChangedNodes] = useState<LineageGraphNode[]>([]);\n\n useEffect(() => {\n setChangedNodes(listChangedNodes(lineageGraph));\n }, [lineageGraph]);\n\n return (\n <>\n <Box\n sx={{\n width: \"100%\",\n pb: \"10px\",\n mb: \"20px\",\n mt: \"20px\",\n }}\n >\n <Typography variant=\"h5\" sx={{ fontSize: 24 }}>\n Schema Summary\n </Typography>\n </Box>\n <Box sx={{ width: \"100%\", pb: \"10px\", mb: \"20px\" }}>\n {changedNodes.length === 0 ? (\n <Typography sx={{ fontSize: 18, color: \"grey.600\" }}>\n No schema changes detected.\n </Typography>\n ) : (\n <Box\n sx={{\n display: \"grid\",\n gridTemplateColumns: \"repeat(auto-fill, minmax(400px, 1fr))\",\n gap: \"2vw\",\n p: \"2.5vw\",\n width: \"100%\",\n bgcolor: \"lightgray\",\n }}\n >\n {changedNodes.map((node) => {\n return (\n <SchemaDiffCard\n key={node.id}\n title={node.data.name}\n node={node}\n />\n );\n })}\n </Box>\n )}\n </Box>\n </>\n );\n}\n","\"use client\";\n\nimport Box from \"@mui/material/Box\";\nimport CircularProgress from \"@mui/material/CircularProgress\";\nimport Typography from \"@mui/material/Typography\";\nimport { memo, useCallback, useMemo } from \"react\";\nimport type { Check } from \"../../providers/contexts/CheckContext\";\nimport { useCheckContext } from \"../../providers/contexts/CheckContext\";\nimport type { CheckAction, CheckActionType } from \"../check/CheckActions\";\nimport type { CheckCardData } from \"../check/CheckCard\";\nimport {\n CheckDetail,\n type CheckDetailProps,\n type CheckDetailTab,\n} from \"../check/CheckDetail\";\nimport { CheckEmptyState } from \"../check/CheckEmptyState\";\nimport { CheckList } from \"../check/CheckList\";\nimport { SplitPane } from \"../ui/SplitPane\";\n\n/**\n * Props for the ChecksView component.\n * Defines options for viewing and managing checks.\n */\nexport interface ChecksViewProps {\n /**\n * Optional checks data. If not provided, uses CheckContext.\n */\n checks?: Check[];\n\n /**\n * Loading state. If not provided, uses CheckContext.\n */\n isLoading?: boolean;\n\n /**\n * Error message. If not provided, uses CheckContext.\n */\n error?: string;\n\n /**\n * Currently selected check ID. If not provided, uses CheckContext.\n */\n selectedCheckId?: string;\n\n /**\n * Callback when a check is selected. If not provided, uses CheckContext.\n */\n onCheckSelect?: (checkId: string) => void;\n\n /**\n * Callback when check approval status changes.\n */\n onApprovalChange?: (checkId: string, isApproved: boolean) => void;\n\n /**\n * Callback when a check action is triggered.\n */\n onAction?: (checkId: string, actionType: CheckActionType) => void;\n\n /**\n * Callback when checks are reordered.\n */\n onReorder?: (sourceIndex: number, destinationIndex: number) => void;\n\n /**\n * Callback when description changes.\n */\n onDescriptionChange?: (checkId: string, description?: string) => void;\n\n /**\n * Callback when name changes.\n */\n onNameChange?: (checkId: string, name: string) => void;\n\n /**\n * Callback when create is clicked (in empty state).\n */\n onCreateCheck?: () => void;\n\n /**\n * Function to generate tabs for a check detail view.\n */\n getCheckTabs?: (check: Check) => CheckDetailTab[];\n\n /**\n * Function to generate primary actions for a check.\n */\n getPrimaryActions?: (check: Check) => CheckAction[];\n\n /**\n * Function to generate secondary actions for a check.\n */\n getSecondaryActions?: (check: Check) => CheckAction[];\n\n /**\n * Whether approval is disabled for all checks.\n */\n disableApproval?: boolean;\n\n /**\n * Tooltip for disabled approval.\n */\n disabledApprovalTooltip?: string;\n\n /**\n * Optional height for the view.\n * @default \"100%\"\n */\n height?: number | string;\n\n /**\n * Initial split pane size (percentage for list).\n * @default 30\n */\n listPaneSize?: number;\n\n /**\n * Minimum list pane size in pixels.\n * @default 200\n */\n minListSize?: number;\n\n /**\n * Maximum list pane size in pixels.\n * @default 500\n */\n maxListSize?: number;\n\n /**\n * Title for the check list.\n */\n listTitle?: string;\n\n /**\n * Optional CSS class name.\n */\n className?: string;\n}\n\n/**\n * ChecksView Component\n *\n * A high-level component for viewing and managing checks using a\n * split-pane layout with a list on the left and details on the right.\n *\n * Can receive data from:\n * 1. CheckContext (wrap with CheckProvider)\n * 2. Direct props (pass checks, selectedCheckId, etc.)\n *\n * @example Using with context\n * ```tsx\n * import { CheckProvider, ChecksView } from '@datarecce/ui';\n *\n * function App() {\n * const { checks, isLoading } = useChecksQuery();\n * const [selectedId, setSelectedId] = useState<string>();\n *\n * return (\n * <CheckProvider\n * checks={checks}\n * isLoading={isLoading}\n * selectedCheckId={selectedId}\n * onSelectCheck={setSelectedId}\n * >\n * <ChecksView\n * getCheckTabs={(check) => [\n * { id: 'result', label: 'Result', content: <ResultView check={check} /> },\n * ]}\n * />\n * </CheckProvider>\n * );\n * }\n * ```\n *\n * @example Using with direct props\n * ```tsx\n * import { ChecksView } from '@datarecce/ui';\n *\n * function App({ checks, selectedId, onSelect }) {\n * return (\n * <ChecksView\n * checks={checks}\n * selectedCheckId={selectedId}\n * onCheckSelect={onSelect}\n * onApprovalChange={(id, approved) => updateCheck(id, { is_checked: approved })}\n * />\n * );\n * }\n * ```\n */\nfunction ChecksViewComponent({\n checks: propChecks,\n isLoading: propIsLoading,\n error: propError,\n selectedCheckId: propSelectedCheckId,\n onCheckSelect: propOnCheckSelect,\n onApprovalChange,\n onAction,\n onReorder,\n onDescriptionChange,\n onNameChange,\n onCreateCheck,\n getCheckTabs,\n getPrimaryActions,\n getSecondaryActions,\n disableApproval = false,\n disabledApprovalTooltip,\n height = \"100%\",\n listPaneSize = 30,\n minListSize = 200,\n maxListSize = 500,\n listTitle,\n className,\n}: ChecksViewProps) {\n // Get data from context or props\n const contextValue = useCheckContext();\n\n const checks = propChecks ?? contextValue.checks;\n const isLoading =\n propIsLoading !== undefined ? propIsLoading : contextValue.isLoading;\n const error = propError ?? contextValue.error;\n const selectedCheckId = propSelectedCheckId ?? contextValue.selectedCheckId;\n const onSelectCheck = propOnCheckSelect ?? contextValue.onSelectCheck;\n\n // Convert Check to CheckCardData\n const checkCards = useMemo<CheckCardData[]>(\n () =>\n checks.map((check) => ({\n id: check.check_id,\n name: check.name,\n type: check.type as CheckCardData[\"type\"],\n description: check.description,\n isApproved: check.is_checked,\n })),\n [checks],\n );\n\n // Find selected check\n const selectedCheck = useMemo(\n () => checks.find((c) => c.check_id === selectedCheckId),\n [checks, selectedCheckId],\n );\n\n // Handle check selection\n const handleCheckSelect = useCallback(\n (checkId: string) => {\n onSelectCheck?.(checkId);\n },\n [onSelectCheck],\n );\n\n // Handle action\n const handleAction = useCallback(\n (checkId: string, actionType: CheckActionType) => {\n onAction?.(checkId, actionType);\n },\n [onAction],\n );\n\n // Handle description change\n const handleDescriptionChange = useCallback(\n (description?: string) => {\n if (selectedCheckId) {\n onDescriptionChange?.(selectedCheckId, description);\n }\n },\n [selectedCheckId, onDescriptionChange],\n );\n\n // Handle name change\n const handleNameChange = useCallback(\n (name: string) => {\n if (selectedCheckId) {\n onNameChange?.(selectedCheckId, name);\n }\n },\n [selectedCheckId, onNameChange],\n );\n\n // Loading state\n if (isLoading) {\n return (\n <Box\n className={className}\n sx={{\n width: \"100%\",\n height,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <CircularProgress />\n </Box>\n );\n }\n\n // Error state\n if (error) {\n return (\n <Box\n className={className}\n sx={{\n width: \"100%\",\n height,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Typography color=\"error\">{error}</Typography>\n </Box>\n );\n }\n\n // Empty state\n if (checks.length === 0) {\n return (\n <Box\n className={className}\n sx={{\n width: \"100%\",\n height,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <CheckEmptyState\n title=\"No checks yet\"\n description=\"Create your first check to start validating your data.\"\n actionText=\"Create Check\"\n onAction={onCreateCheck}\n />\n </Box>\n );\n }\n\n // Build detail props\n const detailTabs = selectedCheck ? getCheckTabs?.(selectedCheck) : undefined;\n const primaryActions = selectedCheck\n ? getPrimaryActions?.(selectedCheck)\n : undefined;\n const secondaryActions = selectedCheck\n ? getSecondaryActions?.(selectedCheck)\n : undefined;\n\n return (\n <Box className={className} sx={{ width: \"100%\", height }}>\n <SplitPane\n direction=\"horizontal\"\n sizes={[listPaneSize, 100 - listPaneSize]}\n minSizes={[minListSize, 200]}\n maxSizes={[maxListSize, Infinity]}\n >\n {/* Left pane: Check list */}\n <Box sx={{ height: \"100%\", overflow: \"auto\" }}>\n <CheckList\n checks={checkCards}\n selectedId={selectedCheckId}\n onCheckSelect={handleCheckSelect}\n onApprovalChange={onApprovalChange}\n onReorder={onReorder}\n disableApproval={disableApproval}\n disabledApprovalTooltip={disabledApprovalTooltip}\n title={listTitle}\n />\n </Box>\n\n {/* Right pane: Check detail */}\n <Box sx={{ height: \"100%\", overflow: \"auto\" }}>\n {selectedCheck ? (\n <CheckDetail\n checkId={selectedCheck.check_id}\n name={selectedCheck.name}\n type={selectedCheck.type}\n description={selectedCheck.description}\n isApproved={selectedCheck.is_checked}\n tabs={detailTabs}\n primaryActions={primaryActions}\n secondaryActions={secondaryActions}\n onAction={handleAction}\n onDescriptionChange={handleDescriptionChange}\n onNameChange={handleNameChange}\n />\n ) : (\n <Box\n sx={{\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n <Typography color=\"text.secondary\">\n Select a check from the list to view details\n </Typography>\n </Box>\n )}\n </Box>\n </SplitPane>\n </Box>\n );\n}\n\n/**\n * Memoized ChecksView component for performance optimization.\n */\nexport const ChecksView = memo(ChecksViewComponent);\nChecksView.displayName = \"ChecksView\";\n","// @datarecce/ui - React component library for data validation interfaces\n\n// Version\nexport const VERSION = \"0.2.0\";\n\n// API utilities\nexport type {\n CatalogMetadata,\n GitInfo,\n LineageData,\n LineageDataFromMetadata,\n LineageDiffData,\n ManifestMetadata,\n ModelInfoResult,\n NodeColumnData,\n NodeData,\n PullRequestInfo,\n RecceInstanceInfo,\n RecceServerFlags,\n RunsAggregated,\n ServerInfoResult,\n ServerMode,\n SQLMeshInfo,\n StateMetadata,\n} from \"./api\";\nexport {\n aggregateRuns,\n cacheKeys,\n getLastKeepAliveTime,\n getModelInfo,\n getRecceInstanceInfo,\n getServerFlag,\n getServerInfo,\n markRelaunchHintCompleted,\n reorderChecks,\n resetKeepAliveState,\n sendKeepAlive,\n setKeepAliveCallback,\n useChecks,\n} from \"./api\";\n\n// Components - UI components for data validation interfaces\n// NOTE: LineageCanvasProps canonical in @datarecce/ui/types\nexport type {\n // High-level views (Layer 3)\n ChecksViewProps,\n // Data type icons\n ColumnTooltipInput,\n DataTypeIconProps,\n // UI primitives\n DiffTextProps,\n // Lineage\n LineageCanvasProps,\n LineageViewProps,\n LineageViewRef,\n SplitProps,\n SquareIconProps,\n TypeCategory,\n} from \"./components\";\nexport {\n // Data type icons\n buildColumnTooltip,\n // High-level views (Layer 3)\n ChecksView,\n classifyType,\n DataTypeIcon,\n // UI primitives\n DiffText,\n HSplit,\n // Lineage\n LineageCanvas,\n LineageView,\n SquareIcon,\n VSplit,\n} from \"./components\";\n\n// Result view factory and types\n// NOTE: canonical in @datarecce/ui/result\nexport type {\n CreatedResultViewProps,\n ResultViewConfig,\n ResultViewData,\n ResultViewProps,\n ResultViewRef,\n ResultViewTransformOptions,\n ScreenshotWrapperType,\n} from \"./components/result\";\nexport { createResultView } from \"./components/result\";\n// Constants - reusable constant values for UI components\nexport {\n type DisableTooltipMessageKey,\n DisableTooltipMessages,\n} from \"./constants\";\n// Contexts - React contexts for state management\n// NOTE: Context hooks canonical in @datarecce/ui/contexts\n// NOTE: Types canonical in @datarecce/ui/types\nexport type {\n // LineageViewContext types\n ActionMode,\n ActionState,\n // Action context\n AxiosQueryParams,\n // Lineage graph types (canonical: @datarecce/ui/types)\n EnvInfo,\n IdleTimeoutContextType,\n InstanceInfoType,\n LineageGraph,\n LineageGraphColumnNode,\n LineageGraphContextType,\n LineageGraphEdge,\n LineageGraphNode,\n LineageGraphNodes,\n LineageGraphProviderProps,\n LineageViewContextType,\n NodeAction,\n NodeColumnSetMap,\n RecceActionContextType,\n RecceActionOptions,\n RecceActionProviderProps,\n RecceFeatureMode,\n RecceFeatureToggles,\n SelectMode,\n SubmitRunTrackProps,\n} from \"./contexts\";\nexport {\n // Lineage graph utilities (canonical: @datarecce/ui/contexts)\n buildLineageGraph,\n COLUMN_HEIGHT,\n // Instance info\n defaultFeatureToggles,\n defaultInstanceInfo,\n getNeighborSet,\n // Idle timeout\n IdleTimeoutProvider,\n intersect,\n isLineageGraphColumnNode,\n isLineageGraphNode,\n // Lineage graph\n LineageGraphProvider,\n layoutWithDagre,\n // Action context\n RecceActionProvider,\n RecceInstanceInfoProvider,\n selectDownstream,\n selectUpstream,\n toReactFlowBasic,\n union,\n useIdleDetection,\n useIdleTimeout,\n useIdleTimeoutSafe,\n useLineageGraphContext,\n useRecceActionContext,\n useRecceInstanceContext,\n useRecceInstanceInfo,\n useRecceServerFlag,\n useRunsAggregated,\n} from \"./contexts\";\n// Hooks - utility hooks for theming and data\nexport type {\n MultiNodesActionCallbacks,\n MultiNodesActionTracking,\n MultiNodesActionTrackProps,\n MultiNodesActionType,\n OSSCheckContext,\n OSSQueryContext,\n RecceActionOptions as OSSRecceActionOptions,\n ThemeColors,\n UseModelColumnsReturn,\n UseMultiNodesActionOptions,\n UseMultiNodesActionReturn,\n UseRunResult,\n} from \"./hooks\";\nexport {\n CheckContextAdapter,\n defaultSqlQuery,\n extractColumns,\n IGNORE_SCREENSHOT_CLASS,\n LineageGraphAdapter,\n QueryContextAdapter,\n RecceActionAdapter,\n RecceShareStateContextProvider,\n unionColumns,\n useCheckEvents,\n useCopyToClipboard,\n useCopyToClipboardButton,\n useCSVExport,\n useFeedbackCollectionToast,\n useImageDownloadModal,\n useIsDark,\n useModelColumns,\n useMultiNodesAction,\n useRecceCheckContext,\n useRecceQueryContext,\n useRecceShareStateContext,\n useRun,\n useThemeColors,\n} from \"./hooks\";\n// NOTE: SchemaDiffRow canonical in @datarecce/ui/types\nexport type {\n SchemaDataGridOptions,\n SchemaDataGridResult,\n SchemaDiffRow,\n SchemaRow,\n SingleEnvSchemaDataGridResult,\n} from \"./lib\";\n// Lib - library utilities including data grid generators\nexport {\n mergeColumns,\n toSchemaDataGrid,\n toSingleEnvDataGrid,\n} from \"./lib\";\n// Provider (main entry point) and Hooks - from providers module\n// NOTE: API hooks (`useApiClient`, `useApiConfig`, `useApiConfigOptional`) canonical in @datarecce/ui/contexts\nexport type {\n Check,\n CheckContextType,\n CheckProviderProps,\n NavigateOptions,\n QueryContextType,\n QueryProviderProps,\n QueryResult,\n RoutingConfig,\n RoutingContextValue,\n} from \"./providers\";\nexport {\n CheckProvider,\n QueryProvider,\n RecceProvider,\n type RecceProviderProps,\n useApiClient,\n useApiConfig,\n useApiConfigOptional,\n useAppLocation,\n useCheckContext,\n useQueryContext,\n useRecceTheme,\n useRouting,\n} from \"./providers\";\n// Theme - colors palette and MUI theme with CSS Variables\n// NOTE: `colors` and `getChartThemeColors` canonical in @datarecce/ui/theme\nexport type { Theme } from \"./theme\";\nexport { colors, theme } from \"./theme\";\n// Utils - utility functions for data manipulation and formatting\n// NOTE: `deltaPercentageString`, `formatTimestamp`, `formatTimeToNow`, `isSchemaChanged`\n// canonical in @datarecce/ui/utils\nexport {\n deltaPercentageString,\n extractSchemas,\n formatTimestamp,\n formatTimeToNow,\n isSchemaChanged,\n} from \"./utils\";\n"],"mappings":"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"}
|