Skip to content

Commit 55e18ba

Browse files
Apply PR #26596: feat(tui): expand pasted summaries on click
2 parents 78c72ba + 5aad254 commit 55e18ba

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
@@ -725,6 +725,67 @@ export function Prompt(props: PromptProps) {
725725
)
726726
}
727727

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

0 commit comments

Comments
 (0)