basic-tool.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { createEffect, createSignal, For, Match, Show, Switch, type JSX } from "solid-js"
  2. import { Collapsible } from "./collapsible"
  3. import { Icon, IconProps } from "./icon"
  4. export type TriggerTitle = {
  5. title: string
  6. titleClass?: string
  7. subtitle?: string
  8. subtitleClass?: string
  9. args?: string[]
  10. argsClass?: string
  11. action?: JSX.Element
  12. }
  13. const isTriggerTitle = (val: any): val is TriggerTitle => {
  14. return (
  15. typeof val === "object" && val !== null && "title" in val && (typeof Node === "undefined" || !(val instanceof Node))
  16. )
  17. }
  18. export interface BasicToolProps {
  19. icon: IconProps["name"]
  20. trigger: TriggerTitle | JSX.Element
  21. children?: JSX.Element
  22. hideDetails?: boolean
  23. defaultOpen?: boolean
  24. forceOpen?: boolean
  25. }
  26. export function BasicTool(props: BasicToolProps) {
  27. const [open, setOpen] = createSignal(props.defaultOpen ?? false)
  28. createEffect(() => {
  29. if (props.forceOpen) setOpen(true)
  30. })
  31. return (
  32. <Collapsible open={open()} onOpenChange={setOpen}>
  33. <Collapsible.Trigger>
  34. <div data-component="tool-trigger">
  35. <div data-slot="basic-tool-tool-trigger-content">
  36. <Icon name={props.icon} size="small" />
  37. <div data-slot="basic-tool-tool-info">
  38. <Switch>
  39. <Match when={isTriggerTitle(props.trigger) && props.trigger}>
  40. {(trigger) => (
  41. <div data-slot="basic-tool-tool-info-structured">
  42. <div data-slot="basic-tool-tool-info-main">
  43. <span
  44. data-slot="basic-tool-tool-title"
  45. classList={{
  46. [trigger().titleClass ?? ""]: !!trigger().titleClass,
  47. }}
  48. >
  49. {trigger().title}
  50. </span>
  51. <Show when={trigger().subtitle}>
  52. <span
  53. data-slot="basic-tool-tool-subtitle"
  54. classList={{
  55. [trigger().subtitleClass ?? ""]: !!trigger().subtitleClass,
  56. }}
  57. >
  58. {trigger().subtitle}
  59. </span>
  60. </Show>
  61. <Show when={trigger().args?.length}>
  62. <For each={trigger().args}>
  63. {(arg) => (
  64. <span
  65. data-slot="basic-tool-tool-arg"
  66. classList={{
  67. [trigger().argsClass ?? ""]: !!trigger().argsClass,
  68. }}
  69. >
  70. {arg}
  71. </span>
  72. )}
  73. </For>
  74. </Show>
  75. </div>
  76. <Show when={trigger().action}>{trigger().action}</Show>
  77. </div>
  78. )}
  79. </Match>
  80. <Match when={true}>{props.trigger as JSX.Element}</Match>
  81. </Switch>
  82. </div>
  83. </div>
  84. <Show when={props.children && !props.hideDetails}>
  85. <Collapsible.Arrow />
  86. </Show>
  87. </div>
  88. </Collapsible.Trigger>
  89. <Show when={props.children && !props.hideDetails}>
  90. <Collapsible.Content>{props.children}</Collapsible.Content>
  91. </Show>
  92. </Collapsible>
  93. )
  94. }
  95. export function GenericTool(props: { tool: string; hideDetails?: boolean }) {
  96. return <BasicTool icon="mcp" trigger={{ title: props.tool }} hideDetails={props.hideDetails} />
  97. }