fix(editor): run the slash command that was clicked

Merged
KA
17d ago
+245-22

Closes #3746.

Problem

With no query typed, the slash command menu can run a different item's command than the one clicked or highlighted. Using the docs' own custom commands example (an Image item with category: 'Media' appended to defaultSlashCommands), the menu shows Text → Media → Layout, but clicking Image inserts a Button, and arrow keys highlight one row while Enter runs another.

Root cause

The two sides numbered the rows differently.

  • ui/slash-command/command-list.tsx reorders the items for display: groupByCategory pulls the categories in CATEGORY_ORDER to the front, everything else follows in first-seen order. Each row then called onSelect(flatIndex) — its position in the grouped display order — and highlighted itself with the same number.
  • ui/slash-command/root.tsx resolves both onSelect and the Enter handler against suggestionItemsRef.current[index], i.e. the original, unordered array, and the arrow keys walk that same array.

As long as the array happens to be in CATEGORY_ORDER the two orders agree, which is why the built-in commands don't show it. Any other order — a custom category, a translated one, or a command appended at the end — shifts every row after the first group. With a query typed the list is rendered flat, so it works.

Change

  • command-list.tsx: groupByCategory now carries each item's index in the list the root holds, and the rows use that index for both onSelect and data-selected, instead of counting display positions. No flatIndex counter.
  • command-list.tsx: exports orderItemsByCategory, the same grouping flattened back into a list.
  • root.tsx: defaultFilterItems returns the items in that display order when there is no query, so the arrow keys move down the list the way it is drawn instead of jumping between groups. With a query the list stays flat and ranked by score.

What I deliberately left alone

  • CATEGORY_ORDER itself, and the choice to group only when there is no query.
  • A custom filterItems still controls its own order — the index bookkeeping keeps clicks correct whatever order it returns, including categories interleaved in the array.
  • search.ts ranking is untouched.

Test

packages/editor/src/ui/slash-command/command-list.spec.tsx renders the menu with [Text (Text), Divider (Layout), Image (Media)] — display order Text → Media → Layout, array order Text → Layout → Media — and covers the click index, the highlighted row and orderItemsByCategory.

$ cd packages/editor && pnpm vitest run --project unit
 Test Files  53 passed (53)
      Tests  516 passed (516)

$ pnpm typecheck        # clean
$ pnpm biome check packages/editor/src/ui/slash-command   # clean

Before the fix, the new tests fail exactly the way the issue describes:

 × selects the clicked item and not the one in its display position
   AssertionError: expected "vi.fn()" to be called with arguments: [ 2 ]
   Received: 1                      // clicking Image selected Divider
 × highlights the item the index points at
   AssertionError: expected 'Divider' to contain 'Image'

🤖 Generated with Claude Code

https://claude.ai/code/session_011Yyp6p2GWzkWKLsrxxJR1U


Summary by cubic

Fixes the slash command menu running a different command than clicked and arrow keys landing on the wrong row when categories display out of array order — for example, a custom Image command appended after the defaults could insert a Button.

  • Rows resolve clicks and highlighting against the original item index, not their display position.
  • Sorts items into displayed category order in the root after filtering, only when the built-in list renders with no query, so arrow keys follow the drawn rows even with a custom filterItems.
  • Keeps custom children renderers receiving items in array order.
  • Exports orderItemsByCategory and adds unit tests for click, highlight, arrow-key, and renderer ordering.