feat(app): better subagent experience (#20708)

This commit is contained in:
Adam
2026-04-07 11:06:23 -05:00
committed by GitHub
parent 65318a80f7
commit ec8b9810b4
18 changed files with 832 additions and 552 deletions
@@ -1,9 +1,11 @@
import { Show, createEffect, createMemo, onCleanup } from "solid-js"
import { createStore } from "solid-js/store"
import { useNavigate } from "@solidjs/router"
import { useSpring } from "@opencode-ai/ui/motion-spring"
import { PromptInput } from "@/components/prompt-input"
import { useLanguage } from "@/context/language"
import { usePrompt } from "@/context/prompt"
import { useSync } from "@/context/sync"
import { getSessionHandoff, setSessionHandoff } from "@/pages/session/handoff"
import { useSessionKey } from "@/pages/session/session-layout"
import { SessionPermissionDock } from "@/pages/session/composer/session-permission-dock"
@@ -43,11 +45,17 @@ export function SessionComposerRegion(props: {
}
setPromptDockRef: (el: HTMLDivElement) => void
}) {
const navigate = useNavigate()
const prompt = usePrompt()
const language = useLanguage()
const route = useSessionKey()
const sync = useSync()
const handoffPrompt = createMemo(() => getSessionHandoff(route.sessionKey())?.prompt)
const info = createMemo(() => (route.params.id ? sync.session.get(route.params.id) : undefined))
const parentID = createMemo(() => info()?.parentID)
const child = createMemo(() => !!parentID())
const showComposer = createMemo(() => !props.state.blocked() || child())
const previewPrompt = () =>
prompt
@@ -113,6 +121,12 @@ export function SessionComposerRegion(props: {
const lift = createMemo(() => (rolled() ? 18 : 36 * value()))
const full = createMemo(() => Math.max(78, store.height))
const openParent = () => {
const id = parentID()
if (!id) return
navigate(`/${route.params.dir}/session/${id}`)
}
createEffect(() => {
const el = store.body
if (!el) return
@@ -156,7 +170,7 @@ export function SessionComposerRegion(props: {
)}
</Show>
<Show when={!props.state.blocked()}>
<Show when={showComposer()}>
<Show
when={prompt.ready()}
fallback={
@@ -232,17 +246,40 @@ export function SessionComposerRegion(props: {
onEdit={props.followup!.onEdit}
/>
</Show>
<PromptInput
ref={props.inputRef}
newSessionWorktree={props.newSessionWorktree}
onNewSessionWorktreeReset={props.onNewSessionWorktreeReset}
edit={props.followup?.edit}
onEditLoaded={props.followup?.onEditLoaded}
shouldQueue={props.followup?.queue}
onQueue={props.followup?.onQueue}
onAbort={props.followup?.onAbort}
onSubmit={props.onSubmit}
/>
<Show
when={child()}
fallback={
<Show when={!props.state.blocked()}>
<PromptInput
ref={props.inputRef}
newSessionWorktree={props.newSessionWorktree}
onNewSessionWorktreeReset={props.onNewSessionWorktreeReset}
edit={props.followup?.edit}
onEditLoaded={props.followup?.onEditLoaded}
shouldQueue={props.followup?.queue}
onQueue={props.followup?.onQueue}
onAbort={props.followup?.onAbort}
onSubmit={props.onSubmit}
/>
</Show>
}
>
<div
ref={props.inputRef}
class="w-full rounded-[12px] border border-border-weak-base bg-background-base p-3 text-16-regular text-text-weak"
>
<span>{language.t("session.child.promptDisabled")} </span>
<Show when={parentID()}>
<button
type="button"
class="text-text-base transition-colors hover:text-text-strong"
onClick={openParent}
>
{language.t("session.child.backToParent")}
</button>
</Show>
</div>
</Show>
</div>
</Show>
</Show>