Fixed up the split screen a lot. 3+ screens at once causes inability to unsplit. Still a WIP.

This commit is contained in:
Karmaa
2025-03-05 00:43:57 -06:00
parent 2038a84c15
commit a6295e5e7e
2 changed files with 118 additions and 124 deletions

View File

@@ -67,29 +67,35 @@ 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);
} }
}; };
// Toggle split for a specific tab // Toggle split for a terminal tab
const toggleSplit = (id) => { const toggleSplit = (id) => {
setSplitTabIds(prev => { if (splitTabIds.length >= 3) return; // Prevent more than 2 tabs from splitting
if (prev.includes(id)) {
return prev.filter(tabId => tabId !== id); setSplitTabIds((prev) =>
} else { prev.includes(id) ? prev.filter((splitId) => splitId !== id) : [...prev, id]
if (prev.length >= 1) return prev; // Limit to 1 split );
return [...prev, id, activeTab].filter((tabId, index, self) => self.indexOf(tabId) === index);
} if (splitTabIds.includes(id)) {
}); setSplitTabIds((prev) => prev.filter((splitId) => splitId !== id));
}
}; };
// Get grid layout class based on split count // Determine the layout based on the number of split tabs
const getGridLayout = (count) => { const getLayoutStyle = () => {
if (count === 1) return 'grid-cols-1'; if (splitTabIds.length === 1) {
if (count === 2) return 'grid-cols-2'; // Horizontal split (2 tabs: left-right)
return 'grid-cols-1'; return "flex flex-row h-full gap-4";
} else if (splitTabIds.length > 1) {
// 2x2 Grid layout (4 tabs max), with evenly spaced rows
return "grid grid-cols-2 grid-rows-2 gap-4 h-full overflow-hidden";
}
// No split, main tab takes the entire screen
return "flex flex-col h-full";
}; };
return ( return (
@@ -153,48 +159,29 @@ function App() {
</div> </div>
{/* Terminal Views */} {/* Terminal Views */}
<div className="flex-1 relative p-4"> <div className={`relative p-4 ${getLayoutStyle()}`}>
{splitTabIds.length > 0 ? ( {terminals.map((terminal) => (
<div className={`grid gap-4 h-full ${getGridLayout(splitTabIds.length)}`}> <div
{splitTabIds.map(id => { key={terminal.id}
const terminal = terminals.find(t => t.id === id); className={`bg-neutral-800 rounded-lg overflow-hidden shadow-xl border-5 border-neutral-700 ${splitTabIds.includes(terminal.id) || activeTab === terminal.id ? "block" : "hidden"} flex-1`}
return terminal ? ( >
<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}
key={terminal.id} hostConfig={terminal.hostConfig}
hostConfig={terminal.hostConfig} ref={(ref) => {
ref={(ref) => { if (ref && !terminal.terminalRef) {
if (ref && !terminal.terminalRef) { setTerminals((prev) =>
setTerminals(prev => prev.map(t => prev.map((t) =>
t.id === terminal.id ? { ...t, terminalRef: ref } : t t.id === terminal.id
)); ? { ...t, terminalRef: ref }
} : t
}} )
/> );
</div> }
) : null; }}
})} />
</div> </div>
) : ( ))}
<div className="absolute top-4 left-4 right-4 bottom-4">
{terminals.find(t => t.id === activeTab) && (
<div className="bg-neutral-800 rounded-lg overflow-hidden shadow-xl border-5 border-neutral-700 h-full">
<NewTerminal
key={activeTab}
hostConfig={terminals.find(t => t.id === activeTab).hostConfig}
ref={(ref) => {
const terminal = terminals.find(t => t.id === activeTab);
if (ref && terminal && !terminal.terminalRef) {
setTerminals(prev => prev.map(t =>
t.id === activeTab ? { ...t, terminalRef: ref } : t
));
}
}}
/>
</div>
)}
</div>
)}
</div> </div>
</div> </div>
@@ -212,4 +199,4 @@ function App() {
); );
} }
export default App; export default App;

View File

@@ -2,74 +2,81 @@ import { Button, ButtonGroup } from "@mui/joy";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
function TabList({ terminals, activeTab, setActiveTab, closeTab, toggleSplit, splitTabIds, theme }) { function TabList({ terminals, activeTab, setActiveTab, closeTab, toggleSplit, splitTabIds, theme }) {
const isSplitScreenActive = splitTabIds.length > 0;
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]" : ""}> const isActive = terminal.id === activeTab;
<ButtonGroup> const isSplit = splitTabIds.includes(terminal.id);
<Button
onClick={() => splitTabIds.length === 0 && setActiveTab(terminal.id)} // Disable split screen button for the active tab (before and after splitting)
disabled={splitTabIds.length > 0} const isSplitButtonDisabled = isActive && !isSplitScreenActive || splitTabIds.length >= 3 && !isSplit;
sx={{
backgroundColor: return (
terminal.id === activeTab <div key={terminal.id} className={index < terminals.length - 1 ? "mr-[0.5rem]" : ""}>
? theme.palette.neutral[500] <ButtonGroup>
: theme.palette.neutral[800], {/* Set active tab button */}
color: theme.palette.text.primary, <Button
"&:hover": { backgroundColor: theme.palette.neutral[300] }, onClick={() => setActiveTab(terminal.id)}
borderTopLeftRadius: "4px", disabled={isSplit} // Disabled for split screen tabs
borderBottomLeftRadius: "4px", sx={{
height: "40px", backgroundColor:
whiteSpace: "nowrap", isActive ? theme.palette.neutral[500] : theme.palette.neutral[800],
overflow: "hidden", color: theme.palette.text.primary,
textOverflow: "ellipsis", "&:hover": { backgroundColor: theme.palette.neutral[300] },
}} ":disabled": { backgroundColor: theme.palette.neutral[800] },
> borderTopLeftRadius: "4px",
{terminal.title} borderBottomLeftRadius: "4px",
</Button> height: "40px",
<Button whiteSpace: "nowrap",
onClick={() => toggleSplit(terminal.id)} overflow: "hidden",
disabled={ textOverflow: "ellipsis",
(splitTabIds.length >= 1 && !splitTabIds.includes(terminal.id)) || }}
(splitTabIds.length === 0 && terminal.id === activeTab) >
} {terminal.title}
sx={{ </Button>
backgroundColor: splitTabIds.includes(terminal.id) {/* Split screen button */}
? theme.palette.neutral[500] <Button
: theme.palette.neutral[700], onClick={() => toggleSplit(terminal.id)}
color: theme.palette.text.primary, disabled={isSplitButtonDisabled || isActive} // Disable for the active tab (before and after split)
"&:hover": { backgroundColor: theme.palette.neutral[300] }, sx={{
borderTopRightRadius: "4px", backgroundColor: isSplit
borderBottomRightRadius: "4px", ? theme.palette.neutral[500] // Split tabs get color 700
height: "40px", : theme.palette.neutral[700], // Active tab has disabled color
fontSize: "1rem", color: theme.palette.text.primary,
opacity: (splitTabIds.length >= 1 && !splitTabIds.includes(terminal.id)) || ":disabled": { backgroundColor: theme.palette.neutral[800] },
(splitTabIds.length === 0 && terminal.id === activeTab) ? 0.5 : 1, "&:hover": { backgroundColor: theme.palette.neutral[300] },
cursor: (splitTabIds.length >= 1 && !splitTabIds.includes(terminal.id)) || borderTopRightRadius: "4px",
(splitTabIds.length === 0 && terminal.id === activeTab) borderBottomRightRadius: "4px",
? "not-allowed" height: "40px",
: "pointer", fontSize: "1rem",
}} cursor: isSplitButtonDisabled ? "not-allowed" : "pointer",
> }}
/ >
</Button> /
<Button </Button>
onClick={() => closeTab(terminal.id)} {/* Close tab button */}
sx={{ <Button
backgroundColor: theme.palette.neutral[700], onClick={() => closeTab(terminal.id)}
color: theme.palette.text.primary, disabled={isSplitScreenActive && isActive || isSplit}
"&:hover": { backgroundColor: theme.palette.neutral[300] }, sx={{
borderTopRightRadius: "4px", backgroundColor: theme.palette.neutral[700],
borderBottomRightRadius: "4px", color: theme.palette.text.primary,
height: "40px", "&:hover": { backgroundColor: theme.palette.neutral[300] },
fontSize: "1rem", ":disabled": { backgroundColor: theme.palette.neutral[800] },
}} borderTopRightRadius: "4px",
> borderBottomRightRadius: "4px",
× height: "40px",
</Button> fontSize: "1rem",
</ButtonGroup> }}
</div> >
))} ×
</Button>
</ButtonGroup>
</div>
);
})}
</div> </div>
); );
} }
@@ -84,4 +91,4 @@ TabList.propTypes = {
theme: PropTypes.object.isRequired, theme: PropTypes.object.isRequired,
}; };
export default TabList; export default TabList;