Dev 0.2.1 #30

Merged
LukeGus merged 47 commits from dev-0.2.1 into main 2025-03-24 03:17:56 +00:00
Showing only changes of commit 3d1ec6660d - Show all commits
+128 -26
View File
@@ -18,17 +18,31 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
if (!parentContainer || parentContainer.clientWidth === 0) return; if (!parentContainer || parentContainer.clientWidth === 0) return;
const parentWidth = parentContainer.clientWidth - 10; const parentWidth = parentContainer.clientWidth - 8;
const parentHeight = parentContainer.clientHeight - 10; const parentHeight = parentContainer.clientHeight - 12;
terminalContainer.style.width = `${parentWidth}px`; terminalContainer.style.width = `${parentWidth}px`;
terminalContainer.style.height = `${parentHeight}px`; terminalContainer.style.height = `${parentHeight}px`;
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (fitAddon.current && terminalInstance.current) {
fitAddon.current.fit(); fitAddon.current.fit();
if (socketRef.current && terminalInstance.current) {
const { cols, rows } = terminalInstance.current; if (socketRef.current) {
let { cols, rows } = terminalInstance.current;
const originalCols = cols;
const originalRows = rows;
cols += 1;
try {
terminalInstance.current.resize(cols, rows);
socketRef.current.emit("resize", { cols, rows }); socketRef.current.emit("resize", { cols, rows });
} catch (e) {
terminalInstance.current.resize(originalCols, originalRows);
socketRef.current.emit("resize", { cols: originalCols, rows: originalRows });
}
}
} }
}); });
}; };
@@ -50,6 +64,11 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
fontSize: 14, fontSize: 14,
scrollback: 1000, scrollback: 1000,
ignoreBracketedPasteMode: true, ignoreBracketedPasteMode: true,
fastScrollModifier: 'alt',
fastScrollSensitivity: 5,
letterSpacing: 0,
lineHeight: 1,
padding: 2,
}); });
terminalInstance.current.loadAddon(fitAddon.current); terminalInstance.current.loadAddon(fitAddon.current);
@@ -115,20 +134,48 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
terminalInstance.current.write(decoder.decode(new Uint8Array(data))); terminalInstance.current.write(decoder.decode(new Uint8Array(data)));
}); });
let buffer = "";
let bufferTimeout = null;
terminalInstance.current.onData((data) => { terminalInstance.current.onData((data) => {
buffer += data; if (data.length === 1) {
if (!bufferTimeout) { socketRef.current.emit("data", data);
bufferTimeout = setTimeout(() => { return;
socketRef.current.emit("data", buffer); }
buffer = "";
bufferTimeout = null; if (socketRef.current) {
}, 20); socketRef.current.emit("data", data);
} }
}); });
const getClipboardText = async () => {
try {
if (navigator.clipboard && navigator.clipboard.readText) {
return await navigator.clipboard.readText();
}
if (document.queryCommandSupported && document.queryCommandSupported('paste')) {
const textarea = document.createElement('textarea');
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.focus();
try {
document.execCommand('paste');
const text = textarea.value;
document.body.removeChild(textarea);
return text;
} catch (e) {
document.body.removeChild(textarea);
throw new Error('Fallback clipboard paste failed');
}
}
throw new Error('No clipboard access methods available');
} catch (err) {
console.error("Failed to read clipboard contents:", err);
return null;
}
};
terminalInstance.current.attachCustomKeyEventHandler(async (event) => { terminalInstance.current.attachCustomKeyEventHandler(async (event) => {
if ((event.ctrlKey || event.metaKey) && event.key === "v") { if ((event.ctrlKey || event.metaKey) && event.key === "v") {
event.preventDefault(); event.preventDefault();
@@ -136,17 +183,33 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
if (!socketRef.current) return false; if (!socketRef.current) return false;
try { try {
const text = await navigator.clipboard.readText(); const text = await getClipboardText();
if (!text) {
terminalInstance.current.write("\r\nClipboard access denied or empty\r\n");
return false;
}
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n"); const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
await Promise.all(lines.map(line => { for (let i = 0; i < lines.length; i++) {
return new Promise(resolve => { const line = lines[i];
if (i === 0) {
socketRef.current.emit("data", line);
}
else if (i < lines.length - 1) {
socketRef.current.emit("data", "\r" + line);
}
else if (i > 0) {
const endsWithNewline = text.endsWith("\n") || text.endsWith("\r\n") || text.endsWith("\r");
socketRef.current.emit("data", "\r" + line + (endsWithNewline ? "\r" : ""));
}
else if (lines.length === 1 && (text.endsWith("\n") || text.endsWith("\r\n") || text.endsWith("\r"))) {
socketRef.current.emit("data", line + "\r"); socketRef.current.emit("data", line + "\r");
resolve(); }
}); }
}));
} catch (err) { } catch (err) {
console.error("Failed to read clipboard contents:", err); console.error("Failed to process clipboard contents:", err);
} }
return false; return false;
@@ -155,11 +218,43 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
return true; return true;
}); });
const setClipboardText = (text) => {
try {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text);
return true;
}
if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
document.body.removeChild(textarea);
return true;
} catch (e) {
document.body.removeChild(textarea);
return false;
}
}
return false;
} catch (err) {
console.error("Failed to write to clipboard:", err);
return false;
}
};
terminalInstance.current.onKey(({ domEvent }) => { terminalInstance.current.onKey(({ domEvent }) => {
if (domEvent.key === "c" && (domEvent.ctrlKey || domEvent.metaKey)) { if (domEvent.key === "c" && (domEvent.ctrlKey || domEvent.metaKey)) {
const selection = terminalInstance.current.getSelection(); const selection = terminalInstance.current.getSelection();
if (selection) { if (selection) {
navigator.clipboard.writeText(selection); setClipboardText(selection);
} }
} }
}); });
@@ -206,14 +301,21 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
const parentContainer = terminalContainer.parentElement; const parentContainer = terminalContainer.parentElement;
if (!parentContainer) return; if (!parentContainer) return;
const observer = new ResizeObserver(() => { const resizeObserver = new ResizeObserver(() => {
resizeTerminal(); resizeTerminal();
}); });
observer.observe(parentContainer); resizeObserver.observe(parentContainer);
const handleWindowResize = () => {
resizeTerminal();
};
window.addEventListener('resize', handleWindowResize);
return () => { return () => {
observer.disconnect(); resizeObserver.disconnect();
window.removeEventListener('resize', handleWindowResize);
}; };
}, []); }, []);
@@ -226,7 +328,7 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
position: 'absolute', position: 'absolute',
width: '100%', width: '100%',
height: '100%', height: '100%',
transform: 'translateY(5px) translateX(5px)', transform: 'translateY(2px) translateX(3px)',
}} }}
/> />
); );