command-shim.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // Legacy `api.command` bridge for v1 plugins; remove in v2.
  2. import type { TuiCommand, TuiPluginApi } from "@opencode-ai/plugin/tui"
  3. import { TuiKeybind } from "../config/keybind"
  4. import type { DialogContext } from "../ui/dialog"
  5. const COMMAND_PALETTE_SHOW = "command.palette.show"
  6. const warned = new Set<string>()
  7. type Warn = (api: string, replacement: string) => void
  8. type LegacyDialog = TuiPluginApi["ui"]["dialog"]
  9. type CommandShimDialog = DialogContext | LegacyDialog
  10. type LegacyKeybinds = TuiPluginApi["tuiConfig"]["keybinds"]
  11. function warnCommandShim(api: string, replacement: string) {
  12. // Warn v1 plugins about deprecated `api.command`; remove this shim path in v2.
  13. console.warn("[tui.plugin] deprecated TUI plugin API", { api, replacement })
  14. }
  15. function createCommandShimDialog(dialog: CommandShimDialog): LegacyDialog {
  16. if (!("stack" in dialog)) return dialog
  17. return {
  18. replace(render, onClose) {
  19. dialog.replace(render, onClose)
  20. },
  21. clear() {
  22. dialog.clear()
  23. },
  24. setSize(size) {
  25. dialog.setSize(size)
  26. },
  27. get size() {
  28. return dialog.size
  29. },
  30. get depth() {
  31. return dialog.stack.length
  32. },
  33. get open() {
  34. return dialog.stack.length > 0
  35. },
  36. }
  37. }
  38. function warnOnce(api: string, replacement: string, warn: Warn) {
  39. if (warned.has(api)) return
  40. warned.add(api)
  41. warn(api, replacement)
  42. }
  43. function toCommand(item: TuiCommand, dialog: LegacyDialog) {
  44. return {
  45. namespace: "palette",
  46. name: item.value,
  47. title: item.title,
  48. desc: item.description,
  49. category: item.category,
  50. suggested: item.suggested,
  51. hidden: item.hidden,
  52. enabled: item.enabled,
  53. slashName: item.slash?.name,
  54. slashAliases: item.slash?.aliases,
  55. run() {
  56. return item.onSelect?.(dialog)
  57. },
  58. }
  59. }
  60. function toBindings(commands: TuiCommand[], keybinds: LegacyKeybinds) {
  61. return commands.flatMap((item) =>
  62. item.keybind
  63. ? keybinds.has(TuiKeybind.CommandMap[item.keybind as keyof typeof TuiKeybind.CommandMap] ?? item.keybind)
  64. ? keybinds
  65. .get(TuiKeybind.CommandMap[item.keybind as keyof typeof TuiKeybind.CommandMap] ?? item.keybind)
  66. .map((binding) => ({ ...binding, cmd: item.value, desc: binding.desc ?? item.title }))
  67. : [
  68. {
  69. key: item.keybind,
  70. cmd: item.value,
  71. desc: item.title,
  72. },
  73. ]
  74. : [],
  75. )
  76. }
  77. export function createCommandShim(
  78. keymap: TuiPluginApi["keymap"],
  79. dialog: CommandShimDialog,
  80. keybinds: LegacyKeybinds,
  81. ): TuiPluginApi["command"] {
  82. const shimDialog = createCommandShimDialog(dialog)
  83. return {
  84. register(cb) {
  85. warnOnce("api.command.register", "api.keymap.registerLayer({ commands, bindings })", warnCommandShim)
  86. const commands = cb()
  87. return keymap.registerLayer({
  88. commands: commands.map((item) => toCommand(item, shimDialog)),
  89. bindings: toBindings(commands, keybinds),
  90. })
  91. },
  92. trigger(value) {
  93. warnOnce("api.command.trigger", "api.keymap.dispatchCommand(name)", warnCommandShim)
  94. keymap.dispatchCommand(value)
  95. },
  96. show() {
  97. warnOnce("api.command.show", `api.keymap.dispatchCommand("${COMMAND_PALETTE_SHOW}")`, warnCommandShim)
  98. keymap.dispatchCommand(COMMAND_PALETTE_SHOW)
  99. },
  100. }
  101. }