Skip to content

feat(ui): export BUILT_IN_COMMAND_IDS, add commands.has and commands.require (SD-2920)#3095

Open
caio-pizzol wants to merge 2 commits intomainfrom
caio/sd-2920-command-discovery
Open

feat(ui): export BUILT_IN_COMMAND_IDS, add commands.has and commands.require (SD-2920)#3095
caio-pizzol wants to merge 2 commits intomainfrom
caio/sd-2920-command-discovery

Conversation

@caio-pizzol
Copy link
Copy Markdown
Contributor

Config-driven toolbars had no way to validate their command id arrays. `ui.commands.get(id)` returns undefined silently for unknown ids, so typos surfaced as buttons that rendered disabled with no warning at startup, in the type checker, or in the dev console.

This PR exposes three small helpers on the public `superdoc/ui` surface:

  • `BUILT_IN_COMMAND_IDS` — readonly array of canonical built-in command ids. `PublicToolbarItemId` is now derived from this const, so the type and runtime list cannot drift from each other.

  • `ui.commands.has(id)` — boolean check, true for built-ins and currently registered customs.

  • `ui.commands.require(id)` — throwing variant for trusted dispatch sites (keyboard routers, tests, internal pipelines) where an unknown id is a bug.

  • Linear: SD-2920

  • 8 vitest cases pin behavior: BUILT_IN_COMMAND_IDS matches the runtime toolbar registry (drift guard), has() flips correctly across custom register/unregister, require() throws for unknown ids.

  • Consumer-typecheck exercises every helper at value level.

Verified: `pnpm --filter @superdoc/super-editor test src/ui/` passes 181/181; `pnpm --filter superdoc build` clean; consumer-typecheck on the modified file clean.

…require (SD-2920)

Config-driven toolbars had no way to validate their command id arrays.
`ui.commands.get(id)` returns undefined silently for unknown ids, so
typos surfaced as buttons that rendered disabled with no warning at
startup, in the type checker, or in the dev console.

This PR exposes three small helpers on the public superdoc/ui surface:

- BUILT_IN_COMMAND_IDS: readonly array of the canonical built-in
  command ids. PublicToolbarItemId is now derived from this const so
  the type and runtime list cannot drift from each other.
- ui.commands.has(id): boolean check, true for built-ins and currently
  registered customs.
- ui.commands.require(id): throwing variant for trusted dispatch
  sites (keyboard routers, tests, internal pipelines) where an unknown
  id is a bug not a user error.

Re-exported through superdoc/ui (and the public superdoc/ui sub-entry)
so consumers can import directly from the entry point they already use.

Tests pin: BUILT_IN_COMMAND_IDS matches the runtime toolbar registry
(drift guard), has() returns true for every built-in and toggles
correctly across custom register/unregister, require() returns
handles for built-ins and customs and throws for unknown ids.
Consumer-typecheck exercises every helper at value level.
@caio-pizzol caio-pizzol requested a review from a team as a code owner May 2, 2026 15:36
@linear
Copy link
Copy Markdown

linear Bot commented May 2, 2026

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6dd9af03e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/super-editor/src/ui/create-super-doc-ui.ts
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…es (SD-2920)

- ui.commands.register() now refuses ids that match Proxy property
  names (register / get / has / require). Index access on the
  ui.commands Proxy intercepts those names before the registry
  lookup, so a custom command registered under one of them would be
  reachable through get(id) / require(id) but unreachable through
  ui.commands[id]. Refuse + warn instead, matching the build-in
  collision pattern.
- override: true does not bypass: Proxy index access cannot be
  routed around at registration time. Use a namespaced id instead
  (e.g. 'company.has').
- Test pins the contract: every reserved name is refused, has(id)
  returns false for the refused id, index access still returns the
  Proxy helper.
- Replace em-dashes added in the prior commit per project rule.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants