fix(tui): preserve global form location

This commit is contained in:
Aiden Cline 2026-07-03 14:05:44 -05:00
commit 1a38f66825
3 changed files with 46 additions and 13 deletions

View file

@ -28,7 +28,7 @@ import { createSignal, onCleanup } from "solid-js"
export type DataSessionStatus = "idle" | "running" export type DataSessionStatus = "idle" | "running"
export type FormInfo = FormFormInfo | FormUrlInfo export type FormInfo = (FormFormInfo | FormUrlInfo) & { readonly location?: LocationRef }
type LocationData = { type LocationData = {
agent?: AgentV2Info[] agent?: AgentV2Info[]
@ -577,7 +577,7 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
if (store.session.form[event.data.form.sessionID]?.some((form) => form.id === event.data.form.id)) break if (store.session.form[event.data.form.sessionID]?.some((form) => form.id === event.data.form.id)) break
setStore("session", "form", event.data.form.sessionID, [ setStore("session", "form", event.data.form.sessionID, [
...(store.session.form[event.data.form.sessionID] ?? []), ...(store.session.form[event.data.form.sessionID] ?? []),
mutable(event.data.form), mutable({ ...event.data.form, location: event.location }),
]) ])
break break
case "form.replied": case "form.replied":
@ -668,10 +668,15 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
registerSession(sessionID) registerSession(sessionID)
}, },
async refreshChildren(sessionID: string) { async refreshChildren(sessionID: string) {
for (const session of mutable((await sdk.api.session.list({ parentID: sessionID, limit: 200 })).data)) { const visit = async (parentID: string, seen: Set<string>): Promise<void> => {
setStore("session", "info", session.id, session) const children = mutable((await sdk.api.session.list({ parentID, limit: 200 })).data).filter(
registerSession(session.id) (session) => !seen.has(session.id),
)
for (const session of children) setStore("session", "info", session.id, session)
for (const session of children) registerSession(session.id)
await Promise.all(children.map((session) => visit(session.id, new Set([...seen, session.id]))))
} }
await visit(sessionID, new Set([sessionID]))
}, },
message: { message: {
ids(sessionID: string) { ids(sessionID: string) {
@ -719,6 +724,23 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
return store.session.form[sessionID] return store.session.form[sessionID]
}, },
async refresh(sessionID: string) { async refresh(sessionID: string) {
if (sessionID === "global") {
const result = await sdk.api.form.listRequests({ location: locationQuery(defaultLocation()) })
setStore(
"session",
"form",
sessionID,
mutable(
result.data
.filter((form) => form.sessionID === "global")
.map((form) => ({
...form,
location: { directory: result.location.directory, workspaceID: result.location.workspaceID },
})),
),
)
return
}
setStore("session", "form", sessionID, mutable(await sdk.api.form.list({ sessionID }))) setStore("session", "form", sessionID, mutable(await sdk.api.form.list({ sessionID })))
}, },
}, },

View file

@ -83,6 +83,16 @@ function display(field: Field, value: FormValue | undefined) {
return label(value) return label(value)
} }
function requestOptions(form: FormInfo) {
if (!form.location) return undefined
return {
headers: {
"x-opencode-directory": encodeURIComponent(form.location.directory),
...(form.location.workspaceID ? { "x-opencode-workspace": form.location.workspaceID } : {}),
},
}
}
export function FormPrompt(props: { form: FormInfo }) { export function FormPrompt(props: { form: FormInfo }) {
return props.form.mode === "url" ? <UrlPrompt form={props.form} /> : <FieldsPrompt form={props.form} /> return props.form.mode === "url" ? <UrlPrompt form={props.form} /> : <FieldsPrompt form={props.form} />
} }
@ -107,7 +117,7 @@ function UrlPrompt(props: { form: FormInfo & { mode: "url" } }) {
title: "Dismiss form", title: "Dismiss form",
category: "Form", category: "Form",
run() { run() {
void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }) void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }, requestOptions(props.form))
}, },
}, },
], ],
@ -125,7 +135,7 @@ function UrlPrompt(props: { form: FormInfo & { mode: "url" } }) {
desc: "Dismiss form", desc: "Dismiss form",
group: "Form", group: "Form",
cmd: () => { cmd: () => {
void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }) void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }, requestOptions(props.form))
}, },
}, },
], ],
@ -276,7 +286,7 @@ function FieldsPrompt(props: { form: FormInfo & { mode: "form" } }) {
sessionID: props.form.sessionID, sessionID: props.form.sessionID,
formID: props.form.id, formID: props.form.id,
answer: { [current.key]: value }, answer: { [current.key]: value },
}) }, requestOptions(props.form))
.catch((error: unknown) => { .catch((error: unknown) => {
setStore( setStore(
"error", "error",
@ -398,7 +408,7 @@ function FieldsPrompt(props: { form: FormInfo & { mode: "form" } }) {
group: "Form", group: "Form",
cmd: () => { cmd: () => {
if (textual()) { if (textual()) {
void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }) void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }, requestOptions(props.form))
return return
} }
setStore("editing", false) setStore("editing", false)
@ -497,7 +507,7 @@ function FieldsPrompt(props: { form: FormInfo & { mode: "form" } }) {
title: "Dismiss form", title: "Dismiss form",
category: "Form", category: "Form",
run() { run() {
void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }) void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }, requestOptions(props.form))
}, },
}, },
], ],
@ -550,7 +560,7 @@ function FieldsPrompt(props: { form: FormInfo & { mode: "form" } }) {
return value === undefined ? [] : [[field.key, value] as const] return value === undefined ? [] : [[field.key, value] as const]
}), }),
), ),
}) }, requestOptions(props.form))
.catch((error: unknown) => { .catch((error: unknown) => {
setStore( setStore(
"error", "error",
@ -569,7 +579,7 @@ function FieldsPrompt(props: { form: FormInfo & { mode: "form" } }) {
desc: "Dismiss form", desc: "Dismiss form",
group: "Form", group: "Form",
cmd: () => { cmd: () => {
void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }) void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }, requestOptions(props.form))
}, },
}, },
{ key: "up", desc: "Scroll review", group: "Form", cmd: () => review?.scrollBy(-1) }, { key: "up", desc: "Scroll review", group: "Form", cmd: () => review?.scrollBy(-1) },
@ -608,7 +618,7 @@ function FieldsPrompt(props: { form: FormInfo & { mode: "form" } }) {
desc: "Dismiss form", desc: "Dismiss form",
group: "Form", group: "Form",
cmd: () => { cmd: () => {
void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }) void sdk.api.form.cancel({ sessionID: props.form.sessionID, formID: props.form.id }, requestOptions(props.form))
}, },
}, },
...tuiConfig.keybinds.get("app.exit"), ...tuiConfig.keybinds.get("app.exit"),

View file

@ -97,6 +97,7 @@ export function createFetch(override?: FetchHandler, events?: ReturnType<typeof
if (url.pathname === "/api/project/proj_test/directories") return json([{ directory: worktree }]) if (url.pathname === "/api/project/proj_test/directories") return json([{ directory: worktree }])
if (url.pathname === "/api/shell") return json({ location: { directory, project: { id: "proj_test", directory: worktree } }, data: [] }) if (url.pathname === "/api/shell") return json({ location: { directory, project: { id: "proj_test", directory: worktree } }, data: [] })
if (url.pathname === "/api/mcp") return json({ location: { directory, project: { id: "proj_test", directory: worktree } }, data: [] }) if (url.pathname === "/api/mcp") return json({ location: { directory, project: { id: "proj_test", directory: worktree } }, data: [] })
if (url.pathname === "/api/form/request") return json({ location: { directory, project: { id: "proj_test", directory: worktree } }, data: [] })
if (url.pathname === "/api/session") return json({ data: [], cursor: {} }) if (url.pathname === "/api/session") return json({ data: [], cursor: {} })
if (url.pathname === "/api/session/active") return json({ data: {}, watermarks: {} }) if (url.pathname === "/api/session/active") return json({ data: {}, watermarks: {} })
if (/^\/api\/session\/[^/]+\/(permission|form)$/.test(url.pathname)) return json([]) if (/^\/api\/session\/[^/]+\/(permission|form)$/.test(url.pathname)) return json([])