Skip to content

Commit db9c41b

Browse files
Apply PR #26596: feat(tui): expand pasted summaries on click
2 parents 399f7e1 + 5aad254 commit db9c41b

1 file changed

Lines changed: 66 additions & 0 deletions

File tree

  • packages/opencode/src/cli/cmd/tui/component/prompt

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,67 @@ export function Prompt(props: PromptProps) {
723723
)
724724
}
725725

726+
function expandPasteExtmark(extmark: { id: number; start: number; end: number }) {
727+
const partIndex = store.extmarkToPartIndex.get(extmark.id)
728+
const part = partIndex === undefined ? undefined : store.prompt.parts[partIndex]
729+
if (part?.type !== "text" || !part.source?.text) return false
730+
731+
const nextInput = store.prompt.input.slice(0, extmark.start) + part.text + store.prompt.input.slice(extmark.end)
732+
const delta = part.text.length - (extmark.end - extmark.start)
733+
const nextParts = store.prompt.parts
734+
.flatMap((item, index) => {
735+
if (index === partIndex) return []
736+
const next = structuredClone(unwrap(item))
737+
if (next.type === "agent" && next.source && next.source.start >= extmark.end) {
738+
next.source.start += delta
739+
next.source.end += delta
740+
}
741+
if (next.type === "file" && next.source?.text && next.source.text.start >= extmark.end) {
742+
next.source.text.start += delta
743+
next.source.text.end += delta
744+
}
745+
if (next.type === "text" && next.source?.text && next.source.text.start >= extmark.end) {
746+
next.source.text.start += delta
747+
next.source.text.end += delta
748+
}
749+
return [next]
750+
})
751+
.filter((item): item is PromptInfo["parts"][number] => item !== undefined)
752+
753+
input.setText(nextInput)
754+
setStore("prompt", {
755+
input: nextInput,
756+
parts: nextParts,
757+
})
758+
restoreExtmarksFromParts(nextParts)
759+
input.cursorOffset = extmark.start + part.text.length
760+
return true
761+
}
762+
763+
function expandPasteBlockAtMouse(event: MouseEvent) {
764+
if (event.button !== 0) return false
765+
const localX = event.x - input.x
766+
const localY = event.y - input.y
767+
if (localX < 0 || localY < 0 || localX >= input.width || localY >= input.height) return false
768+
769+
const previousOffset = input.cursorOffset
770+
input.editorView.setLocalSelection(localX, localY, localX, localY, undefined, undefined, true, false)
771+
input.editorView.resetLocalSelection()
772+
const offset = input.cursorOffset
773+
const extmark = input.extmarks.getAllForTypeId(promptPartTypeId).find((item) => {
774+
const partIndex = store.extmarkToPartIndex.get(item.id)
775+
const part = partIndex === undefined ? undefined : store.prompt.parts[partIndex]
776+
if (part?.type !== "text") return false
777+
return (offset >= item.start && offset <= item.end) || (offset + 1 >= item.start && offset + 1 <= item.end)
778+
})
779+
if (!extmark) {
780+
input.cursorOffset = previousOffset
781+
return false
782+
}
783+
784+
return expandPasteExtmark(extmark)
785+
}
786+
726787
const stashCommands = createMemo(() =>
727788
[
728789
{
@@ -1458,6 +1519,11 @@ export function Prompt(props: PromptProps) {
14581519
}, 0)
14591520
}}
14601521
onMouseDown={(r: MouseEvent) => r.target?.focus()}
1522+
onMouseUp={(event: MouseEvent) => {
1523+
if (!expandPasteBlockAtMouse(event)) return
1524+
event.preventDefault()
1525+
event.stopPropagation()
1526+
}}
14611527
focusedBackgroundColor={theme.backgroundElement}
14621528
cursorColor={props.disabled ? theme.backgroundElement : theme.text}
14631529
syntaxStyle={syntax()}

0 commit comments

Comments
 (0)