File size: 4,634 Bytes
3e05655 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | import { Collapsible } from "@kobalte/core/collapsible"
import { type ComponentProps, type JSX, For, Show, createMemo, splitProps } from "solid-js"
import { DiffChanges } from "@opencode-ai/ui/v2/diff-changes-v2"
import { TextShimmerV2 } from "@opencode-ai/ui/v2/text-shimmer-v2"
import "./basic-tool-v2.css"
function ChevronIcon() {
return (
<svg
data-slot="basic-tool-v2-chevron"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M6.75194 10.6243C6.41861 10.8187 6 10.5783 6 10.1924V5.80837C6 5.42247 6.41861 5.18204 6.75194 5.37648L10.5096 7.56846C10.8404 7.7614 10.8404 8.2393 10.5096 8.43224L6.75194 10.6243Z"
fill="currentColor"
/>
</svg>
)
}
export interface BasicToolV2TriggerTitle {
title: string
subtitle?: string
args?: string[]
changes?: { additions: number; deletions: number } | { additions: number; deletions: number }[]
action?: JSX.Element
}
const isTriggerTitle = (val: unknown): val is BasicToolV2TriggerTitle =>
typeof val === "object" && val !== null && "title" in val && (typeof Node === "undefined" || !(val instanceof Node))
export interface BasicToolV2Props extends Omit<ComponentProps<"div">, "children" | "title"> {
trigger: BasicToolV2TriggerTitle | JSX.Element
children?: JSX.Element
status?: string
open?: boolean
defaultOpen?: boolean
onOpenChange?: (open: boolean) => void
onSubtitleClick?: () => void
}
export function BasicToolV2(props: BasicToolV2Props) {
const [local, rest] = splitProps(props, [
"trigger",
"children",
"status",
"open",
"defaultOpen",
"onOpenChange",
"onSubtitleClick",
"class",
"classList",
])
const pending = createMemo(() => local.status === "pending" || local.status === "running")
const hasChildren = createMemo(() => {
const c = local.children
if (c == null) return false
return true
})
const canExpand = createMemo(() => hasChildren() && !pending())
const handleOpenChange = (value: boolean) => {
if (pending()) return
local.onOpenChange?.(value)
}
return (
<Collapsible
{...rest}
data-component="basic-tool-v2"
open={local.open}
defaultOpen={local.defaultOpen}
onOpenChange={handleOpenChange}
disabled={!canExpand()}
classList={{
...local.classList,
[local.class ?? ""]: !!local.class,
}}
>
<Collapsible.Trigger as="div" role="button" data-slot="basic-tool-v2-trigger">
<div data-slot="basic-tool-v2-labels">
<Show when={isTriggerTitle(local.trigger) && local.trigger} fallback={local.trigger as JSX.Element}>
{(title) => (
<>
<span data-slot="basic-tool-v2-title">
<TextShimmerV2 text={title().title} active={pending()} />
</span>
<Show when={!pending() && title().subtitle}>
<span data-slot="basic-tool-v2-sep" aria-hidden="true">
·
</span>
<span
data-slot="basic-tool-v2-subtitle"
style={local.onSubtitleClick ? { cursor: "pointer" } : undefined}
onClick={(e) => {
if (local.onSubtitleClick) {
e.stopPropagation()
local.onSubtitleClick()
}
}}
>
{title().subtitle}
</span>
</Show>
<Show when={!pending() && title().args?.length}>
<For each={title().args}>{(arg) => <span data-slot="basic-tool-v2-arg">{arg}</span>}</For>
</Show>
<Show when={!pending() && title().changes}>
<span data-slot="basic-tool-v2-diff">
<DiffChanges changes={title().changes!} />
</span>
</Show>
<Show when={!pending() && title().action}>{(action) => action()}</Show>
</>
)}
</Show>
<Show when={canExpand()}>
<span data-slot="basic-tool-v2-chevron-wrap">
<ChevronIcon />
</span>
</Show>
</div>
</Collapsible.Trigger>
<Show when={canExpand()}>
<Collapsible.Content data-slot="basic-tool-v2-content">
<div data-slot="basic-tool-v2-content-inner">{local.children}</div>
</Collapsible.Content>
</Show>
</Collapsible>
)
}
|