summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/components
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-11 07:17:01 -0600
committerAdam <[email protected]>2026-02-11 07:19:33 -0600
commitef5ec5dc28ce668585f11c76d89622d6c13724f0 (patch)
tree5ca651cde3c1850293afa4c91fe010966fdb33bc /packages/app/src/components
parentc426cb0f14f1b130b7819c4f0ef1a33a327c1444 (diff)
downloadopencode-ef5ec5dc28ce668585f11c76d89622d6c13724f0.tar.gz
opencode-ef5ec5dc28ce668585f11c76d89622d6c13724f0.zip
fix(app): terminal copy/paste
Diffstat (limited to 'packages/app/src/components')
-rw-r--r--packages/app/src/components/terminal.tsx55
1 files changed, 25 insertions, 30 deletions
diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx
index 7a1b81f0c..2527c74ec 100644
--- a/packages/app/src/components/terminal.tsx
+++ b/packages/app/src/components/terminal.tsx
@@ -130,11 +130,12 @@ export const Terminal = (props: TerminalProps) => {
const t = term
if (!t) return
t.focus()
+ t.textarea?.focus()
setTimeout(() => t.textarea?.focus(), 0)
}
const handlePointerDown = () => {
const activeElement = document.activeElement
- if (activeElement instanceof HTMLElement && activeElement !== container) {
+ if (activeElement instanceof HTMLElement && activeElement !== container && !container.contains(activeElement)) {
activeElement.blur()
}
focusTerminal()
@@ -204,44 +205,32 @@ export const Terminal = (props: TerminalProps) => {
ghostty = g
term = t
- const copy = () => {
+ const handleCopy = (event: ClipboardEvent) => {
const selection = t.getSelection()
- if (!selection) return false
-
- const body = document.body
- if (body) {
- const textarea = document.createElement("textarea")
- textarea.value = selection
- textarea.setAttribute("readonly", "")
- textarea.style.position = "fixed"
- textarea.style.opacity = "0"
- body.appendChild(textarea)
- textarea.select()
- const copied = document.execCommand("copy")
- body.removeChild(textarea)
- if (copied) return true
- }
+ if (!selection) return
- const clipboard = navigator.clipboard
- if (clipboard?.writeText) {
- clipboard.writeText(selection).catch(() => {})
- return true
- }
+ const clipboard = event.clipboardData
+ if (!clipboard) return
+
+ event.preventDefault()
+ clipboard.setData("text/plain", selection)
+ }
+
+ const handlePaste = (event: ClipboardEvent) => {
+ const clipboard = event.clipboardData
+ const text = clipboard?.getData("text/plain") ?? clipboard?.getData("text") ?? ""
+ if (!text) return
- return false
+ event.preventDefault()
+ event.stopPropagation()
+ t.paste(text)
}
t.attachCustomKeyEventHandler((event) => {
const key = event.key.toLowerCase()
if (event.ctrlKey && event.shiftKey && !event.metaKey && key === "c") {
- copy()
- return true
- }
-
- if (event.metaKey && !event.ctrlKey && !event.altKey && key === "c") {
- if (!t.hasSelection()) return true
- copy()
+ document.execCommand("copy")
return true
}
@@ -252,6 +241,12 @@ export const Terminal = (props: TerminalProps) => {
return matchKeybind(keybinds, event)
})
+ container.addEventListener("copy", handleCopy, true)
+ cleanups.push(() => container.removeEventListener("copy", handleCopy, true))
+
+ container.addEventListener("paste", handlePaste, true)
+ cleanups.push(() => container.removeEventListener("paste", handlePaste, true))
+
const fit = new mod.FitAddon()
const serializer = new SerializeAddon()
cleanups.push(() => disposeIfDisposable(fit))