summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components/basic-tool.tsx
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-04-07 11:06:23 -0500
committerGitHub <[email protected]>2026-04-07 11:06:23 -0500
commitec8b9810b4231cd6a5c69ccd930b6c50999fc997 (patch)
tree562313d6dd3eda9891f3a4a3a2ef6ce3d36acd05 /packages/ui/src/components/basic-tool.tsx
parent65318a80f7a3320ba77b749241f8de997dc65c82 (diff)
downloadopencode-ec8b9810b4231cd6a5c69ccd930b6c50999fc997.tar.gz
opencode-ec8b9810b4231cd6a5c69ccd930b6c50999fc997.zip
feat(app): better subagent experience (#20708)
Diffstat (limited to 'packages/ui/src/components/basic-tool.tsx')
-rw-r--r--packages/ui/src/components/basic-tool.tsx142
1 files changed, 86 insertions, 56 deletions
diff --git a/packages/ui/src/components/basic-tool.tsx b/packages/ui/src/components/basic-tool.tsx
index a02fe941b..7d18dfacd 100644
--- a/packages/ui/src/components/basic-tool.tsx
+++ b/packages/ui/src/components/basic-tool.tsx
@@ -34,6 +34,9 @@ export interface BasicToolProps {
locked?: boolean
animated?: boolean
onSubtitleClick?: () => void
+ onTriggerClick?: JSX.EventHandlerUnion<HTMLElement, MouseEvent>
+ triggerHref?: string
+ clickable?: boolean
}
const SPRING = { type: "spring" as const, visualDuration: 0.35, bounce: 0 }
@@ -121,74 +124,101 @@ export function BasicTool(props: BasicToolProps) {
setState("open", value)
}
- return (
- <Collapsible open={open()} onOpenChange={handleOpenChange} class="tool-collapsible">
- <Collapsible.Trigger>
- <div data-component="tool-trigger">
- <div data-slot="basic-tool-tool-trigger-content">
- <div data-slot="basic-tool-tool-info">
- <Switch>
- <Match when={isTriggerTitle(props.trigger) && props.trigger}>
- {(trigger) => (
- <div data-slot="basic-tool-tool-info-structured">
- <div data-slot="basic-tool-tool-info-main">
+ const trigger = () => (
+ <div
+ data-component="tool-trigger"
+ data-clickable={props.clickable ? "true" : undefined}
+ data-hide-details={props.hideDetails ? "true" : undefined}
+ >
+ <div data-slot="basic-tool-tool-trigger-content">
+ <div data-slot="basic-tool-tool-info">
+ <Switch>
+ <Match when={isTriggerTitle(props.trigger) && props.trigger}>
+ {(title) => (
+ <div data-slot="basic-tool-tool-info-structured">
+ <div data-slot="basic-tool-tool-info-main">
+ <span
+ data-slot="basic-tool-tool-title"
+ classList={{
+ [title().titleClass ?? ""]: !!title().titleClass,
+ }}
+ >
+ <TextShimmer text={title().title} active={pending()} />
+ </span>
+ <Show when={!pending()}>
+ <Show when={title().subtitle}>
<span
- data-slot="basic-tool-tool-title"
+ data-slot="basic-tool-tool-subtitle"
classList={{
- [trigger().titleClass ?? ""]: !!trigger().titleClass,
+ [title().subtitleClass ?? ""]: !!title().subtitleClass,
+ clickable: !!props.onSubtitleClick,
+ }}
+ onClick={(e) => {
+ if (props.onSubtitleClick) {
+ e.stopPropagation()
+ props.onSubtitleClick()
+ }
}}
>
- <TextShimmer text={trigger().title} active={pending()} />
+ {title().subtitle}
</span>
- <Show when={!pending()}>
- <Show when={trigger().subtitle}>
+ </Show>
+ <Show when={title().args?.length}>
+ <For each={title().args}>
+ {(arg) => (
<span
- data-slot="basic-tool-tool-subtitle"
+ data-slot="basic-tool-tool-arg"
classList={{
- [trigger().subtitleClass ?? ""]: !!trigger().subtitleClass,
- clickable: !!props.onSubtitleClick,
- }}
- onClick={(e) => {
- if (props.onSubtitleClick) {
- e.stopPropagation()
- props.onSubtitleClick()
- }
+ [title().argsClass ?? ""]: !!title().argsClass,
}}
>
- {trigger().subtitle}
+ {arg}
</span>
- </Show>
- <Show when={trigger().args?.length}>
- <For each={trigger().args}>
- {(arg) => (
- <span
- data-slot="basic-tool-tool-arg"
- classList={{
- [trigger().argsClass ?? ""]: !!trigger().argsClass,
- }}
- >
- {arg}
- </span>
- )}
- </For>
- </Show>
- </Show>
- </div>
- <Show when={!pending() && trigger().action}>
- <span data-slot="basic-tool-tool-action">{trigger().action}</span>
+ )}
+ </For>
</Show>
- </div>
- )}
- </Match>
- <Match when={true}>{props.trigger as JSX.Element}</Match>
- </Switch>
- </div>
- </div>
- <Show when={props.children && !props.hideDetails && !props.locked && !pending()}>
- <Collapsible.Arrow />
- </Show>
+ </Show>
+ </div>
+ <Show when={!pending() && title().action}>
+ <span data-slot="basic-tool-tool-action">{title().action}</span>
+ </Show>
+ </div>
+ )}
+ </Match>
+ <Match when={true}>{props.trigger as JSX.Element}</Match>
+ </Switch>
</div>
- </Collapsible.Trigger>
+ </div>
+ <Show when={props.children && !props.hideDetails && !props.locked && !pending()}>
+ <Collapsible.Arrow />
+ </Show>
+ </div>
+ )
+
+ return (
+ <Collapsible open={open()} onOpenChange={handleOpenChange} class="tool-collapsible">
+ <Show
+ when={props.triggerHref}
+ fallback={
+ <Collapsible.Trigger
+ data-hide-details={props.hideDetails ? "true" : undefined}
+ onClick={props.onTriggerClick}
+ >
+ {trigger()}
+ </Collapsible.Trigger>
+ }
+ >
+ {(href) => (
+ <Collapsible.Trigger
+ as="a"
+ href={href()}
+ data-hide-details={props.hideDetails ? "true" : undefined}
+ onClick={props.onTriggerClick}
+ >
+ {trigger()}
+ </Collapsible.Trigger>
+ )}
+ </Show>
<Show when={props.animated && props.children && !props.hideDetails}>
<div
ref={contentRef}