mirror of
https://github.com/podman-desktop/podman-desktop
synced 2026-04-21 09:37:22 +00:00
chore: use product.json command palette search entries (#16708)
* chore: use product.json command palette search entries Signed-off-by: Sonia Sandler <ssandler@redhat.com> * chore: add safegaurd for searchOptionsWithShortcuts Signed-off-by: Sonia Sandler <ssandler@redhat.com> * chore: update tests Signed-off-by: Sonia Sandler <ssandler@redhat.com> * chore: add test Signed-off-by: Sonia Sandler <ssandler@redhat.com> * chore: update e2e tests Signed-off-by: Sonia Sandler <ssandler@redhat.com> * chore: apply comments Signed-off-by: Sonia Sandler <ssandler@redhat.com> --------- Signed-off-by: Sonia Sandler <ssandler@redhat.com>
This commit is contained in:
parent
72cf8f2e95
commit
19d3dbb6fe
9 changed files with 191 additions and 52 deletions
|
|
@ -77,6 +77,7 @@ export * from './provider-info.js';
|
|||
export * from './proxy.js';
|
||||
export * from './pull-event.js';
|
||||
export * from './release-notes-info.js';
|
||||
export * from './search-option.js';
|
||||
export * from './status-bar.js';
|
||||
export * from './system-overview-info.js';
|
||||
export * from './taskInfo.js';
|
||||
|
|
|
|||
23
packages/api/src/search-option.ts
Normal file
23
packages/api/src/search-option.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**********************************************************************
|
||||
* Copyright (C) 2026 Red Hat, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
***********************************************************************/
|
||||
|
||||
export interface CommandPaletteSearchOption {
|
||||
category: string;
|
||||
text: string;
|
||||
placeholder: string;
|
||||
}
|
||||
|
|
@ -19,11 +19,15 @@
|
|||
import type { ApiSenderType } from '@podman-desktop/core-api/api-sender';
|
||||
import { beforeEach, expect, expectTypeOf, test, vi } from 'vitest';
|
||||
|
||||
import product from '/@product.json' with { type: 'json' };
|
||||
|
||||
import { CommandRegistry } from './command-registry.js';
|
||||
import type { Telemetry } from './telemetry/telemetry.js';
|
||||
|
||||
let commandRegistry: CommandRegistry;
|
||||
|
||||
vi.mock(import('/@product.json'));
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
beforeEach(() => {
|
||||
commandRegistry = new CommandRegistry(
|
||||
|
|
@ -36,6 +40,18 @@ beforeEach(() => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(product).commandPalette.searchOptions = [
|
||||
{
|
||||
category: 'category1',
|
||||
text: 'Category 1',
|
||||
placeholder: 'Category 1 text',
|
||||
},
|
||||
{
|
||||
category: 'category2',
|
||||
text: 'Category 2',
|
||||
placeholder: 'Category 2 text',
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
test('Should dispose commands from an extension', async () => {
|
||||
|
|
@ -156,3 +172,18 @@ test('Should include category in the title', async () => {
|
|||
// should have category + title
|
||||
expect(myCommand?.title).toBe(`${category}: ${title1}`);
|
||||
});
|
||||
|
||||
test('Expect getCommandPaletteSearchOptions to return SearchOptions from product.json', () => {
|
||||
expect(commandRegistry.getCommandPaletteSearchOptions()).toStrictEqual([
|
||||
{
|
||||
category: 'category1',
|
||||
text: 'Category 1',
|
||||
placeholder: 'Category 1 text',
|
||||
},
|
||||
{
|
||||
category: 'category2',
|
||||
text: 'Category 2',
|
||||
placeholder: 'Category 2 text',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,11 +16,13 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
***********************************************************************/
|
||||
|
||||
import type { CommandInfo } from '@podman-desktop/core-api';
|
||||
import type { CommandInfo, CommandPaletteSearchOption } from '@podman-desktop/core-api';
|
||||
import { ApiSenderType } from '@podman-desktop/core-api/api-sender';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { z } from 'zod';
|
||||
|
||||
import product from '/@product.json' with { type: 'json' };
|
||||
|
||||
import { Telemetry } from './telemetry/telemetry.js';
|
||||
import { Disposable } from './types/disposable.js';
|
||||
|
||||
|
|
@ -137,6 +139,10 @@ export class CommandRegistry {
|
|||
return commandInfos;
|
||||
}
|
||||
|
||||
getCommandPaletteSearchOptions(): CommandPaletteSearchOption[] {
|
||||
return product.commandPalette.searchOptions;
|
||||
}
|
||||
|
||||
registerCommandPalette(...extensionCommands: RawCommand[]): Disposable {
|
||||
const disposables: Disposable[] = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ import type {
|
|||
CliToolInfo,
|
||||
ColorInfo,
|
||||
CommandInfo,
|
||||
CommandPaletteSearchOption,
|
||||
ContainerCreateOptions,
|
||||
ContainerExportOptions,
|
||||
ContainerImportOptions,
|
||||
|
|
@ -2364,6 +2365,10 @@ export class PluginSystem {
|
|||
return commandRegistry.getCommandPaletteCommands();
|
||||
});
|
||||
|
||||
this.ipcHandle('commands:getCommandPaletteSearchOptions', async (): Promise<CommandPaletteSearchOption[]> => {
|
||||
return commandRegistry.getCommandPaletteSearchOptions();
|
||||
});
|
||||
|
||||
this.ipcHandle(
|
||||
'extension-loader:stopExtension',
|
||||
async (_listener: Electron.IpcMainInvokeEvent, extensionId: string): Promise<void> => {
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import type {
|
|||
CliToolInfo,
|
||||
ColorInfo,
|
||||
CommandInfo,
|
||||
CommandPaletteSearchOption,
|
||||
ContainerCreateOptions,
|
||||
ContainerExportOptions,
|
||||
ContainerfileInfo,
|
||||
|
|
@ -1690,6 +1691,10 @@ export function initExposure(): void {
|
|||
return ipcInvoke('commands:getCommandPaletteCommands');
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('getCommandPaletteSearchOptions', async (): Promise<CommandPaletteSearchOption[]> => {
|
||||
return ipcInvoke('commands:getCommandPaletteSearchOptions');
|
||||
});
|
||||
|
||||
contextBridge.exposeInMainWorld('listExtensions', async (): Promise<ExtensionInfo[]> => {
|
||||
return ipcInvoke('extension-loader:listExtensions');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
import '@testing-library/jest-dom/vitest';
|
||||
|
||||
import type { ContainerInfo } from '@podman-desktop/core-api';
|
||||
import { fireEvent, render, screen } from '@testing-library/svelte';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { tick } from 'svelte';
|
||||
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
|
@ -59,12 +59,22 @@ beforeAll(() => {
|
|||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(window.telemetryTrack).mockResolvedValue(undefined);
|
||||
vi.mocked(window.getCommandPaletteSearchOptions).mockResolvedValue([
|
||||
{ category: 'category 1', text: 'Category 1 text', placeholder: 'Enter category 1 item' },
|
||||
{ category: 'category 2', text: 'Category 2 text', placeholder: 'Enter category 2 item' },
|
||||
{ category: 'category 3', text: 'Category 3 text', placeholder: 'Enter category 3 item' },
|
||||
{ category: 'category 4', text: 'Category 4 text', placeholder: 'Enter category 4 item' },
|
||||
]);
|
||||
});
|
||||
|
||||
describe('Command Palette', () => {
|
||||
test('Expect that F1 key is displaying the widget', async () => {
|
||||
render(CommandPalette);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// check we have the command palette input field
|
||||
const inputBefore = screen.queryByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
expect(inputBefore).not.toBeInTheDocument();
|
||||
|
|
@ -80,6 +90,10 @@ describe('Command Palette', () => {
|
|||
test('Expect that esc key is hiding the widget', async () => {
|
||||
render(CommandPalette, { display: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// check we have the command palette input field
|
||||
const input = screen.getByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
expect(input).toBeInTheDocument();
|
||||
|
|
@ -108,11 +122,15 @@ describe('Command Palette', () => {
|
|||
|
||||
render(CommandPalette, { display: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Wait for component to initialize and items to be rendered
|
||||
await screen.findByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
|
||||
// Switch to Commands mode to ensure we're testing command navigation specifically
|
||||
const commandsButton = screen.getByRole('button', { name: /Commands/ });
|
||||
// Switch to Commands (category 2 in this test case) mode to ensure we're testing command navigation specifically
|
||||
const commandsButton = screen.getByRole('button', { name: /Category 2/ });
|
||||
await userEvent.click(commandsButton);
|
||||
|
||||
// Wait for items to appear
|
||||
|
|
@ -170,11 +188,15 @@ describe('Command Palette', () => {
|
|||
|
||||
render(CommandPalette, { display: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Wait for component to initialize and items to be rendered
|
||||
const input = await screen.findByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
|
||||
// Switch to Commands mode to ensure we're testing command navigation specifically
|
||||
const commandsButton = screen.getByRole('button', { name: /Commands/ });
|
||||
// Switch to Commands (category 2 in this test case) mode to ensure we're testing command navigation specifically
|
||||
const commandsButton = screen.getByRole('button', { name: /Category 2/ });
|
||||
await userEvent.click(commandsButton);
|
||||
|
||||
// Wait for all items to appear
|
||||
|
|
@ -235,11 +257,15 @@ describe('Command Palette', () => {
|
|||
|
||||
render(CommandPalette, { display: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// Wait for component to initialize and items to be rendered
|
||||
const input = await screen.findByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
|
||||
// Switch to Commands mode to ensure we're testing command navigation specifically
|
||||
const commandsButton = screen.getByRole('button', { name: /Commands/ });
|
||||
// Switch to Commands (category 2 in this test case) mode to ensure we're testing command navigation specifically
|
||||
const commandsButton = screen.getByRole('button', { name: /Category 2/ });
|
||||
await userEvent.click(commandsButton);
|
||||
|
||||
// Wait for items to appear
|
||||
|
|
@ -290,6 +316,10 @@ describe('Command Palette', () => {
|
|||
|
||||
render(CommandPalette, { display: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// check we have the command palette input field
|
||||
const filterInput = screen.getByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
expect(filterInput).toBeInTheDocument();
|
||||
|
|
@ -362,12 +392,16 @@ describe('Command Palette', () => {
|
|||
|
||||
render(CommandPalette, { display: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// check we have the command palette input field
|
||||
const input = screen.getByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
expect(input).toBeInTheDocument();
|
||||
|
||||
// Switch to Commands mode to ensure we're testing command navigation specifically
|
||||
const commandsButton = screen.getByRole('button', { name: /Commands/ });
|
||||
// Switch to Commands (category 2 in this test case) mode to ensure we're testing command navigation specifically
|
||||
const commandsButton = screen.getByRole('button', { name: /Category 2/ });
|
||||
await userEvent.click(commandsButton);
|
||||
|
||||
// Wait for items to appear
|
||||
|
|
@ -399,31 +433,31 @@ describe('Command Palette', () => {
|
|||
{
|
||||
description: 'Ctrl+Shift+P',
|
||||
shortcut: '{Control>}{Shift>}p{/Shift}{/Control}',
|
||||
expectedTabText: 'Ctrl+Shift+P All',
|
||||
expectedTabText: 'Ctrl+Shift+P Category 1 text',
|
||||
shouldOpen: false,
|
||||
},
|
||||
{
|
||||
description: 'F1 key',
|
||||
shortcut: '{F1}',
|
||||
expectedTabText: 'F1 > Commands',
|
||||
expectedTabText: 'F1 > Category 2 text',
|
||||
shouldOpen: true,
|
||||
},
|
||||
{
|
||||
description: '> key',
|
||||
shortcut: '>',
|
||||
expectedTabText: 'F1 > Commands',
|
||||
expectedTabText: 'F1 > Category 2 text',
|
||||
shouldOpen: false,
|
||||
},
|
||||
{
|
||||
description: 'Ctrl+K',
|
||||
shortcut: '{Control>}k{/Control}',
|
||||
expectedTabText: 'Ctrl+K Documentation',
|
||||
expectedTabText: 'Ctrl+K Category 3 text',
|
||||
shouldOpen: false,
|
||||
},
|
||||
{
|
||||
description: 'Ctrl+F',
|
||||
shortcut: '{Control>}f{/Control}',
|
||||
expectedTabText: 'Ctrl+F Go to',
|
||||
expectedTabText: 'Ctrl+F Category 4 text',
|
||||
shouldOpen: false,
|
||||
},
|
||||
];
|
||||
|
|
@ -434,6 +468,10 @@ describe('Command Palette', () => {
|
|||
}) => {
|
||||
render(CommandPalette, { display: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// press the shortcut
|
||||
await userEvent.keyboard(shortcut);
|
||||
|
||||
|
|
@ -445,10 +483,10 @@ describe('Command Palette', () => {
|
|||
expect(expectedTab).toHaveClass('text-[var(--pd-button-tab-text-selected)]');
|
||||
expect(expectedTab).toHaveClass('border-[var(--pd-button-tab-border-selected)]');
|
||||
|
||||
const allTab = screen.getByRole('button', { name: 'Ctrl+Shift+P All' });
|
||||
const commandsTab = screen.getByRole('button', { name: 'F1 > Commands' });
|
||||
const docsTab = screen.getByRole('button', { name: 'Ctrl+K Documentation' });
|
||||
const gotoTab = screen.getByRole('button', { name: 'Ctrl+F Go to' });
|
||||
const allTab = screen.getByRole('button', { name: 'Ctrl+Shift+P Category 1 text' });
|
||||
const commandsTab = screen.getByRole('button', { name: 'F1 > Category 2 text' });
|
||||
const docsTab = screen.getByRole('button', { name: 'Ctrl+K Category 3 text' });
|
||||
const gotoTab = screen.getByRole('button', { name: 'Ctrl+F Category 4 text' });
|
||||
|
||||
[allTab, commandsTab, docsTab, gotoTab].forEach(button => {
|
||||
if (button !== expectedTab) {
|
||||
|
|
@ -463,6 +501,10 @@ describe('Command Palette', () => {
|
|||
shouldOpen,
|
||||
}) => {
|
||||
render(CommandPalette);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
// check command palette is not displayed initially
|
||||
const inputBefore = screen.queryByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
expect(inputBefore).not.toBeInTheDocument();
|
||||
|
|
@ -490,16 +532,20 @@ describe('Command Palette', () => {
|
|||
|
||||
render(CommandPalette, { display: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// check command palette is displayed
|
||||
const input = screen.getByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
expect(input).toBeInTheDocument();
|
||||
|
||||
await screen.findByRole('button', { name: 'Test Command 1' });
|
||||
|
||||
const allTab = screen.getByRole('button', { name: 'Ctrl+Shift+P All' });
|
||||
const commandsTab = screen.getByRole('button', { name: 'F1 > Commands' });
|
||||
const docsTab = screen.getByRole('button', { name: 'Ctrl+K Documentation' });
|
||||
const gotoTab = screen.getByRole('button', { name: 'Ctrl+F Go to' });
|
||||
const allTab = screen.getByRole('button', { name: 'Ctrl+Shift+P Category 1 text' });
|
||||
const commandsTab = screen.getByRole('button', { name: 'F1 > Category 2 text' });
|
||||
const docsTab = screen.getByRole('button', { name: 'Ctrl+K Category 3 text' });
|
||||
const gotoTab = screen.getByRole('button', { name: 'Ctrl+F Category 4 text' });
|
||||
|
||||
// initially "All" tab should be selected (index 0)
|
||||
expect(allTab).toHaveClass('text-[var(--pd-button-tab-text-selected)]');
|
||||
|
|
@ -540,14 +586,18 @@ describe('Command Palette', () => {
|
|||
|
||||
render(CommandPalette, { display: true });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(window.getCommandPaletteSearchOptions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// check command palette is displayed
|
||||
const input = screen.getByRole('textbox', { name: COMMAND_PALETTE_ARIA_LABEL });
|
||||
expect(input).toBeInTheDocument();
|
||||
|
||||
const allTab = screen.getByRole('button', { name: 'Ctrl+Shift+P All' });
|
||||
const commandsTab = screen.getByRole('button', { name: 'F1 > Commands' });
|
||||
const docsTab = screen.getByRole('button', { name: 'Ctrl+K Documentation' });
|
||||
const gotoTab = screen.getByRole('button', { name: 'Ctrl+F Go to' });
|
||||
const allTab = screen.getByRole('button', { name: 'Ctrl+Shift+P Category 1 text' });
|
||||
const commandsTab = screen.getByRole('button', { name: 'F1 > Category 2 text' });
|
||||
const docsTab = screen.getByRole('button', { name: 'Ctrl+K Category 3 text' });
|
||||
const gotoTab = screen.getByRole('button', { name: 'Ctrl+F Category 4 text' });
|
||||
|
||||
// Test that only one tab is selected at a time
|
||||
expect(allTab).toHaveClass('text-[var(--pd-button-tab-text-selected)]');
|
||||
|
|
@ -562,27 +612,23 @@ describe('Command Palette', () => {
|
|||
expect(gotoTab).not.toHaveClass('border-[var(--pd-button-tab-border-selected)]');
|
||||
|
||||
// Test that placeholder text is correct for each tab
|
||||
expect(input).toHaveAttribute('placeholder', 'Search Podman Desktop, or type > for commands');
|
||||
expect(input).toHaveAttribute('placeholder', 'Enter category 1 item');
|
||||
|
||||
// Click Commands tab and verify placeholder changes
|
||||
await userEvent.click(commandsTab);
|
||||
|
||||
await vi.waitFor(() => expect(input).toHaveAttribute('placeholder', 'Search and execute commands'));
|
||||
await vi.waitFor(() => expect(input).toHaveAttribute('placeholder', 'Enter category 2 item'));
|
||||
|
||||
// Click Documentation tab and verify placeholder changes
|
||||
await userEvent.click(docsTab);
|
||||
await vi.waitFor(() => expect(input).toHaveAttribute('placeholder', 'Search documentation and tutorials'));
|
||||
await vi.waitFor(() => expect(input).toHaveAttribute('placeholder', 'Enter category 3 item'));
|
||||
|
||||
// Click Go to tab and verify placeholder changes
|
||||
await userEvent.click(gotoTab);
|
||||
await vi.waitFor(() =>
|
||||
expect(input).toHaveAttribute('placeholder', 'Search images, containers, pods, and other resources'),
|
||||
);
|
||||
await vi.waitFor(() => expect(input).toHaveAttribute('placeholder', 'Enter category 4 item'));
|
||||
|
||||
// Click All tab and verify placeholder changes back
|
||||
await userEvent.click(allTab);
|
||||
await vi.waitFor(() =>
|
||||
expect(input).toHaveAttribute('placeholder', 'Search Podman Desktop, or type > for commands'),
|
||||
);
|
||||
await vi.waitFor(() => expect(input).toHaveAttribute('placeholder', 'Enter category 1 item'));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import type {
|
||||
CommandInfo,
|
||||
CommandPaletteSearchOption,
|
||||
ContainerInfo,
|
||||
DocumentationInfo,
|
||||
GoToInfo,
|
||||
|
|
@ -52,12 +53,6 @@ interface Props {
|
|||
onclose?: () => void;
|
||||
}
|
||||
|
||||
interface SearchOption {
|
||||
text: string;
|
||||
shortCut?: string[];
|
||||
helperText?: string;
|
||||
}
|
||||
|
||||
type CommandPaletteItem = CommandInfo | DocumentationInfo | GoToInfo;
|
||||
|
||||
let { display = false, onclose }: Props = $props();
|
||||
|
|
@ -75,15 +70,17 @@ let searchIcon = $derived.by(() => {
|
|||
}
|
||||
});
|
||||
|
||||
let searchOptions: CommandPaletteSearchOption[] = $state([]);
|
||||
let isMac: boolean = $state(false);
|
||||
let modifierC: string = $derived(isMac ? '⌘' : 'Ctrl+');
|
||||
let modifierS: string = $derived(isMac ? '⇧' : 'Shift+');
|
||||
let searchOptions: SearchOption[] = $derived([
|
||||
{ text: 'All', shortCut: [`${modifierC}${modifierS}P`], helperText: 'Search Podman Desktop, or type > for commands' },
|
||||
{ text: 'Commands', shortCut: [`${F1}`, '>'], helperText: 'Search and execute commands' },
|
||||
{ text: 'Documentation', shortCut: [`${modifierC}K`], helperText: 'Search documentation and tutorials' },
|
||||
{ text: 'Go to', shortCut: [`${modifierC}F`], helperText: 'Search images, containers, pods, and other resources' },
|
||||
]);
|
||||
let shortcuts = $derived([[`${modifierC}${modifierS}P`], [`${F1}`, '>'], [`${modifierC}K`], [`${modifierC}F`]]);
|
||||
let searchOptionsWithShortcuts = $derived(
|
||||
searchOptions.map((searchOption, index) => {
|
||||
return { ...searchOption, shortCut: index < shortcuts.length ? shortcuts[index] : undefined };
|
||||
}),
|
||||
);
|
||||
|
||||
let searchOptionsSelectedIndex: number = $state(0);
|
||||
|
||||
let documentationItems: DocumentationInfo[] = $state([]);
|
||||
|
|
@ -95,7 +92,7 @@ let navigationItems: NavigationRegistryEntry[] = $derived($navigationRegistry);
|
|||
let goToItems: GoToInfo[] = $derived(
|
||||
createGoToItems(imageInfos, containerInfos, podInfos, volumInfos, navigationItems),
|
||||
);
|
||||
let helperText = $derived(searchOptions[searchOptionsSelectedIndex].helperText);
|
||||
let helperText = $derived(searchOptionsWithShortcuts[searchOptionsSelectedIndex]?.placeholder);
|
||||
|
||||
// Keep backward compatibility with existing variable name
|
||||
let filteredCommandInfoItems: CommandInfo[] = $derived(
|
||||
|
|
@ -144,6 +141,7 @@ onMount(async () => {
|
|||
const platform = await window.getOsPlatform();
|
||||
isMac = platform === 'darwin';
|
||||
documentationItems = await window.getDocumentationItems();
|
||||
searchOptions = await window.getCommandPaletteSearchOptions();
|
||||
});
|
||||
|
||||
// Focus the input when the command palette becomes visible
|
||||
|
|
@ -247,7 +245,7 @@ async function handleKeydown(e: KeyboardEvent): Promise<void> {
|
|||
}
|
||||
|
||||
function switchSearchOption(direction: 1 | -1): void {
|
||||
const searchOptionsLength = searchOptions.length;
|
||||
const searchOptionsLength = searchOptionsWithShortcuts.length;
|
||||
const offset = direction === 1 ? 0 : searchOptionsLength;
|
||||
searchOptionsSelectedIndex = (searchOptionsSelectedIndex + direction + offset) % searchOptionsLength;
|
||||
}
|
||||
|
|
@ -320,7 +318,7 @@ async function executeAction(index: number): Promise<void> {
|
|||
|
||||
const telemetryOptions = {
|
||||
// All / Commands / Docs / Go To
|
||||
selectedTab: searchOptions[searchOptionsSelectedIndex].text,
|
||||
selectedTab: searchOptionsWithShortcuts[searchOptionsSelectedIndex].text,
|
||||
// Pod or Image or Documentation or Command
|
||||
itemType: itemType,
|
||||
pageLink: pageLink,
|
||||
|
|
@ -435,7 +433,7 @@ function getIcon(item: CommandInfo | DocumentationInfo | GoToInfo): IconDefiniti
|
|||
</div>
|
||||
|
||||
<div class="flex flex-row m-2">
|
||||
{#each searchOptions as searchOption, index (index)}
|
||||
{#each searchOptionsWithShortcuts as searchOption, index (index)}
|
||||
<Button
|
||||
type="tab"
|
||||
class="focus:outline-hidden"
|
||||
|
|
|
|||
24
product.json
24
product.json
|
|
@ -133,5 +133,29 @@
|
|||
"category": "Documentation"
|
||||
}
|
||||
]
|
||||
},
|
||||
"commandPalette": {
|
||||
"searchOptions": [
|
||||
{
|
||||
"category": "ALL",
|
||||
"text": "All",
|
||||
"placeholder": "Search Podman Desktop, or type > for commands"
|
||||
},
|
||||
{
|
||||
"category": "COMMANDS",
|
||||
"text": "Commands",
|
||||
"placeholder": "Search and execute commands"
|
||||
},
|
||||
{
|
||||
"category": "DOCUMENTATION",
|
||||
"text": "Documentation",
|
||||
"placeholder": "Search documentation and tutorials"
|
||||
},
|
||||
{
|
||||
"category": "GOTO",
|
||||
"text": "Go to",
|
||||
"placeholder": "Search images, containers, pods, and other resources"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue