Files
cosmos-explorer/src/Explorer/Tabs/CloudShellTab/CloudShellTerminalComponent.tsx
Sourabh Jain 205355bf55 Shell: Integrate Cloudshell to existing shells (#2098)
* first draft

* refactored code

* ux fix

* add custom header support and fix ui

* minor changes

* hide last command also

* remove logger

* bug fixes

* updated loick file

* fix tests

* moved files

* update readme

* documentation update

* fix compilationerror

* undefined check handle

* format fix

* format fix

* fix lints

* format fix

* fix unrelatred test

* code refator

* fix format

* ut fix

* cgmanifest

* Revert "cgmanifest"

This reverts commit 2e76a6926ee0d3d4e0510f2e04e03446c2ca8c47.

* fix snap

* test fix

* formatting code

* updated xterm

* include username in command

* cloudshell add exit

* fix test

* format fix

* tets fix

* fix multiple open cloudshell calls

* socket time out after 20 min

* remove unused code

* 120 min

* Addressed comments
2025-04-30 13:19:01 -07:00

81 lines
2.5 KiB
TypeScript

import { FitAddon } from "@xterm/addon-fit";
import { Terminal } from "@xterm/xterm";
import React, { useEffect, useRef } from "react";
import "xterm/css/xterm.css";
import { DatabaseAccount } from "../../../Contracts/DataModels";
import { TerminalKind } from "../../../Contracts/ViewModels";
import { startCloudShellTerminal } from "./CloudShellTerminalCore";
export interface CloudShellTerminalComponentProps {
databaseAccount: DatabaseAccount;
tabId: string;
username?: string;
shellType?: TerminalKind;
}
export const CloudShellTerminalComponent: React.FC<CloudShellTerminalComponentProps> = (props) => {
const terminalRef = useRef(null); // Reference for terminal container
const xtermRef = useRef(null); // Reference for XTerm instance
const socketRef = useRef(null); // Reference for WebSocket
useEffect(() => {
// Initialize XTerm instance
const terminal = new Terminal({
cursorBlink: true,
cursorStyle: "bar",
fontFamily: "monospace",
fontSize: 11,
theme: {
background: "#1e1e1e",
foreground: "#d4d4d4",
cursor: "#ffcc00",
},
scrollback: 1000,
});
const fitAddon = new FitAddon();
terminal.loadAddon(fitAddon);
// Attach terminal to the DOM
if (terminalRef.current) {
terminal.open(terminalRef.current);
xtermRef.current = terminal;
}
// Defer terminal sizing until after DOM rendering is complete
setTimeout(() => {
fitAddon.fit();
}, 0);
// Use ResizeObserver instead of window resize
const resizeObserver = new ResizeObserver(() => {
const container = terminalRef.current;
if (container && container.offsetWidth > 0 && container.offsetHeight > 0) {
try {
fitAddon.fit();
} catch (e) {
console.warn("Fit failed on resize:", e);
}
}
});
resizeObserver.observe(terminalRef.current);
socketRef.current = startCloudShellTerminal(terminal, props.shellType);
// Cleanup function to close WebSocket and dispose terminal
return () => {
if (!socketRef.current) {
return;
}
if (socketRef.current && socketRef.current.readyState && socketRef.current.readyState === WebSocket.OPEN) {
socketRef.current.close(); // Close WebSocket connection
}
if (resizeObserver && terminalRef.current) {
resizeObserver.unobserve(terminalRef.current);
}
terminal.dispose(); // Clean up XTerm instance
};
}, []);
return <div ref={terminalRef} style={{ width: "100%", height: "500px" }} />;
};