Compare commits

...

1 Commits

Author SHA1 Message Date
Kit Langton 16255dd8b6 feat(tui): add agent picker preview 2026-07-12 23:12:41 +00:00
2 changed files with 83 additions and 11 deletions
+46 -2
View File
@@ -1,27 +1,71 @@
import { createMemo } from "solid-js"
import { useLocal } from "../context/local"
import { DialogSelect } from "../ui/dialog-select"
import { DialogSelect, type DialogSelectOption } from "../ui/dialog-select"
import { useDialog } from "../ui/dialog"
import { useTheme } from "../context/theme"
import { TextAttributes } from "@opentui/core"
import { DialogModel } from "./dialog-model"
export function DialogAgent() {
const local = useLocal()
const dialog = useDialog()
const { theme } = useTheme()
dialog.setSize("xlarge")
const options = createMemo(() =>
local.agent.list().map((item) => {
return {
value: item.id,
title: item.id,
description: item.description,
description: undefined,
}
}),
)
function Preview(props: { option: DialogSelectOption<string> | undefined }) {
const agent = createMemo(() => local.agent.list().find((item) => item.id === props.option?.value))
const model = createMemo(() => {
const value = agent()?.model
if (value) return `${value.providerID}/${value.id}`
return "Uses the current session model"
})
return (
<box gap={1}>
<text fg={theme.text} attributes={TextAttributes.BOLD}>
{agent()?.id}
</text>
<box>
<text fg={theme.textMuted}>DESCRIPTION</text>
<text fg={theme.text} wrapMode="word">
{agent()?.description ?? "No description provided."}
</text>
</box>
<box>
<text fg={theme.textMuted}>MODEL</text>
<text fg={agent()?.model ? theme.text : theme.textMuted}>{model()}</text>
</box>
</box>
)
}
return (
<DialogSelect
title="Select agent"
current={local.agent.current()?.id}
options={options()}
preview={(option) => <Preview option={option} />}
actions={[
{
command: "model.list",
title: "Choose model",
onTrigger(option) {
local.agent.set(option.value)
dialog.replace(() => <DialogModel />)
},
},
]}
onSelect={(option) => {
local.agent.set(option.value)
dialog.clear()
+37 -9
View File
@@ -49,6 +49,7 @@ export interface DialogSelectProps<T> {
label: string
side?: "left" | "right"
}[]
preview?: (option: DialogSelectOption<T> | undefined) => JSX.Element
bindings?: readonly Binding<Renderable, KeyEvent>[]
current?: T
}
@@ -557,17 +558,37 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
}
return (
<box gap={1} paddingBottom={1} flexGrow={1}>
<box paddingLeft={4} paddingRight={4}>
<box gap={1} paddingBottom={1} flexGrow={1} minHeight={props.preview ? 18 : undefined} position="relative">
<Show when={props.preview}>
<box
position="absolute"
top={-1}
bottom={0}
right={0}
width="58%"
paddingLeft={4}
paddingRight={4}
backgroundColor={theme.backgroundElement}
zIndex={2}
>
<text position="absolute" right={4} fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
esc
</text>
{props.preview?.(selected())}
</box>
</Show>
<box paddingLeft={4} paddingRight={props.preview ? 2 : 4} width={props.preview ? "42%" : "100%"}>
<box flexDirection="row" justifyContent="space-between">
{props.titleView ?? (
<text fg={theme.text} attributes={TextAttributes.BOLD}>
{props.title}
</text>
)}
<text fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
esc
</text>
<Show when={!props.preview}>
<text fg={theme.textMuted} onMouseUp={() => dialog.clear()}>
esc
</text>
</Show>
</box>
<Show when={props.renderFilter !== false}>
<box paddingTop={1}>
@@ -597,7 +618,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
</box>
</Show>
</box>
<box flexGrow={1} flexShrink={1}>
<box flexGrow={1} flexShrink={1} width={props.preview ? "42%" : "100%"}>
<Show
when={grouped().length > 0}
fallback={
@@ -609,8 +630,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
}
>
<scrollbox
paddingLeft={1}
paddingRight={1}
paddingLeft={props.preview ? 3 : 1}
paddingRight={props.preview ? 3 : 1}
scrollbarOptions={{ visible: false }}
scrollAcceleration={scrollAcceleration()}
ref={(r: ScrollBoxRenderable) => (scroll = r)}
@@ -721,7 +742,14 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
</Show>
</box>
<Show when={props.footer || visibleActions().length} fallback={<box flexShrink={0} />}>
<box paddingRight={2} paddingLeft={4} flexDirection="row" justifyContent="space-between" flexShrink={0}>
<box
paddingRight={2}
paddingLeft={4}
width={props.preview ? "42%" : "100%"}
flexDirection="row"
justifyContent="space-between"
flexShrink={0}
>
<box flexDirection="row" gap={2}>
{props.footer}
<For each={left()}>{(item) => <FooterAction item={item} />}</For>