Fixed up split screen a little. Still buggy. Made some UI changes too.
This commit is contained in:
56
src/App.jsx
56
src/App.jsx
@@ -22,6 +22,7 @@ function App() {
|
|||||||
port: 22,
|
port: 22,
|
||||||
});
|
});
|
||||||
const [isLaunchpadOpen, setIsLaunchpadOpen] = useState(false);
|
const [isLaunchpadOpen, setIsLaunchpadOpen] = useState(false);
|
||||||
|
const [splitTabIds, setSplitTabIds] = useState([]);
|
||||||
|
|
||||||
// Handle keypress for opening launchpad
|
// Handle keypress for opening launchpad
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -50,8 +51,7 @@ function App() {
|
|||||||
password: form.password,
|
password: form.password,
|
||||||
port: Number(form.port),
|
port: Number(form.port),
|
||||||
},
|
},
|
||||||
isSplit: false,
|
terminalRef: null,
|
||||||
terminalRef: null, // Reference to the terminal instance
|
|
||||||
};
|
};
|
||||||
setTerminals([...terminals, newTerminal]);
|
setTerminals([...terminals, newTerminal]);
|
||||||
setActiveTab(nextId);
|
setActiveTab(nextId);
|
||||||
@@ -67,6 +67,7 @@ function App() {
|
|||||||
const closeTab = (id) => {
|
const closeTab = (id) => {
|
||||||
const newTerminals = terminals.filter((t) => t.id !== id);
|
const newTerminals = terminals.filter((t) => t.id !== id);
|
||||||
setTerminals(newTerminals);
|
setTerminals(newTerminals);
|
||||||
|
setSplitTabIds(prev => prev.filter(tabId => tabId !== id));
|
||||||
if (activeTab === id) {
|
if (activeTab === id) {
|
||||||
setActiveTab(newTerminals[0]?.id || null);
|
setActiveTab(newTerminals[0]?.id || null);
|
||||||
}
|
}
|
||||||
@@ -74,21 +75,29 @@ function App() {
|
|||||||
|
|
||||||
// Toggle split for a specific tab
|
// Toggle split for a specific tab
|
||||||
const toggleSplit = (id) => {
|
const toggleSplit = (id) => {
|
||||||
setTerminals(terminals.map(t =>
|
setSplitTabIds(prev => {
|
||||||
t.id === id ? { ...t, isSplit: !t.isSplit } : t
|
if (prev.includes(id)) {
|
||||||
));
|
return prev.filter(tabId => tabId !== id);
|
||||||
|
} else {
|
||||||
|
if (prev.length >= 1) return prev; // Limit to 1 split
|
||||||
|
return [...prev, id, activeTab].filter((tabId, index, self) => self.indexOf(tabId) === index);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the split terminals
|
// Get grid layout class based on split count
|
||||||
const splitTerminals = terminals.filter(t => t.isSplit);
|
const getGridLayout = (count) => {
|
||||||
const mainTerminal = terminals.find(t => t.id === activeTab);
|
if (count === 1) return 'grid-cols-1';
|
||||||
|
if (count === 2) return 'grid-cols-2';
|
||||||
|
return 'grid-cols-1';
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CssVarsProvider theme={theme}>
|
<CssVarsProvider theme={theme}>
|
||||||
<div className="flex h-screen bg-neutral-900 overflow-hidden">
|
<div className="flex h-screen bg-neutral-900 overflow-hidden">
|
||||||
<div className="flex-1 flex flex-col overflow-hidden">
|
<div className="flex-1 flex flex-col overflow-hidden">
|
||||||
{/* Topbar */}
|
{/* Topbar */}
|
||||||
<div className="bg-neutral-800 text-white p-4 flex items-center justify-between gap-4 min-h-[75px] max-h-[75px]">
|
<div className="bg-neutral-800 text-white p-4 flex items-center justify-between gap-4 min-h-[75px] max-h-[75px] shadow-xl border-b-5 border-neutral-700">
|
||||||
<div className="bg-neutral-700 flex justify-center items-center gap-2 p-3 rounded-lg h-[52px]">
|
<div className="bg-neutral-700 flex justify-center items-center gap-2 p-3 rounded-lg h-[52px]">
|
||||||
<img src={TermixIcon} alt="Termix Icon" className="w-[30px] h-[30px]" />
|
<img src={TermixIcon} alt="Termix Icon" className="w-[30px] h-[30px]" />
|
||||||
<h2 className="text-lg font-bold ml-[-2px]">Termix</h2>
|
<h2 className="text-lg font-bold ml-[-2px]">Termix</h2>
|
||||||
@@ -102,6 +111,7 @@ function App() {
|
|||||||
setActiveTab={setActiveTab}
|
setActiveTab={setActiveTab}
|
||||||
closeTab={closeTab}
|
closeTab={closeTab}
|
||||||
toggleSplit={toggleSplit}
|
toggleSplit={toggleSplit}
|
||||||
|
splitTabIds={splitTabIds}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -144,15 +154,17 @@ function App() {
|
|||||||
|
|
||||||
{/* Terminal Views */}
|
{/* Terminal Views */}
|
||||||
<div className="flex-1 relative p-4">
|
<div className="flex-1 relative p-4">
|
||||||
{splitTerminals.length > 0 ? (
|
{splitTabIds.length > 0 ? (
|
||||||
<div className={`grid ${splitTerminals.length === 1 ? 'grid-cols-1' : 'grid-cols-2'} gap-4 h-full`}>
|
<div className={`grid gap-4 h-full ${getGridLayout(splitTabIds.length)}`}>
|
||||||
{splitTerminals.map((terminal) => (
|
{splitTabIds.map(id => {
|
||||||
|
const terminal = terminals.find(t => t.id === id);
|
||||||
|
return terminal ? (
|
||||||
<div key={terminal.id} className="bg-neutral-800 rounded-lg overflow-hidden shadow-xl border-5 border-neutral-700 h-full">
|
<div key={terminal.id} className="bg-neutral-800 rounded-lg overflow-hidden shadow-xl border-5 border-neutral-700 h-full">
|
||||||
<NewTerminal
|
<NewTerminal
|
||||||
|
key={terminal.id}
|
||||||
hostConfig={terminal.hostConfig}
|
hostConfig={terminal.hostConfig}
|
||||||
ref={(ref) => {
|
ref={(ref) => {
|
||||||
if (ref && !terminal.terminalRef) {
|
if (ref && !terminal.terminalRef) {
|
||||||
// Store the terminal instance reference
|
|
||||||
setTerminals(prev => prev.map(t =>
|
setTerminals(prev => prev.map(t =>
|
||||||
t.id === terminal.id ? { ...t, terminalRef: ref } : t
|
t.id === terminal.id ? { ...t, terminalRef: ref } : t
|
||||||
));
|
));
|
||||||
@@ -160,19 +172,21 @@ function App() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
) : null;
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="absolute top-4 left-4 right-4 bottom-4">
|
<div className="absolute top-4 left-4 right-4 bottom-4">
|
||||||
{mainTerminal && (
|
{terminals.find(t => t.id === activeTab) && (
|
||||||
<div className="bg-neutral-800 rounded-lg overflow-hidden shadow-xl border-5 border-neutral-700 h-full">
|
<div className="bg-neutral-800 rounded-lg overflow-hidden shadow-xl border-5 border-neutral-700 h-full">
|
||||||
<NewTerminal
|
<NewTerminal
|
||||||
hostConfig={mainTerminal.hostConfig}
|
key={activeTab}
|
||||||
|
hostConfig={terminals.find(t => t.id === activeTab).hostConfig}
|
||||||
ref={(ref) => {
|
ref={(ref) => {
|
||||||
if (ref && !mainTerminal.terminalRef) {
|
const terminal = terminals.find(t => t.id === activeTab);
|
||||||
// Store the terminal instance reference
|
if (ref && terminal && !terminal.terminalRef) {
|
||||||
setTerminals(prev => prev.map(t =>
|
setTerminals(prev => prev.map(t =>
|
||||||
t.id === mainTerminal.id ? { ...t, terminalRef: ref } : t
|
t.id === activeTab ? { ...t, terminalRef: ref } : t
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -184,7 +198,7 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Modal for adding a host */}
|
{/* Modals */}
|
||||||
<AddHostModal
|
<AddHostModal
|
||||||
isHidden={isAddHostHidden}
|
isHidden={isAddHostHidden}
|
||||||
form={form}
|
form={form}
|
||||||
@@ -192,8 +206,6 @@ function App() {
|
|||||||
handleAddHost={handleAddHost}
|
handleAddHost={handleAddHost}
|
||||||
setIsAddHostHidden={setIsAddHostHidden}
|
setIsAddHostHidden={setIsAddHostHidden}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Launchpad Component */}
|
|
||||||
{isLaunchpadOpen && <Launchpad onClose={() => setIsLaunchpadOpen(false)} />}
|
{isLaunchpadOpen && <Launchpad onClose={() => setIsLaunchpadOpen(false)} />}
|
||||||
</div>
|
</div>
|
||||||
</CssVarsProvider>
|
</CssVarsProvider>
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import { Button, ButtonGroup } from "@mui/joy";
|
import { Button, ButtonGroup } from "@mui/joy";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
function TabList({ terminals, activeTab, setActiveTab, closeTab, toggleSplit, theme }) {
|
function TabList({ terminals, activeTab, setActiveTab, closeTab, toggleSplit, splitTabIds, theme }) {
|
||||||
return (
|
return (
|
||||||
<div className="inline-flex items-center h-full px-[0.5rem]">
|
<div className="inline-flex items-center h-full px-[0.5rem]">
|
||||||
{terminals.map((terminal, index) => (
|
{terminals.map((terminal, index) => (
|
||||||
<div key={terminal.id} className={index < terminals.length - 1 ? "mr-[0.5rem]" : ""}>
|
<div key={terminal.id} className={index < terminals.length - 1 ? "mr-[0.5rem]" : ""}>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setActiveTab(terminal.id)}
|
onClick={() => splitTabIds.length === 0 && setActiveTab(terminal.id)}
|
||||||
|
disabled={splitTabIds.length > 0}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
terminal.id === activeTab
|
terminal.id === activeTab
|
||||||
@@ -28,17 +29,26 @@ function TabList({ terminals, activeTab, setActiveTab, closeTab, toggleSplit, th
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => toggleSplit(terminal.id)}
|
onClick={() => toggleSplit(terminal.id)}
|
||||||
disabled={terminal.id === activeTab}
|
disabled={
|
||||||
|
(splitTabIds.length >= 1 && !splitTabIds.includes(terminal.id)) ||
|
||||||
|
(splitTabIds.length === 0 && terminal.id === activeTab)
|
||||||
|
}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: theme.palette.neutral[700],
|
backgroundColor: splitTabIds.includes(terminal.id)
|
||||||
|
? theme.palette.neutral[500]
|
||||||
|
: theme.palette.neutral[700],
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
"&:hover": { backgroundColor: theme.palette.neutral[300] },
|
"&:hover": { backgroundColor: theme.palette.neutral[300] },
|
||||||
borderTopRightRadius: "4px",
|
borderTopRightRadius: "4px",
|
||||||
borderBottomRightRadius: "4px",
|
borderBottomRightRadius: "4px",
|
||||||
height: "40px",
|
height: "40px",
|
||||||
fontSize: "1rem",
|
fontSize: "1rem",
|
||||||
opacity: terminal.id === activeTab ? 0.5 : 1,
|
opacity: (splitTabIds.length >= 1 && !splitTabIds.includes(terminal.id)) ||
|
||||||
cursor: terminal.id === activeTab ? "not-allowed" : "pointer",
|
(splitTabIds.length === 0 && terminal.id === activeTab) ? 0.5 : 1,
|
||||||
|
cursor: (splitTabIds.length >= 1 && !splitTabIds.includes(terminal.id)) ||
|
||||||
|
(splitTabIds.length === 0 && terminal.id === activeTab)
|
||||||
|
? "not-allowed"
|
||||||
|
: "pointer",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
/
|
/
|
||||||
@@ -70,6 +80,7 @@ TabList.propTypes = {
|
|||||||
setActiveTab: PropTypes.func.isRequired,
|
setActiveTab: PropTypes.func.isRequired,
|
||||||
closeTab: PropTypes.func.isRequired,
|
closeTab: PropTypes.func.isRequired,
|
||||||
toggleSplit: PropTypes.func.isRequired,
|
toggleSplit: PropTypes.func.isRequired,
|
||||||
|
splitTabIds: PropTypes.array.isRequired,
|
||||||
theme: PropTypes.object.isRequired,
|
theme: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user