feat(tui): label mention autocomplete options (#48551)

This commit is contained in:
Filip
2026-09-19 16:05:03 +02:00
committed by GitHub
Unverified
parent cb6d95b7ef
commit 7e70f7e1ab
@@ -44,7 +44,7 @@ export type AutocompleteOption = {
path?: string
absolute?: string
destructive?: { id: string; confirm: string; run: () => void }
kind?: "skill"
kind?: "skill" | "agent" | "reference"
queueable?: boolean
}
@@ -404,41 +404,13 @@ export function Autocomplete(props: {
return { options: [], failed: false, query: "", resolved: false }
})
const mcpResources = createMemo(() => {
if (store.visible !== "reference") return []
const options: AutocompleteOption[] = []
const width = props.anchor().width - 4
for (const res of data.location.mcp.resource.list(location.current) ?? []) {
options.push({
display: Locale.truncateMiddle(res.name, width),
// Match the name only; matching the URI caused unrelated fuzzy hits.
value: res.name,
description: res.description,
onSelect: () => {
insertPart(res.name, {
type: "file",
value: {
uri: res.uri,
name: res.name,
description: res.description,
mention: { start: 0, end: 0, text: "" },
},
})
},
})
}
return options
})
const agents = createMemo(() => {
return (data.location.agent.list() ?? [])
.filter((agent) => !agent.hidden && agent.mode !== "primary")
.map(
(agent): AutocompleteOption => ({
display: "@" + agent.id,
kind: "agent",
onSelect: () => {
insertPart(agent.id, {
type: "agent",
@@ -474,6 +446,7 @@ export function Autocomplete(props: {
.map(
(reference): AutocompleteOption => ({
display: "@" + reference.name,
kind: "reference",
description: ` ${reference.source.type === "git" ? reference.source.repository : reference.source.path}`,
onSelect: () => {
insertPart(reference.name, {
@@ -567,7 +540,7 @@ export function Autocomplete(props: {
const fileOptions: AutocompleteOption[] = store.visible === "reference" ? fileSearch.options : []
const nonFileOptions: AutocompleteOption[] =
store.visible === "reference"
? [...skillOptions(), ...referenceAliasesValue, ...agentsValue, ...mcpResources()]
? [...skillOptions(), ...referenceAliasesValue, ...agentsValue]
: store.index === 0
? [...commandsValue]
: []
@@ -869,6 +842,11 @@ export function Autocomplete(props: {
return "No matching files, agents, or references"
})
const emptyError = createMemo(() => store.visible === "reference" && !files.loading && visibleFiles().failed)
const labels = {
skill: "skill",
agent: "agent",
reference: "reference",
}
return (
<box
@@ -902,6 +880,14 @@ export function Autocomplete(props: {
>
{(option, index) => {
const destructive = () => option().destructive
const label = () => {
const kind = option().kind
return kind ? labels[kind] : undefined
}
const contentWidth = () => {
const text = label()
return Math.max(1, position().width - 4 - (text ? stringWidth(text) + 2 : 0))
}
const confirmingAction = () => {
const action = destructive()
return action !== undefined && action.id === confirming()
@@ -931,17 +917,32 @@ export function Autocomplete(props: {
: theme.text.base
}
flexShrink={0}
wrapMode="none"
>
{confirmingAction() ? destructive()?.confirm : option().display}
{Locale.truncateMiddle(
confirmingAction() ? (destructive()?.confirm ?? "") : option().display,
contentWidth(),
)}
</text>
<Show when={!confirmingAction() && option().description}>
<text
fg={index === store.selected ? theme.text.action.primary.focused : theme.text.muted}
wrapMode="none"
flexShrink={1}
minWidth={0}
>
{" " + option().description?.replace(/\s+/g, " ").trim()}
</text>
</Show>
<Show when={!confirmingAction() && label()}>
<box flexGrow={1} minWidth={2} />
<text
flexShrink={0}
fg={index === store.selected ? theme.text.action.primary.focused : theme.text.subdued}
>
{label()}
</text>
</Show>
</box>
)
}}