mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
tui: improve agent selection and prompt editing experience
- Filter out built-in agents from autocomplete to show only custom agents - Add proper agent part handling when selecting agents from autocomplete - Fix cursor positioning and part management when editing prompts with agents
This commit is contained in:
parent
502615b98d
commit
362d137e0d
3 changed files with 43 additions and 26 deletions
|
|
@ -105,17 +105,28 @@ export function Autocomplete(props: {
|
|||
const agents = createMemo(() => {
|
||||
if (store.index !== 0) return []
|
||||
const agents = sync.data.agent
|
||||
return agents.map(
|
||||
(agent): AutocompleteOption => ({
|
||||
display: "@" + agent.name,
|
||||
onSelect: () => {
|
||||
props.setPrompt((draft) => {
|
||||
const append = "@" + agent.name + " "
|
||||
draft.input = append
|
||||
})
|
||||
},
|
||||
}),
|
||||
)
|
||||
return agents
|
||||
.filter((agent) => !agent.builtIn && agent.mode !== "primary")
|
||||
.map(
|
||||
(agent): AutocompleteOption => ({
|
||||
display: "@" + agent.name,
|
||||
onSelect: () => {
|
||||
props.setPrompt((draft) => {
|
||||
const append = "@" + agent.name + " "
|
||||
draft.input = append
|
||||
draft.parts.push({
|
||||
type: "agent",
|
||||
source: {
|
||||
start: store.index,
|
||||
end: store.index + agent.name.length + 1,
|
||||
value: "@" + agent.name,
|
||||
},
|
||||
name: agent.name,
|
||||
})
|
||||
})
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
const session = createMemo(() => (props.sessionID ? sync.session.get(props.sessionID) : undefined))
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import { createStore, produce } from "solid-js/store"
|
|||
import { clone } from "remeda"
|
||||
import { createSimpleContext } from "../../context/helper"
|
||||
import { appendFile } from "fs/promises"
|
||||
import type { FilePart } from "@opencode-ai/sdk"
|
||||
import type { AgentPart, FilePart } from "@opencode-ai/sdk"
|
||||
|
||||
export type PromptInfo = {
|
||||
input: string
|
||||
parts: Omit<FilePart, "id" | "messageID" | "sessionID">[]
|
||||
parts: (Omit<FilePart, "id" | "messageID" | "sessionID"> | Omit<AgentPart, "id" | "messageID" | "sessionID">)[]
|
||||
}
|
||||
|
||||
export const { use: usePromptHistory, provider: PromptHistoryProvider } = createSimpleContext({
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import { useKeybind } from "@tui/context/keybind"
|
|||
import { Clipboard } from "@/util/clipboard"
|
||||
import { usePromptHistory, type PromptInfo } from "./history"
|
||||
import { type AutocompleteRef, Autocomplete } from "./autocomplete"
|
||||
import { iife } from "@/util/iife"
|
||||
|
||||
export type PromptProps = {
|
||||
sessionID?: string
|
||||
|
|
@ -113,17 +114,17 @@ export function Prompt(props: PromptProps) {
|
|||
for (let i = 0; i < draft.parts.length; i++) {
|
||||
const part = draft.parts[i]
|
||||
if (!part.source) continue
|
||||
if (part.source.text.start >= input.cursorPosition) {
|
||||
part.source.text.start += diff
|
||||
part.source.text.end += diff
|
||||
const source = part.type === "agent" ? part.source : part.source.text
|
||||
if (source.start >= input.cursorPosition) {
|
||||
source.start += diff
|
||||
source.end += diff
|
||||
}
|
||||
const sliced = draft.input.slice(part.source.text.start, part.source.text.end)
|
||||
if (sliced != part.source.text.value && diff < 0) {
|
||||
diff -= part.source.text.value.length
|
||||
draft.input =
|
||||
draft.input.slice(0, part.source.text.start) + draft.input.slice(part.source.text.end)
|
||||
const sliced = draft.input.slice(source.start, source.end)
|
||||
if (sliced != source.value && diff < 0) {
|
||||
diff -= source.value.length
|
||||
draft.input = draft.input.slice(0, source.start) + draft.input.slice(source.end)
|
||||
draft.parts.splice(i, 1)
|
||||
input.cursorPosition = Math.max(0, part.source.text.start - 1)
|
||||
input.cursorPosition = Math.max(0, source.start - 1)
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
|
@ -160,13 +161,18 @@ export function Prompt(props: PromptProps) {
|
|||
const position = input.cursorPosition
|
||||
const direction = Math.sign(old - position)
|
||||
for (const part of store.parts) {
|
||||
if (part.source && part.source.type === "file") {
|
||||
if (position >= part.source.text.start && position < part.source.text.end) {
|
||||
const source = iife(() => {
|
||||
if (part.type === "agent") return part.source
|
||||
if (part.type === "file") return part.source?.text
|
||||
return
|
||||
})
|
||||
if (source) {
|
||||
if (position >= source.start && position < source.end) {
|
||||
if (direction === 1) {
|
||||
input.cursorPosition = Math.max(0, part.source.text.start - 1)
|
||||
input.cursorPosition = Math.max(0, source.start - 1)
|
||||
}
|
||||
if (direction === -1) {
|
||||
input.cursorPosition = part.source.text.end
|
||||
input.cursorPosition = source.end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue