Skip to content

feat(desktop): add global command palette#4488

Merged
saddlepaddle merged 3 commits into
mainfrom
command-palette-design
May 13, 2026
Merged

feat(desktop): add global command palette#4488
saddlepaddle merged 3 commits into
mainfrom
command-palette-design

Conversation

@saddlepaddle

@saddlepaddle saddlepaddle commented May 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a ⌘⇧K global command palette, mounted in the dashboard layout
  • Module-based architecture: feature areas contribute commands through a CommandProvider interface, no central switch
  • v2 workspace coverage: New, Search files, Link task, Remove from sidebar, Delete, Open in default editor + sub-palette, Toggle theme (with full theme picker), Toggle left/right sidebars, Mute notifications, Show keyboard shortcuts, Check for updates (wired to the same auto-update flow as the menu), Settings (sub-palette of all tabs), Recently Viewed (matches HistoryDropdown rendering), Open documentation

Architecture

apps/desktop/src/renderer/commandPalette/

  • core/types, registry, ContextProvider, useActiveCommands, sections, execute, frames. Section order is resolved per route; commands carry an optional when predicate and either run, children, or renderFrame for sub-palettes
  • modules/workspace, actions, openIn, navigation, settings. Each exports a CommandProvider
  • ui/ — Dialog, frame stack with back-arrow + breadcrumb, custom sub-frames for Theme picker / Link task / Recently Viewed
  • Workspace-local mutations (delete, remove-from-sidebar, right-sidebar toggle, set-preferred-open-in-app) flow through small zustand intent stores. Mount components inside CommandPaletteHost subscribe to those intents and apply the mutation using the relevant hook — same pattern as useQuickOpenStore
  • New autoUpdate.checkInteractive trpc proc so the palette + Help menu share one entry point

Test plan

  • Press ⌘⇧K on dashboard, in a v2 workspace, and in settings — only renders inside _dashboard/*
  • In a v2 workspace: Workspace section shows New / Search files / Link task / Remove from sidebar / Delete (hidden on main workspaces) / Open in / Open in…
  • Outside a v2 workspace: Workspace section is gone, Actions + Navigation still show
  • Open in… sub-palette: real app icons render, selecting an app updates v2SidebarProjects.defaultOpenInApp so the top-level "Open in X" reflects the choice next time
  • Toggle theme: System / Light / Dark / Custom groups with the same ThemeSwatch as Settings → Appearance
  • Mute notifications: toggles settings.setNotificationSoundsMuted (same setting as Ringtones page); title flips between Mute/Unmute
  • Toggle right sidebar: v2 only, drives setRightSidebarOpen via the right-sidebar intent store
  • Recently Viewed: same data and layout as HistoryDropdown
  • Check for updates: native dialog in dev / on Windows; in production it kicks off the normal auto-update flow
  • Resize the window: dialog stays visually centered when content fits, anchors near top + shrinks list cap on short windows

Summary by cubic

Adds a global command palette (⌘⇧K / Ctrl+Shift+K) in the dashboard to speed up navigation and common actions using a module-based provider system with sub-palettes and contextual commands.

  • New Features

    • Global trigger via OPEN_COMMAND_PALETTE; mounted in the dashboard only.
    • Workspace (v2): New workspace, Search files, Link task, Remove from sidebar, Delete, Open in preferred app + “Open in…” sub-palette with real app icons; preferred app saved per project.
    • Actions: Toggle theme (full picker with ThemeSwatch), Toggle left/right sidebars, Mute/Unmute notifications, Show keyboard shortcuts, Check for updates via autoUpdate.checkInteractive.
    • Navigation: Settings (tab sub-palette), Recently Viewed (parity with HistoryDropdown), Open documentation.
  • Refactors

    • Navigation calls CommandContext.navigate (from useNavigate) instead of a router singleton.
    • Feature areas register via CommandProvider; small intent stores drive workspace-local mutations (delete, remove from sidebar, right sidebar toggle, set preferred “open in” app).

Written for commit 41aca77. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Added a global command palette accessible via hotkey for quick navigation and actions across the app
    • Added commands to toggle themes, manage sidebars, and control notification sounds
    • Added quick access to settings, recently viewed items, and workspace operations
    • Added "Open in" functionality to open workspaces in external applications

Review Change Stack

Adds ⌘⇧K command palette mounted in the dashboard layout, with a
module-based architecture so feature areas contribute commands without
touching a central switch. Modules ship: workspace (New/Search files/
Link task/Remove from sidebar/Delete/Open in), actions (Toggle theme/
sidebars, Mute notifications, keyboard shortcuts, Check for updates),
navigation (Settings sub-palette, Recently Viewed, Docs).

Workspace-local mutations route through small intent stores (delete,
remove-from-sidebar, right-sidebar toggle, set-preferred-open-in-app)
so the palette can drive state owned by per-workspace hooks.
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR adds a comprehensive global command palette to the desktop renderer, exposing commands for theme switching, sidebar toggling, notifications, settings navigation, workspace operations, and file opening with external apps. The system includes modular command providers, a hierarchical frame stack for submenus, context-driven filtering, and intent-based state management for sidebar and dialog operations.

Changes

Desktop Command Palette Feature

Layer / File(s) Summary
Type contracts and shared definitions
apps/desktop/src/renderer/commandPalette/core/types.ts, apps/desktop/src/renderer/commandPalette/core/sections.ts
SectionId, CommandContext, Command, CommandProvider, and CommandSection types establish contracts for the command system; SECTION_LABELS and resolveSectionOrder organize sections contextually.
Context provider and core infrastructure
apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx, apps/desktop/src/renderer/commandPalette/core/frames.ts, apps/desktop/src/renderer/commandPalette/core/registry.ts, apps/desktop/src/renderer/commandPalette/core/execute.ts
CommandContextProvider derives workspace/host/notification state via router and live queries; frame stack stores for navigation history; provider registry with subscriber notifications; executeCommand records analytics and surfaces errors via toast.
Command filtering and organization
apps/desktop/src/renderer/commandPalette/core/useActiveCommands.ts
useActiveCommands hook subscribes to provider registry, filters by when predicates, deduplicates, groups by section, and orders sections based on context.
Command provider modules
apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx, apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx, apps/desktop/src/renderer/commandPalette/modules/settings/commands.ts, apps/desktop/src/renderer/commandPalette/modules/workspace/commands.tsx, apps/desktop/src/renderer/commandPalette/modules/openIn/commands.ts, apps/desktop/src/renderer/commandPalette/modules/index.ts
actionsProvider toggles theme/sidebar/mute and checks updates; navigationProvider offers settings tabs and recently viewed; workspaceProvider handles new/quick-open/link-task/remove/delete; openInProvider opens Finder or external apps with preference tracking; registerAllModules wires all providers on mount.
Core command list and palette UI
apps/desktop/src/renderer/commandPalette/ui/CommandItemRow/CommandItemRow.tsx, apps/desktop/src/renderer/commandPalette/ui/CommandListView/CommandListView.tsx, apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx, apps/desktop/src/renderer/commandPalette/ui/SubPaletteView/SubPaletteView.tsx
CommandItemRow renders rows with icons/hotkeys; CommandListView groups sections; CommandPalette manages frame stack and query with dialog; SubPaletteView renders command children with optional custom frame rendering.
Specialized frame UI renderers
apps/desktop/src/renderer/commandPalette/ui/ThemeFrame/ThemeFrame.tsx, apps/desktop/src/renderer/commandPalette/ui/RecentlyViewed/RecentlyViewedFrame.tsx, apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx, apps/desktop/src/renderer/commandPalette/ui/QuickOpen/quickOpenStore.ts
ThemeFrame lists selectable themes with swatches; RecentlyViewedFrame displays workspace/automation/task entries with typed row components and icons; LinkTaskFrame searches tasks by slug/title via Fuse index; quickOpenStore manages workspace-scoped quick-open state.
Intent stores for async operations
apps/desktop/src/renderer/stores/delete-workspace-intent.ts, apps/desktop/src/renderer/stores/remove-workspace-from-sidebar-intent.ts, apps/desktop/src/renderer/stores/right-sidebar-toggle-intent.ts, apps/desktop/src/renderer/stores/set-preferred-open-in-app-intent.ts
Four Zustand stores manage async operations with tick guards: delete workspace, remove from sidebar, toggle right sidebar, and set preferred open-in-app with projectId tracking.
Intent-driven mount components
apps/desktop/src/renderer/commandPalette/ui/DeleteWorkspaceMount/DeleteWorkspaceMount.tsx, apps/desktop/src/renderer/commandPalette/ui/RemoveFromSidebarMount/RemoveFromSidebarMount.tsx, apps/desktop/src/renderer/commandPalette/ui/SetPreferredOpenInAppMount/SetPreferredOpenInAppMount.tsx
Mount components bridge intents to dialogs/state: DeleteWorkspaceMount renders delete dialog, RemoveFromSidebarMount navigates and updates sidebar, SetPreferredOpenInAppMount syncs app preference to projects.
CommandPaletteHost and module registration
apps/desktop/src/renderer/commandPalette/CommandPaletteHost.tsx, apps/desktop/src/renderer/commandPalette/index.ts, apps/desktop/src/renderer/hotkeys/registry.ts
CommandPaletteHost wraps app in context, registers modules, renders palette and mounts; hotkey listener opens palette; barrel exports all public APIs and types; OPEN_COMMAND_PALETTE hotkey added to registry.
Dashboard layout and page wiring
apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useWorkspaceHotkeys/useWorkspaceHotkeys.ts, apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx, apps/desktop/src/lib/trpc/routers/auto-update/index.ts
Dashboard layout embeds CommandPaletteHost; workspace hotkeys subscribe to right-sidebar toggle intent; v2-workspace page uses shared quickOpenStore; auto-update router exposes checkInteractive mutation for app updates.
Supporting changes and component extraction
apps/desktop/src/renderer/components/ThemeSwatch/ThemeSwatch.tsx, apps/desktop/src/renderer/components/ThemeSwatch/index.ts, apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/components/ThemeSection/ThemeSection.tsx, packages/ui/src/components/ui/command.tsx
ThemeSwatch extracted as reusable component for theme visualization; CommandInput enhanced with optional leading prop to support back button rendering.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A palette unfolds with a tap of the keys,
Commands dance by, a symphony please!
Themes swirl and switch, workspaces gleam,
Sidebars toggle—a developer's dream!
Frame stacks and intents keep intentions so clean,
The finest command system you've ever seen! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main feature being added: a global command palette for the desktop application.
Description check ✅ Passed The pull request description comprehensively covers the feature, architecture, built-in commands, and test plan as required by the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch command-palette-design

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.

@capy-ai

capy-ai Bot commented May 13, 2026

Copy link
Copy Markdown

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@greptile-apps

greptile-apps Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a global command palette (⌘⇧K) to the desktop app using a module-based architecture where feature areas contribute commands through a CommandProvider interface, with sub-palette support via a frame stack. The implementation is well-structured and covers a broad set of workspace actions, navigation, theme selection, and settings.

  • Core architecture: useSyncExternalStore-backed provider registry, memoized CommandContext, intent stores for cross-component mutations — all follow consistent patterns.
  • linkTaskToWorkspace in LinkTaskFrame.tsx is a stub: the function body is void taskId; void workspaceId;, so the "Link task" command fires a success toast but persists nothing.
  • Dead run callbacks: actions.toggleTheme and nav.settings both define run alongside renderFrame/children; handleSelect always short-circuits to pushFrame, so those run bodies are never reachable.

Confidence Score: 3/5

The palette infrastructure is solid, but the Link task command silently does nothing despite reporting success — that feature should not ship in its current state.

The Link task sub-palette fires a success toast and closes the palette without actually linking anything to the workspace — linkTaskToWorkspace is an empty placeholder. Two run callbacks on actions.toggleTheme and nav.settings are permanently unreachable due to handleSelect precedence rules, and the router singleton typed as AnyRouter means path typos in 20+ settings tab commands won't be caught at compile time.

LinkTaskFrame.tsx needs the actual implementation before the command palette ships. actions/commands.tsx and navigation/commands.tsx have dead run callbacks to clean up. router-instance.ts would benefit from a concrete router type to restore route-path type checking.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx Renders a searchable task list but linkTaskToWorkspace is an unimplemented stub — success toast fires yet nothing is persisted.
apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx Main dialog; correctly manages frame stack and query state, but children/renderFrame precedence means any co-defined run on framed commands is permanently dead code.
apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx Provides actions commands; run on actions.toggleTheme is unreachable dead code because renderFrame causes handleSelect to push a frame instead.
apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx Navigation provider; run on nav.settings is unreachable because children causes the palette to push a sub-frame instead of executing run.
apps/desktop/src/renderer/lib/router-instance.ts Module-level router singleton; typed as AnyRouter, so all callers lose TanStack Router's compile-time route-path safety.
apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx Aggregates workspace, sidebar-project, and notification-mute state into a memoized CommandContext; correctly guards against null workspace IDs in live queries.
apps/desktop/src/renderer/commandPalette/modules/openIn/commands.ts Open-in provider; correctly gates on workspace presence and host identity; updates preferred app via intent store before opening.
apps/desktop/src/renderer/commandPalette/ui/RecentlyViewed/RecentlyViewedFrame.tsx Mirrors HistoryDropdown rendering; filtering logic correctly gates entry types on isV2CloudEnabled; no issues found.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    HK["⌘⇧K hotkey"] --> FSS["useFrameStackStore setOpen(true)"]
    FSS --> CP["CommandPalette Dialog"]
    CP --> CLV["CommandListView root level"]
    CP --> SPV["SubPaletteView when frame pushed"]
    CLV --> UAC["useActiveCommands useSyncExternalStore"]
    UAC --> REG["registry Map of providers"]
    REG --> WP["workspaceProvider"]
    REG --> AP["actionsProvider"]
    REG --> OIP["openInProvider"]
    REG --> NP["navigationProvider"]
    CP -- "children/renderFrame?" --> PUSH["pushFrame SubPaletteView"]
    CP -- "leaf command" --> EXEC["executeCommand track + run"]
    EXEC --> CLOSE["handleOpenChange false"]
    PUSH --> TF["ThemeFrame"]
    PUSH --> LTF["LinkTaskFrame stub"]
    PUSH --> RVF["RecentlyViewedFrame"]
    PUSH --> STAB["Settings tabs list"]
    EXEC --> IS["intent stores"]
    IS --> MOUNTS["Mount components"]
Loading
Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx:88-94
**`linkTaskToWorkspace` is a no-op stub**

The function discards both arguments and returns immediately — only `void taskId; void workspaceId;`. When a user selects a task, the toast `"Linked {slug} to workspace"` fires, but nothing is persisted. The link operation is completely missing, making the entire "Link task" command non-functional despite reporting success.

### Issue 2 of 4
apps/desktop/src/renderer/lib/router-instance.ts:1-14
**`AnyRouter` loses all route-type safety at call sites**

`getRouterInstance()` is typed as `AnyRouter`, so every `navigate({ to: tab.path })` call (including the 20+ settings tabs in `settings/commands.ts`) accepts an arbitrary string without compile-time validation. Typos in path strings will silently navigate to a 404 rather than producing a type error. Typing the ref as the concrete router type (returned by `createRouter`) and exporting that type would restore the full route-type checking that TanStack Router provides.

### Issue 3 of 4
apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx:46-51
**`run` on `actions.toggleTheme` is unreachable dead code**

`handleSelect` in `CommandPalette` checks `command.children || command.renderFrame` first and returns early via `pushFrame`, so any command that defines `renderFrame` will never have its `run` executed. The `cycleTheme()` call here is permanently skipped. If cycling the theme without opening the picker is still desired, it needs a separate command; otherwise `run` should be removed.

### Issue 4 of 4
apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx:13-22
**`run` on `nav.settings` is unreachable dead code**

`CommandPalette.handleSelect` short-circuits to `pushFrame` whenever `command.children` is truthy — the `run` callback that navigates to `/settings/account` is never invoked. If the intent is to navigate directly when the user selects the top-level "Settings" item, `children` would need to be absent. Otherwise `run` can be dropped.

Reviews (1): Last reviewed commit: "feat(desktop): add global command palett..." | Re-trigger Greptile

Comment on lines +88 to +94
}

async function linkTaskToWorkspace(
taskId: string,
workspaceId: string,
): Promise<void> {
void taskId;

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.

P1 linkTaskToWorkspace is a no-op stub

The function discards both arguments and returns immediately — only void taskId; void workspaceId;. When a user selects a task, the toast "Linked {slug} to workspace" fires, but nothing is persisted. The link operation is completely missing, making the entire "Link task" command non-functional despite reporting success.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx
Line: 88-94

Comment:
**`linkTaskToWorkspace` is a no-op stub**

The function discards both arguments and returns immediately — only `void taskId; void workspaceId;`. When a user selects a task, the toast `"Linked {slug} to workspace"` fires, but nothing is persisted. The link operation is completely missing, making the entire "Link task" command non-functional despite reporting success.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +1 to +14
import type { AnyRouter } from "@tanstack/react-router";

let routerRef: AnyRouter | null = null;

export function setRouterInstance(router: AnyRouter): void {
routerRef = router;
}

export function getRouterInstance(): AnyRouter {
if (!routerRef) {
throw new Error("Router instance not set yet");
}
return routerRef;
}

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.

P2 AnyRouter loses all route-type safety at call sites

getRouterInstance() is typed as AnyRouter, so every navigate({ to: tab.path }) call (including the 20+ settings tabs in settings/commands.ts) accepts an arbitrary string without compile-time validation. Typos in path strings will silently navigate to a 404 rather than producing a type error. Typing the ref as the concrete router type (returned by createRouter) and exporting that type would restore the full route-type checking that TanStack Router provides.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/lib/router-instance.ts
Line: 1-14

Comment:
**`AnyRouter` loses all route-type safety at call sites**

`getRouterInstance()` is typed as `AnyRouter`, so every `navigate({ to: tab.path })` call (including the 20+ settings tabs in `settings/commands.ts`) accepts an arbitrary string without compile-time validation. Typos in path strings will silently navigate to a 404 rather than producing a type error. Typing the ref as the concrete router type (returned by `createRouter`) and exporting that type would restore the full route-type checking that TanStack Router provides.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +46 to +51
{
id: "actions.toggleTheme",
title: "Toggle theme",
section: "actions",
icon: PaletteIcon,
keywords: ["dark", "light", "appearance", "color"],

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.

P2 run on actions.toggleTheme is unreachable dead code

handleSelect in CommandPalette checks command.children || command.renderFrame first and returns early via pushFrame, so any command that defines renderFrame will never have its run executed. The cycleTheme() call here is permanently skipped. If cycling the theme without opening the picker is still desired, it needs a separate command; otherwise run should be removed.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx
Line: 46-51

Comment:
**`run` on `actions.toggleTheme` is unreachable dead code**

`handleSelect` in `CommandPalette` checks `command.children || command.renderFrame` first and returns early via `pushFrame`, so any command that defines `renderFrame` will never have its `run` executed. The `cycleTheme()` call here is permanently skipped. If cycling the theme without opening the picker is still desired, it needs a separate command; otherwise `run` should be removed.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +13 to +22
title: "Settings",
section: "navigation",
icon: SettingsIcon,
hotkeyId: "OPEN_SETTINGS",
children: settingsTabCommands,
run: () => {
void getRouterInstance().navigate({ to: "/settings/account" });
},
},
{

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.

P2 run on nav.settings is unreachable dead code

CommandPalette.handleSelect short-circuits to pushFrame whenever command.children is truthy — the run callback that navigates to /settings/account is never invoked. If the intent is to navigate directly when the user selects the top-level "Settings" item, children would need to be absent. Otherwise run can be dropped.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx
Line: 13-22

Comment:
**`run` on `nav.settings` is unreachable dead code**

`CommandPalette.handleSelect` short-circuits to `pushFrame` whenever `command.children` is truthy — the `run` callback that navigates to `/settings/account` is never invoked. If the intent is to navigate directly when the user selects the top-level "Settings" item, `children` would need to be absent. Otherwise `run` can be dropped.

How can I resolve this? If you propose a fix, please make it concise.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 7

🧹 Nitpick comments (12)
apps/desktop/src/renderer/commandPalette/CommandPaletteHost.tsx (1)

29-33: ⚡ Quick win

Consider inlining CommandPaletteTrigger into the host component.

The CommandPaletteTrigger component is a small internal helper that could be inlined into CommandPaletteHost to follow the single-component-per-file convention and improve code locality.

♻️ Proposed inline refactor
 export function CommandPaletteHost({ children }: { children?: ReactNode }) {
+	const setOpen = useFrameStackStore((s) => s.setOpen);
+
 	useEffect(() => {
 		const unregister = registerAllModules();
 		return unregister;
 	}, []);
+
+	useHotkey("OPEN_COMMAND_PALETTE", () => setOpen(true));

 	return (
 		<CommandContextProvider>
-			<CommandPaletteTrigger />
 			<CommandPalette />
 			<DeleteWorkspaceMount />
 			<RemoveFromSidebarMount />
 			<SetPreferredOpenInAppMount />
 			{children}
 		</CommandContextProvider>
 	);
 }
-
-function CommandPaletteTrigger() {
-	const setOpen = useFrameStackStore((s) => s.setOpen);
-	useHotkey("OPEN_COMMAND_PALETTE", () => setOpen(true));
-	return null;
-}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/CommandPaletteHost.tsx` around lines
29 - 33, The small helper component CommandPaletteTrigger should be inlined into
CommandPaletteHost: remove the separate CommandPaletteTrigger function and move
its logic into CommandPaletteHost by calling useFrameStackStore((s) =>
s.setOpen) and useHotkey("OPEN_COMMAND_PALETTE", () => setOpen(true)) directly
inside CommandPaletteHost (ensuring hooks remain at the top level of the
component), then delete the unused CommandPaletteTrigger export/definition to
keep a single-component-per-file and improve locality.
apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx (2)

104-104: 💤 Low value

Remove redundant responsive breakpoint.

The sm:!max-w-[720px] class duplicates the base !max-w-[720px] with the same value, making the breakpoint override unnecessary.

♻️ Simplify by removing redundant class
-				className="!max-w-[720px] sm:!max-w-[720px] translate-y-0 max-h-[80vh] overflow-hidden p-0"
+				className="!max-w-[720px] translate-y-0 max-h-[80vh] overflow-hidden p-0"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx`
at line 104, The className string on the CommandPalette component contains a
redundant responsive override; remove the duplicated "sm:!max-w-[720px]" from
the className (leave "!max-w-[720px]" intact) so the base max-width value isn't
needlessly overridden; update the className in CommandPalette.tsx where the
className is defined to drop the "sm:!max-w-[720px]" token.

105-105: 💤 Low value

Consider documenting the magic number.

The hardcoded 278px in the vertical positioning calculation appears to be approximately half the expected dialog height for centering. A brief comment would help future maintainers understand this offset.

📝 Add clarifying comment
+				{/* Position dialog near top-center: 278px ≈ half the typical dialog height */}
 				style={{ top: "max(16px, calc(50% - 278px))" }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx`
at line 105, The inline style on the CommandPalette component uses a magic
number "278px" in style={{ top: "max(16px, calc(50% - 278px))" }}; add a short
clarifying comment next to this expression (or extract to a named constant like
DIALOG_HALF_HEIGHT) explaining that 278px represents half the dialog's intended
height used to vertically center the palette and why the lower bound 16px is
required, so future maintainers understand the offset and can update it if the
dialog size changes.
apps/desktop/src/renderer/commandPalette/ui/SubPaletteView/SubPaletteView.tsx (1)

25-25: 💤 Low value

Consider memoizing the filtered children.

The visible array is recomputed on every render. Since the filtering depends on children (memoized) and context (from provider), wrapping this in a separate useMemo would avoid redundant filter operations.

♻️ Memoize the filtering step
-	const visible = children.filter((c) => (c.when ? c.when(context) : true));
+	const visible = useMemo(
+		() => children.filter((c) => (c.when ? c.when(context) : true)),
+		[children, context]
+	);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/commandPalette/ui/SubPaletteView/SubPaletteView.tsx`
at line 25, The visible computation is re-run on every render; wrap the filter
in a useMemo to memoize it: replace the direct assignment to visible with a
useMemo that returns children.filter((c) => (c.when ? c.when(context) : true))
and set the dependency array to [children, context] (ensure useMemo is
imported), referencing the visible variable and the children and context used in
SubPaletteView to avoid unnecessary recalculations.
apps/desktop/src/renderer/commandPalette/ui/QuickOpen/quickOpenStore.ts (1)

18-18: ⚡ Quick win

Inconsistent state cleanup in close action.

The close action sets open: false but does not reset target to null. This differs from the pattern used in other intent stores in this PR:

  • delete-workspace-intent.ts line 18: close: () => set({ target: null })
  • remove-workspace-from-sidebar-intent.ts: clear() sets target: null
  • set-preferred-open-in-app-intent.ts: clear() sets target: null

Leaving a stale target after close may cause issues if consumers check target without also checking open.

🔄 Proposed fix to align with other intent stores
-	close: () => set({ open: false }),
+	close: () => set({ open: false, target: null }),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/ui/QuickOpen/quickOpenStore.ts` at
line 18, The close action currently only sets open: false leaving a stale
target; update the close handler in quickOpenStore (the close: () => set({...})
action) to also reset target to null (e.g., set({ open: false, target: null }))
so its cleanup matches other intent stores (delete-workspace-intent,
remove-workspace-from-sidebar-intent, set-preferred-open-in-app-intent) and
prevents consumers from relying on a stale target after close.
apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx (1)

90-96: 🏗️ Heavy lift

Incomplete implementation: linkTaskToWorkspace is a no-op stub.

The function currently discards both parameters without performing any linking operation. However, line 64 shows a success toast to the user ("Linked ${slug} to workspace"), creating misleading feedback when no actual linking occurs.

Do you want me to help implement the actual linking logic, or is this intentionally stubbed for future work? If it's future work, consider either:

  • Removing the success toast until the implementation is complete
  • Adding a TODO comment and tracking issue reference
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx`
around lines 90 - 96, The function linkTaskToWorkspace is currently a no-op stub
that discards taskId and workspaceId while a success toast ("Linked ${slug} to
workspace") is shown elsewhere; implement real linking by replacing the void
stubs in linkTaskToWorkspace with an async call to the appropriate
service/API/store (e.g., call your backend endpoint or task-store method) to
associate the taskId with workspaceId, await the response, and throw or return
errors on failure; ensure the success toast is only shown after a successful
response and that errors are caught and surfaced (e.g., show an error toast or
rethrow), or if you intend to leave it unimplemented, remove the premature
success toast and add a TODO with an issue reference in linkTaskToWorkspace
instead.
apps/desktop/src/renderer/stores/set-preferred-open-in-app-intent.ts (1)

19-22: ⚡ Quick win

Prefer updater form of set for consistency and idiomatic Zustand.

The current implementation uses get() followed by set(). Using the updater form of set is more idiomatic in Zustand and matches the pattern used in right-sidebar-toggle-intent.ts.

♻️ Refactor to use updater form
 request: (target) => {
-  const prevTick = get().target?.tick ?? 0;
-  set({ target: { ...target, tick: prevTick + 1 } });
+  set((state) => ({
+    target: { ...target, tick: (state.target?.tick ?? 0) + 1 }
+  }));
 },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/stores/set-preferred-open-in-app-intent.ts` around
lines 19 - 22, The request handler currently uses get() then set(), which is
non-idiomatic for Zustand; change it to the updater form of set by calling
set(state => ({ target: { ...target, tick: (state.target?.tick ?? 0) + 1 } }));
this ensures you read the previous tick from the state snapshot atomically and
matches the pattern used in right-sidebar-toggle-intent (use set with a
function, reference the existing state.target?.tick, and increment it by 1 while
spreading the incoming target).
apps/desktop/src/renderer/stores/remove-workspace-from-sidebar-intent.ts (1)

19-22: ⚡ Quick win

Prefer updater form of set for consistency and idiomatic Zustand.

The current implementation uses get() followed by set(). Using the updater form of set is more idiomatic in Zustand and matches the pattern used in right-sidebar-toggle-intent.ts.

♻️ Refactor to use updater form
 request: (target) => {
-  const prevTick = get().target?.tick ?? 0;
-  set({ target: { ...target, tick: prevTick + 1 } });
+  set((state) => ({
+    target: { ...target, tick: (state.target?.tick ?? 0) + 1 }
+  }));
 },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/stores/remove-workspace-from-sidebar-intent.ts`
around lines 19 - 22, The request handler in
remove-workspace-from-sidebar-intent currently calls get() then set(), so change
it to the updater form of set used elsewhere (e.g.,
right-sidebar-toggle-intent.ts): call set with a function that receives the
current state and returns the new state, compute prevTick from
state.target?.tick ?? 0 and set target to { ...target, tick: prevTick + 1 }
inside that returned object; keep the same property names (request, target) so
only the set/get pattern is replaced with set(state => ({ ...state, target: {
...target, tick: prevTick + 1 } })).
apps/desktop/src/renderer/commandPalette/core/types.ts (1)

35-37: ⚡ Quick win

Consider documenting the precedence between run, children, and renderFrame.

The Command interface allows all three properties (run, children, renderFrame) to be defined simultaneously. While this provides flexibility, it's unclear which takes precedence during execution. Consider adding a JSDoc comment clarifying the intended behavior when multiple properties are present.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/core/types.ts` around lines 35 - 37,
The Command interface in types.ts currently allows run, children, and
renderFrame to coexist without documented precedence; add a clear JSDoc comment
on the Command interface (or directly above the run/children/renderFrame
properties) that states the execution/rendering order and which property wins
when multiple are present (e.g., "If run is present it will be executed on
activation; otherwise children will be used to expand subcommands; renderFrame
is only used for custom rendering and does not affect activation"). Reference
the Command interface and the run, children, and renderFrame property names in
the comment so future readers know the intended behavior and precedence.
apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx (2)

39-49: ⚡ Quick win

Consider skipping the query when projectId is null.

Similar to the workspace query, this executes with an empty string filter when projectId is null. Skipping the query would be more efficient.

⚡ Proposed optimization
 	const { data: preferredAppRows = [] } = useLiveQuery(
 		(q) =>
+			projectId === null
+				? []
+				:
 			q
 				.from({ sp: collections.v2SidebarProjects })
-				.where(({ sp }) => eq(sp.projectId, projectId ?? ""))
+				.where(({ sp }) => eq(sp.projectId, projectId))
 				.select(({ sp }) => ({ defaultOpenInApp: sp.defaultOpenInApp })),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx` around
lines 39 - 49, The query for preferredAppRows runs even when projectId is null,
causing an unnecessary filter with an empty string; update the useLiveQuery call
in ContextProvider (the block using useLiveQuery, collections.v2SidebarProjects,
projectId and preferredAppRows) to short-circuit and return an empty array when
projectId is null/undefined—i.e., only call useLiveQuery (or pass the query
function) when projectId is truthy, otherwise set preferredAppRows to [] so
preferredOpenInApp logic remains unchanged.

22-35: ⚡ Quick win

Consider skipping the query when workspaceId is null.

The query executes unconditionally with eq(workspaces.id, v2WorkspaceId ?? ""), which means it runs a query filtering on an empty string when v2WorkspaceId is null. While this returns empty results, it's more efficient to skip the query entirely.

⚡ Proposed optimization
 	const { data: v2WorkspaceRows = [] } = useLiveQuery(
 		(q) =>
+			v2WorkspaceId === null
+				? []
+				:
 			q
 				.from({ workspaces: collections.v2Workspaces })
-				.where(({ workspaces }) => eq(workspaces.id, v2WorkspaceId ?? ""))
+				.where(({ workspaces }) => eq(workspaces.id, v2WorkspaceId))
 				.select(({ workspaces }) => ({
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx` around
lines 22 - 35, Summary: Skip running the Dexie useLiveQuery when v2WorkspaceId
is null to avoid executing a query filtered by an empty string. Fix: inside the
useLiveQuery callback (the function passed to useLiveQuery that currently builds
q.from({ workspaces: collections.v2Workspaces })...), add an early guard like if
(!v2WorkspaceId) return [] to return an empty array immediately; keep the
dependency array [collections, v2WorkspaceId] and leave the rest of the query
(where + select) unchanged so the query only executes when v2WorkspaceId is
truthy.
apps/desktop/src/renderer/commandPalette/modules/openIn/commands.ts (1)

107-107: ⚡ Quick win

Consider using async keyword for consistency and error handling.

The run handler at line 107 returns a promise from openIn without the async keyword, while the submenu handlers (line 86-94) use async/await. This inconsistency could lead to unhandled promise rejections if the execute handler doesn't properly catch errors from non-async functions that return promises.

Proposed fix for consistency
-run: (ctx) => openIn(ctx, preferredOption.id),
+run: async (ctx) => {
+  await openIn(ctx, preferredOption.id);
+},

Alternatively, if synchronous wrapping is preferred:

-run: (ctx) => openIn(ctx, preferredOption.id),
+run: (ctx) => void openIn(ctx, preferredOption.id),

The first option is recommended for consistent error surfacing via the execute handler's catch block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/modules/openIn/commands.ts` at line
107, The run handler currently returns the promise from openIn(ctx,
preferredOption.id) without using async/await; update the run handler (the
function assigned to run where preferredOption.id is used) to be async and await
the call to openIn so errors are propagated consistently like the submenu
handlers (i.e., change the run function to an async function that awaits
openIn(ctx, preferredOption.id)).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx`:
- Line 56: The route.params is currently hardcoded to {} in ContextProvider.tsx;
update the CommandContext route construction to derive params from the router
match returned by useMatchRoute (the v2Match object) instead of the empty
object. Locate where route: { pathname: location.pathname, params: {} } is
created and replace params with the actual match params (e.g., v2Match?.params
or an empty fallback) so dynamic route segments are passed into CommandContext
and commands can access them.

In `@apps/desktop/src/renderer/commandPalette/core/execute.ts`:
- Around line 9-10: Analytics "command_run" is being sent before verifying the
command is executable; move or conditionally invoke track so it only runs when
command.run exists—i.e., check command.run (the run handler on the command
object) first and then call track("command_run", { commandId: command.id,
section: command.section }) immediately before executing command.run (or inside
the branch that calls command.run) to avoid recording events for non-executable
commands such as those with only children or renderFrame.

In `@apps/desktop/src/renderer/commandPalette/core/types.ts`:
- Line 10: The route params type is too strict: change the declaration of params
in the command palette route types (the params field in types.ts) from
Record<string, string> to allow undefined values produced by TanStack Router —
e.g., use Record<string, string | undefined> (or an equivalent optional/partial
mapping) so optional or missing route params from matchRoute type-check
correctly when building context.

In `@apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx`:
- Around line 37-39: The query key passed to
electronQueryClient.invalidateQueries is incorrectly nested as a double array
which prevents matching the tRPC cache key; update the call in commands.tsx that
calls electronQueryClient.invalidateQueries (the same call that targets
"settings","getNotificationSoundsMuted") to use a flat array key matching
utils.settings.getNotificationSoundsMuted.invalidate() — i.e., replace the
nested [["settings","getNotificationSoundsMuted"]] with a single-level
["settings","getNotificationSoundsMuted"] so the query is properly invalidated.

In `@apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx`:
- Line 36: Replace the direct window.open("https://docs.superset.sh", "_blank",
"noreferrer") call with the tRPC mutation openUrl: import/use the existing trpc
client and call the openUrl mutation with the URL (e.g., trpc.openUrl.mutate({
url: "https://docs.superset.sh" })) instead of window.open, remove the erroneous
third parameter, and await/handle the promise or errors appropriately where this
is invoked (replace the window.open call in commands.tsx).

In
`@apps/desktop/src/renderer/commandPalette/ui/CommandListView/CommandListView.tsx`:
- Line 17: The CommandEmpty usage renders a non-selectable message; update the
CommandEmpty component's className to include the required select-text and
cursor-text classes so the text becomes user-selectable and shows the text
cursor; locate the CommandEmpty definition in the ui component (symbol:
CommandEmpty in packages/ui/src/components/ui/command.tsx) and append
"select-text cursor-text" to its existing className ("py-6 text-center
text-sm"), and also ensure any instance in CommandListView (symbol:
CommandListView in
apps/desktop/src/renderer/commandPalette/ui/CommandListView/CommandListView.tsx)
reflects this change if it overrides or supplies classes.

In
`@apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx`:
- Line 115: In the className string in CommandPalette (the JSX element in
CommandPalette.tsx) remove the invalid Tailwind prefix
"**:data-[slot=command-input-wrapper]:h-12" and replace it with the correct
arbitrary variant "[&_[data-slot=command-input-wrapper]]:h-12" so the intended
height style on the command-input-wrapper descendant is applied; update the
className entry near the existing bracketed selectors (the same string that
contains [&_[cmdk-group-heading]] and [&_[cmdk-item]]) to use the corrected
selector.

---

Nitpick comments:
In `@apps/desktop/src/renderer/commandPalette/CommandPaletteHost.tsx`:
- Around line 29-33: The small helper component CommandPaletteTrigger should be
inlined into CommandPaletteHost: remove the separate CommandPaletteTrigger
function and move its logic into CommandPaletteHost by calling
useFrameStackStore((s) => s.setOpen) and useHotkey("OPEN_COMMAND_PALETTE", () =>
setOpen(true)) directly inside CommandPaletteHost (ensuring hooks remain at the
top level of the component), then delete the unused CommandPaletteTrigger
export/definition to keep a single-component-per-file and improve locality.

In `@apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx`:
- Around line 39-49: The query for preferredAppRows runs even when projectId is
null, causing an unnecessary filter with an empty string; update the
useLiveQuery call in ContextProvider (the block using useLiveQuery,
collections.v2SidebarProjects, projectId and preferredAppRows) to short-circuit
and return an empty array when projectId is null/undefined—i.e., only call
useLiveQuery (or pass the query function) when projectId is truthy, otherwise
set preferredAppRows to [] so preferredOpenInApp logic remains unchanged.
- Around line 22-35: Summary: Skip running the Dexie useLiveQuery when
v2WorkspaceId is null to avoid executing a query filtered by an empty string.
Fix: inside the useLiveQuery callback (the function passed to useLiveQuery that
currently builds q.from({ workspaces: collections.v2Workspaces })...), add an
early guard like if (!v2WorkspaceId) return [] to return an empty array
immediately; keep the dependency array [collections, v2WorkspaceId] and leave
the rest of the query (where + select) unchanged so the query only executes when
v2WorkspaceId is truthy.

In `@apps/desktop/src/renderer/commandPalette/core/types.ts`:
- Around line 35-37: The Command interface in types.ts currently allows run,
children, and renderFrame to coexist without documented precedence; add a clear
JSDoc comment on the Command interface (or directly above the
run/children/renderFrame properties) that states the execution/rendering order
and which property wins when multiple are present (e.g., "If run is present it
will be executed on activation; otherwise children will be used to expand
subcommands; renderFrame is only used for custom rendering and does not affect
activation"). Reference the Command interface and the run, children, and
renderFrame property names in the comment so future readers know the intended
behavior and precedence.

In `@apps/desktop/src/renderer/commandPalette/modules/openIn/commands.ts`:
- Line 107: The run handler currently returns the promise from openIn(ctx,
preferredOption.id) without using async/await; update the run handler (the
function assigned to run where preferredOption.id is used) to be async and await
the call to openIn so errors are propagated consistently like the submenu
handlers (i.e., change the run function to an async function that awaits
openIn(ctx, preferredOption.id)).

In
`@apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx`:
- Line 104: The className string on the CommandPalette component contains a
redundant responsive override; remove the duplicated "sm:!max-w-[720px]" from
the className (leave "!max-w-[720px]" intact) so the base max-width value isn't
needlessly overridden; update the className in CommandPalette.tsx where the
className is defined to drop the "sm:!max-w-[720px]" token.
- Line 105: The inline style on the CommandPalette component uses a magic number
"278px" in style={{ top: "max(16px, calc(50% - 278px))" }}; add a short
clarifying comment next to this expression (or extract to a named constant like
DIALOG_HALF_HEIGHT) explaining that 278px represents half the dialog's intended
height used to vertically center the palette and why the lower bound 16px is
required, so future maintainers understand the offset and can update it if the
dialog size changes.

In `@apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx`:
- Around line 90-96: The function linkTaskToWorkspace is currently a no-op stub
that discards taskId and workspaceId while a success toast ("Linked ${slug} to
workspace") is shown elsewhere; implement real linking by replacing the void
stubs in linkTaskToWorkspace with an async call to the appropriate
service/API/store (e.g., call your backend endpoint or task-store method) to
associate the taskId with workspaceId, await the response, and throw or return
errors on failure; ensure the success toast is only shown after a successful
response and that errors are caught and surfaced (e.g., show an error toast or
rethrow), or if you intend to leave it unimplemented, remove the premature
success toast and add a TODO with an issue reference in linkTaskToWorkspace
instead.

In `@apps/desktop/src/renderer/commandPalette/ui/QuickOpen/quickOpenStore.ts`:
- Line 18: The close action currently only sets open: false leaving a stale
target; update the close handler in quickOpenStore (the close: () => set({...})
action) to also reset target to null (e.g., set({ open: false, target: null }))
so its cleanup matches other intent stores (delete-workspace-intent,
remove-workspace-from-sidebar-intent, set-preferred-open-in-app-intent) and
prevents consumers from relying on a stale target after close.

In
`@apps/desktop/src/renderer/commandPalette/ui/SubPaletteView/SubPaletteView.tsx`:
- Line 25: The visible computation is re-run on every render; wrap the filter in
a useMemo to memoize it: replace the direct assignment to visible with a useMemo
that returns children.filter((c) => (c.when ? c.when(context) : true)) and set
the dependency array to [children, context] (ensure useMemo is imported),
referencing the visible variable and the children and context used in
SubPaletteView to avoid unnecessary recalculations.

In `@apps/desktop/src/renderer/stores/remove-workspace-from-sidebar-intent.ts`:
- Around line 19-22: The request handler in remove-workspace-from-sidebar-intent
currently calls get() then set(), so change it to the updater form of set used
elsewhere (e.g., right-sidebar-toggle-intent.ts): call set with a function that
receives the current state and returns the new state, compute prevTick from
state.target?.tick ?? 0 and set target to { ...target, tick: prevTick + 1 }
inside that returned object; keep the same property names (request, target) so
only the set/get pattern is replaced with set(state => ({ ...state, target: {
...target, tick: prevTick + 1 } })).

In `@apps/desktop/src/renderer/stores/set-preferred-open-in-app-intent.ts`:
- Around line 19-22: The request handler currently uses get() then set(), which
is non-idiomatic for Zustand; change it to the updater form of set by calling
set(state => ({ target: { ...target, tick: (state.target?.tick ?? 0) + 1 } }));
this ensures you read the previous tick from the state snapshot atomically and
matches the pattern used in right-sidebar-toggle-intent (use set with a
function, reference the existing state.target?.tick, and increment it by 1 while
spreading the incoming target).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3de20f6c-cb95-4796-b15d-5bbc1966c6e3

📥 Commits

Reviewing files that changed from the base of the PR and between 9d290c7 and e23d2da.

📒 Files selected for processing (41)
  • apps/desktop/src/lib/trpc/routers/auto-update/index.ts
  • apps/desktop/src/renderer/commandPalette/CommandPaletteHost.tsx
  • apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx
  • apps/desktop/src/renderer/commandPalette/core/execute.ts
  • apps/desktop/src/renderer/commandPalette/core/frames.ts
  • apps/desktop/src/renderer/commandPalette/core/registry.ts
  • apps/desktop/src/renderer/commandPalette/core/sections.ts
  • apps/desktop/src/renderer/commandPalette/core/types.ts
  • apps/desktop/src/renderer/commandPalette/core/useActiveCommands.ts
  • apps/desktop/src/renderer/commandPalette/index.ts
  • apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx
  • apps/desktop/src/renderer/commandPalette/modules/index.ts
  • apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx
  • apps/desktop/src/renderer/commandPalette/modules/openIn/commands.ts
  • apps/desktop/src/renderer/commandPalette/modules/settings/commands.ts
  • apps/desktop/src/renderer/commandPalette/modules/workspace/commands.tsx
  • apps/desktop/src/renderer/commandPalette/ui/CommandItemRow/CommandItemRow.tsx
  • apps/desktop/src/renderer/commandPalette/ui/CommandListView/CommandListView.tsx
  • apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx
  • apps/desktop/src/renderer/commandPalette/ui/DeleteWorkspaceMount/DeleteWorkspaceMount.tsx
  • apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx
  • apps/desktop/src/renderer/commandPalette/ui/QuickOpen/quickOpenStore.ts
  • apps/desktop/src/renderer/commandPalette/ui/RecentlyViewed/RecentlyViewedFrame.tsx
  • apps/desktop/src/renderer/commandPalette/ui/RemoveFromSidebarMount/RemoveFromSidebarMount.tsx
  • apps/desktop/src/renderer/commandPalette/ui/SetPreferredOpenInAppMount/SetPreferredOpenInAppMount.tsx
  • apps/desktop/src/renderer/commandPalette/ui/SubPaletteView/SubPaletteView.tsx
  • apps/desktop/src/renderer/commandPalette/ui/ThemeFrame/ThemeFrame.tsx
  • apps/desktop/src/renderer/components/ThemeSwatch/ThemeSwatch.tsx
  • apps/desktop/src/renderer/components/ThemeSwatch/index.ts
  • apps/desktop/src/renderer/hotkeys/registry.ts
  • apps/desktop/src/renderer/index.tsx
  • apps/desktop/src/renderer/lib/router-instance.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/layout.tsx
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/useWorkspaceHotkeys/useWorkspaceHotkeys.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/page.tsx
  • apps/desktop/src/renderer/routes/_authenticated/settings/appearance/components/AppearanceSettings/components/ThemeSection/ThemeSection.tsx
  • apps/desktop/src/renderer/stores/delete-workspace-intent.ts
  • apps/desktop/src/renderer/stores/remove-workspace-from-sidebar-intent.ts
  • apps/desktop/src/renderer/stores/right-sidebar-toggle-intent.ts
  • apps/desktop/src/renderer/stores/set-preferred-open-in-app-intent.ts
  • packages/ui/src/components/ui/command.tsx


const context = useMemo<CommandContext>(
() => ({
route: { pathname: location.pathname, params: {} },

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 | ⚡ Quick win

Route params should be derived from the router match.

The params field is hardcoded to an empty object {}, but the route match from useMatchRoute can provide actual route parameters. This causes the CommandContext.route.params to always be empty, preventing commands from accessing dynamic route segments.

🔧 Proposed fix to derive params from v2Match
 	const context = useMemo<CommandContext>(
 		() => ({
-			route: { pathname: location.pathname, params: {} },
+			route: { 
+				pathname: location.pathname, 
+				params: v2Match !== false ? v2Match : {} 
+			},
 			workspace: v2Workspace
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
route: { pathname: location.pathname, params: {} },
route: {
pathname: location.pathname,
params: v2Match !== false ? v2Match : {}
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx` at line
56, The route.params is currently hardcoded to {} in ContextProvider.tsx; update
the CommandContext route construction to derive params from the router match
returned by useMatchRoute (the v2Match object) instead of the empty object.
Locate where route: { pathname: location.pathname, params: {} } is created and
replace params with the actual match params (e.g., v2Match?.params or an empty
fallback) so dynamic route segments are passed into CommandContext and commands
can access them.

Comment on lines +9 to +10
track("command_run", { commandId: command.id, section: command.section });
if (!command.run) return;

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 | 🟡 Minor | ⚡ Quick win

Analytics event may track non-executable commands.

The analytics event is recorded on line 9 before checking whether command.run exists on line 10. This means commands that have no run handler (e.g., commands with only children or renderFrame) will still emit a "command_run" event, which may inflate analytics counts or misrepresent user behavior.

📊 Proposed fix to track only executable commands
 export async function executeCommand(
 	command: Command,
 	context: CommandContext,
 ): Promise<void> {
-	track("command_run", { commandId: command.id, section: command.section });
 	if (!command.run) return;
+	track("command_run", { commandId: command.id, section: command.section });
 	try {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/core/execute.ts` around lines 9 -
10, Analytics "command_run" is being sent before verifying the command is
executable; move or conditionally invoke track so it only runs when command.run
exists—i.e., check command.run (the run handler on the command object) first and
then call track("command_run", { commandId: command.id, section: command.section
}) immediately before executing command.run (or inside the branch that calls
command.run) to avoid recording events for non-executable commands such as those
with only children or renderFrame.

export interface CommandContext {
route: {
pathname: string;
params: Record<string, string>;

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 | 🟡 Minor | ⚡ Quick win

Route params type may be too strict.

params: Record<string, string> requires all param values to be strings, but Tanstack Router can produce undefined values for optional or missing params. This may cause type mismatches when building the context from matchRoute.

🔧 Proposed fix
-		params: Record<string, string>;
+		params: Record<string, string | undefined>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
params: Record<string, string>;
params: Record<string, string | undefined>;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/core/types.ts` at line 10, The route
params type is too strict: change the declaration of params in the command
palette route types (the params field in types.ts) from Record<string, string>
to allow undefined values produced by TanStack Router — e.g., use Record<string,
string | undefined> (or an equivalent optional/partial mapping) so optional or
missing route params from matchRoute type-check correctly when building context.

Comment on lines +37 to +39
await electronQueryClient.invalidateQueries({
queryKey: [["settings", "getNotificationSoundsMuted"]],
});

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for similar invalidateQueries patterns in the codebase
rg -n -C3 'invalidateQueries.*queryKey.*settings' --type=tsx --type=ts

Repository: superset-sh/superset

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Search for invalidateQueries calls in the codebase
rg -n -C3 'invalidateQueries' apps/desktop/src --max-count=20

Repository: superset-sh/superset

Length of output: 13780


🏁 Script executed:

#!/bin/bash
# Find tRPC router definitions for settings
fd -e ts -e tsx | xargs rg -l 'settings.*getNotificationSoundsMuted' | head -10

Repository: superset-sh/superset

Length of output: 591


🏁 Script executed:

#!/bin/bash
# Look at the file in question to see the full context
cat -n apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx | head -50

Repository: superset-sh/superset

Length of output: 1963


🏁 Script executed:

#!/bin/bash
# Find tRPC router definition for settings
rg -n 'settings' apps/desktop/src/lib/trpc -A 5 -B 5 | head -100

Repository: superset-sh/superset

Length of output: 9560


🏁 Script executed:

#!/bin/bash
# Search for useQuery calls with getNotificationSoundsMuted
rg -n 'getNotificationSoundsMuted' apps/desktop/src

Repository: superset-sh/superset

Length of output: 1786


🏁 Script executed:

#!/bin/bash
# Look for tRPC settings router definition
fd -e ts -e tsx apps/desktop/src | xargs rg -l 'createTRPCRouter.*settings|settings.*createTRPCRouter' 2>/dev/null

Repository: superset-sh/superset

Length of output: 436


🏁 Script executed:

#!/bin/bash
# Check the settings router definition
sed -n '840,870p' apps/desktop/src/lib/trpc/routers/settings/index.ts

Repository: superset-sh/superset

Length of output: 837


🏁 Script executed:

#!/bin/bash
# Look at how other procedures in the same router are used with invalidateQueries
rg -B 2 -A 2 'utils\.settings\.' apps/desktop/src/renderer --max-count=10

Repository: superset-sh/superset

Length of output: 39128


🏁 Script executed:

#!/bin/bash
# Check how RingtonesSettings uses the tRPC utils for getNotificationSoundsMuted
sed -n '140,155p' apps/desktop/src/renderer/routes/_authenticated/settings/ringtones/components/RingtonesSettings/RingtonesSettings.tsx

Repository: superset-sh/superset

Length of output: 624


Fix the tRPC query key nesting level.

The query key at line 38 has an extra level of array nesting. Change:

queryKey: [["settings", "getNotificationSoundsMuted"]],

to:

queryKey: ["settings", "getNotificationSoundsMuted"],

The tRPC utilities (e.g., utils.settings.getNotificationSoundsMuted.invalidate()) use a flat array key. When calling queryClient.invalidateQueries() directly, the key format must match. The double-nested array will prevent the query from being invalidated, leaving stale data in the cache.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx` around
lines 37 - 39, The query key passed to electronQueryClient.invalidateQueries is
incorrectly nested as a double array which prevents matching the tRPC cache key;
update the call in commands.tsx that calls electronQueryClient.invalidateQueries
(the same call that targets "settings","getNotificationSoundsMuted") to use a
flat array key matching utils.settings.getNotificationSoundsMuted.invalidate() —
i.e., replace the nested [["settings","getNotificationSoundsMuted"]] with a
single-level ["settings","getNotificationSoundsMuted"] so the query is properly
invalidated.

section: "navigation",
icon: BookOpenIcon,
run: () => {
window.open("https://docs.superset.sh", "_blank", "noreferrer");

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify that Electron shell API is available and preferred for external URLs

rg -n 'shell\.(openExternal|openPath)' --type=ts -C2

Repository: superset-sh/superset

Length of output: 7312


🏁 Script executed:

#!/bin/bash
# First, verify the actual code at the specified location
echo "=== Checking the actual file content ===" 
cat -n apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx | head -50

Repository: superset-sh/superset

Length of output: 1599


🏁 Script executed:

#!/bin/bash
# Check the tRPC external router to see what's available
echo "=== Checking tRPC external router ===" 
cat -n apps/desktop/src/lib/trpc/routers/external/index.ts | head -150

Repository: superset-sh/superset

Length of output: 5082


Use tRPC's openUrl mutation instead of window.open() for opening external URLs.

This code violates the Electron IPC guideline: renderer process communication must use tRPC. Additionally, the third parameter "noreferrer" has no effect in window.open() — the third parameter expects window features (like "width=500,height=400"), not a rel attribute, so this will be silently ignored.

A tRPC route for this already exists:

run: () => {
-  window.open("https://docs.superset.sh", "_blank", "noreferrer");
+  void electronTrpcClient.external.openUrl.mutate("https://docs.superset.sh");
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx` at
line 36, Replace the direct window.open("https://docs.superset.sh", "_blank",
"noreferrer") call with the tRPC mutation openUrl: import/use the existing trpc
client and call the openUrl mutation with the URL (e.g., trpc.openUrl.mutate({
url: "https://docs.superset.sh" })) instead of window.open, remove the erroneous
third parameter, and await/handle the promise or errors appropriately where this
is invoked (replace the window.open call in commands.tsx).


return (
<CommandList>
<CommandEmpty>No commands found.</CommandEmpty>

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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if CommandEmpty component includes select-text cursor-text classes

# Search for CommandEmpty definition in ui package
ast-grep --pattern $'const CommandEmpty = $$$'

# Also search with forwardRef pattern
ast-grep --pattern $'export const CommandEmpty = forwardRef($$$)'

# Check className assignments in command.tsx
rg -n "CommandEmpty" --type=tsx --type=ts -A 5 -B 2 packages/ui/src/components/ui/command.tsx

Repository: superset-sh/superset

Length of output: 92


🏁 Script executed:

#!/bin/bash
# Find the command component files
fd -e ts -e tsx -e js -e jsx "command" packages/ui/src/components

# Search for CommandEmpty definition
rg -n "CommandEmpty" packages/ui/src/components --type ts --type js -A 10 -B 2

# Also check if there's a command.tsx or command.ts file
find packages/ui/src/components -name "*command*"

Repository: superset-sh/superset

Length of output: 6542


Add select-text cursor-text classes to CommandEmpty component.

The CommandEmpty component in packages/ui/src/components/ui/command.tsx (line 110) currently has className="py-6 text-center text-sm" but is missing the required select-text cursor-text classes. Since this component is used in the desktop renderer, the message must be user-selectable per coding guidelines. Update the className to include these required classes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/commandPalette/ui/CommandListView/CommandListView.tsx`
at line 17, The CommandEmpty usage renders a non-selectable message; update the
CommandEmpty component's className to include the required select-text and
cursor-text classes so the text becomes user-selectable and shows the text
cursor; locate the CommandEmpty definition in the ui component (symbol:
CommandEmpty in packages/ui/src/components/ui/command.tsx) and append
"select-text cursor-text" to its existing className ("py-6 text-center
text-sm"), and also ensure any instance in CommandListView (symbol:
CommandListView in
apps/desktop/src/renderer/commandPalette/ui/CommandListView/CommandListView.tsx)
reflects this change if it overrides or supplies classes.

</DialogHeader>
<Command
shouldFilter={!currentFrame || !currentFrame.command.renderFrame}
className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5 [&_[cmdk-list]]:max-h-[min(500px,calc(80vh-3rem))]"

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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if **: is a valid Tailwind prefix or if it should be [&_

# Search for other uses of **: prefix in the codebase
rg -n '\*\*:' --type=tsx --type=ts

# Search for similar command-input-wrapper styling patterns
rg -n 'command-input-wrapper' --type=tsx --type=ts -B 2 -A 2

Repository: superset-sh/superset

Length of output: 120


🏁 Script executed:

#!/bin/bash
# Search for **: prefix pattern without file type specification
rg '\*\*:' -A 2 -B 2

# Also search for command-input-wrapper to find usage patterns
rg 'command-input-wrapper' -A 2 -B 2

Repository: superset-sh/superset

Length of output: 50379


🏁 Script executed:

#!/bin/bash
# Read the CommandPalette.tsx file to see the exact code
cat -n apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx | sed -n '110,120p'

Repository: superset-sh/superset

Length of output: 902


🌐 Web query:

Tailwind CSS **: prefix syntax validity and meaning

💡 Result:

Tailwind CSS “: prefix” syntax depends on what you mean by “prefix”: 1) “Tailwind class name prefixes” (like tw:, kz:) — validity and meaning In Tailwind CSS v4, configuring a prefix is done via import syntax, and the prefix behaves like a variant that is applied at the beginning of the class name. Tailwind’s docs show: @import "tailwindcss" prefix(tw);, which makes generated utility classes require the prefix when used in HTML (for example, the docs show classes like tw:flex and tw:bg-red-500). [1] In other words, a “tw:”-style prefix is valid Tailwind v4 syntax, and it means “only match/use Tailwind utilities when the class is prefixed with tw:”. The Tailwind docs also note that these prefixes work “a little differently than in v3” and “look like variants and are always at the beginning of the class name.” [1] Additionally, the prefix is applied to generated CSS variables as well (Tailwind generates --tw-… variables). [1] Tailwind also restricts what characters are allowed in the prefix; the Tailwind team’s implementation notes mention “Only alpha (a-z) characters are allowed in a prefix”. [2] 2) The unrelated “variant prefixes” (sm:, md:, hover:, dark:) — validity and meaning Tailwind also uses “state/responsive prefixes” like hover: and sm:. Those are valid and mean the utility applies only when that variant condition matches (hover, breakpoint, dark mode, etc.). [3] 3) “Colon” usage inside arbitrary values/properties (e.g. [color:red], [property:value]) If by “: prefix syntax” you meant colon syntax inside arbitrary values/properties, that’s a different feature: Tailwind arbitrary properties use [property:value] syntax. [4] This is not a “class-name prefix” (like tw:), but the colon is part of the arbitrary CSS declaration inside square brackets. What NOT to do - Don’t confuse a “class-name prefix” like tw: (configured prefix/variant-like) with “arbitrary property syntax” [property:value]. They live in different parts of the syntax. - In Tailwind, the configured prefix should be set via @import "tailwindcss" prefix(...); (v4) rather than older v3-style config in a JS file. [5][1] Example: using a Tailwind v4 configured prefix If you import with prefix(tw): [1] @import "tailwindcss" prefix(tw); Then Tailwind utilities are written with the prefix at the beginning of the class: tw:flex, tw:bg-red-500, tw:hover:bg-red-600. [1] If you tell me exactly what string you’re trying to use (e.g. “tw:bg-white” vs “tw:[color:red]” vs something like “:bg-red-500”), I can confirm which Tailwind parsing rule applies and whether it should generate CSS.

Citations:


Remove **: prefix from the className; use [&_[data-slot=command-input-wrapper]]:h-12 instead.

The **:data-[slot=command-input-wrapper]:h-12 class uses **: which is not valid Tailwind syntax. Tailwind prefixes support only alpha characters (a–z), so the ** prefix violates this restriction and will be ignored, leaving the height rule unapplied. The intended selector should use the arbitrary variant form [&_[data-slot=command-input-wrapper]]:h-12 to target the descendant element.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/desktop/src/renderer/commandPalette/ui/CommandPalette/CommandPalette.tsx`
at line 115, In the className string in CommandPalette (the JSX element in
CommandPalette.tsx) remove the invalid Tailwind prefix
"**:data-[slot=command-input-wrapper]:h-12" and replace it with the correct
arbitrary variant "[&_[data-slot=command-input-wrapper]]:h-12" so the intended
height style on the command-input-wrapper descendant is applied; update the
className entry near the existing bracketed selectors (the same string that
contains [&_[cmdk-group-heading]] and [&_[cmdk-item]]) to use the corrected
selector.

@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

6 issues found across 41 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx">

<violation number="1" location="apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx:90">
P1: Selecting a task never links it to the workspace because `linkTaskToWorkspace` is unimplemented (no-op), despite showing a success toast.</violation>
</file>

<file name="apps/desktop/src/renderer/commandPalette/modules/settings/commands.ts">

<violation number="1" location="apps/desktop/src/renderer/commandPalette/modules/settings/commands.ts:151">
P2: Handle navigation promise rejections instead of discarding them with `void`; failed navigations can become unhandled promise rejections.

(Based on your team's feedback about handling async rejections explicitly.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/desktop/src/renderer/stores/set-preferred-open-in-app-intent.ts">

<violation number="1" location="apps/desktop/src/renderer/stores/set-preferred-open-in-app-intent.ts:16">
P1: `tick` is reset after `clear()`, which can cause subsequent intent requests to be ignored by the mount's `lastTickRef` guard.</violation>
</file>

<file name="apps/desktop/src/renderer/commandPalette/modules/openIn/commands.ts">

<violation number="1" location="apps/desktop/src/renderer/commandPalette/modules/openIn/commands.ts:78">
P2: The preferred "Open in …" command is dropped when `preferredOpenInApp` is a valid app that is not in `APP_OPTIONS`, which can disable the `OPEN_IN_APP` command/hotkey for those workspaces.</violation>
</file>

<file name="apps/desktop/src/renderer/hotkeys/registry.ts">

<violation number="1" location="apps/desktop/src/renderer/hotkeys/registry.ts:706">
P2: Windows/Linux `Open Command Palette` reuses `Ctrl+Shift+K`, which already maps to `Clear Terminal`, creating a conflicting hotkey when the terminal is focused.</violation>
</file>

<file name="apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx">

<violation number="1" location="apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx:52">
P3: `run` is unreachable when `renderFrame` is present because selection pushes a frame and exits early. Remove this callback or split the behavior into separate commands.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

);
}

async function linkTaskToWorkspace(

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.

P1: Selecting a task never links it to the workspace because linkTaskToWorkspace is unimplemented (no-op), despite showing a success toast.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/commandPalette/ui/LinkTask/LinkTaskFrame.tsx, line 90:

<comment>Selecting a task never links it to the workspace because `linkTaskToWorkspace` is unimplemented (no-op), despite showing a success toast.</comment>

<file context>
@@ -0,0 +1,96 @@
+	);
+}
+
+async function linkTaskToWorkspace(
+	taskId: string,
+	workspaceId: string,
</file context>

clear: () => void;
}

export const useSetPreferredOpenInAppIntent =

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.

P1: tick is reset after clear(), which can cause subsequent intent requests to be ignored by the mount's lastTickRef guard.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/stores/set-preferred-open-in-app-intent.ts, line 16:

<comment>`tick` is reset after `clear()`, which can cause subsequent intent requests to be ignored by the mount's `lastTickRef` guard.</comment>

<file context>
@@ -0,0 +1,24 @@
+	clear: () => void;
+}
+
+export const useSetPreferredOpenInAppIntent =
+	create<SetPreferredOpenInAppIntentState>((set, get) => ({
+		target: null,
</file context>

Comment thread apps/desktop/src/renderer/commandPalette/modules/settings/commands.ts Outdated
}

const preferredApp = context.workspace.preferredOpenInApp ?? "finder";
const preferredOption = findOption(preferredApp);

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.

P2: The preferred "Open in …" command is dropped when preferredOpenInApp is a valid app that is not in APP_OPTIONS, which can disable the OPEN_IN_APP command/hotkey for those workspaces.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/commandPalette/modules/openIn/commands.ts, line 78:

<comment>The preferred "Open in …" command is dropped when `preferredOpenInApp` is a valid app that is not in `APP_OPTIONS`, which can disable the `OPEN_IN_APP` command/hotkey for those workspaces.</comment>

<file context>
@@ -0,0 +1,122 @@
+		}
+
+		const preferredApp = context.workspace.preferredOpenInApp ?? "finder";
+		const preferredOption = findOption(preferredApp);
+
+		const submenuChildren: Command[] = APP_OPTIONS.map((option) => ({
</file context>

OPEN_COMMAND_PALETTE: {
key: {
mac: L("meta+shift+k"),
windows: L("ctrl+shift+k"),

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.

P2: Windows/Linux Open Command Palette reuses Ctrl+Shift+K, which already maps to Clear Terminal, creating a conflicting hotkey when the terminal is focused.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/hotkeys/registry.ts, line 706:

<comment>Windows/Linux `Open Command Palette` reuses `Ctrl+Shift+K`, which already maps to `Clear Terminal`, creating a conflicting hotkey when the terminal is focused.</comment>

<file context>
@@ -700,6 +700,16 @@ export const HOTKEYS_REGISTRY = {
+	OPEN_COMMAND_PALETTE: {
+		key: {
+			mac: L("meta+shift+k"),
+			windows: L("ctrl+shift+k"),
+			linux: L("ctrl+shift+k"),
+		},
</file context>

section: "actions",
icon: PaletteIcon,
keywords: ["dark", "light", "appearance", "color"],
run: () => cycleTheme(),

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.

P3: run is unreachable when renderFrame is present because selection pushes a frame and exits early. Remove this callback or split the behavior into separate commands.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx, line 52:

<comment>`run` is unreachable when `renderFrame` is present because selection pushes a frame and exits early. Remove this callback or split the behavior into separate commands.</comment>

<file context>
@@ -0,0 +1,119 @@
+				section: "actions",
+				icon: PaletteIcon,
+				keywords: ["dark", "light", "appearance", "color"],
+				run: () => cycleTheme(),
+				renderFrame: () => <ThemeFrame />,
+			},
</file context>

…context

Command run handlers fire from inside React, so we can expose navigate
through CommandContext (sourced from useNavigate in ContextProvider)
instead of stashing the router on a module-level singleton. Also drops
the agentConfig field from the Recently Viewed automations live query
to track the agent-config -> agent column rename.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
apps/desktop/src/renderer/commandPalette/modules/settings/commands.ts (1)

34-140: ⚡ Quick win

Consider adding keywords to improve command palette search discoverability.

Many tabs currently lack keywords, which reduces search effectiveness in the command palette. Since search is a primary interaction mode, adding aliases and alternate terms would improve the user experience.

💡 Suggested keyword additions
 	{
 		id: "account",
 		title: "Account",
 		path: "/settings/account",
 		icon: UserIcon,
+		keywords: ["profile", "user", "email"],
 	},
 	{
 		id: "billing",
 		title: "Billing",
 		path: "/settings/billing",
 		icon: CreditCardIcon,
+		keywords: ["payment", "subscription", "invoice"],
 	},
 	{
 		id: "security",
 		title: "Security",
 		path: "/settings/security",
 		icon: KeyRoundIcon,
+		keywords: ["auth", "authentication", "password", "2fa"],
 	},
 	{
 		id: "organization",
 		title: "Organization",
 		path: "/settings/organization",
 		icon: BuildingIcon,
+		keywords: ["company", "workspace"],
 	},
 	{ id: "teams", title: "Teams", path: "/settings/teams", icon: UsersIcon },
+	{ id: "teams", title: "Teams", path: "/settings/teams", icon: UsersIcon, keywords: ["members", "collaborate", "users"] },

Similar additions could be made for other tabs based on common user search terms.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/commandPalette/modules/settings/commands.ts` around
lines 34 - 140, The TABS array has many settings entries missing a keywords
field which hurts command palette search; for each SettingsTab object lacking
keywords (e.g., ids "account", "behavior", "terminal", "git", "experimental",
"integrations", "organization", "teams", "links", "permissions", "hosts",
"projects", "ringtones", "billing", "security", "agents", "presets") add a
keywords: string[] with common aliases and search terms (e.g., "account" ->
["profile","user"], "behavior" -> ["prefs","preferences"], "terminal" ->
["cli","shell"], "git" -> ["version control","repo"], etc.) so each tab has
relevant synonyms to improve discoverability in the command palette; update the
TABS entries to include these keywords arrays where missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/desktop/src/renderer/commandPalette/modules/settings/commands.ts`:
- Around line 34-140: The TABS array has many settings entries missing a
keywords field which hurts command palette search; for each SettingsTab object
lacking keywords (e.g., ids "account", "behavior", "terminal", "git",
"experimental", "integrations", "organization", "teams", "links", "permissions",
"hosts", "projects", "ringtones", "billing", "security", "agents", "presets")
add a keywords: string[] with common aliases and search terms (e.g., "account"
-> ["profile","user"], "behavior" -> ["prefs","preferences"], "terminal" ->
["cli","shell"], "git" -> ["version control","repo"], etc.) so each tab has
relevant synonyms to improve discoverability in the command palette; update the
TABS entries to include these keywords arrays where missing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 931ce4b7-8437-459d-9c0c-1905822e3acd

📥 Commits

Reviewing files that changed from the base of the PR and between e23d2da and 41aca77.

📒 Files selected for processing (6)
  • apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx
  • apps/desktop/src/renderer/commandPalette/core/types.ts
  • apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx
  • apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx
  • apps/desktop/src/renderer/commandPalette/modules/settings/commands.ts
  • apps/desktop/src/renderer/commandPalette/ui/RecentlyViewed/RecentlyViewedFrame.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/desktop/src/renderer/commandPalette/modules/navigation/commands.tsx
  • apps/desktop/src/renderer/commandPalette/core/ContextProvider.tsx
  • apps/desktop/src/renderer/commandPalette/core/types.ts
  • apps/desktop/src/renderer/commandPalette/ui/RecentlyViewed/RecentlyViewedFrame.tsx
  • apps/desktop/src/renderer/commandPalette/modules/actions/commands.tsx

@saddlepaddle saddlepaddle merged commit ae11a60 into main May 13, 2026
17 checks passed
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.

1 participant