feat: windows selection behavior, manual ctrl+c (#13315)

This commit is contained in:
Luke Parker
2026-02-13 07:38:27 +10:00
committed by GitHub
parent c0814da785
commit a8f2884521
4 changed files with 105 additions and 32 deletions
+40 -13
View File
@@ -1,6 +1,7 @@
import { render, useKeyboard, useRenderer, useTerminalDimensions } from "@opentui/solid"
import { Clipboard } from "@tui/util/clipboard"
import { TextAttributes } from "@opentui/core"
import { Selection } from "@tui/util/selection"
import { MouseButton, TextAttributes } from "@opentui/core"
import { RouteProvider, useRoute } from "@tui/context/route"
import { Switch, Match, createEffect, untrack, ErrorBoundary, createSignal, onMount, batch, Show, on } from "solid-js"
import { win32DisableProcessedInput, win32FlushInputBuffer, win32InstallCtrlCGuard } from "./win32"
@@ -210,6 +211,35 @@ function App() {
const exit = useExit()
const promptRef = usePromptRef()
useKeyboard((evt) => {
if (!Flag.OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT) return
if (!renderer.getSelection()) return
// Windows Terminal-like behavior:
// - Ctrl+C copies and dismisses selection
// - Esc dismisses selection
// - Most other key input dismisses selection and is passed through
if (evt.ctrl && evt.name === "c") {
if (!Selection.copy(renderer, toast)) {
renderer.clearSelection()
return
}
evt.preventDefault()
evt.stopPropagation()
return
}
if (evt.name === "escape") {
renderer.clearSelection()
evt.preventDefault()
evt.stopPropagation()
return
}
renderer.clearSelection()
})
// Wire up console copy-to-clipboard via opentui's onCopySelection callback
renderer.console.onCopySelection = async (text: string) => {
if (!text || text.length === 0) return
@@ -217,6 +247,7 @@ function App() {
await Clipboard.copy(text)
.then(() => toast.show({ message: "Copied to clipboard", variant: "info" }))
.catch(toast.error)
renderer.clearSelection()
}
const [terminalTitleEnabled, setTerminalTitleEnabled] = createSignal(kv.get("terminal_title_enabled", true))
@@ -703,19 +734,15 @@ function App() {
width={dimensions().width}
height={dimensions().height}
backgroundColor={theme.background}
onMouseUp={async () => {
if (Flag.OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT) {
renderer.clearSelection()
return
}
const text = renderer.getSelection()?.getSelectedText()
if (text && text.length > 0) {
await Clipboard.copy(text)
.then(() => toast.show({ message: "Copied to clipboard", variant: "info" }))
.catch(toast.error)
renderer.clearSelection()
}
onMouseDown={(evt) => {
if (!Flag.OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT) return
if (evt.button !== MouseButton.RIGHT) return
if (!Selection.copy(renderer, toast)) return
evt.preventDefault()
evt.stopPropagation()
}}
onMouseUp={Flag.OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT ? undefined : () => Selection.copy(renderer, toast)}
>
<Switch>
<Match when={route.data.type === "home"}>