Skip to content

feat: Command palette#1820

Merged
shinokada merged 9 commits intothemesberg:mainfrom
shinokada:command-palette
Nov 4, 2025
Merged

feat: Command palette#1820
shinokada merged 9 commits intothemesberg:mainfrom
shinokada:command-palette

Conversation

@shinokada
Copy link
Copy Markdown
Collaborator

@shinokada shinokada commented Nov 3, 2025

🔗 Related issue (optional)

Closes #


📑 Description


🔍 PR Type

  • Bug fix
  • Feature
  • Documentation
  • Refactor / Code cleanup
  • Build / Tooling
  • Other (please describe)

🚦 PR Status

  • Draft (work in progress, not ready for review)
  • Ready for review ✅

✅ Checklist

  • My code follows the existing code style
  • I have run pnpm check && pnpm test:e2e and all tests pass
  • CoderabbitAI review has been completed and actionable suggestions were reviewed
  • I have updated documentation if my changes require it
  • I have updated the api-check directory if a component API changed
  • My PR is based on the latest main branch (not the published npm version)
  • I have checked accessibility where applicable (ARIA, keyboard nav, etc.)
  • I have reviewed the rendered component in the browser

🧪 Screenshots / Demos (optional)


⚠️ Breaking Changes (optional)


ℹ️ Additional Information

Summary by CodeRabbit

  • New Features

    • Added a Command Palette UI with keyboard-driven search, item filtering, empty-state messaging, optional Vim-style navigation, auto-focus, selection/scrolling behavior, and a global shortcut.
  • Documentation

    • Added docs and a live demo page illustrating usage, command examples, keyboard hints, and integration guidance.
  • Tests

    • Added an end-to-end test covering the Command Palette demo page.

@vercel
Copy link
Copy Markdown

vercel bot commented Nov 3, 2025

@shinokada is attempting to deploy a commit to the Themesberg Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Nov 3, 2025

Walkthrough

Adds a new keyboard-navigable Command Palette component (Svelte) with theme variants and types, exposes it via public exports, includes a docs page and demo, and adds an end-to-end test asserting the demo page heading.

Changes

Cohort / File(s) Summary
Component
src/lib/command-palette/CommandPalette.svelte, src/lib/command-palette/index.ts
New Svelte CommandPalette component with open/close, global Ctrl/Cmd shortcut toggle, search/filter by label/description/keywords, keyboard navigation (Arrow/j/k with optional Vim bindings), selection (click/Enter), focus/scroll handling, backdrop handling, and an index export.
Theme
src/lib/command-palette/theme.ts, src/lib/theme/themes.ts
New Tailwind Variants commandPalette with named slots and selected variant; exported and re-exported from central themes file.
Types
src/lib/types.ts
Adds CommandItem and CommandPaletteProps interfaces and imports CommandPaletteVariants from the new theme file.
Public API barrel
src/lib/index.ts
Adds export * from "./command-palette" and export * from "./tour" to public exports.
Docs & Demo
src/routes/docs/extend/command-palette.md, src/routes/docs-examples/extend/command-palette/+page.svelte
New documentation page and SvelteKit demo page listing commands, binding paletteOpen, and passing items, shortcutKey, and vim to the component.
E2E Test
e2e/extend.spec.ts
New end-to-end test navigating to /docs/extend/command-palette and asserting the H1 equals "Svelte Command Palette".

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DemoPage as Demo Page
    participant CP as CommandPalette
    participant List as ItemList

    User->>DemoPage: press Ctrl/Cmd+K or click toggle
    DemoPage->>CP: set open = true
    activate CP
    CP->>CP: focus input

    User->>CP: type query
    CP->>CP: filter items (label/description/keywords)
    CP->>List: render filtered items

    alt Navigate (Arrow / j / k)
        User->>CP: press navigation key
        CP->>CP: update selectedIndex & scrollIntoView
        CP->>List: highlight selected item
    end

    User->>List: click item or press Enter
    List->>CP: select item
    CP->>CP: call item.onselect()
    CP->>DemoPage: call onclose()
    CP->>CP: set open = false
    deactivate CP
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review global keyboard shortcut registration/cleanup and event scoping in CommandPalette.svelte.
  • Verify selection clamping, scrollIntoView, focus management, and Vim-mode key handling.
  • Confirm CommandItem/CommandPaletteProps typings align with component props and theme variant types.
  • Check theme slot names and the re-export in src/lib/theme/themes.ts.

Possibly related PRs

  • Theme provider #1695 — overlaps on theme exports and related theme/type surface; may interact with the new commandPalette export and variant types.

Poem

🐇 I hop the keys and open light,
I sift through labels, quick and bright.
j and k, I nudge and peek,
Enter finds what I did seek,
A palette sprung — the rabbit's delight.

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The description primarily contains the template structure with the Feature type selected and checklist items marked, but lacks substantive details about the implementation, motivation, or specific changes. Add a brief description of what the Command Palette component does, why it was added, and any key implementation details or dependencies introduced.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title "feat: Command palette" is clear and directly relates to the main changeset, which adds a new Command Palette component with comprehensive implementation.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75e9c45 and 0abdaf9.

📒 Files selected for processing (1)
  • src/lib/types.ts (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/types.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Playwright and Lint

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81362c7 and 77e40ea.

📒 Files selected for processing (8)
  • e2e/extend.spec.ts (1 hunks)
  • src/lib/command-palette/CommandPalette.svelte (1 hunks)
  • src/lib/command-palette/index.ts (1 hunks)
  • src/lib/command-palette/theme.ts (1 hunks)
  • src/lib/index.ts (1 hunks)
  • src/lib/types.ts (1 hunks)
  • src/routes/docs-examples/extend/command-palette/+page.svelte (1 hunks)
  • src/routes/docs/extend/command-palette.md (1 hunks)
🧰 Additional context used
🪛 ESLint
src/routes/docs-examples/extend/command-palette/+page.svelte

[error] 38-38: Unexpected goto() call without resolve().

(svelte/no-navigation-without-resolve)


[error] 70-70: Unexpected goto() call without resolve().

(svelte/no-navigation-without-resolve)


[error] 156-179: Each block should have a key

(svelte/require-each-key)


[error] 169-173: Each block should have a key

(svelte/require-each-key)

src/lib/command-palette/CommandPalette.svelte

[error] 139-139: Unexpected any. Specify a different type.

(@typescript-eslint/no-explicit-any)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Playwright and Lint

@jjagielka
Copy link
Copy Markdown
Contributor

Why don't you use Dialog as a base for that component?

…e to respect the app base, Add keys to each blocks
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/routes/docs-examples/extend/command-palette/+page.svelte (1)

160-183: LGTM! Previous lint issues resolved.

The #each blocks now include proper keys (command.id and keyword), resolving the previous svelte/require-each-key lint errors. The DOM reconciliation will now be predictable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77e40ea and 781865f.

📒 Files selected for processing (2)
  • src/lib/command-palette/CommandPalette.svelte (1 hunks)
  • src/routes/docs-examples/extend/command-palette/+page.svelte (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/command-palette/CommandPalette.svelte
🧰 Additional context used
🪛 ESLint
src/routes/docs-examples/extend/command-palette/+page.svelte

[error] 39-39: Unexpected goto() call without resolve().

(svelte/no-navigation-without-resolve)


[error] 73-73: Unexpected goto() call without resolve().

(svelte/no-navigation-without-resolve)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Playwright and Lint
🔇 Additional comments (2)
src/routes/docs-examples/extend/command-palette/+page.svelte (2)

1-6: LGTM! Clean imports and state setup.

The imports are appropriate and the use of Svelte 5's $state rune for reactivity is correct.


189-189: LGTM! CommandPalette properly configured.

The component binding and props are correctly set up with bind:open for reactivity, a custom shortcut key, and vim mode enabled.

@shinokada
Copy link
Copy Markdown
Collaborator Author

I tried it before but it didn't work. For now, I use div. We can change it later.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/lib/command-palette/theme.ts (1)

35-42: Consider removing the unused selected variant.

The selected variant (lines 36-39) appears unused since the item slot already handles selection via aria-selected: pseudo-classes (line 22). Having both could be confusing.

If the variant is not needed, apply this diff:

-  variants: {
-    selected: {
-      true: "bg-primary-600 text-white",
-      false: "text-gray-900 dark:text-gray-100"
-    }
-  },
-
-  defaultVariants: {}
+  variants: {},
+  defaultVariants: {}

Alternatively, if you plan to use the variant for programmatic styling, keep it but add a comment explaining its purpose.

src/lib/types.ts (1)

2178-2185: Consider clarifying the icon field type.

The icon?: string field could benefit from more specific typing or documentation to clarify whether it represents an icon name, URL, SVG string, or component identifier.

If it's meant to be an icon identifier from a specific icon library, consider:

export interface CommandItem {
  id: string;
  label: string;
  description?: string;
  icon?: string; // e.g., heroicon name or custom icon identifier
  keywords?: string[];
  onselect: () => void;
}

Alternatively, if you want more flexibility:

export interface CommandItem {
  id: string;
  label: string;
  description?: string;
  icon?: string | Component; // Allow icon name or Svelte component
  keywords?: string[];
  onselect: () => void;
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98bcb61 and a1617fc.

📒 Files selected for processing (4)
  • src/lib/command-palette/CommandPalette.svelte (1 hunks)
  • src/lib/command-palette/theme.ts (1 hunks)
  • src/lib/theme/themes.ts (1 hunks)
  • src/lib/types.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/command-palette/CommandPalette.svelte
🧰 Additional context used
🧬 Code graph analysis (2)
src/lib/command-palette/theme.ts (1)
src/lib/theme/themeUtils.ts (1)
  • Classes (12-14)
src/lib/types.ts (1)
src/lib/command-palette/theme.ts (1)
  • CommandPaletteVariants (4-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Playwright and Lint
🔇 Additional comments (5)
src/lib/theme/themes.ts (1)

81-81: LGTM!

The export follows the established pattern and is correctly placed in the extend section.

src/lib/command-palette/theme.ts (2)

1-4: LGTM!

The imports and type definition follow the established pattern used throughout the codebase.


6-43: Verify modal accessibility implementation.

Based on the PR comments, this component uses a div with backdrop instead of the Dialog component. Ensure the implementation includes essential modal patterns: focus trap, Escape key handling, and proper ARIA attributes (role="dialog", aria-modal="true", aria-labelledby, etc.).

You can verify accessibility by checking if the CommandPalette component implementation includes:

  1. Focus trap that keeps keyboard navigation within the modal
  2. Escape key to close
  3. Proper ARIA roles and attributes
  4. Return focus to trigger element on close

If these are missing, consider revisiting the Dialog component approach or implementing these patterns manually.

src/lib/types.ts (2)

120-120: LGTM!

The import follows the established pattern and is correctly placed with other extend component imports.


2187-2195: LGTM!

The interface is well-typed with appropriate optional fields and callbacks. It extends the correct base types and provides good flexibility for component usage.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/lib/types.ts (1)

2178-2185: Consider clarifying the icon type.

The icon?: string | Component type is flexible but potentially ambiguous. If the string variant represents an icon name, CSS class, or path, consider using a more specific type or adding a JSDoc comment to clarify the expected format.

Example:

/**
 * Icon can be a Svelte Component or a string representing an icon class/name
 */
icon?: string | Component;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1617fc and fe059c3.

📒 Files selected for processing (2)
  • src/lib/command-palette/theme.ts (1 hunks)
  • src/lib/types.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/command-palette/theme.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/lib/types.ts (1)
src/lib/command-palette/theme.ts (1)
  • CommandPaletteVariants (4-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Playwright and Lint
🔇 Additional comments (2)
src/lib/types.ts (2)

120-120: LGTM!

The import follows the established pattern for variant types in this file.


2187-2195: LGTM!

The interface follows established patterns in the codebase. The extension of HTMLAttributes<HTMLDivElement> is appropriate given the implementation approach mentioned in the PR discussion.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/types.ts (1)

2177-2188: Consider simplifying the JSDoc comment formatting.

The interface structure looks solid with appropriate required and optional fields. The flexibility of icon being either a string or Component is good for different use cases.

For consistency with other single-line comments in this file, consider simplifying the JSDoc:

   description?: string;
-  /**
- * Icon can be a Svelte Component or a string representing an icon class/name
- */
+  /** Icon can be a Svelte Component or a string representing an icon class/name */
   icon?: string | Component;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12e8e6a and 75e9c45.

📒 Files selected for processing (2)
  • src/lib/types.ts (9 hunks)
  • src/routes/docs-examples/extend/command-palette/+page.svelte (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-11-22T08:37:27.996Z
Learnt from: mrh1997
Repo: themesberg/flowbite-svelte PR: 1442
File: src/lib/utils/Popper.svelte:144-145
Timestamp: 2024-11-22T08:37:27.996Z
Learning: In `src/lib/utils/Popper.svelte`, inconsistencies in event handling (e.g., using both `mouseenter`/`mouseleave` and `pointerenter`/`pointerleave`) may be intentional to minimize modifications and reduce the risk of new problems.

Applied to files:

  • src/routes/docs-examples/extend/command-palette/+page.svelte
🧬 Code graph analysis (1)
src/lib/types.ts (6)
src/lib/drawer/theme.ts (1)
  • DrawerHandleVariants (78-78)
src/lib/forms/helper/theme.ts (1)
  • HelperVariants (4-4)
src/lib/toolbar/theme.ts (1)
  • ToolbarGroupVariants (6-6)
src/lib/skeleton/theme.ts (2)
  • TestimonialPlaceholderVariants (9-9)
  • WidgetPlaceholderVariants (12-12)
src/lib/tooltip/theme.ts (1)
  • TooltipVariants (4-4)
src/lib/command-palette/theme.ts (1)
  • CommandPaletteVariants (4-4)
🪛 ESLint
src/routes/docs-examples/extend/command-palette/+page.svelte

[error] 43-43: Unexpected goto() call without resolve().

(svelte/no-navigation-without-resolve)


[error] 75-75: Unexpected goto() call without resolve().

(svelte/no-navigation-without-resolve)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Playwright and Lint
🔇 Additional comments (3)
src/routes/docs-examples/extend/command-palette/+page.svelte (1)

136-189: LGTM!

The template markup is well-structured with proper responsive layout, keyboard shortcut instructions, and a demo grid showing all available commands. The {#each} blocks correctly use keys (command.id and keyword), and the CommandPalette component binding and props look appropriate.

src/lib/types.ts (2)

120-120: LGTM!

The import of CommandPaletteVariants follows the established pattern for importing component variants in this file.


2190-2198: LGTM!

The CommandPaletteProps interface follows the established pattern in this file by extending both component variants and HTML attributes. All props are appropriately typed and optional, providing good flexibility for component usage.

@@ -0,0 +1,189 @@
<script lang="ts">
import { CommandPalette, Toggle } from '$lib';
import { goto } from '$app/navigation';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix goto() calls to respect the app base.

The goto() calls on lines 43 and 75 are missing resolve(), which will cause navigation to fail when the app is deployed with a non-root config.kit.paths.base. Import resolve from $app/navigation and wrap the paths before calling goto().

Apply this diff:

-  import { goto } from '$app/navigation';
+  import { goto, resolve } from '$app/navigation';

And update the navigation calls:

-      onselect: () => goto('/')
+      onselect: () => {
+        void goto(resolve('/'));
+      }
-      onselect: () => goto('/docs/pages/introduction')
+      onselect: () => {
+        void goto(resolve('/docs/pages/introduction'));
+      }

Also applies to: 43-43, 75-75

🤖 Prompt for AI Agents
In src/routes/docs-examples/extend/command-palette/+page.svelte around line 3
and navigation calls at lines 43 and 75, the goto() calls do not use resolve()
so navigation will break when the app is deployed with a non-root base path;
update the import to: import { goto, resolve } from '$app/navigation' and change
the two goto('/some/path') calls to use goto(resolve('/some/path')) (wrap the
existing path arguments with resolve) so the app base is respected.

@shinokada shinokada merged commit 659b70f into themesberg:main Nov 4, 2025
1 of 2 checks passed
@shinokada shinokada deleted the command-palette branch November 7, 2025 08:23
@coderabbitai coderabbitai bot mentioned this pull request Dec 29, 2025
84 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants