Views
Views are React components that render inside the launcher when a gadget’s
search() returns a CustomUi or
InlineUi response. There are two kinds:
- Custom views replace the entire result list with a gadget-controlled UI. The Emoji Picker’s grid and the Calculator’s history panel are custom views. Only available when the gadget is the exclusive responder (via prefix routing).
- Inline views render above the result list while the results remain visible below. Only one gadget can claim the inline slot per search. The Calculator’s live result display is an inline view.
Component contract
Section titled “Component contract”GadgetViewProps
Section titled “GadgetViewProps”Custom view components receive:
interface GadgetViewProps { results: SourcedEntry[]; data?: unknown; query: string; matchedPrefix: string;}| Prop | Description |
|---|---|
results |
The ScoredEntry list from the ViewResponse. Empty for full-takeover views. |
data |
The opaque JSON payload from ViewResponse.data, already parsed. |
query |
The current search query with the matched prefix already stripped. |
matchedPrefix |
The prefix that triggered this view (e.g. "=" for the calculator). |
InlineViewProps
Section titled “InlineViewProps”Inline view components receive:
interface InlineViewProps { data: unknown; query: string; matchedPrefix: string; selected: boolean;}| Prop | Description |
|---|---|
data |
The opaque JSON payload from ViewResponse.data, already parsed. |
query |
The current search query with the matched prefix already stripped. |
matchedPrefix |
The prefix that triggered this view. |
selected |
Whether the inline view currently has focus (index 0 in the result list). |
Context hooks
Section titled “Context hooks”Views access gadget state and launcher actions through hooks imported from
@torchsnap/gadget-sdk/hooks. These hooks work because the host wraps every
view in a GadgetContextProvider.
useGadgetInfo
Section titled “useGadgetInfo”function useGadgetInfo(): { id: string; enabled: boolean }Returns the gadget’s ID and enabled state. The ID matches the [gadget] id from
the manifest.
useGadgetRuntime
Section titled “useGadgetRuntime”function useGadgetRuntime(): { sendMessage: GadgetSendMessage; logger: Logger;}Provides the messaging interface for frontend-to-backend RPC and a logger for the gadget.
useLauncher
Section titled “useLauncher”function useLauncher(): LauncherActionsReturns actions for controlling the launcher from within a view:
| Action | Description |
|---|---|
goBack() |
Return to the normal result list, dismissing the custom view. |
dismiss() |
Close the launcher entirely. |
onExecute(entryId, actionId) |
Trigger execution of a result entry with the given action. |
onFooterChange(state) |
Replace the launcher’s footer bar with custom content. |
setDisplayQuery(query) |
Update the search bar text without triggering a new search. |
mouseActiveRef |
A ref indicating whether the mouse is currently active (for keyboard/mouse interaction management). |
Footer management
Section titled “Footer management”By default, the launcher shows an action bar in the footer derived from the
selected entry’s actions. A custom view can override this by calling
onFooterChange(state) with a FooterState object describing the custom
footer content. The host resets the footer when the view unmounts.
For inline views, the custom footer is only shown when the inline slot is selected (focused at index 0).
Utilities
Section titled “Utilities”highlightText
Section titled “highlightText”import { highlightText } from "@torchsnap/gadget-sdk/utils";
function highlightText( text: string, positions: readonly number[], highlightClassName?: string,): ReactNode[]Renders text with highlighted characters at the given UTF-16 code-unit offsets.
The positions array matches the titlePositions and subtitlePositions fields
on SourcedEntry. The default highlight class is "text-accent font-semibold".
Useful when your custom view renders its own result list and needs to show the same match highlighting as the host’s built-in list.
useWindowedList
Section titled “useWindowedList”import { useWindowedList } from "@torchsnap/gadget-sdk/hooks";
function useWindowedList(params: { selectedIndex: number; setSelectedIndex: (index: number) => void; resultCount: number; pageSize: number;}): { windowStart: number; wheelRef: (el: HTMLDivElement | null) => void;}Manages a virtual scrolling window for custom views that render long lists. Returns the current window start index and a ref callback for the scroll container that handles mouse wheel events.
Keybindings
Section titled “Keybindings”Custom views can register keyboard shortcuts that override or extend the launcher’s default bindings while the view is mounted:
import { useKeyBindings, LAYER } from "@torchsnap/gadget-sdk/keybindings";
useKeyBindings([ { id: "clear-history", layer: LAYER.COMPONENT, handler: () => clearHistory(), keybindings: [{ combo: { modifiers: ["Meta"], key: "Backspace" } }], },]);Bindings are organized in layers. Higher layers take precedence:
| Layer | Constant | Used by |
|---|---|---|
| 0 | LAYER.APP |
Host application defaults |
| 1 | LAYER.VIEW |
View-level bindings |
| 2 | LAYER.COMPONENT |
Component-level overrides |
Set active: false on a binding to make it consume the key event without
executing the handler (useful for suppressing a host binding without replacing
it).
Manifest registration
Section titled “Manifest registration”Views are registered in the manifest’s [frontend] section. The keys are view
names your search() uses in ViewResponse.view. The values are JavaScript
named exports from the launcher bundle:
[frontend]launcher-bundle = "frontend/dist/launcher.js"
[frontend.views]grid = "EmojiGrid"detail = "EmojiDetail"
[frontend.inline-views]result = "CalculatorInline"Your launcher entry point must export the matching names:
export { EmojiGrid } from "./views/EmojiGrid";export { EmojiDetail } from "./views/EmojiDetail";export { CalculatorInline } from "./views/CalculatorInline";