LCOV - code coverage report
Current view: top level - dashql-app/src/session - session_page.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 100.0 % 71 71
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : import * as styles from './session_page.module.css';
       3              : 
       4              : import { useLocation } from 'react-router-dom';
       5              : 
       6              : import { useSessionScripts } from './scripts/session_scripts_registry.js';
       7              : import { useConnectionRegistry } from '../connections/connection_registry.js';
       8              : import type { ConnectionState } from '../connections/connection_state.js';
       9              : import { isEphemeralSession, newScriptConnection, sessionConnectionIds } from './scripts/session_scripts.js';
      10              : import { useLogger } from '../platform/logger/logger_provider.js';
      11              : import { PROMOTE_SESSION, useRouteContext, useRouterNavigate } from '../app/router/router.js';
      12              : 
      13              : import { SessionFeedPage } from './feed/session_feed_page.js';
      14              : import { SessionViewMode, useSessionViewMode } from './session_view_mode.js';
      15              : 
      16            1 : const SessionShellsPage = React.lazy(() => import('./shell/session_shells_page.js'));
      17              : 
      18            1 : const LOG_CTX = 'session_page';
      19              : 
      20              : export interface SessionPageDependencies {
      21              :     useSessionScripts: typeof useSessionScripts;
      22              :     useConnectionRegistry: typeof useConnectionRegistry;
      23              :     useLogger: typeof useLogger;
      24              :     useRouteContext: typeof useRouteContext;
      25              :     useRouterNavigate: typeof useRouterNavigate;
      26              :     useLocation: typeof useLocation;
      27              :     useSessionViewMode: typeof useSessionViewMode;
      28              :     SessionFeedPage: typeof SessionFeedPage;
      29              :     SessionShellsPage: typeof SessionShellsPage;
      30              : }
      31              : 
      32            1 : const DEFAULT_DEPENDENCIES: SessionPageDependencies = {
      33            1 :     useSessionScripts,
      34            1 :     useConnectionRegistry,
      35            1 :     useLogger,
      36            1 :     useRouteContext,
      37            1 :     useRouterNavigate,
      38            1 :     useLocation,
      39            1 :     useSessionViewMode,
      40            1 :     SessionFeedPage,
      41            1 :     SessionShellsPage,
      42            1 : };
      43              : 
      44              : interface Props {
      45              :     dependencies?: SessionPageDependencies;
      46              : }
      47              : 
      48            1 : export const SessionPage: React.FC<Props> = (props: Props) => {
      49            1 :     const dependencies = props.dependencies ?? DEFAULT_DEPENDENCIES;
      50            1 :     const route = dependencies.useRouteContext();
      51            1 :     const logger = dependencies.useLogger();
      52            1 :     const [sessionScripts, modifySessionScripts] = dependencies.useSessionScripts(route.sessionId ?? null);
      53              :     // The connections that the scripts of the session run on, and the one the next script runs on
      54            1 :     const [connectionRegistry] = dependencies.useConnectionRegistry();
      55            1 :     const nextConnectionId = sessionScripts == null ? null : newScriptConnection(sessionScripts, null)?.connectionId ?? null;
      56            1 :     const conn = nextConnectionId == null ? null : connectionRegistry.connections.get(nextConnectionId) ?? null;
      57            1 :     const scriptConnections = new Map<string, ConnectionState>();
      58            1 :     for (const connectionId of sessionScripts == null ? [] : sessionConnectionIds(sessionScripts)) {
      59            1 :         const connection = connectionRegistry.connections.get(connectionId);
      60            1 :         if (connection != null) scriptConnections.set(connectionId, connection);
      61            1 :     }
      62            1 :     const { mode: sessionMode } = dependencies.useSessionViewMode();
      63            1 :     const sessionPageVisible = sessionMode !== SessionViewMode.Shell;
      64              :     // The connection of the visible shell, the connection of the next script until another one is picked.
      65              :     // Kept while the shell is closed, the shell reopens on the picked connection of the session.
      66            1 :     const [shellPick, setShellPick] = React.useState<{ sessionId: string; connectionId: string } | null>(null);
      67            1 :     const shellConnectionId = shellPick != null && shellPick.sessionId === route.sessionId ? shellPick.connectionId : null;
      68            1 :     const shellConnection = (shellConnectionId == null ? null : connectionRegistry.connections.get(shellConnectionId)) ?? conn ?? null;
      69              : 
      70            1 :     React.useEffect(() => {
      71            1 :         if (route.sessionId === null) {
      72            1 :             logger.warn('missing session id', {}, LOG_CTX);
      73              :         }
      74            1 :     }, [route.sessionId]);
      75              : 
      76              :     // The greeter stays at the root until a script promotes its session
      77            1 :     const navigate = dependencies.useRouterNavigate();
      78            1 :     const pathname = dependencies.useLocation().pathname;
      79            1 :     const promotedSessionId = sessionScripts != null && !isEphemeralSession(sessionScripts) && pathname === '/' ? sessionScripts.sessionId : null;
      80            1 :     React.useEffect(() => {
      81            1 :         if (promotedSessionId != null) navigate({ type: PROMOTE_SESSION, value: promotedSessionId });
      82            1 :     }, [navigate, promotedSessionId]);
      83              : 
      84            1 :     if (route.sessionId === null || sessionScripts == null) {
      85            1 :         return <div />;
      86              :     }
      87            1 :     return (
      88            1 :         <div className={styles.page}>
      89            1 :             <div className={sessionPageVisible ? styles.view : styles.view_hidden}>
      90            1 :                 <dependencies.SessionFeedPage
      91            1 :                     sessionScripts={sessionScripts}
      92            1 :                     modifySessionScripts={modifySessionScripts}
      93            1 :                     connection={conn ?? null}
      94            1 :                     scriptConnections={scriptConnections}
      95            1 :                     active={sessionMode === SessionViewMode.Session}
      96            1 :                 />
      97            1 :             </div>
      98            1 :             {sessionMode === SessionViewMode.Shell && (
      99            1 :                 <div className={styles.view}>
     100            1 :                     <React.Suspense fallback={(
     101            1 :                         <div className={styles.shellLoading} role="status">
     102            1 :                             <strong>[ RUN ]</strong> Loading shell
     103            1 :                         </div>
     104              :                     )}>
     105            1 :                         <dependencies.SessionShellsPage
     106            1 :                             key={sessionScripts.sessionId}
     107            1 :                             sessionId={sessionScripts.sessionId}
     108            1 :                             sessionName={sessionScripts.name}
     109            1 :                             connectionId={shellConnection?.connectionId ?? null}
     110            1 :                             onPickConnection={connectionId => setShellPick({ sessionId: sessionScripts.sessionId, connectionId })}
     111            1 :                         />
     112            1 :                     </React.Suspense>
     113            1 :                 </div>
     114              :             )}
     115            1 :         </div>
     116              :     );
     117            1 : };
        

Generated by: LCOV version 2.0-1