mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-22 11:25:15 +00:00
refactor(tui): model editable slash commands
This commit is contained in:
parent
b5c6763d11
commit
c7a17aea4b
4 changed files with 24 additions and 32 deletions
|
|
@ -553,11 +553,10 @@ export function Autocomplete(props: {
|
|||
|
||||
const commands = createMemo((): AutocompleteOption[] => {
|
||||
const results: AutocompleteOption[] = slashes().map((command) => {
|
||||
const name = command.display.trim().slice(1)
|
||||
if (name !== "compact") return command
|
||||
if (!command.input) return command
|
||||
return {
|
||||
...command,
|
||||
onSelect: () => insertSlashCommand(name),
|
||||
onSelect: () => insertSlashCommand(command.name),
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1132,6 +1132,8 @@ export function Prompt(props: PromptProps) {
|
|||
]
|
||||
: []
|
||||
|
||||
const slash = parseSlashCommand(inputText)
|
||||
|
||||
if (store.mode === "shell") {
|
||||
void sdk.client.session.shell({
|
||||
sessionID,
|
||||
|
|
@ -1143,34 +1145,20 @@ export function Prompt(props: PromptProps) {
|
|||
command: inputText,
|
||||
})
|
||||
setStore("mode", "normal")
|
||||
} else if (isCompactSlash(inputText)) {
|
||||
const instructions = slashArguments(inputText).trim()
|
||||
const payload: Parameters<typeof sdk.client.session.summarize>[0] & { $body_instructions?: string } = {
|
||||
} else if (slash && (slash.name === "compact" || slash.name === "summarize")) {
|
||||
const instructions = slash.arguments.trim()
|
||||
const payload = {
|
||||
sessionID,
|
||||
modelID: selectedModel.modelID,
|
||||
providerID: selectedModel.providerID,
|
||||
...(instructions ? { $body_instructions: instructions } : {}),
|
||||
}
|
||||
} satisfies Parameters<typeof sdk.client.session.summarize>[0] & { $body_instructions?: string }
|
||||
void sdk.client.session.summarize(payload)
|
||||
} else if (
|
||||
inputText.startsWith("/") &&
|
||||
iife(() => {
|
||||
const firstLine = inputText.split("\n")[0]
|
||||
const command = firstLine.split(" ")[0].slice(1)
|
||||
return sync.data.command.some((x) => x.name === command)
|
||||
})
|
||||
) {
|
||||
// Parse command from first line, preserve multi-line content in arguments
|
||||
const firstLineEnd = inputText.indexOf("\n")
|
||||
const firstLine = firstLineEnd === -1 ? inputText : inputText.slice(0, firstLineEnd)
|
||||
const [command, ...firstLineArgs] = firstLine.split(" ")
|
||||
const restOfInput = firstLineEnd === -1 ? "" : inputText.slice(firstLineEnd + 1)
|
||||
const args = firstLineArgs.join(" ") + (restOfInput ? "\n" + restOfInput : "")
|
||||
|
||||
} else if (slash && sync.data.command.some((x) => x.name === slash.name)) {
|
||||
void sdk.client.session.command({
|
||||
sessionID,
|
||||
command: command.slice(1),
|
||||
arguments: args,
|
||||
command: slash.name,
|
||||
arguments: slash.arguments,
|
||||
agent: agent.name,
|
||||
model: `${selectedModel.providerID}/${selectedModel.modelID}`,
|
||||
messageID,
|
||||
|
|
@ -1230,17 +1218,16 @@ export function Prompt(props: PromptProps) {
|
|||
return true
|
||||
}
|
||||
|
||||
function isCompactSlash(text: string) {
|
||||
const command = text.split("\n")[0].split(" ")[0].slice(1)
|
||||
return text.startsWith("/") && (command === "compact" || command === "summarize")
|
||||
}
|
||||
|
||||
function slashArguments(text: string) {
|
||||
function parseSlashCommand(text: string) {
|
||||
if (!text.startsWith("/")) return
|
||||
const firstLineEnd = text.indexOf("\n")
|
||||
const firstLine = firstLineEnd === -1 ? text : text.slice(0, firstLineEnd)
|
||||
const [, ...firstLineArgs] = firstLine.split(" ")
|
||||
const [command, ...firstLineArgs] = firstLine.split(" ")
|
||||
const restOfInput = firstLineEnd === -1 ? "" : text.slice(firstLineEnd + 1)
|
||||
return firstLineArgs.join(" ") + (restOfInput ? "\n" + restOfInput : "")
|
||||
return {
|
||||
name: command.slice(1),
|
||||
arguments: firstLineArgs.join(" ") + (restOfInput ? "\n" + restOfInput : ""),
|
||||
}
|
||||
}
|
||||
const exit = useExit()
|
||||
|
||||
|
|
|
|||
|
|
@ -25,9 +25,11 @@ export { useBindings, useKeymapSelector }
|
|||
export type OpenTuiKeymap = ReturnType<typeof useKeymap>
|
||||
type OpencodeModeStack = ReturnType<typeof createOpencodeModeStack>
|
||||
type CommandSlashEntry = {
|
||||
name: string
|
||||
display: string
|
||||
description?: string
|
||||
aliases?: string[]
|
||||
input?: boolean
|
||||
onSelect: () => void
|
||||
}
|
||||
type Command = ReturnType<OpenTuiKeymap["getCommands"]>[number]
|
||||
|
|
@ -256,6 +258,7 @@ export function useCommandSlashes(): Accessor<readonly CommandSlashEntry[]> {
|
|||
if (typeof slashName !== "string" || !slashName) return []
|
||||
const slashAliases = entry.command.slashAliases
|
||||
return {
|
||||
name: slashName,
|
||||
display: `/${slashName}`,
|
||||
description:
|
||||
typeof entry.command.desc === "string"
|
||||
|
|
@ -266,6 +269,7 @@ export function useCommandSlashes(): Accessor<readonly CommandSlashEntry[]> {
|
|||
aliases: Array.isArray(slashAliases)
|
||||
? slashAliases.filter((alias): alias is string => typeof alias === "string").map((alias) => `/${alias}`)
|
||||
: undefined,
|
||||
input: entry.command.slashInput === true,
|
||||
onSelect: () => keymap.dispatchCommand(entry.command.name),
|
||||
}
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -546,6 +546,7 @@ export function Session() {
|
|||
slash: {
|
||||
name: "compact",
|
||||
aliases: ["summarize"],
|
||||
input: true,
|
||||
},
|
||||
run: () => {
|
||||
const selectedModel = local.model.current()
|
||||
|
|
@ -1046,6 +1047,7 @@ export function Session() {
|
|||
desc: "description" in command ? command.description : undefined,
|
||||
slashName: "slash" in command ? command.slash?.name : undefined,
|
||||
slashAliases: "slash" in command ? command.slash?.aliases : undefined,
|
||||
slashInput: "slash" in command ? command.slash?.input : undefined,
|
||||
...command,
|
||||
})),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue