feat(session): support directory moves from slash commands
This commit is contained in:
parent
e8964ce672
commit
c1d2d7aba3
23 changed files with 521 additions and 206 deletions
|
|
@ -427,14 +427,23 @@ export function Autocomplete(props: {
|
|||
),
|
||||
)
|
||||
|
||||
function insertSlash(name: string) {
|
||||
const newText = `/${name} `
|
||||
const cursor = props.input().logicalCursor
|
||||
props.input().deleteRange(0, 0, cursor.row, cursor.col)
|
||||
props.input().insertText(newText)
|
||||
props.input().cursorOffset = Bun.stringWidth(newText)
|
||||
}
|
||||
|
||||
const commands = createMemo((): AutocompleteOption[] => {
|
||||
const results: AutocompleteOption[] = keymapCommands().flatMap((command) => {
|
||||
if (!command.slash) return []
|
||||
const slash = command.slash
|
||||
if (!slash) return []
|
||||
return {
|
||||
display: `/${command.slash.name}`,
|
||||
display: `/${slash.name}`,
|
||||
description: command.description ?? command.title,
|
||||
aliases: command.slash.aliases?.map((alias) => `/${alias}`),
|
||||
onSelect: command.run,
|
||||
aliases: slash.aliases?.map((alias) => `/${alias}`),
|
||||
onSelect: slash.arguments ? () => insertSlash(slash.name) : command.run,
|
||||
}
|
||||
})
|
||||
const commandNames = new Set<string>()
|
||||
|
|
@ -444,13 +453,7 @@ export function Autocomplete(props: {
|
|||
results.push({
|
||||
display: "/" + serverCommand.name,
|
||||
description: serverCommand.description,
|
||||
onSelect: () => {
|
||||
const newText = "/" + serverCommand.name + " "
|
||||
const cursor = props.input().logicalCursor
|
||||
props.input().deleteRange(0, 0, cursor.row, cursor.col)
|
||||
props.input().insertText(newText)
|
||||
props.input().cursorOffset = Bun.stringWidth(newText)
|
||||
},
|
||||
onSelect: () => insertSlash(serverCommand.name),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -460,13 +463,7 @@ export function Autocomplete(props: {
|
|||
results.push({
|
||||
display: "/" + skill.id,
|
||||
description: skill.description,
|
||||
onSelect: () => {
|
||||
const newText = "/" + skill.id + " "
|
||||
const cursor = props.input().logicalCursor
|
||||
props.input().deleteRange(0, 0, cursor.row, cursor.col)
|
||||
props.input().insertText(newText)
|
||||
props.input().cursorOffset = Bun.stringWidth(newText)
|
||||
},
|
||||
onSelect: () => insertSlash(skill.id),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ import { usePromptMove } from "./move"
|
|||
import { readLocalAttachment } from "./local-attachment"
|
||||
import { useData } from "../../context/data"
|
||||
import { useLocation } from "../../context/location"
|
||||
import { Keymap, type KeymapCommand } from "../../context/keymap"
|
||||
import { contextUsage } from "../../util/session"
|
||||
import { abbreviateHome } from "../../runtime"
|
||||
|
||||
|
|
@ -137,6 +138,18 @@ function formatEditorContext(selection: EditorSelection) {
|
|||
|
||||
let stashed: { prompt: PromptInfo; cursor: number } | undefined
|
||||
|
||||
function argumentSlash(input: string, commands: readonly KeymapCommand[]) {
|
||||
if (!input.startsWith("/")) return
|
||||
const separator = input.search(/\s/)
|
||||
const name = input.slice(1, separator === -1 ? undefined : separator)
|
||||
const command = commands.find(
|
||||
(command) =>
|
||||
command.slash?.arguments && (command.slash.name === name || command.slash.aliases?.includes(name) === true),
|
||||
)
|
||||
if (!command) return
|
||||
return { command, input: separator === -1 ? "" : input.slice(separator + 1) }
|
||||
}
|
||||
|
||||
export function Prompt(props: PromptProps) {
|
||||
let input: TextareaRenderable
|
||||
let anchor: BoxRenderable
|
||||
|
|
@ -152,6 +165,7 @@ export function Prompt(props: PromptProps) {
|
|||
const editor = useEditorContext()
|
||||
const route = useRoute()
|
||||
const data = useData()
|
||||
const keymapCommands = Keymap.useCommands()
|
||||
const currentLocation = useLocation()
|
||||
const config = useConfig().data
|
||||
const dialog = useDialog()
|
||||
|
|
@ -218,6 +232,30 @@ export function Prompt(props: PromptProps) {
|
|||
(props.sessionID ? data.session.get(props.sessionID)?.projectID : undefined) ?? data.location.info()?.project.id,
|
||||
sessionID: () => props.sessionID,
|
||||
})
|
||||
Keymap.createLayer(() => ({
|
||||
mode: "global",
|
||||
enabled: props.sessionID !== undefined,
|
||||
commands: [
|
||||
{
|
||||
id: "session.cd",
|
||||
title: "Change working directory",
|
||||
slash: { name: "cd", arguments: true },
|
||||
run: async (input) => {
|
||||
const sessionID = props.sessionID
|
||||
if (!sessionID) return
|
||||
if (!input?.trim()) {
|
||||
toast.show({ message: "Directory is required", variant: "error" })
|
||||
return
|
||||
}
|
||||
await client.api.session
|
||||
.move({ sessionID, directory: input })
|
||||
.catch((error) =>
|
||||
toast.show({ title: "Failed to change directory", message: errorMessage(error), variant: "error" }),
|
||||
)
|
||||
},
|
||||
},
|
||||
],
|
||||
}))
|
||||
const [cursorVersion, setCursorVersion] = createSignal(0)
|
||||
const currentProviderLabel = createMemo(() => local.model.parsed().provider)
|
||||
const connected = useConnected()
|
||||
|
|
@ -948,6 +986,12 @@ export function Prompt(props: PromptProps) {
|
|||
void exit()
|
||||
return true
|
||||
}
|
||||
const slash = argumentSlash(store.prompt.text, keymapCommands())
|
||||
if (slash) {
|
||||
clearPrompt()
|
||||
await slash.command.run(slash.input)
|
||||
return true
|
||||
}
|
||||
const agent = local.agent.current()
|
||||
if (!agent) return false
|
||||
const selectedModel = local.model.current()
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { useDialog } from "../../ui/dialog"
|
|||
import { useClient } from "../../context/client"
|
||||
import { useToast } from "../../ui/toast"
|
||||
import { DialogMoveSession, type MoveSessionSelection } from "../dialog-move-session"
|
||||
import { DialogWorkspaceFileChanges } from "../dialog-workspace-file-changes"
|
||||
import { useData } from "../../context/data"
|
||||
|
||||
function moveReminderText(directory: string) {
|
||||
|
|
@ -94,12 +93,6 @@ export function usePromptMove(input: { projectID: () => string | undefined; sess
|
|||
}
|
||||
|
||||
async function moveExistingSession(sessionID: string, selection: MoveSessionSelection) {
|
||||
const session = await resolveSession(sessionID)
|
||||
const status = await client.api.vcs
|
||||
.status({ location: session?.location.directory ? { directory: session.location.directory } : undefined })
|
||||
.catch(() => undefined)
|
||||
const choice = status?.data?.length ? await DialogWorkspaceFileChanges.show(dialog, status.data) : "no"
|
||||
if (!choice) return
|
||||
dialog.clear()
|
||||
const directory = selection.type === "new" ? await create(selection.name) : selection.directory
|
||||
if (!directory) {
|
||||
|
|
@ -109,7 +102,7 @@ export function usePromptMove(input: { projectID: () => string | undefined; sess
|
|||
}
|
||||
setProgress("Moving session")
|
||||
try {
|
||||
await client.api.session.move({ sessionID, destination: { directory }, moveChanges: choice === "yes" })
|
||||
await client.api.session.move({ sessionID, directory })
|
||||
await client.api.session
|
||||
.synthetic({ sessionID, text: moveReminderText(directory), resume: false })
|
||||
.catch(() => undefined)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue