181 lines
5.7 KiB
TypeScript
181 lines
5.7 KiB
TypeScript
import { createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
|
import path from "path"
|
|
import { useTuiPaths } from "../../context/runtime"
|
|
import { errorMessage } from "../../util/error"
|
|
import { useDialog } from "../../ui/dialog"
|
|
import { useClient } from "../../context/client"
|
|
import { useToast } from "../../ui/toast"
|
|
import { DialogMoveSession, type MoveSessionSelection } from "../dialog-move-session"
|
|
import { useData } from "../../context/data"
|
|
|
|
function moveReminderText(directory: string) {
|
|
return `<system-reminder>The user has changed the current working directory to "${directory}". This is still the same project but at a possibly new location; take this into account when working with any files from now on.</system-reminder>`
|
|
}
|
|
|
|
export function usePromptMove(input: { projectID: () => string | undefined; sessionID: () => string | undefined }) {
|
|
const dialog = useDialog()
|
|
const client = useClient()
|
|
const toast = useToast()
|
|
const data = useData()
|
|
const paths = useTuiPaths()
|
|
const [creating, setCreating] = createSignal(false)
|
|
const [creatingDots, setCreatingDots] = createSignal(3)
|
|
const [progress, setProgress] = createSignal<string>()
|
|
const [destination, setDestination] = createSignal<MoveSessionSelection>()
|
|
|
|
async function create(name: string) {
|
|
const projectID = await resolveProjectID()
|
|
if (!projectID) return
|
|
setCreating(true)
|
|
setProgress("Creating copy")
|
|
try {
|
|
const result = await client.api.projectCopy.create({
|
|
projectID,
|
|
location: { directory: data.location.info()?.directory || paths.cwd },
|
|
strategy: "git_worktree",
|
|
directory: path.join(paths.worktree, projectID.slice(0, 6)),
|
|
name,
|
|
})
|
|
const directory = result.directory
|
|
if (!directory) throw new Error("No project copy directory returned")
|
|
|
|
// Call a location-based route to initialize it before moving on.
|
|
await client.api.location.get({ location: { directory } })
|
|
|
|
setProgress("Creating session")
|
|
return directory
|
|
} catch (err) {
|
|
setDestination(undefined)
|
|
setProgress(undefined)
|
|
setCreating(false)
|
|
toast.show({ title: "Creating workspace failed", message: errorMessage(err), variant: "error" })
|
|
return
|
|
}
|
|
}
|
|
|
|
async function open() {
|
|
const projectID = await resolveProjectID()
|
|
if (!projectID) {
|
|
toast.show({ message: "Unable to determine current project", variant: "error" })
|
|
return
|
|
}
|
|
const sessionID = input.sessionID()
|
|
const session = sessionID ? await resolveSession(sessionID) : undefined
|
|
dialog.replace(() => (
|
|
<DialogMoveSession
|
|
projectID={projectID}
|
|
current={
|
|
destination() ??
|
|
(session
|
|
? {
|
|
type: "directory",
|
|
directory: session.location.directory,
|
|
subdirectory: !!session.subpath,
|
|
}
|
|
: {
|
|
type: "directory",
|
|
directory: data.location.default().directory,
|
|
subdirectory: data.location.default().directory !== data.location.info()?.project.directory,
|
|
})
|
|
}
|
|
onCurrentChange={setDestination}
|
|
onSelect={(selection) => {
|
|
const sessionID = input.sessionID()
|
|
if (!sessionID) {
|
|
setDestination(selection)
|
|
dialog.clear()
|
|
return
|
|
}
|
|
void moveExistingSession(sessionID, selection)
|
|
}}
|
|
/>
|
|
))
|
|
}
|
|
|
|
async function moveExistingSession(sessionID: string, selection: MoveSessionSelection) {
|
|
dialog.clear()
|
|
const directory = selection.type === "new" ? await create(selection.name) : selection.directory
|
|
if (!directory) {
|
|
setProgress(undefined)
|
|
dialog.clear()
|
|
return
|
|
}
|
|
setProgress("Moving session")
|
|
try {
|
|
await client.api.session.move({ sessionID, directory })
|
|
await client.api.session
|
|
.synthetic({ sessionID, text: moveReminderText(directory), resume: false })
|
|
.catch(() => undefined)
|
|
dialog.clear()
|
|
} catch (error) {
|
|
toast.error(error)
|
|
dialog.clear()
|
|
} finally {
|
|
setProgress(undefined)
|
|
setCreating(false)
|
|
}
|
|
}
|
|
|
|
async function resolveProjectID() {
|
|
const projectID = input.projectID()
|
|
if (projectID) return projectID
|
|
const sessionID = input.sessionID()
|
|
if (sessionID) return (await resolveSession(sessionID))?.projectID
|
|
const current = data.location.info()
|
|
if (current) return current.project.id
|
|
return client.api.project
|
|
.current({ location: { directory: data.location.default().directory || paths.cwd } })
|
|
.then((project) => project.id)
|
|
.catch(() => undefined)
|
|
}
|
|
|
|
async function resolveSession(sessionID: string) {
|
|
const session = data.session.get(sessionID)
|
|
if (session) return session
|
|
await data.session.sync(sessionID).catch(() => undefined)
|
|
return data.session.get(sessionID)
|
|
}
|
|
|
|
const pending = createMemo(() => Boolean(destination()))
|
|
const pendingNew = createMemo(() => destination()?.type === "new")
|
|
|
|
async function getDirectory() {
|
|
const value = destination()
|
|
if (!value) return
|
|
if (value.type === "directory") {
|
|
return value.directory
|
|
}
|
|
return await create(value.name)
|
|
}
|
|
|
|
function startSubmit() {
|
|
if (progress()) setProgress("Submitting prompt")
|
|
}
|
|
|
|
function finishSubmit() {
|
|
setDestination(undefined)
|
|
setProgress(undefined)
|
|
setCreating(false)
|
|
}
|
|
|
|
createEffect(() => {
|
|
if (!creating()) {
|
|
setCreatingDots(3)
|
|
return
|
|
}
|
|
const timer = setInterval(() => setCreatingDots((dots) => (dots % 3) + 1), 1000)
|
|
onCleanup(() => clearInterval(timer))
|
|
})
|
|
|
|
return {
|
|
creating,
|
|
creatingDots,
|
|
finishSubmit,
|
|
getDirectory,
|
|
open,
|
|
pending,
|
|
pendingNew,
|
|
progress,
|
|
startSubmit,
|
|
}
|
|
}
|