diff options
| author | opencode-agent[bot] <219766164+opencode-agent[bot]@users.noreply.github.com> | 2025-11-19 00:40:47 -0600 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-11-19 00:40:47 -0600 |
| commit | 90044196bf06b630b81182a3eb0673a3032b2f06 (patch) | |
| tree | ef7d61b9e1026110e62626441f89f4dd3ed16dbd | |
| parent | 963a926db24ea70f6002cde66d0ff858de06fae2 (diff) | |
| download | opencode-90044196bf06b630b81182a3eb0673a3032b2f06.tar.gz opencode-90044196bf06b630b81182a3eb0673a3032b2f06.zip | |
Added subagents to agents modal, non-selectable (#4460)
Co-authored-by: Aiden Cline <[email protected]>
| -rw-r--r-- | packages/opencode/src/cli/cmd/tui/component/dialog-agent.tsx | 42 | ||||
| -rw-r--r-- | packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx | 51 |
2 files changed, 71 insertions, 22 deletions
diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-agent.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-agent.tsx index 65aaeb22b..15e8307f4 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-agent.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-agent.tsx @@ -1,21 +1,39 @@ import { createMemo } from "solid-js" import { useLocal } from "@tui/context/local" +import { useSync } from "@tui/context/sync" import { DialogSelect } from "@tui/ui/dialog-select" import { useDialog } from "@tui/ui/dialog" +import { useTheme } from "@tui/context/theme" export function DialogAgent() { const local = useLocal() + const sync = useSync() const dialog = useDialog() + const { theme } = useTheme() - const options = createMemo(() => - local.agent.list().map((item) => { - return { - value: item.name, - title: item.name, - description: item.builtIn ? "native" : item.description, - } - }), - ) + const options = createMemo(() => { + const allAgents = sync.data.agent + const primaryAgents = allAgents.filter((x) => x.mode !== "subagent") + const subagents = allAgents.filter((x) => x.mode === "subagent") + + const primaryOptions = primaryAgents.map((item) => ({ + value: item.name, + title: item.name, + description: item.builtIn ? "native" : item.description, + category: "Primary Agents", + })) + + const subagentOptions = subagents.map((item) => ({ + value: item.name, + title: item.name, + description: item.builtIn ? "native" : item.description, + category: "Subagents (non-selectable)", + disabled: true, + bg: theme.backgroundPanel, + })) + + return [...primaryOptions, ...subagentOptions] + }) return ( <DialogSelect @@ -23,8 +41,10 @@ export function DialogAgent() { current={local.agent.current().name} options={options()} onSelect={(option) => { - local.agent.set(option.value) - dialog.clear() + if (!option.disabled) { + local.agent.set(option.value) + dialog.clear() + } }} /> ) diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx index 285c039c1..8f3e71bfd 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx @@ -54,10 +54,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) { const filtered = createMemo(() => { const needle = store.filter.toLowerCase() - const result = pipe( - props.options, - filter((x) => x.disabled !== true), - (x) => (!needle ? x : fuzzysort.go(needle, x, { keys: ["title", "category"] }).map((x) => x.obj)), + const result = pipe(props.options, (x) => + !needle ? x : fuzzysort.go(needle, x, { keys: ["title", "category"] }).map((x) => x.obj), ) return result }) @@ -96,6 +94,16 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) { let next = store.selected + direction if (next < 0) next = flat().length - 1 if (next >= flat().length) next = 0 + + // Skip disabled options when flipping through agents + let attempts = 0 + while (flat()[next]?.disabled && attempts < flat().length) { + next = next + direction + if (next < 0) next = flat().length - 1 + if (next >= flat().length) next = 0 + attempts++ + } + moveTo(next) } @@ -126,7 +134,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) { if (evt.name === "pagedown") move(10) if (evt.name === "return") { const option = selected() - if (option) { + if (option && !option.disabled) { // evt.preventDefault() if (option.onSelect) option.onSelect(dialog) props.onSelect?.(option) @@ -136,7 +144,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) { for (const item of props.keybind ?? []) { if (Keybind.match(item.keybind, keybind.parse(evt))) { const s = selected() - if (s) { + if (s && !s.disabled) { evt.preventDefault() item.onTrigger(s) } @@ -208,15 +216,19 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) { id={JSON.stringify(option.value)} flexDirection="row" onMouseUp={() => { - option.onSelect?.(dialog) - props.onSelect?.(option) + if (!option.disabled) { + option.onSelect?.(dialog) + props.onSelect?.(option) + } }} onMouseOver={() => { const index = filtered().findIndex((x) => isDeepEqual(x.value, option.value)) if (index === -1) return moveTo(index) }} - backgroundColor={active() ? (option.bg ?? theme.primary) : RGBA.fromInts(0, 0, 0, 0)} + backgroundColor={ + active() && !option.disabled ? (option.bg ?? theme.primary) : RGBA.fromInts(0, 0, 0, 0) + } paddingLeft={1} paddingRight={1} gap={1} @@ -227,6 +239,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) { description={option.description !== category ? option.description : undefined} active={active()} current={isDeepEqual(option.value, props.current)} + disabled={option.disabled} /> </box> ) @@ -256,13 +269,22 @@ function Option(props: { active?: boolean current?: boolean footer?: JSX.Element | string + disabled?: boolean onMouseOver?: () => void }) { const { theme } = useTheme() + const textColor = props.disabled + ? theme.textMuted + : props.active + ? theme.background + : props.current + ? theme.primary + : theme.text + return ( <> - <Show when={props.current}> + <Show when={props.current && !props.disabled}> <text flexShrink={0} fg={props.active ? theme.background : props.current ? theme.primary : theme.text} @@ -271,10 +293,17 @@ function Option(props: { ● </text> </Show> + <Show when={props.disabled}> + <text flexShrink={0} fg={theme.textMuted} marginRight={0.5}> + ○ + </text> + </Show> <text flexGrow={1} fg={props.active ? theme.background : props.current ? theme.primary : theme.text} - attributes={props.active ? TextAttributes.BOLD : undefined} + attributes={ + props.active && !props.disabled ? TextAttributes.BOLD : props.disabled ? TextAttributes.DIM : undefined + } overflow="hidden" wrapMode="none" > |
