prompt-workspace-selector.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import { For, Show } from "solid-js"
  2. import { MenuV2 } from "@kirincode-ai/ui/v2/menu-v2"
  3. import { Icon } from "@kirincode-ai/ui/icon"
  4. import { Icon as IconV2 } from "@kirincode-ai/ui/v2/icon"
  5. import { getFilename } from "@kirincode-ai/core/util/path"
  6. import { useLanguage } from "@/context/language"
  7. export function PromptWorkspaceSelector(props: {
  8. value: string
  9. projectRoot: string
  10. workspaces: string[]
  11. branch?: string
  12. onChange: (value: string) => void
  13. onDone: () => void
  14. }) {
  15. const language = useLanguage()
  16. let pending: string | undefined
  17. const selected = () => (props.value === props.projectRoot ? "main" : props.value)
  18. const icon = () => {
  19. if (selected() === "main") return "monitor"
  20. if (selected() === "create") return "workspace-new"
  21. return "workspace"
  22. }
  23. const select = (value: string) => {
  24. pending = value
  25. }
  26. const onOpenChange = (open: boolean) => {
  27. if (open) return
  28. const value = pending
  29. pending = undefined
  30. if (value) props.onChange(value)
  31. props.onDone()
  32. }
  33. const label = () => {
  34. if (selected() === "main") return language.t("session.new.workspace.triggerLocal")
  35. if (props.value === "create") return language.t("workspace.new")
  36. return getFilename(props.value)
  37. }
  38. return (
  39. <>
  40. <span class="hidden select-none opacity-50 sm:inline mx-1">/</span>
  41. <MenuV2 placement="bottom" gutter={4} onOpenChange={onOpenChange}>
  42. <MenuV2.Trigger class="flex h-7 min-w-0 max-w-[203px] items-center gap-1.5 rounded-sm px-1.5 hover:bg-v2-overlay-simple-overlay-hover focus-visible:bg-v2-overlay-simple-overlay-hover focus-visible:outline-none data-[expanded]:bg-v2-overlay-simple-overlay-pressed data-[expanded]:text-v2-text-text-muted">
  43. <IconV2 name={icon()} class="shrink-0 text-v2-icon-icon-muted" />
  44. <span class="min-w-0 truncate">{label()}</span>
  45. <Icon name="chevron-down" size="small" class="shrink-0 text-v2-icon-icon-muted" />
  46. </MenuV2.Trigger>
  47. <MenuV2.Portal>
  48. <MenuV2.Content class="w-[180px]">
  49. <MenuV2.Group>
  50. <MenuV2.GroupLabel>{language.t("session.new.workspace.runIn")}</MenuV2.GroupLabel>
  51. <MenuV2.Item onSelect={() => select("main")}>
  52. <IconV2 name="monitor" />
  53. <span class="min-w-0 flex-1 truncate">{language.t("session.new.workspace.local")}</span>
  54. <Show when={selected() === "main"}>
  55. <Icon name="check" size="small" class="shrink-0" />
  56. </Show>
  57. </MenuV2.Item>
  58. <MenuV2.Item onSelect={() => select("create")}>
  59. <IconV2 name="workspace-new" />
  60. <span class="min-w-0 flex-1 truncate">{language.t("workspace.new")}</span>
  61. <Show when={selected() === "create"}>
  62. <Icon name="check" size="small" class="shrink-0" />
  63. </Show>
  64. </MenuV2.Item>
  65. </MenuV2.Group>
  66. <Show when={props.workspaces.length > 0}>
  67. <MenuV2.Separator />
  68. <MenuV2.Sub gutter={0} overlap overflowPadding={8}>
  69. <MenuV2.SubTrigger>
  70. <IconV2 name="workspace" />
  71. {language.t("session.new.workspace.existing")}
  72. </MenuV2.SubTrigger>
  73. <MenuV2.Portal>
  74. <MenuV2.SubContent class="max-w-[200px]">
  75. <For each={props.workspaces}>
  76. {(workspace) => (
  77. <MenuV2.Item onSelect={() => select(workspace)}>
  78. <IconV2 name="workspace-isolated" />
  79. <span class="min-w-0 flex-1 truncate">{getFilename(workspace)}</span>
  80. <Show when={selected() === workspace}>
  81. <Icon name="check" size="small" class="shrink-0" />
  82. </Show>
  83. </MenuV2.Item>
  84. )}
  85. </For>
  86. </MenuV2.SubContent>
  87. </MenuV2.Portal>
  88. </MenuV2.Sub>
  89. </Show>
  90. </MenuV2.Content>
  91. </MenuV2.Portal>
  92. </MenuV2>
  93. <Show when={props.branch}>
  94. {(branch) => (
  95. <>
  96. <span class="hidden select-none opacity-50 sm:inline mx-1">/</span>
  97. <div class="flex h-7 min-w-0 max-w-[220px] items-center gap-1.5 px-2 text-[13px] font-[440] leading-5 tracking-[-0.04px]">
  98. <Icon name="branch" size="small" class="shrink-0 text-v2-icon-icon-muted" />
  99. <span class="min-w-0 truncate">{branch()}</span>
  100. </div>
  101. </>
  102. )}
  103. </Show>
  104. </>
  105. )
  106. }