Skip to content

Commit 3477b2e

Browse files
Apply PR #26596: feat(tui): expand pasted summaries on click
2 parents ea0947d + 5aad254 commit 3477b2e

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
@@ -806,6 +806,67 @@ export function Prompt(props: PromptProps) {
806806
)
807807
}
808808

809+
function expandPasteExtmark(extmark: { id: number; start: number; end: number }) {
810+
const partIndex = store.extmarkToPartIndex.get(extmark.id)
811+
const part = partIndex === undefined ? undefined : store.prompt.parts[partIndex]
812+
if (part?.type !== "text" || !part.source?.text) return false
813+
814+
const nextInput = store.prompt.input.slice(0, extmark.start) + part.text + store.prompt.input.slice(extmark.end)
815+
const delta = part.text.length - (extmark.end - extmark.start)
816+
const nextParts = store.prompt.parts
817+
.flatMap((item, index) => {
818+
if (index === partIndex) return []
819+
const next = structuredClone(unwrap(item))
820+
if (next.type === "agent" && next.source && next.source.start >= extmark.end) {
821+
next.source.start += delta
822+
next.source.end += delta
823+
}
824+
if (next.type === "file" && next.source?.text && next.source.text.start >= extmark.end) {
825+
next.source.text.start += delta
826+
next.source.text.end += delta
827+
}
828+
if (next.type === "text" && next.source?.text && next.source.text.start >= extmark.end) {
829+
next.source.text.start += delta
830+
next.source.text.end += delta
831+
}
832+
return [next]
833+
})
834+
.filter((item): item is PromptInfo["parts"][number] => item !== undefined)
835+
836+
input.setText(nextInput)
837+
setStore("prompt", {
838+
input: nextInput,
839+
parts: nextParts,
840+
})
841+
restoreExtmarksFromParts(nextParts)
842+
input.cursorOffset = extmark.start + part.text.length
843+
return true
844+
}
845+
846+
function expandPasteBlockAtMouse(event: MouseEvent) {
847+
if (event.button !== 0) return false
848+
const localX = event.x - input.x
849+
const localY = event.y - input.y
850+
if (localX < 0 || localY < 0 || localX >= input.width || localY >= input.height) return false
851+
852+
const previousOffset = input.cursorOffset
853+
input.editorView.setLocalSelection(localX, localY, localX, localY, undefined, undefined, true, false)
854+
input.editorView.resetLocalSelection()
855+
const offset = input.cursorOffset
856+
const extmark = input.extmarks.getAllForTypeId(promptPartTypeId).find((item) => {
857+
const partIndex = store.extmarkToPartIndex.get(item.id)
858+
const part = partIndex === undefined ? undefined : store.prompt.parts[partIndex]
859+
if (part?.type !== "text") return false
860+
return (offset >= item.start && offset <= item.end) || (offset + 1 >= item.start && offset + 1 <= item.end)
861+
})
862+
if (!extmark) {
863+
input.cursorOffset = previousOffset
864+
return false
865+
}
866+
867+
return expandPasteExtmark(extmark)
868+
}
869+
809870
const stashCommands = createMemo(() =>
810871
[
811872
{
@@ -1572,6 +1633,11 @@ export function Prompt(props: PromptProps) {
15721633
}, 0)
15731634
}}
15741635
onMouseDown={(r: MouseEvent) => r.target?.focus()}
1636+
onMouseUp={(event: MouseEvent) => {
1637+
if (!expandPasteBlockAtMouse(event)) return
1638+
event.preventDefault()
1639+
event.stopPropagation()
1640+
}}
15751641
focusedBackgroundColor={theme.backgroundElement}
15761642
cursorColor={props.disabled ? theme.backgroundElement : theme.text}
15771643
syntaxStyle={syntax()}

0 commit comments

Comments
 (0)