Skip to content

Commit 564cde3

Browse files
authored
fix(tui): copy pasted prompt content (#28156)
1 parent c813927 commit 564cde3

3 files changed

Lines changed: 21 additions & 3 deletions

File tree

packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { MessageID, PartID } from "@/session/schema"
2828
import { createStore, produce, unwrap } from "solid-js/store"
2929
import { usePromptHistory, type PromptInfo } from "./history"
3030
import { computePromptTraits } from "./traits"
31-
import { assign } from "./part"
31+
import { assign, expandPastedTextPlaceholders } from "./part"
3232
import { usePromptStash } from "./stash"
3333
import { DialogStash } from "../dialog-stash"
3434
import { type AutocompleteRef, Autocomplete } from "./autocomplete"
@@ -1544,6 +1544,9 @@ export function Prompt(props: PromptProps) {
15441544
}}
15451545
ref={(r: TextareaRenderable) => {
15461546
input = r
1547+
Object.assign(r, {
1548+
getClipboardText: (text: string) => expandPastedTextPlaceholders(text, store.prompt.parts),
1549+
})
15471550
setInputTarget(r)
15481551
if (promptPartTypeId === 0) {
15491552
promptPartTypeId = input.extmarks.registerType("prompt-part")

packages/opencode/src/cli/cmd/tui/component/prompt/part.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,10 @@ export function assign(part: Item): Item & { id: PartID } {
1414
id: PartID.ascending(),
1515
}
1616
}
17+
18+
export function expandPastedTextPlaceholders(text: string, parts: PromptInfo["parts"]) {
19+
return parts.reduce((result, part) => {
20+
if (part.type !== "text" || !part.source?.text) return result
21+
return result.replace(part.source.text.value, part.text)
22+
}, text)
23+
}

packages/opencode/src/cli/cmd/tui/util/selection.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type Toast = {
77

88
type FocusableSelectionTarget = {
99
hasSelection: () => boolean
10+
getClipboardText?: (text: string) => string
1011
}
1112

1213
type Renderer = {
@@ -23,10 +24,17 @@ type SelectionKeyEvent = {
2324
}
2425

2526
export function copy(renderer: Renderer, toast: Toast): boolean {
26-
const text = renderer.getSelection()?.getSelectedText()
27+
const selection = renderer.getSelection()
28+
if (!selection) return false
29+
30+
const text = selection.getSelectedText()
2731
if (!text) return false
2832

29-
Clipboard.copy(text)
33+
const focus = renderer.currentFocusedRenderable
34+
const clipboardText =
35+
focus?.getClipboardText && selection.selectedRenderables.includes(focus) ? focus.getClipboardText(text) : text
36+
37+
Clipboard.copy(clipboardText)
3038
.then(() => toast.show({ message: "Copied to clipboard", variant: "info" }))
3139
.catch(toast.error)
3240

0 commit comments

Comments
 (0)