import type { ElementType, KeyboardEvent, MouseEvent, ReactNode } from "react"; import type { AppOption } from "../AppSwitcher/AppSwitcherProps"; import type { LauncherSynonyms } from "./engine"; /** Rendered as ``, so any lucide-compatible component works. */ export type LauncherIcon = ElementType; /** One destination the launcher can open; screens are what pins, Frequent and Recent refer to. */ export type LauncherScreen = { /** Unique across all sections; the id that pins, Frequent and Recent refer to. */ id: string; /** Row text; also what search matches first. */ label: string; /** Destination of the row's link; Enter passes it to `onNavigate`. */ href: string; /** Row icon; falls back to the module's icon in search results. */ icon?: LauncherIcon; /** Count or short text shown on the row. */ badge?: number | string; /** Extra terms the query may match; never rendered. */ keywords?: readonly string[]; /** Listed on the module's second line (first `keyScreenLimit` of them). */ isKey?: boolean; }; /** A group of screens shown as one row in the browse view. */ export type LauncherModule = { /** Unique across sections. */ id: string; /** Row title; also what search matches first. */ label: string; /** Normally its first visible screen's href. */ href: string; /** Icon beside the title. */ icon?: LauncherIcon; /** Count or short text shown on the module row. */ badge?: number | string; /** Shows a small "New" tag after the name. */ isNew?: boolean; /** Extra terms the query may match; never rendered. */ keywords?: readonly string[]; /** Pinning the module pins `screens[0]`; a module with no screens is not rendered. */ screens: readonly LauncherScreen[]; }; /** A labelled run of modules; a section with no renderable module is dropped. */ export type LauncherSection = { /** Unique key of the section. */ id: string; /** Section heading. */ label: string; /** Modules in display order; modules with no screens are skipped. */ modules: readonly LauncherModule[]; }; /** One label/value pair of a result, e.g. PAN: ABCDE1234F; plain text only, never HTML. */ export type LauncherFact = { /** Name shown before the value, e.g. "PAN". */ label: string; /** Text shown, and copied when `copyable`. */ value: string; /** Adds a Copy button; a blocked clipboard selects the value instead. */ copyable?: boolean; }; /** Direct-answer payload, e.g. label "Next holiday", value "24 Oct — Diwali". */ export type LauncherAnswer = { /** Heading of the answer card. */ label: string; /** The answer itself, shown large; plain text. */ value: string; /** Small line under the value. */ hint?: string; }; /** A search row that is not a screen: an action (`run`) or an entity result (`href`). */ export type LauncherSearchItem = { /** Unique within its list; prefixed internally with the group or "action". */ id: string; /** Row text; also what search matches first. */ label: string; /** Second line, e.g. the module a result belongs to. */ context?: string; /** Row icon. */ icon?: LauncherIcon; /** Extra terms the query may match; never rendered. Separate phrases with commas. */ keywords?: string; /** Short text appended to `context` on the second line, separated by a dot. */ hint?: string; /** Destination of an entity result; Enter passes it to `onNavigate`. */ href?: string; /** Action to perform; the launcher closes first, then calls it. */ run?: () => void; /** Shown in the Preview pane for the active result; searchable in spotlight. */ facts?: readonly LauncherFact[]; /** Read only for items passed in `answers`: the card's label, value and hint. */ answer?: LauncherAnswer; }; /** A titled batch of remote results, rendered after Screens in the order given. */ export type LauncherResultGroup = { /** Unique key of the group; also its default scope id. */ id: string; /** Group heading. */ label: string; /** Results in display order. */ items: readonly LauncherSearchItem[]; /** Scope chip this group belongs to; defaults to `id`. */ scope?: string; /** Launcher filters and ranks these items itself; by default they show as given. */ matchLocally?: boolean; }; /** A filter chip; "screens" and "actions" are built in, others match `LauncherResultGroup.scope`. */ export type LauncherScope = { /** "all", "screens", "actions" or a `LauncherResultGroup.scope`. */ id: string; /** Chip text. */ label: string; /** Field placeholder while this chip is selected, e.g. "Search legal name, PAN, GSTIN…"; the field's accessible name stays `labels.searchPlaceholder`. */ placeholder?: string; }; /** "classic": registry order, substring matching; "spotlight": relevance ranking plus a Top hit. */ export type LauncherSearchMode = "classic" | "spotlight"; /** Every visible string and accessible name; pass any subset, the rest fall back to English. */ export type LauncherLabels = { /** Accessible name of the dialog and the results list; no longer drawn as a header pill. */ title: string; /** Search field placeholder and accessible name. */ searchPlaceholder: string; /** Accessible name of the clear-query button. */ clearSearch: string; /** Accessible name of the close button. */ close: string; /** Heading of the Pinned section. */ pinned: string; /** Message shown in Pinned when nothing is pinned. */ pinnedEmpty: string; /** Heading of the Frequently visited section. */ frequent: string; /** Heading of the Recent section. */ recent: string; /** Name for a Screens result group; NavLauncher does not read it, hosts pass it as `screensLabel` to `searchLauncher`. */ screensGroup: string; /** Name of the group holding matching `actions` in search results. */ actionsGroup: string; /** Accessible name of a pin button for the named screen, e.g. "Pin Leave". */ pin: (label: string) => string; /** Accessible name of an unpin button for the named screen. */ unpin: (label: string) => string; /** Accessible name of the new-tab arrow beside the named row. */ openInNewTab: (label: string) => string; /** Heading of the empty state for the given query. */ noResults: (query: string) => string; /** Line under the empty-state heading when no `suggestions` are given. */ noResultsHint: string; /** Empty-state heading while `isSearching` and nothing local matched. */ searching: string; /** Accessible name of the skeleton region while `isLoading`. */ loading: string; /** Heading of the empty state shown when `sections` has nothing to render. */ empty: string; /** Footer hint after the arrow caps; the legend hides below md and on touch. */ hintMove: string; /** Hint after ENTER when it opens the active result: footer, answer card, preview pane. */ hintOpen: string; /** Footer hint after the mod + ENTER key caps. */ hintNewTab: string; /** Footer hint after the P key cap. */ hintPin: string; /** Footer hint after the ESC key cap. */ hintClose: string; /** @deprecated No longer rendered: the launcher is already open, so the open chord is not shown. */ hintOpenMenu?: string; /** Footer hint after the mod key cap for a modified click, e.g. "+ click for new tab". */ hintClickNewTab: string; /** Tooltip of the arrow link beside a row. */ newTabTip: string; /** Muted count beside the search field and its polite announcement, e.g. "12 results". */ resultsCount: (count: number) => string; /** Name of the Menu / Quick access / Apps tab list shown below `lg`. */ tabsLabel: string; /** Tab showing every module (below `lg`). */ tabMenu: string; /** Tab showing Pinned, Frequently visited and Recent (below `lg`). */ tabQuickAccess: string; /** Tab showing the suite's apps (below `lg`). */ tabApps: string; /** Heading and list name of the apps section. */ apps: string; /** Per-section count shown while searching, e.g. "3 found". */ found: (count: number) => string; /** Accessible name of the right-hand quick access rail. */ quickAccess: string; /** Tag after a module marked `isNew`. */ newBadge: string; /** Noun used in a module's key-screens group name, e.g. "Leave screens". */ screens: string; /** Text on the Pin button of Frequently visited rows. */ pinShort: string; /** Right-hand footer note. */ footerNote: string; /** Line above the suggestion chips of the no-results state. */ suggestionsHint: string; /** Tooltip on an unpinned star. */ pinTip: string; /** Tooltip on a pinned star. */ unpinTip: string; /** Heading of the best-match card in spotlight mode. */ topHit?: string; /** Prefix of the Top hit line naming the keyword or fact that matched. */ matchedOn?: string; /** Name of the answers group. */ answers?: string; /** Hint after ENTER when it copies the active answer: footer, answer card, preview pane. */ hintCopy?: string; /** Footer hint after ENTER while a Show more option is active. */ hintExpand?: string; /** Name of the scope chip group. */ scopes?: string; /** Chip prepended to `scopes` when the list has no "all". */ scopeAll?: string; /** Polite announcement when the scope changes. */ scopeChanged?: (label: string) => string; /** Accessible name of a chip while a query is active, e.g. "People, 3 results". */ scopeCount?: (label: string, count: number) => string; /** Shown when a chip is picked with an empty query and its scope has nothing to list yet. */ scopeEmpty?: (label: string) => string; /** Name of the quick actions row. */ quickActions?: string; /** Option that expands a collapsed result group. */ showMore?: (count: number) => string; /** Name of the preview pane. */ preview?: string; /** Accessible name and tooltip of a fact's Copy button. */ copy?: (label: string) => string; /** Announced after a copy; shown for 1.5 s in place of the Enter hint. */ copied?: string; /** Announcement when the clipboard is blocked and the value was selected instead. */ copySelected?: string; }; /** Passed to `onNavigate`; `newTab` is true for mod+Enter. */ export type LauncherNavigateOptions = { /** True when the open was requested in a new tab (mod+Enter). */ newTab: boolean; }; /** Full-height navigation launcher: browse every screen, search, pins, Frequent, Recent and optionally the suite's apps. */ export interface NavLauncherProps { /** Shows the launcher; the query resets when it closes. */ isOpen: boolean; /** Called on Escape with an empty query, the close button, a plain-click navigation, `run` and app selection. */ onClose: () => void; /** Already permission-filtered by the app. Empty sections/modules are dropped, never rendered as gaps. */ sections: readonly LauncherSection[]; /** Screen ids in display order; ids not in `sections` are ignored silently. */ pinnedIds: readonly string[]; /** Screen ids in display order; pinned ids are skipped and the list is cut at `frequentLimit`. */ frequentIds: readonly string[]; /** Screen ids in display order; ids not in `sections` are ignored silently. */ recentIds: readonly string[]; /** Omit to hide every pin toggle (read-only launcher). */ onTogglePin?: (screenId: string) => void; /** Keyboard opens only (Enter / mod+Enter); pointer clicks follow the rendered links natively. */ onNavigate: (href: string, options: LauncherNavigateOptions) => void; /** Anchor-like component receiving `href`, `target`, `rel`, `onClick`, `ref`. Defaults to `"a"`. */ linkAs?: ElementType; /** Command rows matched by search under the Actions group; each has `run` or `href`. */ actions?: readonly LauncherSearchItem[]; /** Remote results, already filtered by the app; rendered after Screens in the given order. */ extraResults?: readonly LauncherResultGroup[]; /** Controlled query; omit for uncontrolled. Reset to "" on close either way. */ query?: string; /** Fires on every change, including the reset on close. */ onQueryChange?: (query: string) => void; /** Remote search in flight: shows the searching state when nothing local matched. */ isSearching?: boolean; /** Sections not known yet (access loading): skeleton browse view. */ isLoading?: boolean; /** Defaults to 3. */ keyScreenLimit?: number; /** Most Frequently visited rows shown (pinned ones are skipped); defaults to 4. */ frequentLimit?: number; /** Query chips offered by the no-results state; clicking one sets the query. */ suggestions?: readonly string[]; /** Key cap shown at the end of the search field, e.g. "/" when the app opens the launcher with it. */ searchHint?: string; /** @deprecated No longer rendered: the launcher is already open. Cmd/Ctrl+K and "/" now focus the search field while it is open. */ openShortcutHint?: string; /** Replaces the modifier cap of the mod+Enter hint; defaults to "⌘" on Apple platforms, "Ctrl" elsewhere. */ modKeyLabel?: string; /** Rendered just before the close button, e.g. a help or customise icon button; the dialog name stays `labels.title` whatever is here. */ headerActions?: ReactNode; /** Panel width at `lg`+; always full width below `lg`. Defaults to "1080px". */ maxWidth?: string; /** Overrides for any `LauncherLabels` key; unset keys keep their English default. */ labels?: Partial; /** Other Pixelize apps, shown as an "Apps" section at the bottom of the right rail. Same shape as AppSwitcher. */ apps?: AppOption[]; /** The app this UI runs inside: its row is marked and never a link. */ currentAppId?: string; /** Same contract as AppSwitcher.onSelect; preventDefault keeps the app's own routing. */ onAppSelect?: (app: AppOption, event: MouseEvent | KeyboardEvent) => void; /** Default "apps-on-compact": the section shows only below `md`, where the 9-dot is usually hidden. */ appsVisibility?: "always" | "apps-on-compact"; /** Default "classic"; "spotlight" adds typo-tolerant ranking, synonyms, usage boosts and a Top hit. */ searchMode?: LauncherSearchMode; /** Spotlight only: equivalent terms, both directions, e.g. `{ pf: ["provident fund"] }`. */ synonyms?: LauncherSynonyms; /** Filter chips in the header row (their own row below `md`), always shown, with live counts while a query is active; "all" is prepended unless listed. A chip other than All or Screens picked with an empty query lists that scope's items. */ scopes?: readonly LauncherScope[]; /** Controlled scope id; omit for uncontrolled ("all", reset on close). */ activeScope?: string; /** Called with the new scope id whenever the user picks a chip, controlled or not. */ onScopeChange?: (scope: string) => void; /** Up to 5 pill buttons at the top of the menu while the query is empty and the scope is All; each runs or links like an action. */ quickActions?: readonly LauncherSearchItem[]; /** Direct answers, matched against the query and shown above every result. */ answers?: readonly LauncherSearchItem[]; /** Rows per group before "Show N more"; default 5 in spotlight, unlimited in classic. */ groupLimit?: number; /** Test id on the drawer content. */ "data-testid"?: string; } /** Props for the star pin toggle used on launcher rows and reusable on a landing page; shows only while its row is hovered or focused. */ export interface PinToggleProps { /** Draws the star filled when true. */ isPinned: boolean; /** Accessible name and tooltip, e.g. "Pin Leave". */ label: string; /** Called when the star is pressed. */ onToggle: () => void; /** Tooltip text; defaults to `label`. */ title?: string; /** Star size in px; defaults to 13. */ size?: number; /** Pass -1 where the row itself is the tab stop. */ tabIndex?: number; } /** Props for the small "open in new tab" anchor beside a launcher row. */ export interface NewTabLinkProps { /** Link target, opened in a new tab. */ href: string; /** Accessible name, e.g. "Open Leave in a new tab". */ label: string; /** Visible tooltip text; defaults to `label`. */ tip?: string; /** Anchor-like component receiving `href`, `target`, `rel`; defaults to `"a"`. */ linkAs?: ElementType; /** Pass -1 where the row itself is the tab stop. */ tabIndex?: number; }