🛠️
LNUI-1000Command palette v2 — fuzzy actions, per-context ranking and a plugin API
1 / 292

Command palette v2 — fuzzy actions, per-context ranking and a plugin API

Context

The command palette is the most-used surface of the library — 41% of sessions open it at least once — and the current implementation has hit its ceiling: flat action list, no context awareness, and every consumer app forking it to inject custom actions. v2 makes the palette a platform.

Three pillars: a proper fuzzy matcher (typo-tolerant, weighted by recency), per-context ranking (the palette knows whether you are in a table, a form or an editor), and a typed plugin API so apps stop patching internals.

Command palette v2 — ranked results with context group headers
Ranked results: context actions first, then recents, then the global catalog.

Plugin API sketch

import { definePaletteSource } from '@lndev-ui/palette';

export const deploySource = definePaletteSource({
   id: 'deploys',
   // Ranked only when the current route matches.
   context: (ctx) => ctx.route.startsWith('/deploys'),
   actions: async (query) => {
      const environments = await fetchEnvironments(query);
      return environments.map((env) => ({
         id: `deploy-${env.id}`,
         label: `Deploy to ${env.name}`,
         icon: RocketIcon,
         shortcut: env.isProd ? undefined : ['d', env.key],
         run: () => deploy(env),
      }));
   },
});

Scope

  • Fuzzy matcher with per-character scoring, typo tolerance of 1, and recency boost (half-life: 7 days)
  • Context providers: table, form, editor, navigation — extensible through the plugin API
  • Plugin API: typed sources, async actions, loading states, error boundaries per source
  • Full keyboard model: nested pages, back with Backspace, peek with Tab
  • Latency budget: 8ms per keystroke at 5,000 registered actions

Acceptance criteria

Matcher beats the current one on the recorded query corpus (top-3 hit rate ≥ 92%)
p95 keystroke-to-paint under 8ms with 5,000 actions on the perf rig
Plugin API covers the four forked use cases found in the ecosystem scan
Zero regressions on the palette a11y audit (focus, announcements, reduced motion)
Migration guide + codemod for the v1 actions prop

Every app we surveyed rebuilt ranking on top of the palette. If the library ranked by context, we could delete about 400 lines each.— ecosystem scan, July 2026
LNUI-703Rework Dialog focus trap to support nested portals — the nested-portal focus work this builds on

Activity

leonel.ngoya created the issue· 3w ago
leonel.ngoya added Feature, UI Enhancement and Performance· 3w ago
ssophia.reed2w ago

Ran the recorded corpus against the prototype matcher: top-3 hit rate goes from 81% to 93.4%, and typo tolerance catches 610 of the 640 near-miss queries. The recency boost matters more than I expected — without it we lose 4 points.

corpus: 18,420 queries
top-1  72.1%  (+9.8)
top-3  93.4%  (+12.4)
p95    3.1ms @ 5k actions
🔥 6🚀 3
sophia.reed linked feat(palette): scoring core + recorded-corpus benchmark· 2w ago
leonel.ngoya moved from Todo to In Progress· 12d ago
eemma.jones10d ago

Design note: context groups need their own headers in the results list, otherwise the ranking looks random to people who do not know why a table action outranks their recent files. Mock attached to the description — the group header carries the context icon.

👍 4
ggabriel.nguyen1w ago

A11y pass on the prototype: announcements read the group name before the action, which doubles the verbosity. Suggest announcing the group only when it changes — same pattern the combobox rewrite landed in LNUI-388.

Announce group transitions only
aria-activedescendant kept during async loads
Reduced motion: replace slide with fade for page transitions
🙏 2
mason.carter raised the priority to Urgent — v4.5 headline feature· 6d ago
leonel.ngoya added to Cycle 21· 6d ago
nnoah.davis3d ago

Perf rig results on the ranking pipeline PR: 7.2ms p95 at 5,000 actions, 11.8ms at 10,000. We are inside budget at the target size; past 8k we should chunk the scoring pass across frames. Filed the follow-up as a sub-issue.

📈 3💯 2
lleonel.ngoya2h ago

Plugin API surface is up for review (PR linked). Last open questions before the freeze: error boundaries per source vs global, and whether context() should be sync-only. Weighing in on both in the PR — review by Thursday and we make the cycle.

👀 5