command-palette.tsx 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { createMemo } from "solid-js"
  2. import { DialogSelect, type DialogSelectRef } from "../ui/dialog-select"
  3. import { type DialogContext } from "../ui/dialog"
  4. import {
  5. COMMAND_PALETTE_COMMAND,
  6. formatKeyBindings,
  7. type OpenTuiKeymap,
  8. useKeymapSelector,
  9. useOpencodeKeymap,
  10. } from "../keymap"
  11. import { useTuiConfig } from "../config"
  12. type PaletteCommandEntry = ReturnType<OpenTuiKeymap["getCommandEntries"]>[number]
  13. function isVisiblePaletteCommand(command: PaletteCommandEntry["command"]) {
  14. return command.hidden !== true && command.name !== COMMAND_PALETTE_COMMAND
  15. }
  16. function isSuggestedPaletteCommand(entry: PaletteCommandEntry) {
  17. const suggested = entry.command.suggested
  18. if (typeof suggested === "boolean") return suggested
  19. if (typeof suggested === "function") return suggested() === true
  20. return false
  21. }
  22. export function CommandPaletteDialog() {
  23. const config = useTuiConfig()
  24. const keymap = useOpencodeKeymap()
  25. const entries = useKeymapSelector((keymap: OpenTuiKeymap) => {
  26. const query = {
  27. namespace: "palette",
  28. }
  29. const reachable = keymap.getCommandEntries({
  30. ...query,
  31. visibility: "reachable",
  32. filter: isVisiblePaletteCommand,
  33. })
  34. const registeredBindings = keymap.getCommandBindings({
  35. visibility: "registered",
  36. commands: reachable.map((entry) => entry.command.name),
  37. })
  38. return reachable.map((entry) => ({
  39. ...entry,
  40. bindings: registeredBindings.get(entry.command.name) ?? entry.bindings,
  41. }))
  42. })
  43. const options = createMemo(() =>
  44. entries().map((entry) => ({
  45. title: typeof entry.command.title === "string" ? entry.command.title : entry.command.name,
  46. description: typeof entry.command.desc === "string" ? entry.command.desc : undefined,
  47. category: typeof entry.command.category === "string" ? entry.command.category : undefined,
  48. footer: formatKeyBindings(entry.bindings, config),
  49. value: entry.command.name,
  50. suggested: isSuggestedPaletteCommand(entry),
  51. onSelect: (dialog: DialogContext) => {
  52. dialog.clear()
  53. keymap.dispatchCommand(entry.command.name)
  54. },
  55. })),
  56. )
  57. let ref: DialogSelectRef<string>
  58. const list = () => {
  59. if (ref?.filter) return options()
  60. return [
  61. ...options()
  62. .filter((option) => option.suggested)
  63. .map((option) => ({
  64. ...option,
  65. value: `suggested:${option.value}`,
  66. category: "Suggested",
  67. })),
  68. ...options(),
  69. ]
  70. }
  71. return <DialogSelect ref={(value) => (ref = value)} title="Commands" options={list()} />
  72. }