{/* Overlay service demo: hover to show person details popover */}
diff --git a/lib/papi-dts/papi.d.ts b/lib/papi-dts/papi.d.ts
index b81b540a19f..61ec79813b4 100644
--- a/lib/papi-dts/papi.d.ts
+++ b/lib/papi-dts/papi.d.ts
@@ -7918,7 +7918,7 @@ declare module 'renderer/components/overlays/overlay-context-menu.component' {
declare module 'renderer/services/overlays/overlay.service-model' {
/**
* Type definitions for the overlay service, a renderer-only service that manages overlays (context
- * menus, popovers, command palettes) rendered in the renderer's top-level document outside iframe
+ * menus, popovers, combo boxes) rendered in the renderer's top-level document outside iframe
* boundaries. Extensions running in sandboxed WebView iframes cannot render UI above other content,
* so this service provides a way for them to request overlays that the renderer hosts on their
* behalf.
@@ -8003,13 +8003,13 @@ declare module 'renderer/services/overlays/overlay.service-model' {
showArrow?: boolean;
}
/**
- * A single item in a command palette. Items are displayed in a searchable, filterable list. The
- * user types to filter and selects one item.
+ * A single item in a combo box. Items are displayed in a searchable, filterable list. The user
+ * types to filter and selects one item.
*/
- export type CommandPaletteItem = {
+ export type ComboBoxItem = {
/** Unique identifier returned when this item is selected */
id: string;
- /** Primary display text (e.g., marker code like "ft" or command name) */
+ /** Primary display text (e.g., marker code like "ft") */
label: string | LocalizeKey;
/** Secondary description text displayed below the label */
description?: string | LocalizeKey;
@@ -8022,12 +8022,12 @@ declare module 'renderer/services/overlays/overlay.service-model' {
/** Whether the item is grayed out and non-selectable. Defaults to false. */
disabled?: boolean;
};
- /** Request payload for {@link IOverlayService.showCommandPalette}. */
- export interface CommandPaletteRequest {
+ /** Request payload for {@link IOverlayService.showComboBox}. */
+ export interface ComboBoxRequest {
/** The selectable items to display */
- items: CommandPaletteItem[];
+ items: ComboBoxItem[];
/**
- * Anchor position in pixels relative to the requesting WebView's iframe origin. The palette is
+ * Anchor position in pixels relative to the requesting WebView's iframe origin. The combo box is
* positioned adjacent to this point. If omitted, centers in the viewport.
*/
anchor?: {
@@ -8036,7 +8036,7 @@ declare module 'renderer/services/overlays/overlay.service-model' {
width?: number;
height?: number;
};
- /** Preferred side of the anchor to place the palette. Defaults to 'bottom'. */
+ /** Preferred side of the anchor to place the combo box. Defaults to 'bottom'. */
side?: 'top' | 'bottom' | 'left' | 'right';
/** Placeholder text for the search input */
placeholder?: string | LocalizeKey;
@@ -8044,13 +8044,21 @@ declare module 'renderer/services/overlays/overlay.service-model' {
maxWidth?: number;
/** Maximum height in pixels. Defaults to 400. */
maxHeight?: number;
- /** Whether clicking outside dismisses the palette. Defaults to true. */
+ /** Whether clicking outside dismisses the combo box. Defaults to true. */
dismissOnClickOutside?: boolean;
}
+ /**
+ * @deprecated Use {@link ComboBoxItem}. The "command palette" terminology was misleading — this
+ * overlay is a generic per-WebView searchable picker (used today for USFM marker selection), not
+ * the global Action Palette concept. See `docs/plans/2026-05-15-action-palette-proposal.md`.
+ */
+ export type CommandPaletteItem = ComboBoxItem;
+ /** @deprecated Use {@link ComboBoxRequest}. See {@link CommandPaletteItem} for context. */
+ export type CommandPaletteRequest = ComboBoxRequest;
/**
*
- * Service for showing overlays (context menus, popovers, command palettes) that render outside
- * iframe boundaries in the renderer's top-level document. Renderer-only service.
+ * Service for showing overlays (context menus, popovers, combo boxes) that render outside iframe
+ * boundaries in the renderer's top-level document. Renderer-only service.
*
* Extensions in sandboxed WebView iframes cannot render UI above other content or outside their
* iframe bounds. This service accepts overlay requests from WebViews, translates their
@@ -8058,9 +8066,9 @@ declare module 'renderer/services/overlays/overlay.service-model' {
* renderer's React tree. Each method returns a promise that resolves when the user interacts with
* the overlay or it is dismissed.
*
- * Only one overlay of each type (context menu, popover, command palette) can be active per WebView
- * at a time. Requesting a new overlay of the same type from the same WebView replaces the previous
- * one and rejects its promise with a PlatformError with code ABORTED.
+ * Only one overlay of each type (context menu, popover, combo box) can be active per WebView at a
+ * time. Requesting a new overlay of the same type from the same WebView replaces the previous one
+ * and rejects its promise with a PlatformError with code ABORTED.
*/
export interface IOverlayService {
/**
@@ -8131,20 +8139,21 @@ declare module 'renderer/services/overlays/overlay.service-model' {
*/
onPopoverDismissed(overlayId: string): Promise;
/**
- * Shows a command palette with searchable/filterable items. Returns a promise that resolves with
- * the selected item's `id`, or `undefined` if dismissed.
+ * Shows a combo box with searchable/filterable items. Returns a promise that resolves with the
+ * selected item's `id`, or `undefined` if dismissed.
*
* @param request The items, optional anchor position, and display options
- * @param webViewId The ID of the WebView requesting the command palette
+ * @param webViewId The ID of the WebView requesting the combo box
* @returns The selected item's ID, or `undefined` if dismissed
* @throws PlatformError with code INVALID_ARGUMENT if the request is invalid
- * @throws PlatformError with code ABORTED if replaced by another command palette from the same
- * WebView
+ * @throws PlatformError with code ABORTED if replaced by another combo box from the same WebView
*/
- showCommandPalette(
- request: CommandPaletteRequest,
- webViewId: string,
- ): Promise;
+ showComboBox(request: ComboBoxRequest, webViewId: string): Promise;
+ /**
+ * @deprecated Use {@link showComboBox}. The "command palette" terminology was misleading; see
+ * `docs/plans/2026-05-15-action-palette-proposal.md`.
+ */
+ showCommandPalette(request: ComboBoxRequest, webViewId: string): Promise;
}
/**
* Internal representation of an active overlay stored in the overlay store. Each entry holds the
@@ -8156,7 +8165,7 @@ declare module 'renderer/services/overlays/overlay.service-model' {
* - `'contextMenu'` — An active context menu with translated position and menu items.
* - `'modalDialog'` — An active modal dialog with its type-specific options.
* - `'popover'` — An active popover with mutable `content` (updatable via `updatePopover`).
- * - `'commandPalette'` — An active command palette with searchable/filterable items.
+ * - `'comboBox'` — An active combo box with searchable/filterable items.
*
* UI components read entries from the overlay store to render overlays, then call `resolve` or
* `reject` when the user interacts with or dismisses them.
@@ -8222,15 +8231,15 @@ declare module 'renderer/services/overlays/overlay.service-model' {
reject: (error: PlatformError) => void;
}
| {
- type: 'commandPalette';
+ type: 'comboBox';
/** Unique overlay identifier generated by the service */
id: string;
/** The WebView that requested this overlay */
webViewId: string;
/** The original request */
- request: CommandPaletteRequest;
+ request: ComboBoxRequest;
/** Items to render */
- items: CommandPaletteItem[];
+ items: ComboBoxItem[];
/** Document-relative position (translated + clamped), or undefined for centered */
position?: {
x: number;
@@ -8249,7 +8258,7 @@ declare module 'renderer/services/overlays/overlay.service-model' {
contextMenu: string | undefined;
modalDialog: unknown;
popover: string | undefined;
- commandPalette: string | undefined;
+ comboBox: string | undefined;
};
}
declare module 'shared/services/app.service-model' {
@@ -8948,7 +8957,11 @@ declare module '@papi/core' {
export type { DialogTypes } from 'renderer/components/dialogs/dialog-definition.model';
export type { UseDialogCallbackOptions } from 'renderer/hooks/papi-hooks/use-dialog-callback.hook';
export type {
+ ComboBoxItem,
+ ComboBoxRequest,
+ /** @deprecated Use {@link ComboBoxItem}. */
CommandPaletteItem,
+ /** @deprecated Use {@link ComboBoxRequest}. */
CommandPaletteRequest,
IOverlayService,
PopoverAction,
@@ -10249,7 +10262,7 @@ declare module 'renderer/services/overlays/overlay-menu-converter' {
declare module 'renderer/services/overlays/overlay-validation' {
import type { OverlayContextMenuItem } from 'renderer/components/overlays/overlay-context-menu.component';
import {
- CommandPaletteRequest,
+ ComboBoxRequest,
PopoverRequest,
} from 'renderer/services/overlays/overlay.service-model';
/**
@@ -10275,12 +10288,12 @@ declare module 'renderer/services/overlays/overlay-validation' {
*/
export function validatePopoverRequest(request: PopoverRequest): void;
/**
- * Validates a command palette request's items, anchor, and options.
+ * Validates a combo box request's items, anchor, and options.
*
- * @param request The command palette request to validate
+ * @param request The combo box request to validate
* @throws PlatformError with code INVALID_ARGUMENT if validation fails
*/
- export function validateCommandPaletteRequest(request: CommandPaletteRequest): void;
+ export function validateComboBoxRequest(request: ComboBoxRequest): void;
}
declare module 'renderer/services/overlays/overlay-coordinates' {
/**
@@ -10793,8 +10806,8 @@ declare module '@papi/frontend' {
window: IWindowService;
/**
*
- * Service for showing overlays (context menus, popovers, command palettes) that render outside
- * iframe boundaries in the renderer's top-level document. Renderer-only service.
+ * Service for showing overlays (context menus, popovers, combo boxes) that render outside iframe
+ * boundaries in the renderer's top-level document. Renderer-only service.
*
* Extensions in sandboxed WebView iframes cannot render UI above other content or outside their
* iframe bounds. This service accepts overlay requests from WebViews, translates their
@@ -10802,9 +10815,9 @@ declare module '@papi/frontend' {
* renderer's React tree. Each method returns a promise that resolves when the user interacts with
* the overlay or it is dismissed.
*
- * Only one overlay of each type (context menu, popover, command palette) can be active per WebView
- * at a time. Requesting a new overlay of the same type from the same WebView replaces the previous
- * one and rejects its promise with a PlatformError with code ABORTED.
+ * Only one overlay of each type (context menu, popover, combo box) can be active per WebView at a
+ * time. Requesting a new overlay of the same type from the same WebView replaces the previous one
+ * and rejects its promise with a PlatformError with code ABORTED.
*/
overlays: IOverlayService;
};
@@ -10962,8 +10975,8 @@ declare module '@papi/frontend' {
export const window: IWindowService;
/**
*
- * Service for showing overlays (context menus, popovers, command palettes) that render outside
- * iframe boundaries in the renderer's top-level document. Renderer-only service.
+ * Service for showing overlays (context menus, popovers, combo boxes) that render outside iframe
+ * boundaries in the renderer's top-level document. Renderer-only service.
*
* Extensions in sandboxed WebView iframes cannot render UI above other content or outside their
* iframe bounds. This service accepts overlay requests from WebViews, translates their
@@ -10971,9 +10984,9 @@ declare module '@papi/frontend' {
* renderer's React tree. Each method returns a promise that resolves when the user interacts with
* the overlay or it is dismissed.
*
- * Only one overlay of each type (context menu, popover, command palette) can be active per WebView
- * at a time. Requesting a new overlay of the same type from the same WebView replaces the previous
- * one and rejects its promise with a PlatformError with code ABORTED.
+ * Only one overlay of each type (context menu, popover, combo box) can be active per WebView at a
+ * time. Requesting a new overlay of the same type from the same WebView replaces the previous one
+ * and rejects its promise with a PlatformError with code ABORTED.
*/
export const overlays: IOverlayService;
export type Papi = typeof papi;
diff --git a/src/renderer/components/overlay-host.component.tsx b/src/renderer/components/overlay-host.component.tsx
index 603e489543d..1cf9d80eed1 100644
--- a/src/renderer/components/overlay-host.component.tsx
+++ b/src/renderer/components/overlay-host.component.tsx
@@ -3,7 +3,7 @@
* overlay store and renders the appropriate overlay components.
*/
-import { OverlayCommandPalette } from '@renderer/components/overlays/overlay-command-palette.component';
+import { OverlayComboBox } from '@renderer/components/overlays/overlay-combo-box.component';
import { OverlayContextMenu } from '@renderer/components/overlays/overlay-context-menu.component';
import { OverlayModalDialog } from '@renderer/components/overlays/overlay-modal-dialog.component';
import { OverlayPopover } from '@renderer/components/overlays/overlay-popover.component';
@@ -39,8 +39,8 @@ export function OverlayHost() {
if (overlay.type === 'popover') {
return ;
}
- if (overlay.type === 'commandPalette') {
- return ;
+ if (overlay.type === 'comboBox') {
+ return ;
}
return undefined;
})}
diff --git a/src/renderer/components/overlays/overlay-command-palette.component.test.tsx b/src/renderer/components/overlays/overlay-combo-box.component.test.tsx
similarity index 87%
rename from src/renderer/components/overlays/overlay-command-palette.component.test.tsx
rename to src/renderer/components/overlays/overlay-combo-box.component.test.tsx
index d69a9b819d9..2f105888992 100644
--- a/src/renderer/components/overlays/overlay-command-palette.component.test.tsx
+++ b/src/renderer/components/overlays/overlay-combo-box.component.test.tsx
@@ -1,8 +1,8 @@
import { vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
-import { OverlayCommandPalettePresentational } from './overlay-command-palette.component';
-import { CommandPaletteItem } from '../../services/overlays/overlay.service-model';
+import { OverlayComboBoxPresentational } from './overlay-combo-box.component';
+import { ComboBoxItem } from '../../services/overlays/overlay.service-model';
beforeAll(() => {
// Radix Popover uses ResizeObserver internally; jsdom doesn't provide it, so we stub a no-op
@@ -22,8 +22,8 @@ beforeAll(() => {
Element.prototype.scrollIntoView = vi.fn();
});
-describe('OverlayCommandPalettePresentational', () => {
- const sampleItems: CommandPaletteItem[] = [
+describe('OverlayComboBoxPresentational', () => {
+ const sampleItems: ComboBoxItem[] = [
{ id: 'open', label: 'Open File' },
{ id: 'save', label: 'Save File' },
{ id: 'close', label: 'Close Tab' },
@@ -35,7 +35,7 @@ describe('OverlayCommandPalettePresentational', () => {
const onDismiss = vi.fn();
render(
- {
const onDismiss = vi.fn();
render(
- {
const onDismiss = vi.fn();
render(
- ,
);
- const backdrop = document.querySelector('[data-overlay-command-palette-backdrop]');
+ const backdrop = document.querySelector('[data-overlay-combo-box-backdrop]');
expect(backdrop).toBeInTheDocument();
// querySelector returns Element | null; the expect above guards null, but TS can't narrow it
// eslint-disable-next-line no-type-assertion/no-type-assertion
@@ -90,7 +90,7 @@ describe('OverlayCommandPalettePresentational', () => {
it('should display custom noResultsText', () => {
render(
- {
it('should display custom placeholder text', () => {
render(
- {
it('should not call onSelect when a disabled item is clicked', () => {
const onSelect = vi.fn();
- const items: CommandPaletteItem[] = [
+ const items: ComboBoxItem[] = [
{ id: 'disabled-item', label: 'Cannot Click', disabled: true },
];
render(
- ,
+ ,
);
fireEvent.click(screen.getByText('Cannot Click'));
@@ -140,7 +136,7 @@ describe('OverlayCommandPalettePresentational', () => {
const onDismiss = vi.fn();
render(
- {
describe('search filtering', () => {
it('should filter visible items when typing in the search input', () => {
render(
- {
it('should show noResultsText when search matches nothing', () => {
render(
- {
const onSelect = vi.fn();
render(
- {
const onSelect = vi.fn();
render(
- {
describe('grouped items', () => {
it('should render group headings when items have group keys', () => {
- const groupedItems: CommandPaletteItem[] = [
+ const groupedItems: ComboBoxItem[] = [
{ id: 'open', label: 'Open File', group: 'File' },
{ id: 'save', label: 'Save File', group: 'File' },
{ id: 'find', label: 'Find', group: 'Edit' },
];
render(
- void;
-}) {
+/** Renders a single combo box item with label, description, icon, and badge */
+function PaletteItem({ item, onSelect }: { item: ComboBoxItem; onSelect: (id: string) => void }) {
// Build a searchable value from label + description + badge for cmdk filtering
const searchValue = [item.label, item.description, item.badge].filter(Boolean).join(' ');
@@ -111,11 +112,11 @@ function GroupedItems({
items,
onSelect,
}: {
- items: CommandPaletteItem[];
+ items: ComboBoxItem[];
onSelect: (id: string) => void;
}) {
const grouped = useMemo(() => {
- const groups = new Map();
+ const groups = new Map();
items.forEach((item) => {
const key = item.group ?? '';
const arr = groups.get(key);
@@ -153,15 +154,15 @@ function GroupedItems({
// ── Presentational Component ──
/**
- * Pure presentational command palette component. Renders a searchable list of items using cmdk.
+ * Pure presentational combo box component. Renders a searchable list of items using cmdk.
* Positioned via a Radix Popover virtual anchor when `position` is provided, or centered in the
* viewport when omitted.
*
* This component has no dependency on the overlay store or localization hooks. Use it directly in
* tests and Storybook stories. For production rendering via the overlay service, use
- * {@link OverlayCommandPalette} instead — it handles LocalizeKey resolution and store lifecycle.
+ * {@link OverlayComboBox} instead — it handles LocalizeKey resolution and store lifecycle.
*/
-export function OverlayCommandPalettePresentational({
+export function OverlayComboBoxPresentational({
items,
position,
anchor,
@@ -172,7 +173,7 @@ export function OverlayCommandPalettePresentational({
maxHeight = DEFAULT_MAX_HEIGHT,
onSelect,
onDismiss,
-}: OverlayCommandPalettePresentationalProps) {
+}: OverlayComboBoxPresentationalProps) {
// React's useRef requires null as the initial value for DOM refs
// eslint-disable-next-line no-null/no-null
const inputRef = useRef(null);
@@ -194,11 +195,7 @@ export function OverlayCommandPalettePresentational({
);
const paletteContent = (
-
+ {noResultsText}
@@ -213,7 +210,7 @@ export function OverlayCommandPalettePresentational({
// Backdrop handles click-to-dismiss; keyboard events (Escape) are handled by the child Command component
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
{
@@ -235,7 +232,7 @@ export function OverlayCommandPalettePresentational({
({
...item,
label: resolveValue(item.label, localizedStrings),
@@ -333,30 +330,30 @@ function localizeCommandPaletteItems(
// ── Store-Connected Component ──
-type OverlayCommandPaletteProps = {
- overlay: Extract;
+type OverlayComboBoxProps = {
+ overlay: Extract;
};
/**
- * Production command palette component. Resolves LocalizeKey values in items (labels, descriptions,
+ * Production combo box component. Resolves LocalizeKey values in items (labels, descriptions,
* badges) and placeholder/no-results text via `useLocalizedStrings`, manages overlay lifecycle, and
- * delegates rendering to {@link OverlayCommandPalettePresentational}.
+ * delegates rendering to {@link OverlayComboBoxPresentational}.
*
* This is the component rendered by `OverlayHost`. Do not use it directly in tests or Storybook —
- * use {@link OverlayCommandPalettePresentational} instead, which accepts plain props without
- * requiring an `OverlayEntry`.
+ * use {@link OverlayComboBoxPresentational} instead, which accepts plain props without requiring an
+ * `OverlayEntry`.
*/
-export function OverlayCommandPalette({ overlay }: OverlayCommandPaletteProps) {
+export function OverlayComboBox({ overlay }: OverlayComboBoxProps) {
const hasResolved = useRef(false);
const localizeKeys = useMemo(
- () => collectCommandPaletteKeys(overlay.items, overlay.request.placeholder),
+ () => collectComboBoxKeys(overlay.items, overlay.request.placeholder),
[overlay.items, overlay.request.placeholder],
);
const [localizedStrings] = useLocalizedStrings(localizeKeys);
const localizedItems = useMemo(
- () => localizeCommandPaletteItems(overlay.items, localizedStrings),
+ () => localizeComboBoxItems(overlay.items, localizedStrings),
[overlay.items, localizedStrings],
);
@@ -388,7 +385,7 @@ export function OverlayCommandPalette({ overlay }: OverlayCommandPaletteProps) {
}, [overlay]);
return (
- = {
- title: 'Advanced/OverlayCommandPalette',
- component: OverlayCommandPalettePresentational,
+const meta: Meta = {
+ title: 'Advanced/OverlayComboBox',
+ component: OverlayComboBoxPresentational,
tags: ['autodocs'],
parameters: {
docs: {
description: {
component:
- 'A searchable command palette overlay. Displays a filterable list of items with optional descriptions, badges, icons, and grouping.',
+ 'A searchable combo box overlay. Displays a filterable list of items with optional descriptions, badges, icons, and grouping.',
},
},
},
@@ -21,7 +21,7 @@ const meta: Meta = {
};
export default meta;
-type Story = StoryObj;
+type Story = StoryObj;
export const BasicItems: Story = {
args: {
diff --git a/src/renderer/services/overlays/overlay-validation.test.ts b/src/renderer/services/overlays/overlay-validation.test.ts
index 72e957d86c0..dea23cbf311 100644
--- a/src/renderer/services/overlays/overlay-validation.test.ts
+++ b/src/renderer/services/overlays/overlay-validation.test.ts
@@ -1,9 +1,9 @@
import { describe, it, expect } from 'vitest';
import { isPlatformError, INVALID_ARGUMENT } from 'platform-bible-utils';
import type { OverlayContextMenuItem } from '@renderer/components/overlays/overlay-context-menu.component';
-import { CommandPaletteRequest, PopoverRequest } from './overlay.service-model';
+import { ComboBoxRequest, PopoverRequest } from './overlay.service-model';
import {
- validateCommandPaletteRequest,
+ validateComboBoxRequest,
validateContextMenuItems,
validateMenuItems,
validatePopoverRequest,
@@ -331,21 +331,21 @@ describe('overlay-validation', () => {
});
});
- describe('validateCommandPaletteRequest', () => {
- const validRequest: CommandPaletteRequest = {
+ describe('validateComboBoxRequest', () => {
+ const validRequest: ComboBoxRequest = {
items: [
{ id: 'ft', label: 'Footnote' },
{ id: 'xt', label: 'Cross Reference' },
],
};
- it('should pass for a valid command palette request', () => {
- expect(() => validateCommandPaletteRequest(validRequest)).not.toThrow();
+ it('should pass for a valid combo box request', () => {
+ expect(() => validateComboBoxRequest(validRequest)).not.toThrow();
});
it('should throw for empty items array', () => {
expectValidationError(
- () => validateCommandPaletteRequest({ ...validRequest, items: [] }),
+ () => validateComboBoxRequest({ ...validRequest, items: [] }),
'Items array must not be empty',
);
});
@@ -353,61 +353,58 @@ describe('overlay-validation', () => {
it('should throw for too many items (>200)', () => {
const items = Array.from({ length: 201 }, (_, i) => ({ id: `id-${i}`, label: `Item ${i}` }));
expectValidationError(
- () => validateCommandPaletteRequest({ ...validRequest, items }),
+ () => validateComboBoxRequest({ ...validRequest, items }),
'Too many items (max 200)',
);
});
it('should pass for exactly 200 items', () => {
const items = Array.from({ length: 200 }, (_, i) => ({ id: `id-${i}`, label: `Item ${i}` }));
- expect(() => validateCommandPaletteRequest({ ...validRequest, items })).not.toThrow();
+ expect(() => validateComboBoxRequest({ ...validRequest, items })).not.toThrow();
});
it('should throw for item missing id', () => {
// Intentionally malformed input to test validation; must bypass TS to simulate bad runtime data
// eslint-disable-next-line no-type-assertion/no-type-assertion
- const request = { items: [{ label: 'No ID' }] } as unknown as CommandPaletteRequest;
- expectValidationError(
- () => validateCommandPaletteRequest(request),
- 'Each item must have an id',
- );
+ const request = { items: [{ label: 'No ID' }] } as unknown as ComboBoxRequest;
+ expectValidationError(() => validateComboBoxRequest(request), 'Each item must have an id');
});
it('should throw for label too long', () => {
const items = [{ id: 'long', label: 'a'.repeat(501) }];
expectValidationError(
- () => validateCommandPaletteRequest({ ...validRequest, items }),
+ () => validateComboBoxRequest({ ...validRequest, items }),
'Label exceeds maximum length of 500 characters',
);
});
it('should throw for invalid anchor coordinates (NaN)', () => {
expectValidationError(
- () => validateCommandPaletteRequest({ ...validRequest, anchor: { x: NaN, y: 100 } }),
+ () => validateComboBoxRequest({ ...validRequest, anchor: { x: NaN, y: 100 } }),
'Anchor must have valid x and y coordinates',
);
});
it('should throw for negative maxWidth', () => {
expectValidationError(
- () => validateCommandPaletteRequest({ ...validRequest, maxWidth: -10 }),
+ () => validateComboBoxRequest({ ...validRequest, maxWidth: -10 }),
'maxWidth must be greater than 0',
);
});
it('should throw for zero maxWidth', () => {
- expectValidationError(() => validateCommandPaletteRequest({ ...validRequest, maxWidth: 0 }));
+ expectValidationError(() => validateComboBoxRequest({ ...validRequest, maxWidth: 0 }));
});
it('should throw for negative maxHeight', () => {
expectValidationError(
- () => validateCommandPaletteRequest({ ...validRequest, maxHeight: -5 }),
+ () => validateComboBoxRequest({ ...validRequest, maxHeight: -5 }),
'maxHeight must be greater than 0',
);
});
it('should throw for zero maxHeight', () => {
- expectValidationError(() => validateCommandPaletteRequest({ ...validRequest, maxHeight: 0 }));
+ expectValidationError(() => validateComboBoxRequest({ ...validRequest, maxHeight: 0 }));
});
it('should pass with all optional fields on items', () => {
@@ -422,12 +419,12 @@ describe('overlay-validation', () => {
disabled: false,
},
];
- expect(() => validateCommandPaletteRequest({ ...validRequest, items })).not.toThrow();
+ expect(() => validateComboBoxRequest({ ...validRequest, items })).not.toThrow();
});
it('should pass with valid anchor coordinates', () => {
expect(() =>
- validateCommandPaletteRequest({
+ validateComboBoxRequest({
...validRequest,
anchor: { x: 100, y: 200 },
}),
@@ -435,7 +432,7 @@ describe('overlay-validation', () => {
});
it('should pass with no anchor (centered mode)', () => {
- expect(() => validateCommandPaletteRequest(validRequest)).not.toThrow();
+ expect(() => validateComboBoxRequest(validRequest)).not.toThrow();
});
});
});
diff --git a/src/renderer/services/overlays/overlay-validation.ts b/src/renderer/services/overlays/overlay-validation.ts
index 8f2bd6a2aae..7490b4bcc7f 100644
--- a/src/renderer/services/overlays/overlay-validation.ts
+++ b/src/renderer/services/overlays/overlay-validation.ts
@@ -5,7 +5,7 @@
import { newPlatformError, INVALID_ARGUMENT } from 'platform-bible-utils';
import type { OverlayContextMenuItem } from '@renderer/components/overlays/overlay-context-menu.component';
-import { CommandPaletteRequest, PopoverRequest } from './overlay.service-model';
+import { ComboBoxRequest, PopoverRequest } from './overlay.service-model';
/** Throws a PlatformError with INVALID_ARGUMENT code */
function throwValidationError(message: string): never {
@@ -112,20 +112,20 @@ export function validatePopoverRequest(request: PopoverRequest): void {
}
}
-const MAX_COMMAND_PALETTE_ITEMS = 200;
+const MAX_COMBO_BOX_ITEMS = 200;
/**
- * Validates a command palette request's items, anchor, and options.
+ * Validates a combo box request's items, anchor, and options.
*
- * @param request The command palette request to validate
+ * @param request The combo box request to validate
* @throws PlatformError with code INVALID_ARGUMENT if validation fails
*/
-export function validateCommandPaletteRequest(request: CommandPaletteRequest): void {
+export function validateComboBoxRequest(request: ComboBoxRequest): void {
if (!request.items || request.items.length === 0) {
throwValidationError('Items array must not be empty');
}
- if (request.items.length > MAX_COMMAND_PALETTE_ITEMS) {
- throwValidationError(`Too many items (max ${MAX_COMMAND_PALETTE_ITEMS})`);
+ if (request.items.length > MAX_COMBO_BOX_ITEMS) {
+ throwValidationError(`Too many items (max ${MAX_COMBO_BOX_ITEMS})`);
}
request.items.forEach((item) => {
diff --git a/src/renderer/services/overlays/overlay.service-host.contribution.test.ts b/src/renderer/services/overlays/overlay.service-host.contribution.test.ts
index 55a770dddc2..50e09a8f1fe 100644
--- a/src/renderer/services/overlays/overlay.service-host.contribution.test.ts
+++ b/src/renderer/services/overlays/overlay.service-host.contribution.test.ts
@@ -36,7 +36,7 @@ vi.mock('@shared/services/logger.service', () => ({
// Mock validation (no-op)
vi.mock('./overlay-validation', () => ({
- validateCommandPaletteRequest: vi.fn(),
+ validateComboBoxRequest: vi.fn(),
validateContextMenuItems: vi.fn(),
validateModalDialogOptions: vi.fn(),
validatePopoverRequest: vi.fn(),
diff --git a/src/renderer/services/overlays/overlay.service-host.test.ts b/src/renderer/services/overlays/overlay.service-host.test.ts
index 39e19161ad3..b950ef44201 100644
--- a/src/renderer/services/overlays/overlay.service-host.test.ts
+++ b/src/renderer/services/overlays/overlay.service-host.test.ts
@@ -7,7 +7,7 @@ import {
} from 'platform-bible-utils';
import { sendCommand } from '@shared/services/command.service';
import { menuDataService } from '@shared/services/menu-data.service';
-import { CommandPaletteRequest, PopoverContent, PopoverRequest } from './overlay.service-model';
+import { ComboBoxRequest, PopoverContent, PopoverRequest } from './overlay.service-model';
import { getOverlays, getOverlayById, clearAllOverlays } from './overlay-store';
import { isWebViewVisible } from './overlay-coordinates';
@@ -16,7 +16,7 @@ const DEBOUNCE_COOLDOWN_MS = 50;
// Mock dependencies
vi.mock('./overlay-validation', () => ({
- validateCommandPaletteRequest: vi.fn(),
+ validateComboBoxRequest: vi.fn(),
validateContextMenuItems: vi.fn(),
validatePopoverRequest: vi.fn(),
}));
@@ -507,8 +507,8 @@ describe('overlay.service-host', () => {
});
});
- describe('command palettes', () => {
- const validRequest: CommandPaletteRequest = {
+ describe('combo boxes', () => {
+ const validRequest: ComboBoxRequest = {
items: [
{ id: 'ft', label: 'Footnote' },
{ id: 'xt', label: 'Cross Reference' },
@@ -516,12 +516,12 @@ describe('overlay.service-host', () => {
anchor: { x: 100, y: 200 },
};
- it('should create an overlay entry of type commandPalette', () => {
- const promise = overlayService.showCommandPalette(validRequest, 'test-webview');
+ it('should create an overlay entry of type comboBox', () => {
+ const promise = overlayService.showComboBox(validRequest, 'test-webview');
const overlays = getOverlays();
expect(overlays).toHaveLength(1);
- expect(overlays[0].type).toBe('commandPalette');
+ expect(overlays[0].type).toBe('comboBox');
// Clean up
overlays[0].resolve(undefined);
@@ -529,12 +529,12 @@ describe('overlay.service-host', () => {
});
it('should resolve with selected item ID', async () => {
- const promise = overlayService.showCommandPalette(validRequest, 'test-webview');
+ const promise = overlayService.showComboBox(validRequest, 'test-webview');
const overlays = getOverlays();
- // Only commandPalette overlays exist in this test
+ // Only comboBox overlays exist in this test
// eslint-disable-next-line no-type-assertion/no-type-assertion
- const overlay = overlays[0] as Extract<(typeof overlays)[0], { type: 'commandPalette' }>;
+ const overlay = overlays[0] as Extract<(typeof overlays)[0], { type: 'comboBox' }>;
overlay.resolve('ft');
const result = await promise;
@@ -542,37 +542,37 @@ describe('overlay.service-host', () => {
});
it('should resolve with undefined when dismissed', async () => {
- const promise = overlayService.showCommandPalette(validRequest, 'test-webview');
+ const promise = overlayService.showComboBox(validRequest, 'test-webview');
const overlays = getOverlays();
// TypeScript cannot narrow a discriminated union after getOverlays(); cast needed to access typed fields
// eslint-disable-next-line no-type-assertion/no-type-assertion
- const overlay = overlays[0] as Extract<(typeof overlays)[0], { type: 'commandPalette' }>;
+ const overlay = overlays[0] as Extract<(typeof overlays)[0], { type: 'comboBox' }>;
overlay.resolve(undefined);
const result = await promise;
expect(result).toBeUndefined();
});
- it('should replace existing command palette from same webView', async () => {
+ it('should replace existing combo box from same webView', async () => {
vi.useFakeTimers();
- const promise1 = overlayService.showCommandPalette(validRequest, 'test-webview');
+ const promise1 = overlayService.showComboBox(validRequest, 'test-webview');
vi.advanceTimersByTime(DEBOUNCE_COOLDOWN_MS);
- const request2: CommandPaletteRequest = {
+ const request2: ComboBoxRequest = {
items: [{ id: 'p', label: 'Paragraph' }],
anchor: { x: 60, y: 110 },
};
- const promise2 = overlayService.showCommandPalette(request2, 'test-webview');
+ const promise2 = overlayService.showComboBox(request2, 'test-webview');
await expect(promise1).rejects.toSatisfy(
(error: unknown) => isPlatformError(error) && error.code === ABORTED,
);
const overlays = getOverlays();
- const palettes = overlays.filter((o) => o.type === 'commandPalette');
+ const palettes = overlays.filter((o) => o.type === 'comboBox');
expect(palettes).toHaveLength(1);
palettes[0].resolve(undefined);
@@ -581,11 +581,9 @@ describe('overlay.service-host', () => {
});
it('should reject with RESOURCE_EXHAUSTED within debounce cooldown', async () => {
- const promise1 = overlayService.showCommandPalette(validRequest, 'test-webview');
+ const promise1 = overlayService.showComboBox(validRequest, 'test-webview');
// Second call within 50ms should throw
- await expect(
- overlayService.showCommandPalette(validRequest, 'test-webview'),
- ).rejects.toSatisfy(
+ await expect(overlayService.showComboBox(validRequest, 'test-webview')).rejects.toSatisfy(
(error: unknown) => isPlatformError(error) && error.code === RESOURCE_EXHAUSTED,
);
expect(getOverlays()).toHaveLength(1);
@@ -595,17 +593,17 @@ describe('overlay.service-host', () => {
});
it('should handle centered mode (no anchor)', () => {
- const request: CommandPaletteRequest = {
+ const request: ComboBoxRequest = {
items: [{ id: 'ft', label: 'Footnote' }],
// no anchor — centered mode
};
- const promise = overlayService.showCommandPalette(request, 'test-webview');
+ const promise = overlayService.showComboBox(request, 'test-webview');
const overlays = getOverlays();
expect(overlays).toHaveLength(1);
// TypeScript cannot narrow a discriminated union after getOverlays(); cast needed to access typed fields
// eslint-disable-next-line no-type-assertion/no-type-assertion
- const overlay = overlays[0] as Extract<(typeof overlays)[0], { type: 'commandPalette' }>;
+ const overlay = overlays[0] as Extract<(typeof overlays)[0], { type: 'comboBox' }>;
expect(overlay.position).toBeUndefined();
overlay.resolve(undefined);
@@ -642,14 +640,14 @@ describe('overlay.service-host', () => {
vi.mocked(isWebViewVisible).mockReturnValue(true);
});
- it('should reject command palette with FAILED_PRECONDITION when webView is not visible', async () => {
+ it('should reject combo box with FAILED_PRECONDITION when webView is not visible', async () => {
vi.mocked(isWebViewVisible).mockReturnValue(false);
- const request: CommandPaletteRequest = {
+ const request: ComboBoxRequest = {
items: [{ id: 'ft', label: 'Footnote' }],
};
- await expect(overlayService.showCommandPalette(request, 'hidden-webview')).rejects.toSatisfy(
+ await expect(overlayService.showComboBox(request, 'hidden-webview')).rejects.toSatisfy(
(error: unknown) => isPlatformError(error) && error.code === FAILED_PRECONDITION,
);
diff --git a/src/renderer/services/overlays/overlay.service-host.ts b/src/renderer/services/overlays/overlay.service-host.ts
index d37a6ad3f7d..9adfbc4d86b 100644
--- a/src/renderer/services/overlays/overlay.service-host.ts
+++ b/src/renderer/services/overlays/overlay.service-host.ts
@@ -31,7 +31,7 @@ import {
import type { PlatformError } from 'platform-bible-utils';
import type { ReactElement } from 'react';
import {
- CommandPaletteRequest,
+ ComboBoxRequest,
IOverlayService,
OverlayEntry,
PopoverContent,
@@ -39,7 +39,7 @@ import {
} from './overlay.service-model';
import { convertContributionToContextMenuItems } from './overlay-menu-converter';
import {
- validateCommandPaletteRequest,
+ validateComboBoxRequest,
validateContextMenuItems,
validatePopoverRequest,
} from './overlay-validation';
@@ -523,35 +523,33 @@ async function onPopoverDismissed(overlayId: string): Promise {
- validateCommandPaletteRequest(request);
+ validateComboBoxRequest(request);
- // Visibility check (command palettes require visible WebView)
+ // Visibility check (combo boxes require visible WebView)
if (!isWebViewVisible(webViewId)) {
throw newPlatformError('Requesting WebView is not visible', FAILED_PRECONDITION);
}
// Leading-edge debounce: drop rapid re-triggers within 50ms
- if (!debounceCheck('commandPalette', webViewId)) {
+ if (!debounceCheck('comboBox', webViewId)) {
throw newPlatformError('Overlay request dropped by debounce cooldown', RESOURCE_EXHAUSTED);
}
- // Replace any existing command palette from this webView
- const existingOverlays = getOverlaysByWebView(webViewId).filter(
- (o) => o.type === 'commandPalette',
- );
+ // Replace any existing combo box from this webView
+ const existingOverlays = getOverlaysByWebView(webViewId).filter((o) => o.type === 'comboBox');
existingOverlays.forEach((existing) => {
rejectAndRemoveOverlay(
existing.id,
@@ -572,13 +570,13 @@ async function showCommandPalette(
position = clampToViewport(translatedPosition, 4);
}
- announceLocalizedToScreenReader('%overlay_aria_commandPaletteOpened%');
+ announceLocalizedToScreenReader('%overlay_aria_comboBoxOpened%');
lastOverlayCreatedAt = Date.now();
return new Promise((resolve, reject) => {
addOverlay({
- type: 'commandPalette',
+ type: 'comboBox',
id: overlayId,
webViewId,
request,
@@ -603,7 +601,9 @@ export const overlayService: IOverlayService = {
updatePopover,
dismissPopover,
onPopoverDismissed,
- showCommandPalette,
+ showComboBox,
+ /** @deprecated Use {@link showComboBox}. */
+ showCommandPalette: showComboBox,
};
// ── Event Listeners for Auto-Dismiss ──
@@ -622,7 +622,7 @@ function registerAutoDismissListeners(): void {
// Don't dismiss overlays when scrolling inside overlay content (e.g., popover with overflow)
if (
e.target instanceof Element &&
- e.target.closest('[data-overlay-popover], [data-overlay-command-palette]')
+ e.target.closest('[data-overlay-popover], [data-overlay-combo-box]')
)
return;
@@ -631,12 +631,12 @@ function registerAutoDismissListeners(): void {
{ capture: true },
);
- // Dismiss context menus, command palettes, and popovers on window blur
+ // Dismiss context menus, combo boxes, and popovers on window blur
window.addEventListener('blur', () => {
// Skip if an overlay was just created — focus shifts from panel activation can trigger blur
if (Date.now() - lastOverlayCreatedAt < OVERLAY_CREATION_GRACE_MS) return;
- dismissAll('contextMenu', 'commandPalette');
+ dismissAll('contextMenu', 'comboBox');
// Popovers with dismissOnClickOutside: false may persist across blur
const allOverlays = getOverlays();
allOverlays.forEach((overlay) => {
@@ -663,7 +663,7 @@ function registerAutoDismissListeners(): void {
// changes that would otherwise immediately dismiss the just-created context menu
if (Date.now() - lastOverlayCreatedAt < OVERLAY_CREATION_GRACE_MS) return;
- dismissAll('contextMenu', 'commandPalette', 'popover');
+ dismissAll('contextMenu', 'comboBox', 'popover');
})
.catch((err) => logger.warn(`Failed to subscribe to window focus changes: ${err}`));
}
diff --git a/src/renderer/services/overlays/overlay.service-model.ts b/src/renderer/services/overlays/overlay.service-model.ts
index 840618c3cfe..c67229fc4dd 100644
--- a/src/renderer/services/overlays/overlay.service-model.ts
+++ b/src/renderer/services/overlays/overlay.service-model.ts
@@ -1,6 +1,6 @@
/**
* Type definitions for the overlay service, a renderer-only service that manages overlays (context
- * menus, popovers, command palettes) rendered in the renderer's top-level document outside iframe
+ * menus, popovers, combo boxes) rendered in the renderer's top-level document outside iframe
* boundaries. Extensions running in sandboxed WebView iframes cannot render UI above other content,
* so this service provides a way for them to request overlays that the renderer hosts on their
* behalf.
@@ -86,16 +86,16 @@ export interface PopoverRequest {
showArrow?: boolean;
}
-// ── Command Palette Types ──
+// ── Quick Pick Types ──
/**
- * A single item in a command palette. Items are displayed in a searchable, filterable list. The
- * user types to filter and selects one item.
+ * A single item in a combo box. Items are displayed in a searchable, filterable list. The user
+ * types to filter and selects one item.
*/
-export type CommandPaletteItem = {
+export type ComboBoxItem = {
/** Unique identifier returned when this item is selected */
id: string;
- /** Primary display text (e.g., marker code like "ft" or command name) */
+ /** Primary display text (e.g., marker code like "ft") */
label: string | LocalizeKey;
/** Secondary description text displayed below the label */
description?: string | LocalizeKey;
@@ -109,16 +109,16 @@ export type CommandPaletteItem = {
disabled?: boolean;
};
-/** Request payload for {@link IOverlayService.showCommandPalette}. */
-export interface CommandPaletteRequest {
+/** Request payload for {@link IOverlayService.showComboBox}. */
+export interface ComboBoxRequest {
/** The selectable items to display */
- items: CommandPaletteItem[];
+ items: ComboBoxItem[];
/**
- * Anchor position in pixels relative to the requesting WebView's iframe origin. The palette is
+ * Anchor position in pixels relative to the requesting WebView's iframe origin. The combo box is
* positioned adjacent to this point. If omitted, centers in the viewport.
*/
anchor?: { x: number; y: number; width?: number; height?: number };
- /** Preferred side of the anchor to place the palette. Defaults to 'bottom'. */
+ /** Preferred side of the anchor to place the combo box. Defaults to 'bottom'. */
side?: 'top' | 'bottom' | 'left' | 'right';
/** Placeholder text for the search input */
placeholder?: string | LocalizeKey;
@@ -126,17 +126,27 @@ export interface CommandPaletteRequest {
maxWidth?: number;
/** Maximum height in pixels. Defaults to 400. */
maxHeight?: number;
- /** Whether clicking outside dismisses the palette. Defaults to true. */
+ /** Whether clicking outside dismisses the combo box. Defaults to true. */
dismissOnClickOutside?: boolean;
}
+/**
+ * @deprecated Use {@link ComboBoxItem}. The "command palette" terminology was misleading — this
+ * overlay is a generic per-WebView searchable picker (used today for USFM marker selection), not
+ * the global Action Palette concept. See `docs/plans/2026-05-15-action-palette-proposal.md`.
+ */
+export type CommandPaletteItem = ComboBoxItem;
+
+/** @deprecated Use {@link ComboBoxRequest}. See {@link CommandPaletteItem} for context. */
+export type CommandPaletteRequest = ComboBoxRequest;
+
// ── Service Interface ──
/**
* JSDOC SOURCE overlayService
*
- * Service for showing overlays (context menus, popovers, command palettes) that render outside
- * iframe boundaries in the renderer's top-level document. Renderer-only service.
+ * Service for showing overlays (context menus, popovers, combo boxes) that render outside iframe
+ * boundaries in the renderer's top-level document. Renderer-only service.
*
* Extensions in sandboxed WebView iframes cannot render UI above other content or outside their
* iframe bounds. This service accepts overlay requests from WebViews, translates their
@@ -144,9 +154,9 @@ export interface CommandPaletteRequest {
* renderer's React tree. Each method returns a promise that resolves when the user interacts with
* the overlay or it is dismissed.
*
- * Only one overlay of each type (context menu, popover, command palette) can be active per WebView
- * at a time. Requesting a new overlay of the same type from the same WebView replaces the previous
- * one and rejects its promise with a PlatformError with code ABORTED.
+ * Only one overlay of each type (context menu, popover, combo box) can be active per WebView at a
+ * time. Requesting a new overlay of the same type from the same WebView replaces the previous one
+ * and rejects its promise with a PlatformError with code ABORTED.
*/
export interface IOverlayService {
/**
@@ -212,20 +222,21 @@ export interface IOverlayService {
*/
onPopoverDismissed(overlayId: string): Promise;
/**
- * Shows a command palette with searchable/filterable items. Returns a promise that resolves with
- * the selected item's `id`, or `undefined` if dismissed.
+ * Shows a combo box with searchable/filterable items. Returns a promise that resolves with the
+ * selected item's `id`, or `undefined` if dismissed.
*
* @param request The items, optional anchor position, and display options
- * @param webViewId The ID of the WebView requesting the command palette
+ * @param webViewId The ID of the WebView requesting the combo box
* @returns The selected item's ID, or `undefined` if dismissed
* @throws PlatformError with code INVALID_ARGUMENT if the request is invalid
- * @throws PlatformError with code ABORTED if replaced by another command palette from the same
- * WebView
+ * @throws PlatformError with code ABORTED if replaced by another combo box from the same WebView
*/
- showCommandPalette(
- request: CommandPaletteRequest,
- webViewId: string,
- ): Promise;
+ showComboBox(request: ComboBoxRequest, webViewId: string): Promise;
+ /**
+ * @deprecated Use {@link showComboBox}. The "command palette" terminology was misleading; see
+ * `docs/plans/2026-05-15-action-palette-proposal.md`.
+ */
+ showCommandPalette(request: ComboBoxRequest, webViewId: string): Promise;
}
// ── Internal Overlay Store Types ──
@@ -240,7 +251,7 @@ export interface IOverlayService {
* - `'contextMenu'` — An active context menu with translated position and menu items.
* - `'modalDialog'` — An active modal dialog with its type-specific options.
* - `'popover'` — An active popover with mutable `content` (updatable via `updatePopover`).
- * - `'commandPalette'` — An active command palette with searchable/filterable items.
+ * - `'comboBox'` — An active combo box with searchable/filterable items.
*
* UI components read entries from the overlay store to render overlays, then call `resolve` or
* `reject` when the user interacts with or dismisses them.
@@ -300,15 +311,15 @@ export type OverlayEntry =
reject: (error: PlatformError) => void;
}
| {
- type: 'commandPalette';
+ type: 'comboBox';
/** Unique overlay identifier generated by the service */
id: string;
/** The WebView that requested this overlay */
webViewId: string;
/** The original request */
- request: CommandPaletteRequest;
+ request: ComboBoxRequest;
/** Items to render */
- items: CommandPaletteItem[];
+ items: ComboBoxItem[];
/** Document-relative position (translated + clamped), or undefined for centered */
position?: { x: number; y: number };
/** Settles the caller's promise with the selected item ID, or undefined if dismissed */
@@ -325,5 +336,5 @@ export type OverlayResolveType = {
contextMenu: string | undefined;
modalDialog: unknown;
popover: string | undefined;
- commandPalette: string | undefined;
+ comboBox: string | undefined;
};
diff --git a/src/shared/services/papi-core.service.ts b/src/shared/services/papi-core.service.ts
index 9b123fc641a..1f0880c7a23 100644
--- a/src/shared/services/papi-core.service.ts
+++ b/src/shared/services/papi-core.service.ts
@@ -7,7 +7,11 @@ export type { ExecutionToken } from '@node/models/execution-token.model';
export type { DialogTypes } from '@renderer/components/dialogs/dialog-definition.model';
export type { UseDialogCallbackOptions } from '@renderer/hooks/papi-hooks/use-dialog-callback.hook';
export type {
+ ComboBoxItem,
+ ComboBoxRequest,
+ /** @deprecated Use {@link ComboBoxItem}. */
CommandPaletteItem,
+ /** @deprecated Use {@link ComboBoxRequest}. */
CommandPaletteRequest,
IOverlayService,
PopoverAction,