Merge branch 'dev' of ssh://git.commumedia.org:2222/alex/headquarter into dev
This commit is contained in:
@@ -21,7 +21,7 @@ interface TerminalProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const FONT_SIZE_KEY = "terminal-font-size";
|
const FONT_SIZE_KEY = "terminal-font-size";
|
||||||
const MIN_FONT_SIZE = 16;
|
const MIN_FONT_SIZE = 10;
|
||||||
const MAX_FONT_SIZE = 24;
|
const MAX_FONT_SIZE = 24;
|
||||||
const RECONNECT_ATTEMPTS = 3;
|
const RECONNECT_ATTEMPTS = 3;
|
||||||
const RECONNECT_DELAY_BASE = 1000;
|
const RECONNECT_DELAY_BASE = 1000;
|
||||||
@@ -163,11 +163,17 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
|
|||||||
term.loadAddon(new WebLinksAddon());
|
term.loadAddon(new WebLinksAddon());
|
||||||
|
|
||||||
term.open(terminalRef.current);
|
term.open(terminalRef.current);
|
||||||
fitAddon.fit();
|
|
||||||
|
|
||||||
// Connect WebSocket
|
// Connect WebSocket
|
||||||
const ws = connectWebSocket();
|
const ws = connectWebSocket();
|
||||||
|
|
||||||
|
// Fit after layout settles - use rAF to ensure DOM is ready
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
fitAddon.fit();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Handle terminal input
|
// Handle terminal input
|
||||||
term.onData((data) => {
|
term.onData((data) => {
|
||||||
if (ws.readyState !== WebSocket.OPEN) return;
|
if (ws.readyState !== WebSocket.OPEN) return;
|
||||||
@@ -242,7 +248,8 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
|
|||||||
ws.close();
|
ws.close();
|
||||||
term.dispose();
|
term.dispose();
|
||||||
};
|
};
|
||||||
}, [instanceId, connectWebSocket, calculateFontSize]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [instanceId, connectWebSocket]);
|
||||||
|
|
||||||
// Update parent about status changes
|
// Update parent about status changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -266,9 +273,17 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
|
|||||||
const newSize = Math.max(MIN_FONT_SIZE, Math.min(MAX_FONT_SIZE, fontSize + delta));
|
const newSize = Math.max(MIN_FONT_SIZE, Math.min(MAX_FONT_SIZE, fontSize + delta));
|
||||||
setFontSize(newSize);
|
setFontSize(newSize);
|
||||||
localStorage.setItem(FONT_SIZE_KEY, newSize.toString());
|
localStorage.setItem(FONT_SIZE_KEY, newSize.toString());
|
||||||
if (termRef.current) {
|
if (termRef.current && fitAddonRef.current) {
|
||||||
termRef.current.options.fontSize = newSize;
|
termRef.current.options.fontSize = newSize;
|
||||||
fitAddonRef.current?.fit();
|
requestAnimationFrame(() => {
|
||||||
|
if (termRef.current && fitAddonRef.current) {
|
||||||
|
try {
|
||||||
|
fitAddonRef.current.fit();
|
||||||
|
} catch {
|
||||||
|
// Ignore fit errors during re-initialization
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -345,26 +360,22 @@ export const TerminalComponent: React.FC<TerminalProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="terminal-header-right">
|
<div className="terminal-header-right">
|
||||||
{isMobile && (
|
<button
|
||||||
<>
|
className="terminal-header-button"
|
||||||
<button
|
onClick={() => handleFontSizeChange(-1)}
|
||||||
className="terminal-header-button"
|
type="button"
|
||||||
onClick={() => handleFontSizeChange(-1)}
|
aria-label="Decrease font size"
|
||||||
type="button"
|
>
|
||||||
aria-label="Decrease font size"
|
A-
|
||||||
>
|
</button>
|
||||||
A-
|
<button
|
||||||
</button>
|
className="terminal-header-button"
|
||||||
<button
|
onClick={() => handleFontSizeChange(1)}
|
||||||
className="terminal-header-button"
|
type="button"
|
||||||
onClick={() => handleFontSizeChange(1)}
|
aria-label="Increase font size"
|
||||||
type="button"
|
>
|
||||||
aria-label="Increase font size"
|
A+
|
||||||
>
|
</button>
|
||||||
A+
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<button className="terminal-close" onClick={onClose} type="button">
|
<button className="terminal-close" onClick={onClose} type="button">
|
||||||
Close
|
Close
|
||||||
|
|||||||
+22
-1
@@ -166,6 +166,9 @@ a {
|
|||||||
.shell-content {
|
.shell-content {
|
||||||
padding: 1.25rem;
|
padding: 1.25rem;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.eyebrow {
|
.eyebrow {
|
||||||
@@ -2482,7 +2485,8 @@ a.nav-item,
|
|||||||
.terminal-page {
|
.terminal-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
padding: var(--space-4);
|
padding: var(--space-4);
|
||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
}
|
}
|
||||||
@@ -2592,12 +2596,24 @@ a.nav-item,
|
|||||||
|
|
||||||
.terminal-container .xterm {
|
.terminal-container .xterm {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-container .xterm-viewport {
|
.terminal-container .xterm-viewport {
|
||||||
|
height: 100% !important;
|
||||||
|
width: 100% !important;
|
||||||
background: #1e1e1e !important;
|
background: #1e1e1e !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.terminal-container .xterm-screen {
|
||||||
|
height: 100% !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-container canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive terminal */
|
/* Responsive terminal */
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
.terminal-page {
|
.terminal-page {
|
||||||
@@ -3058,6 +3074,8 @@ a.nav-item,
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Special Keys Strip */
|
/* Special Keys Strip */
|
||||||
@@ -3183,6 +3201,8 @@ a.nav-item,
|
|||||||
border: none;
|
border: none;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal-wrapper.mobile .terminal-header {
|
.terminal-wrapper.mobile .terminal-header {
|
||||||
@@ -3252,6 +3272,7 @@ a.nav-item,
|
|||||||
|
|
||||||
.terminal-container {
|
.terminal-container {
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user