LCOV - code coverage report
Current view: top level - pkg/playground - terminal.tsx Coverage Total Hit
Test: cockpit Lines: 100.0 % 87 87
Test Date: 2026-06-16 14:09:37

            Line data    Source code
       1              : /*
       2              :  * Copyright (C) 2025 Red Hat, Inc.
       3              :  * SPDX-License-Identifier: LGPL-2.1-or-later
       4              :  */
       5              : 
       6            1 : import cockpit from "cockpit";
       7            1 : import React, { useState } from "react";
       8            1 : import { createRoot, Container } from 'react-dom/client';
       9              : 
      10              : import '../lib/patternfly/patternfly-6-cockpit.scss';
      11              : 
      12              : import { Page, PageSection } from "@patternfly/react-core/dist/esm/components/Page/index.js";
      13              : import { ToggleGroup, ToggleGroupItem } from "@patternfly/react-core/dist/esm/components/ToggleGroup/index.js";
      14              : import { Button } from "@patternfly/react-core/dist/esm/components/Button/index.js";
      15              : import { Split, SplitItem } from "@patternfly/react-core/dist/esm/layouts/Split/index.js";
      16              : import { Switch } from "@patternfly/react-core/dist/esm/components/Switch/index.js";
      17              : 
      18              : import { useObject } from 'hooks';
      19              : import { Terminal, TerminalState } from 'cockpit-components-terminal';
      20              : 
      21              : import 'cockpit-dark-theme'; // once per page
      22              : import 'page.scss';
      23              : 
      24            1 : function create_channel() {
      25            1 :     return cockpit.channel({
      26            1 :         payload: "stream",
      27            1 :         spawn: ["/bin/bash"],
      28            1 :         environ: [
      29            1 :             "TERM=xterm-256color",
      30            1 :         ],
      31            1 :         directory: "/",
      32            1 :         pty: true,
      33            1 :     });
      34            1 : }
      35              : 
      36            1 : function create_states(n: number): TerminalState[] {
      37            1 :     const states = [];
      38            1 :     for (let i = 0; i < n; i++)
      39            1 :         states.push(new TerminalState(create_channel()));
      40            1 :     return states;
      41            1 : }
      42              : 
      43            1 : function close_states(states: TerminalState[]) {
      44            1 :     states.forEach(s => s.close());
      45            1 : }
      46              : 
      47            1 : const TabbedTerminal = () => {
      48            1 :     const num_terminals = 4;
      49            1 :     const states = useObject(() => create_states(num_terminals), states => close_states(states), []);
      50            1 :     const [tab, setTab] = useState<number>(0);
      51              : 
      52            1 :     function make_toggles() {
      53            1 :         const res = [];
      54              : 
      55            1 :         for (let i = 0; i < num_terminals; i++) {
      56            1 :             const t = i;
      57            1 :             res.push(<ToggleGroupItem
      58            1 :                          key={t}
      59            1 :                          text={"Terminal " + String(t + 1)}
      60            1 :                          isSelected={tab == t}
      61            1 :                          onChange={() => setTab(t)}
      62            1 :             />);
      63            1 :         }
      64              : 
      65            1 :         return res;
      66            1 :     }
      67              : 
      68            1 :     return (
      69            1 :         <PageSection isFilled hasBodyWrapper={false}>
      70            1 :             <Split>
      71            1 :                 <SplitItem>
      72            1 :                     <ToggleGroup>
      73            1 :                         {make_toggles()}
      74            1 :                     </ToggleGroup>
      75            1 :                 </SplitItem>
      76            1 :                 <SplitItem isFilled />
      77            1 :                 <SplitItem>
      78            1 :                     <Button variant="secondary" onClick={() => states[tab].resetChannel(create_channel())}>
      79              :                         Reset
      80            1 :                     </Button>
      81            1 :                 </SplitItem>
      82            1 :             </Split>
      83            1 :             <div id="terminal" style={{ height: "100%" }}>
      84            1 :                 <Terminal
      85            1 :                     parentId="terminal"
      86            1 :                     state={states[tab]}
      87            1 :                     cols={80}
      88            1 :                     rows={40}
      89            1 :                 />
      90            1 :             </div>
      91            1 :         </PageSection>
      92              :     );
      93            1 : };
      94              : 
      95            1 : const TerminalDemo = () => {
      96            1 :     const [showTerminal, setShowTerminal] = useState(true);
      97              : 
      98            1 :     return (
      99            1 :         <Page isContentFilled className="no-masthead-sidebar">
     100            1 :             <PageSection>
     101            1 :                 <Switch
     102            1 :                     label="Show terminals"
     103            1 :                     isChecked={showTerminal}
     104            1 :                     onChange={(_event: React.FormEvent<HTMLInputElement>, checked: boolean) => setShowTerminal(checked)}
     105            1 :                 />
     106            1 :             </PageSection>
     107            1 :             { showTerminal && <TabbedTerminal /> }
     108            1 :         </Page>
     109              :     );
     110            1 : };
     111              : 
     112            1 : function init_app(rootElement: Container) {
     113            1 :     const root = createRoot(rootElement);
     114            1 :     root.render(<TerminalDemo />);
     115            1 : }
     116              : 
     117            1 : document.addEventListener("DOMContentLoaded", function() {
     118            1 :     cockpit.transport.wait(function() {
     119            1 :         init_app(document.getElementById('app')!);
     120            1 :     });
     121            1 : });
        

Generated by: LCOV version 2.0-1