Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit 9004419

Browse files
Added subagents to agents modal, non-selectable (anomalyco#4460) Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
1 parent 963a926 commit 9004419

2 files changed

Lines changed: 71 additions & 22 deletions

File tree

packages/opencode/src/cli/cmd/tui/component/dialog-agent.tsx

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,50 @@
11
import { createMemo } from "solid-js"
22
import { useLocal } from "@tui/context/local"
3+
import { useSync } from "@tui/context/sync"
34
import { DialogSelect } from "@tui/ui/dialog-select"
45
import { useDialog } from "@tui/ui/dialog"
6+
import { useTheme } from "@tui/context/theme"
57

68
export function DialogAgent() {
79
const local = useLocal()
10+
const sync = useSync()
811
const dialog = useDialog()
12+
const { theme } = useTheme()
913

10-
const options = createMemo(() =>
11-
local.agent.list().map((item) => {
12-
return {
13-
value: item.name,
14-
title: item.name,
15-
description: item.builtIn ? "native" : item.description,
16-
}
17-
}),
18-
)
14+
const options = createMemo(() => {
15+
const allAgents = sync.data.agent
16+
const primaryAgents = allAgents.filter((x) => x.mode !== "subagent")
17+
const subagents = allAgents.filter((x) => x.mode === "subagent")
18+
19+
const primaryOptions = primaryAgents.map((item) => ({
20+
value: item.name,
21+
title: item.name,
22+
description: item.builtIn ? "native" : item.description,
23+
category: "Primary Agents",
24+
}))
25+
26+
const subagentOptions = subagents.map((item) => ({
27+
value: item.name,
28+
title: item.name,
29+
description: item.builtIn ? "native" : item.description,
30+
category: "Subagents (non-selectable)",
31+
disabled: true,
32+
bg: theme.backgroundPanel,
33+
}))
34+
35+
return [...primaryOptions, ...subagentOptions]
36+
})
1937

2038
return (
2139
<DialogSelect
2240
title="Select agent"
2341
current={local.agent.current().name}
2442
options={options()}
2543
onSelect={(option) => {
26-
local.agent.set(option.value)
27-
dialog.clear()
44+
if (!option.disabled) {
45+
local.agent.set(option.value)
46+
dialog.clear()
47+
}
2848
}}
2949
/>
3050
)

packages/opencode/src/cli/cmd/tui/ui/dialog-select.tsx

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,8 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
5454

5555
const filtered = createMemo(() => {
5656
const needle = store.filter.toLowerCase()
57-
const result = pipe(
58-
props.options,
59-
filter((x) => x.disabled !== true),
60-
(x) => (!needle ? x : fuzzysort.go(needle, x, { keys: ["title", "category"] }).map((x) => x.obj)),
57+
const result = pipe(props.options, (x) =>
58+
!needle ? x : fuzzysort.go(needle, x, { keys: ["title", "category"] }).map((x) => x.obj),
6159
)
6260
return result
6361
})
@@ -96,6 +94,16 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
9694
let next = store.selected + direction
9795
if (next < 0) next = flat().length - 1
9896
if (next >= flat().length) next = 0
97+
98+
// Skip disabled options when flipping through agents
99+
let attempts = 0
100+
while (flat()[next]?.disabled && attempts < flat().length) {
101+
next = next + direction
102+
if (next < 0) next = flat().length - 1
103+
if (next >= flat().length) next = 0
104+
attempts++
105+
}
106+
99107
moveTo(next)
100108
}
101109

@@ -126,7 +134,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
126134
if (evt.name === "pagedown") move(10)
127135
if (evt.name === "return") {
128136
const option = selected()
129-
if (option) {
137+
if (option && !option.disabled) {
130138
// evt.preventDefault()
131139
if (option.onSelect) option.onSelect(dialog)
132140
props.onSelect?.(option)
@@ -136,7 +144,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
136144
for (const item of props.keybind ?? []) {
137145
if (Keybind.match(item.keybind, keybind.parse(evt))) {
138146
const s = selected()
139-
if (s) {
147+
if (s && !s.disabled) {
140148
evt.preventDefault()
141149
item.onTrigger(s)
142150
}
@@ -208,15 +216,19 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
208216
id={JSON.stringify(option.value)}
209217
flexDirection="row"
210218
onMouseUp={() => {
211-
option.onSelect?.(dialog)
212-
props.onSelect?.(option)
219+
if (!option.disabled) {
220+
option.onSelect?.(dialog)
221+
props.onSelect?.(option)
222+
}
213223
}}
214224
onMouseOver={() => {
215225
const index = filtered().findIndex((x) => isDeepEqual(x.value, option.value))
216226
if (index === -1) return
217227
moveTo(index)
218228
}}
219-
backgroundColor={active() ? (option.bg ?? theme.primary) : RGBA.fromInts(0, 0, 0, 0)}
229+
backgroundColor={
230+
active() && !option.disabled ? (option.bg ?? theme.primary) : RGBA.fromInts(0, 0, 0, 0)
231+
}
220232
paddingLeft={1}
221233
paddingRight={1}
222234
gap={1}
@@ -227,6 +239,7 @@ export function DialogSelect<T>(props: DialogSelectProps<T>) {
227239
description={option.description !== category ? option.description : undefined}
228240
active={active()}
229241
current={isDeepEqual(option.value, props.current)}
242+
disabled={option.disabled}
230243
/>
231244
</box>
232245
)
@@ -256,13 +269,22 @@ function Option(props: {
256269
active?: boolean
257270
current?: boolean
258271
footer?: JSX.Element | string
272+
disabled?: boolean
259273
onMouseOver?: () => void
260274
}) {
261275
const { theme } = useTheme()
262276

277+
const textColor = props.disabled
278+
? theme.textMuted
279+
: props.active
280+
? theme.background
281+
: props.current
282+
? theme.primary
283+
: theme.text
284+
263285
return (
264286
<>
265-
<Show when={props.current}>
287+
<Show when={props.current && !props.disabled}>
266288
<text
267289
flexShrink={0}
268290
fg={props.active ? theme.background : props.current ? theme.primary : theme.text}
@@ -271,10 +293,17 @@ function Option(props: {
271293
272294
</text>
273295
</Show>
296+
<Show when={props.disabled}>
297+
<text flexShrink={0} fg={theme.textMuted} marginRight={0.5}>
298+
299+
</text>
300+
</Show>
274301
<text
275302
flexGrow={1}
276303
fg={props.active ? theme.background : props.current ? theme.primary : theme.text}
277-
attributes={props.active ? TextAttributes.BOLD : undefined}
304+
attributes={
305+
props.active && !props.disabled ? TextAttributes.BOLD : props.disabled ? TextAttributes.DIM : undefined
306+
}
278307
overflow="hidden"
279308
wrapMode="none"
280309
>

0 commit comments

Comments
 (0)