message-nav.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { UserMessage } from "@opencode-ai/sdk/v2"
  2. import { ComponentProps, For, Match, Show, splitProps, Switch } from "solid-js"
  3. import { DiffChanges } from "./diff-changes"
  4. import { Tooltip } from "@kobalte/core/tooltip"
  5. import { useI18n } from "../context/i18n"
  6. export function MessageNav(
  7. props: ComponentProps<"ul"> & {
  8. messages: UserMessage[]
  9. current?: UserMessage
  10. size: "normal" | "compact"
  11. onMessageSelect: (message: UserMessage) => void
  12. getLabel?: (message: UserMessage) => string | undefined
  13. },
  14. ) {
  15. const i18n = useI18n()
  16. const [local, others] = splitProps(props, ["messages", "current", "size", "onMessageSelect", "getLabel"])
  17. const content = () => (
  18. <ul role="list" data-component="message-nav" data-size={local.size} {...others}>
  19. <For each={local.messages}>
  20. {(message) => {
  21. const handleClick = () => local.onMessageSelect(message)
  22. const handleKeyPress = (event: KeyboardEvent) => {
  23. if (event.key !== "Enter" && event.key !== " ") return
  24. event.preventDefault()
  25. local.onMessageSelect(message)
  26. }
  27. return (
  28. <li data-slot="message-nav-item">
  29. <Switch>
  30. <Match when={local.size === "compact"}>
  31. <div
  32. data-slot="message-nav-tick-button"
  33. data-active={message.id === local.current?.id || undefined}
  34. role="button"
  35. tabindex={0}
  36. onClick={handleClick}
  37. onKeyDown={handleKeyPress}
  38. >
  39. <div data-slot="message-nav-tick-line" />
  40. </div>
  41. </Match>
  42. <Match when={local.size === "normal"}>
  43. <button data-slot="message-nav-message-button" onClick={handleClick} onKeyDown={handleKeyPress}>
  44. <DiffChanges changes={message.summary?.diffs ?? []} variant="bars" />
  45. <div
  46. data-slot="message-nav-title-preview"
  47. data-active={message.id === local.current?.id || undefined}
  48. >
  49. <Show
  50. when={local.getLabel?.(message) ?? message.summary?.title}
  51. fallback={i18n.t("ui.messageNav.newMessage")}
  52. >
  53. {local.getLabel?.(message) ?? message.summary?.title}
  54. </Show>
  55. </div>
  56. </button>
  57. </Match>
  58. </Switch>
  59. </li>
  60. )
  61. }}
  62. </For>
  63. </ul>
  64. )
  65. return (
  66. <Switch>
  67. <Match when={local.size === "compact"}>
  68. <Tooltip openDelay={0} closeDelay={300} placement="right-start" gutter={-40} shift={-10} overlap>
  69. <Tooltip.Trigger as="div">{content()}</Tooltip.Trigger>
  70. <Tooltip.Portal>
  71. <Tooltip.Content data-slot="message-nav-tooltip">
  72. <div data-slot="message-nav-tooltip-content">
  73. <MessageNav {...props} size="normal" class="" />
  74. </div>
  75. </Tooltip.Content>
  76. </Tooltip.Portal>
  77. </Tooltip>
  78. </Match>
  79. <Match when={local.size === "normal"}>{content()}</Match>
  80. </Switch>
  81. )
  82. }