mirror of
https://github.com/louislam/dockge.git
synced 2026-05-21 14:02:17 +00:00
Add clipboard copy/paste support to terminal component (#822)
Co-authored-by: cmcooper1980 <31871143+cmcooper1980@users.noreply.github.com>
This commit is contained in:
@@ -101,6 +101,14 @@ export default {
|
||||
this.terminal.open(this.$refs.terminal);
|
||||
this.terminal.focus();
|
||||
|
||||
// Add right-click context menu handler for paste
|
||||
this.$refs.terminal.addEventListener('contextmenu', this.handleContextMenu);
|
||||
|
||||
// Add selection handler for copy to clipboard
|
||||
this.terminal.onSelectionChange(() => {
|
||||
this.handleSelection();
|
||||
});
|
||||
|
||||
// Notify parent component when data is received
|
||||
this.terminal.onCursorMove(() => {
|
||||
console.debug("onData triggered");
|
||||
@@ -135,6 +143,7 @@ export default {
|
||||
window.removeEventListener("resize", this.onResizeEvent); // Remove the resize event listener from the window object.
|
||||
this.$root.unbindTerminal(this.name);
|
||||
this.terminal.dispose();
|
||||
this.$refs.terminal?.removeEventListener('contextmenu', this.handleContextMenu);
|
||||
},
|
||||
|
||||
methods: {
|
||||
@@ -163,6 +172,14 @@ export default {
|
||||
this.terminalInputBuffer = "";
|
||||
},
|
||||
|
||||
clearCurrentLine() {
|
||||
// Move cursor to the beginning of the input and clear it
|
||||
const backspaces = "\b".repeat(this.cursorPosition);
|
||||
const spaces = " ".repeat(this.terminalInputBuffer.length);
|
||||
const moreBackspaces = "\b".repeat(this.terminalInputBuffer.length);
|
||||
this.terminal.write(backspaces + spaces + moreBackspaces);
|
||||
},
|
||||
|
||||
mainTerminalConfig() {
|
||||
this.terminal.onKey(e => {
|
||||
console.debug("Encode: " + JSON.stringify(e.key));
|
||||
@@ -183,12 +200,24 @@ export default {
|
||||
});
|
||||
} else if (e.key === "\u007F") { // Backspace
|
||||
if (this.cursorPosition > 0) {
|
||||
const trimmedTextBeforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition - 1);
|
||||
const textAfterCursor = this.terminalInputBuffer.slice(this.cursorPosition);
|
||||
const clearAfterCursor = " ".repeat(textAfterCursor.length) + "\b \b".repeat(textAfterCursor.length + 1);
|
||||
this.terminalInputBuffer = trimmedTextBeforeCursor + textAfterCursor;
|
||||
this.terminal.write(clearAfterCursor + textAfterCursor + "\b".repeat(textAfterCursor.length));
|
||||
// Remove character to the left of cursor
|
||||
const beforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition - 1);
|
||||
const afterCursor = this.terminalInputBuffer.slice(this.cursorPosition);
|
||||
this.terminalInputBuffer = beforeCursor + afterCursor;
|
||||
this.cursorPosition--;
|
||||
|
||||
// Redraw the line
|
||||
this.terminal.write("\b" + afterCursor + " \b".repeat(afterCursor.length + 1));
|
||||
}
|
||||
} else if (e.key === "\u001B\u005B\u0033\u007E") { // Delete key
|
||||
if (this.cursorPosition < this.terminalInputBuffer.length) {
|
||||
// Remove character to the right of cursor
|
||||
const beforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition);
|
||||
const afterCursor = this.terminalInputBuffer.slice(this.cursorPosition + 1);
|
||||
this.terminalInputBuffer = beforeCursor + afterCursor;
|
||||
|
||||
// Redraw the line from cursor position
|
||||
this.terminal.write(afterCursor + " \b".repeat(afterCursor.length + 1));
|
||||
}
|
||||
} else if (e.key === "\u001B\u005B\u0041" || e.key === "\u001B\u005B\u0042") { // UP OR DOWN
|
||||
// Do nothing
|
||||
@@ -206,6 +235,8 @@ export default {
|
||||
console.debug("Ctrl + C");
|
||||
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, e.key);
|
||||
this.removeInput();
|
||||
} else if (e.key === "\u0016" || (e.domEvent?.ctrlKey && e.key.toLowerCase() === "v")) { // Ctrl + V
|
||||
this.handlePaste();
|
||||
} else if (e.key === "\u0009" || e.key.startsWith("\u001B")) { // TAB or other special keys
|
||||
// Do nothing
|
||||
} else {
|
||||
@@ -214,14 +245,18 @@ export default {
|
||||
this.terminalInputBuffer = textBeforeCursor + e.key + textAfterCursor;
|
||||
this.terminal.write(e.key + textAfterCursor + "\b".repeat(textAfterCursor.length));
|
||||
this.cursorPosition++;
|
||||
this.terminalInputBuffer += e.key;
|
||||
this.terminal.write(e.key);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
interactiveTerminalConfig() {
|
||||
this.terminal.onKey(e => {
|
||||
// Handle Ctrl+V for paste
|
||||
if (e.key === "\u0016" || (e.ctrlKey && e.key === "v")) {
|
||||
this.handlePaste();
|
||||
return;
|
||||
}
|
||||
|
||||
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, e.key, (res) => {
|
||||
if (!res.ok) {
|
||||
this.$root.toastRes(res);
|
||||
@@ -252,7 +287,87 @@ export default {
|
||||
let rows = this.terminal.rows;
|
||||
let cols = this.terminal.cols;
|
||||
this.$root.emitAgent(this.endpoint, "terminalResize", this.name, rows, cols);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle clipboard paste operation
|
||||
*/
|
||||
async handlePaste() {
|
||||
try {
|
||||
const text = await navigator.clipboard.readText();
|
||||
if (text) {
|
||||
this.pasteText(text);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to read from clipboard:", error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Paste text into the terminal based on current mode
|
||||
*/
|
||||
pasteText(text) {
|
||||
if (this.mode === "mainTerminal") {
|
||||
// For main terminal, insert text at current cursor position
|
||||
const beforeCursor = this.terminalInputBuffer.slice(0, this.cursorPosition);
|
||||
const afterCursor = this.terminalInputBuffer.slice(this.cursorPosition);
|
||||
|
||||
// Update the buffer with inserted text
|
||||
this.terminalInputBuffer = beforeCursor + text + afterCursor;
|
||||
|
||||
// Clear the current line and rewrite it
|
||||
this.clearCurrentLine();
|
||||
this.terminal.write(this.terminalInputBuffer);
|
||||
|
||||
// Move cursor to the correct position (after the pasted text)
|
||||
this.cursorPosition += text.length;
|
||||
const backspaces = "\b".repeat(afterCursor.length);
|
||||
this.terminal.write(backspaces);
|
||||
|
||||
} else if (this.mode === "interactive") {
|
||||
// For interactive terminal, send directly to server
|
||||
this.$root.emitAgent(this.endpoint, "terminalInput", this.name, text, (res) => {
|
||||
if (!res.ok) {
|
||||
this.$root.toastRes(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle right-click context menu for paste operation
|
||||
*/
|
||||
handleContextMenu(event) {
|
||||
// Prevent default context menu
|
||||
event.preventDefault();
|
||||
|
||||
// Only handle paste for modes that support input
|
||||
if (this.mode === "mainTerminal" || this.mode === "interactive") {
|
||||
this.handlePaste();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle text selection in terminal - copy to clipboard
|
||||
*/
|
||||
handleSelection() {
|
||||
const selectedText = this.terminal.getSelection();
|
||||
if (selectedText && selectedText.length > 0) {
|
||||
this.copyToClipboard(selectedText);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy text to clipboard
|
||||
*/
|
||||
async copyToClipboard(text) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
console.debug("Text copied to clipboard:", text);
|
||||
} catch (error) {
|
||||
console.error("Failed to copy to clipboard:", error);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user