mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-25 23:43:37 +00:00
refactor(tui): centralize active location
This commit is contained in:
parent
f4f761246a
commit
a32c480d45
9 changed files with 218 additions and 249 deletions
|
|
@ -172,6 +172,7 @@ export interface UI {
|
|||
|
||||
export interface Context {
|
||||
readonly options: Readonly<Record<string, any>>
|
||||
readonly location: LocationRef | undefined
|
||||
readonly client: OpenCodeClient
|
||||
readonly data: Data
|
||||
readonly keymap: Keymap
|
||||
|
|
|
|||
|
|
@ -333,15 +333,15 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
|
|||
<PermissionProvider>
|
||||
<ProjectProvider>
|
||||
<DataProvider>
|
||||
<ThemeProvider mode={mode}>
|
||||
<LocalProvider>
|
||||
<PromptStashProvider>
|
||||
<DialogProvider>
|
||||
<FrecencyProvider>
|
||||
<PromptHistoryProvider>
|
||||
<PromptRefProvider>
|
||||
<EditorContextProvider>
|
||||
<LocationProvider>
|
||||
<LocationProvider>
|
||||
<ThemeProvider mode={mode}>
|
||||
<LocalProvider>
|
||||
<PromptStashProvider>
|
||||
<DialogProvider>
|
||||
<FrecencyProvider>
|
||||
<PromptHistoryProvider>
|
||||
<PromptRefProvider>
|
||||
<EditorContextProvider>
|
||||
<PluginProvider packages={input.packages}>
|
||||
<App
|
||||
pair={
|
||||
|
|
@ -354,15 +354,15 @@ export const run = Effect.fn("Tui.run")(function* (input: TuiInput) {
|
|||
}
|
||||
/>
|
||||
</PluginProvider>
|
||||
</LocationProvider>
|
||||
</EditorContextProvider>
|
||||
</PromptRefProvider>
|
||||
</PromptHistoryProvider>
|
||||
</FrecencyProvider>
|
||||
</DialogProvider>
|
||||
</PromptStashProvider>
|
||||
</LocalProvider>
|
||||
</ThemeProvider>
|
||||
</EditorContextProvider>
|
||||
</PromptRefProvider>
|
||||
</PromptHistoryProvider>
|
||||
</FrecencyProvider>
|
||||
</DialogProvider>
|
||||
</PromptStashProvider>
|
||||
</LocalProvider>
|
||||
</ThemeProvider>
|
||||
</LocationProvider>
|
||||
</DataProvider>
|
||||
</ProjectProvider>
|
||||
</PermissionProvider>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ 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 { useHomeSessionDestination } from "../../routes/home/session-destination"
|
||||
import { useProject } from "../../context/project"
|
||||
import { useData } from "../../context/data"
|
||||
|
||||
|
|
@ -19,13 +18,13 @@ export function usePromptMove(input: { projectID: () => string | undefined; sess
|
|||
const dialog = useDialog()
|
||||
const client = useClient()
|
||||
const toast = useToast()
|
||||
const homeDestination = useHomeSessionDestination()
|
||||
const project = useProject()
|
||||
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()
|
||||
|
|
@ -49,7 +48,7 @@ export function usePromptMove(input: { projectID: () => string | undefined; sess
|
|||
setProgress("Creating session")
|
||||
return directory
|
||||
} catch (err) {
|
||||
homeDestination?.clear()
|
||||
setDestination(undefined)
|
||||
setProgress(undefined)
|
||||
setCreating(false)
|
||||
toast.show({ title: "Creating workspace failed", message: errorMessage(err), variant: "error" })
|
||||
|
|
@ -69,7 +68,7 @@ export function usePromptMove(input: { projectID: () => string | undefined; sess
|
|||
<DialogMoveSession
|
||||
projectID={projectID}
|
||||
current={
|
||||
homeDestination?.destination() ??
|
||||
destination() ??
|
||||
(session
|
||||
? {
|
||||
type: "directory",
|
||||
|
|
@ -82,11 +81,11 @@ export function usePromptMove(input: { projectID: () => string | undefined; sess
|
|||
subdirectory: project.instance.directory() !== project.instance.path().worktree,
|
||||
})
|
||||
}
|
||||
onCurrentChange={(selection) => homeDestination?.setDestination(selection)}
|
||||
onCurrentChange={setDestination}
|
||||
onSelect={(selection) => {
|
||||
const sessionID = input.sessionID()
|
||||
if (!sessionID) {
|
||||
homeDestination?.setDestination(selection)
|
||||
setDestination(selection)
|
||||
dialog.clear()
|
||||
return
|
||||
}
|
||||
|
|
@ -144,11 +143,11 @@ export function usePromptMove(input: { projectID: () => string | undefined; sess
|
|||
return data.session.get(sessionID)
|
||||
}
|
||||
|
||||
const pending = createMemo(() => Boolean(homeDestination?.destination()))
|
||||
const pendingNew = createMemo(() => homeDestination?.destination()?.type === "new")
|
||||
const pending = createMemo(() => Boolean(destination()))
|
||||
const pendingNew = createMemo(() => destination()?.type === "new")
|
||||
|
||||
async function getDirectory() {
|
||||
const value = homeDestination?.destination()
|
||||
const value = destination()
|
||||
if (!value) return
|
||||
if (value.type === "directory") {
|
||||
return value.directory
|
||||
|
|
@ -161,7 +160,7 @@ export function usePromptMove(input: { projectID: () => string | undefined; sess
|
|||
}
|
||||
|
||||
function finishSubmit() {
|
||||
homeDestination?.clear()
|
||||
setDestination(undefined)
|
||||
setProgress(undefined)
|
||||
setCreating(false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,24 @@
|
|||
import type { LocationRef } from "@opencode-ai/client"
|
||||
import { createContext, useContext, type Accessor, type ParentProps } from "solid-js"
|
||||
import { createContext, createSignal, useContext, type Accessor, type ParentProps, type Setter } from "solid-js"
|
||||
|
||||
const context = createContext<Accessor<LocationRef | undefined>>()
|
||||
const context = createContext<{
|
||||
current: Accessor<LocationRef | undefined>
|
||||
set: Setter<LocationRef | undefined>
|
||||
}>()
|
||||
|
||||
export function LocationProvider(props: ParentProps<{ location?: LocationRef }>) {
|
||||
return <context.Provider value={() => props.location}>{props.children}</context.Provider>
|
||||
export function LocationProvider(props: ParentProps) {
|
||||
const [current, set] = createSignal<LocationRef>()
|
||||
return <context.Provider value={{ current, set }}>{props.children}</context.Provider>
|
||||
}
|
||||
|
||||
export function useLocation() {
|
||||
const value = useContext(context)
|
||||
if (!value) throw new Error("Location context must be used within a LocationProvider")
|
||||
return value
|
||||
return value.current
|
||||
}
|
||||
|
||||
export function useSetLocation() {
|
||||
const value = useContext(context)
|
||||
if (!value) throw new Error("Location context must be used within a LocationProvider")
|
||||
return value.set
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,19 +4,15 @@ import { createMemo, Match, Show, Switch } from "solid-js"
|
|||
import { useTerminalDimensions } from "@opentui/solid"
|
||||
import { useTuiPaths } from "../../context/runtime"
|
||||
import { useTheme } from "../../context/theme"
|
||||
import { useHomeSessionDestination } from "../../routes/home/session-destination"
|
||||
import { abbreviateHome } from "../../runtime"
|
||||
import { FilePath } from "../../ui/file-path"
|
||||
|
||||
function Directory(props: { context: Plugin.Context; maxWidth: number }) {
|
||||
const { theme } = useTheme()
|
||||
const destination = useHomeSessionDestination()
|
||||
const paths = useTuiPaths()
|
||||
const directory = createMemo(() => {
|
||||
const selected = destination?.destination()
|
||||
if (!selected || selected.type === "new") return
|
||||
return abbreviateHome(selected.directory || props.context.data.location.default().directory, paths.home)
|
||||
})
|
||||
const directory = createMemo(() =>
|
||||
props.context.location ? abbreviateHome(props.context.location.directory, paths.home) : undefined,
|
||||
)
|
||||
|
||||
return (
|
||||
<Show when={directory()}>
|
||||
|
|
@ -27,7 +23,7 @@ function Directory(props: { context: Plugin.Context; maxWidth: number }) {
|
|||
|
||||
function Mcp(props: { context: Plugin.Context }) {
|
||||
const { theme } = useTheme()
|
||||
const list = createMemo(() => props.context.data.location.mcp.server.list() ?? [])
|
||||
const list = createMemo(() => props.context.data.location.mcp.server.list(props.context.location) ?? [])
|
||||
const failed = createMemo(() => list().some((item) => item.status.status === "failed"))
|
||||
const count = createMemo(() => list().filter((item) => item.status.status === "connected").length)
|
||||
|
||||
|
|
@ -55,7 +51,7 @@ function View(props: { context: Plugin.Context }) {
|
|||
const { theme } = useTheme()
|
||||
const dimensions = useTerminalDimensions()
|
||||
const mcpWidth = createMemo(() => {
|
||||
const list = props.context.data.location.mcp.server.list() ?? []
|
||||
const list = props.context.data.location.mcp.server.list(props.context.location) ?? []
|
||||
if (list.length === 0) return 0
|
||||
const count = list.filter((item) => item.status.status === "connected").length
|
||||
return Bun.stringWidth(`⊙ ${count} MCP /status`) + 2
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { useData } from "../context/data"
|
|||
import { Keymap } from "../context/keymap"
|
||||
import { useRoute } from "../context/route"
|
||||
import { useTuiLifecycle } from "../context/runtime"
|
||||
import { useLocation } from "../context/location"
|
||||
import { builtins } from "./builtins"
|
||||
|
||||
export interface PackageResolver {
|
||||
|
|
@ -63,6 +64,7 @@ export function PluginProvider(props: ParentProps<{ packages: PackageResolver }>
|
|||
const keymap = Keymap.use()
|
||||
const shortcuts = Keymap.useShortcuts()
|
||||
const lifecycle = useTuiLifecycle()
|
||||
const location = useLocation()
|
||||
const directory = config.path ? path.dirname(config.path) : process.cwd()
|
||||
const [store, setStore] = createStore({
|
||||
ready: false,
|
||||
|
|
@ -82,6 +84,9 @@ export function PluginProvider(props: ParentProps<{ packages: PackageResolver }>
|
|||
const owned: Dispose[] = []
|
||||
const context: Context = {
|
||||
options: item.options ?? {},
|
||||
get location() {
|
||||
return location()
|
||||
},
|
||||
client: client.api,
|
||||
data,
|
||||
keymap: {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@ import { usePromptRef } from "../context/prompt"
|
|||
import { useLocal } from "../context/local"
|
||||
import { usePluginRuntime } from "../plugin/runtime"
|
||||
import { useEditorContext } from "../context/editor"
|
||||
import { HomeSessionDestinationProvider } from "./home/session-destination"
|
||||
import { useData } from "../context/data"
|
||||
import { LocationProvider } from "../context/location"
|
||||
import { useSetLocation } from "../context/location"
|
||||
import { FormPrompt } from "./session/form"
|
||||
import { PluginSlot } from "../plugin/context"
|
||||
|
||||
|
|
@ -29,10 +28,13 @@ export function Home() {
|
|||
const local = useLocal()
|
||||
const editor = useEditorContext()
|
||||
const data = useData()
|
||||
const setLocation = useSetLocation()
|
||||
// Global MCP elicitations can arrive without a session route, so keep them reachable from Home.
|
||||
const forms = createMemo(() => data.session.form.list("global", data.location.default()) ?? [])
|
||||
let sent = false
|
||||
|
||||
createEffect(() => setLocation(data.location.default()))
|
||||
|
||||
onMount(() => {
|
||||
editor.clearSelection()
|
||||
})
|
||||
|
|
@ -64,47 +66,45 @@ export function Home() {
|
|||
})
|
||||
|
||||
return (
|
||||
<LocationProvider location={data.location.default()}>
|
||||
<HomeSessionDestinationProvider>
|
||||
<box flexGrow={1} alignItems="center" paddingLeft={2} paddingRight={2}>
|
||||
<box flexGrow={1} minHeight={0} />
|
||||
<box height={4} minHeight={0} flexShrink={1} />
|
||||
<box flexShrink={0}>
|
||||
<pluginRuntime.Slot name="home_logo" mode="replace">
|
||||
<Logo />
|
||||
</pluginRuntime.Slot>
|
||||
</box>
|
||||
<box height={1} minHeight={0} flexShrink={1} />
|
||||
<box width="100%" maxWidth={75} zIndex={1000} paddingTop={1} flexShrink={0}>
|
||||
<pluginRuntime.Slot name="home_prompt" mode="replace" ref={bind}>
|
||||
<Prompt
|
||||
ref={bind}
|
||||
right={<pluginRuntime.Slot name="home_prompt_right" />}
|
||||
placeholders={placeholder}
|
||||
disabled={forms().length > 0}
|
||||
/>
|
||||
</pluginRuntime.Slot>
|
||||
</box>
|
||||
<PluginSlot name="home.bottom" />
|
||||
<box flexGrow={1} minHeight={0} />
|
||||
<Toast />
|
||||
<>
|
||||
<box flexGrow={1} alignItems="center" paddingLeft={2} paddingRight={2}>
|
||||
<box flexGrow={1} minHeight={0} />
|
||||
<box height={4} minHeight={0} flexShrink={1} />
|
||||
<box flexShrink={0}>
|
||||
<pluginRuntime.Slot name="home_logo" mode="replace">
|
||||
<Logo />
|
||||
</pluginRuntime.Slot>
|
||||
</box>
|
||||
<box width="100%" flexShrink={0}>
|
||||
<PluginSlot name="home.footer" />
|
||||
<box height={1} minHeight={0} flexShrink={1} />
|
||||
<box width="100%" maxWidth={75} zIndex={1000} paddingTop={1} flexShrink={0}>
|
||||
<pluginRuntime.Slot name="home_prompt" mode="replace" ref={bind}>
|
||||
<Prompt
|
||||
ref={bind}
|
||||
right={<pluginRuntime.Slot name="home_prompt_right" />}
|
||||
placeholders={placeholder}
|
||||
disabled={forms().length > 0}
|
||||
/>
|
||||
</pluginRuntime.Slot>
|
||||
</box>
|
||||
<Show when={forms()[0]?.id} keyed>
|
||||
{(_) => {
|
||||
const form = forms()[0]
|
||||
return form ? (
|
||||
<box position="absolute" zIndex={2000} left={0} right={0} bottom={1} paddingLeft={2} paddingRight={2}>
|
||||
<box width="100%">
|
||||
<FormPrompt form={form} />
|
||||
</box>
|
||||
<PluginSlot name="home.bottom" />
|
||||
<box flexGrow={1} minHeight={0} />
|
||||
<Toast />
|
||||
</box>
|
||||
<box width="100%" flexShrink={0}>
|
||||
<PluginSlot name="home.footer" />
|
||||
</box>
|
||||
<Show when={forms()[0]?.id} keyed>
|
||||
{(_) => {
|
||||
const form = forms()[0]
|
||||
return form ? (
|
||||
<box position="absolute" zIndex={2000} left={0} right={0} bottom={1} paddingLeft={2} paddingRight={2}>
|
||||
<box width="100%">
|
||||
<FormPrompt form={form} />
|
||||
</box>
|
||||
) : null
|
||||
}}
|
||||
</Show>
|
||||
</HomeSessionDestinationProvider>
|
||||
</LocationProvider>
|
||||
</box>
|
||||
) : null
|
||||
}}
|
||||
</Show>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
import {
|
||||
createContext,
|
||||
createMemo,
|
||||
createSignal,
|
||||
useContext,
|
||||
type Accessor,
|
||||
type ParentProps,
|
||||
type Setter,
|
||||
} from "solid-js"
|
||||
import { useTuiPaths } from "../../context/runtime"
|
||||
import { useProject } from "../../context/project"
|
||||
|
||||
export type HomeSessionDestination = { type: "directory"; directory: string; subdirectory: boolean } | { type: "new"; name: string }
|
||||
|
||||
type Context = {
|
||||
destination: Accessor<HomeSessionDestination | undefined>
|
||||
setDestination: Setter<HomeSessionDestination | undefined>
|
||||
clear: () => void
|
||||
}
|
||||
|
||||
const HomeSessionDestinationContext = createContext<Context>()
|
||||
|
||||
export function HomeSessionDestinationProvider(props: ParentProps) {
|
||||
const project = useProject()
|
||||
const paths = useTuiPaths()
|
||||
const [selected, setDestination] = createSignal<HomeSessionDestination>()
|
||||
const destination = createMemo<HomeSessionDestination>(
|
||||
() => selected() ?? { type: "directory", directory: project.instance.directory() || paths.cwd, subdirectory: false },
|
||||
)
|
||||
return (
|
||||
<HomeSessionDestinationContext.Provider
|
||||
value={{ destination, setDestination, clear: () => setDestination(undefined) }}
|
||||
>
|
||||
{props.children}
|
||||
</HomeSessionDestinationContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useHomeSessionDestination() {
|
||||
return useContext(HomeSessionDestinationContext)
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ import { collapseToolOutput } from "../../util/collapse-tool-output"
|
|||
import { usePluginRuntime } from "../../plugin/runtime"
|
||||
import { OPENCODE_BASE_MODE, useBindings, useCommandShortcut } from "../../keymap"
|
||||
import { usePathFormatter } from "../../context/path-format"
|
||||
import { LocationProvider } from "../../context/location"
|
||||
import { useSetLocation } from "../../context/location"
|
||||
import { createSessionRows, resolvePart, type PartRef, type SessionRow } from "./rows"
|
||||
import { switchLabel } from "../../util/model"
|
||||
|
||||
|
|
@ -115,6 +115,9 @@ export function Session() {
|
|||
const session = createMemo(() => data.session.get(route.sessionID))
|
||||
const messages = () => data.session.message.list(route.sessionID)
|
||||
const location = createMemo(() => session()?.location)
|
||||
const setLocation = useSetLocation()
|
||||
|
||||
createEffect(() => setLocation(location()))
|
||||
|
||||
createEffect(() => {
|
||||
const title = Locale.truncate(session()?.title ?? "", 50)
|
||||
|
|
@ -823,133 +826,131 @@ export function Session() {
|
|||
)
|
||||
|
||||
return (
|
||||
<LocationProvider location={location()}>
|
||||
<context.Provider
|
||||
value={{
|
||||
get width() {
|
||||
return contentWidth()
|
||||
},
|
||||
sessionID: route.sessionID,
|
||||
thinkingMode,
|
||||
showThinking,
|
||||
markdownMode,
|
||||
groupExploration,
|
||||
diffWrapMode,
|
||||
models,
|
||||
config,
|
||||
}}
|
||||
>
|
||||
<box flexDirection="row" flexGrow={1} minHeight={0}>
|
||||
<box flexGrow={1} minHeight={0} paddingBottom={1} paddingLeft={2} paddingRight={2} gap={1}>
|
||||
<Show when={session()}>
|
||||
<scrollbox
|
||||
ref={(r) => (scroll = r)}
|
||||
viewportOptions={{
|
||||
paddingRight: showScrollbar() ? 1 : 0,
|
||||
}}
|
||||
verticalScrollbarOptions={{
|
||||
paddingLeft: 1,
|
||||
visible: showScrollbar(),
|
||||
trackOptions: {
|
||||
backgroundColor: theme.backgroundElement,
|
||||
foregroundColor: theme.border,
|
||||
},
|
||||
}}
|
||||
stickyScroll={true}
|
||||
stickyStart="bottom"
|
||||
flexGrow={1}
|
||||
scrollAcceleration={scrollAcceleration()}
|
||||
>
|
||||
<For each={rows}>
|
||||
{(row) => (
|
||||
<SessionRowView
|
||||
row={row}
|
||||
message={(messageID) => data.session.message.get(route.sessionID, messageID)}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
<BackgroundToolHint messages={messages()} />
|
||||
<Show when={session()?.revert?.messageID}>
|
||||
<RevertMessage
|
||||
count={
|
||||
messages().filter(
|
||||
(message) => message.id >= session()!.revert!.messageID && message.type === "user",
|
||||
).length
|
||||
}
|
||||
files={session()!.revert!.files ?? []}
|
||||
<context.Provider
|
||||
value={{
|
||||
get width() {
|
||||
return contentWidth()
|
||||
},
|
||||
sessionID: route.sessionID,
|
||||
thinkingMode,
|
||||
showThinking,
|
||||
markdownMode,
|
||||
groupExploration,
|
||||
diffWrapMode,
|
||||
models,
|
||||
config,
|
||||
}}
|
||||
>
|
||||
<box flexDirection="row" flexGrow={1} minHeight={0}>
|
||||
<box flexGrow={1} minHeight={0} paddingBottom={1} paddingLeft={2} paddingRight={2} gap={1}>
|
||||
<Show when={session()}>
|
||||
<scrollbox
|
||||
ref={(r) => (scroll = r)}
|
||||
viewportOptions={{
|
||||
paddingRight: showScrollbar() ? 1 : 0,
|
||||
}}
|
||||
verticalScrollbarOptions={{
|
||||
paddingLeft: 1,
|
||||
visible: showScrollbar(),
|
||||
trackOptions: {
|
||||
backgroundColor: theme.backgroundElement,
|
||||
foregroundColor: theme.border,
|
||||
},
|
||||
}}
|
||||
stickyScroll={true}
|
||||
stickyStart="bottom"
|
||||
flexGrow={1}
|
||||
scrollAcceleration={scrollAcceleration()}
|
||||
>
|
||||
<For each={rows}>
|
||||
{(row) => (
|
||||
<SessionRowView
|
||||
row={row}
|
||||
message={(messageID) => data.session.message.get(route.sessionID, messageID)}
|
||||
/>
|
||||
</Show>
|
||||
</scrollbox>
|
||||
<box flexShrink={0}>
|
||||
<Composer
|
||||
sessionID={route.sessionID}
|
||||
open={composer.open || (!!session()?.parentID && forms().length === 0)}
|
||||
defaultTab={composer.tab ?? (session()?.parentID ? "subagents" : undefined)}
|
||||
onClose={() => setComposer("open", false)}
|
||||
)}
|
||||
</For>
|
||||
<BackgroundToolHint messages={messages()} />
|
||||
<Show when={session()?.revert?.messageID}>
|
||||
<RevertMessage
|
||||
count={
|
||||
messages().filter(
|
||||
(message) => message.id >= session()!.revert!.messageID && message.type === "user",
|
||||
).length
|
||||
}
|
||||
files={session()!.revert!.files ?? []}
|
||||
/>
|
||||
<Switch>
|
||||
<Match when={composer.open || (!!session()?.parentID && forms().length === 0)}>{null}</Match>
|
||||
<Match when={permissions().length > 0}>
|
||||
<PermissionPrompt request={permissions()[0]} directory={session()?.location.directory} />
|
||||
</Match>
|
||||
<Match when={forms().length > 0}>
|
||||
<Show when={forms()[0]?.id} keyed>
|
||||
{(_) => {
|
||||
const form = forms()[0]
|
||||
return form ? <FormPrompt form={form} /> : null
|
||||
}}
|
||||
</Show>
|
||||
</Match>
|
||||
<Match when={!disabled()}>
|
||||
<pluginRuntime.Slot
|
||||
name="session_prompt"
|
||||
mode="replace"
|
||||
session_id={route.sessionID}
|
||||
</Show>
|
||||
</scrollbox>
|
||||
<box flexShrink={0}>
|
||||
<Composer
|
||||
sessionID={route.sessionID}
|
||||
open={composer.open || (!!session()?.parentID && forms().length === 0)}
|
||||
defaultTab={composer.tab ?? (session()?.parentID ? "subagents" : undefined)}
|
||||
onClose={() => setComposer("open", false)}
|
||||
/>
|
||||
<Switch>
|
||||
<Match when={composer.open || (!!session()?.parentID && forms().length === 0)}>{null}</Match>
|
||||
<Match when={permissions().length > 0}>
|
||||
<PermissionPrompt request={permissions()[0]} directory={session()?.location.directory} />
|
||||
</Match>
|
||||
<Match when={forms().length > 0}>
|
||||
<Show when={forms()[0]?.id} keyed>
|
||||
{(_) => {
|
||||
const form = forms()[0]
|
||||
return form ? <FormPrompt form={form} /> : null
|
||||
}}
|
||||
</Show>
|
||||
</Match>
|
||||
<Match when={!disabled()}>
|
||||
<pluginRuntime.Slot
|
||||
name="session_prompt"
|
||||
mode="replace"
|
||||
session_id={route.sessionID}
|
||||
visible={true}
|
||||
disabled={false}
|
||||
on_submit={toBottom}
|
||||
ref={bind}
|
||||
>
|
||||
<Prompt
|
||||
visible={true}
|
||||
disabled={false}
|
||||
on_submit={toBottom}
|
||||
ref={bind}
|
||||
>
|
||||
<Prompt
|
||||
visible={true}
|
||||
ref={bind}
|
||||
disabled={false}
|
||||
onSubmit={() => {
|
||||
toBottom()
|
||||
}}
|
||||
sessionID={route.sessionID}
|
||||
right={<pluginRuntime.Slot name="session_prompt_right" session_id={route.sessionID} />}
|
||||
/>
|
||||
</pluginRuntime.Slot>
|
||||
</Match>
|
||||
</Switch>
|
||||
</box>
|
||||
</Show>
|
||||
<Toast />
|
||||
</box>
|
||||
<Show when={sidebarVisible()}>
|
||||
<Switch>
|
||||
<Match when={wide()}>
|
||||
<Sidebar sessionID={route.sessionID} />
|
||||
</Match>
|
||||
<Match when={!wide()}>
|
||||
<box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
alignItems="flex-end"
|
||||
backgroundColor={RGBA.fromInts(0, 0, 0, 70)}
|
||||
>
|
||||
<Sidebar sessionID={route.sessionID} />
|
||||
</box>
|
||||
</Match>
|
||||
</Switch>
|
||||
disabled={false}
|
||||
onSubmit={() => {
|
||||
toBottom()
|
||||
}}
|
||||
sessionID={route.sessionID}
|
||||
right={<pluginRuntime.Slot name="session_prompt_right" session_id={route.sessionID} />}
|
||||
/>
|
||||
</pluginRuntime.Slot>
|
||||
</Match>
|
||||
</Switch>
|
||||
</box>
|
||||
</Show>
|
||||
<Toast />
|
||||
</box>
|
||||
</context.Provider>
|
||||
</LocationProvider>
|
||||
<Show when={sidebarVisible()}>
|
||||
<Switch>
|
||||
<Match when={wide()}>
|
||||
<Sidebar sessionID={route.sessionID} />
|
||||
</Match>
|
||||
<Match when={!wide()}>
|
||||
<box
|
||||
position="absolute"
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
alignItems="flex-end"
|
||||
backgroundColor={RGBA.fromInts(0, 0, 0, 70)}
|
||||
>
|
||||
<Sidebar sessionID={route.sessionID} />
|
||||
</box>
|
||||
</Match>
|
||||
</Switch>
|
||||
</Show>
|
||||
</box>
|
||||
</context.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -2500,9 +2501,7 @@ function Subagent(props: ToolProps) {
|
|||
const { navigate } = useRoute()
|
||||
const data = useData()
|
||||
const input = createMemo(() => (typeof props.part.state.input === "string" ? {} : props.part.state.input))
|
||||
const metadata = createMemo(() =>
|
||||
props.part.state.status === "streaming" ? {} : props.part.state.structured,
|
||||
)
|
||||
const metadata = createMemo(() => (props.part.state.status === "streaming" ? {} : props.part.state.structured))
|
||||
const sessionID = createMemo(() => stringValue(metadata().sessionID) ?? stringValue(metadata().sessionId))
|
||||
const description = createMemo(() => stringValue(input().description))
|
||||
const isRunning = createMemo(() => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue