The desktop package has two bridges: tray and menu bar icons with menus, badges, notifications and animation, and a quick entry prompt that opens over other applications from a global hotkey. Both hand what the user does back to the web app.
dotnet add package Shiny.AppDeviceBridge.Desktop
builder. UseAppDeviceBridge (
bridge => bridge. AddTrayIconBridge (). AddQuickEntryBridge (),
Client
C#
TypeScript
ITrayBridge
AddTrayBridgeClient()
TrayBridge
IQuickEntryBridge
AddQuickEntryBridgeClient()
QuickEntryBridge
The system tray on Windows, the menu bar on
macOS, the status notifier area on Linux — desktop only, 501 elsewhere.
Topic
Details
Naming an icon
use PUT /_bridge/tray/main rather than POST /_bridge/tray. The first call creates the icon and later ones adopt it, so a page reload or an applied update doesn’t stack up a second icon. A PUT changes only the properties it sends; "" clears tooltip, title or badge.
Images
either { "root": "data", "path": "icons/tray.png" } — the same file roots the files bridge takes, so the page can’t point the tray at anything it couldn’t already read — or { "data": "…" } holding base64, with or without a data: URI prefix, which is how a page ships an icon it drew itself. Set "templateImage": true for a black-with-alpha image macOS and Linux tint for light and dark menu bars.
Menus
each entry is Item, Check, Separator or Submenu, and its id is what comes back on tray.menu; omit it and one is assigned. Ids must be unique across the whole menu.
Clicks reach the web app either way
tray.click and tray.menu go out as events and as calls the page handles when it is open and background.js handles when it is not — which is the case a tray menu exists for. tray.click isn’t raised on Linux, where the app indicator handles clicks itself and opens the menu on its own (so menu/show is a no-op there).
Lifetime
icons outlive the page and are removed when the app shuts down, or by DELETE /_bridge/tray/{id}. MaxIcons (4) caps how many exist at once.
await new TrayBridge (). put ( "main" , {
icon: { root: "data" , path: "icons/tray.png" },
{ id: "open" , label: "Open" },
{ id: "sync" , type: "Check" , label: "Sync" , checked: true }
Shiny’s quick entry prompt as a borderless,
always-on-top window that opens over other applications, in the style of Spotlight or a desktop assistant. The page
configures it, hears what’s submitted and writes the answer back. Desktop only: macOS (AppKit and Catalyst), Windows and
Linux; 501 elsewhere.
bridge. AddQuickEntryBridge (
o => o.HotKey = "Ctrl+Alt+Space" , // toggles the window from anywhere
quickEntry => quickEntry.ScreenGlow = ScreenGlowTrigger.WhileBusy
Topic
Details
Opening it
the global hotkey, POST quickentry/show or toggle, or a tray click that calls one. PUT quickentry/options changes the hotkey ("" removes it), size, placement and dismissal while the app runs; hotKeyRegistered: false in the status means another application already owns the combination.
The prompt
PUT quickentry/prompt sets the placeholder, suggestions (each with an optional value that comes back when it’s chosen), isBusy with busyText, and response, the text shown under the entry. Only what you send changes; "" clears the response and [] the suggestions. What you set survives a window that rebuilds its prompt.
Answering
quickentry.submitted goes out as an event and as a call the page handles when it’s open and background.js handles when it isn’t, which is the usual case for a window summoned over other applications. Set isBusy, do the work, then set response.
The glow
glow/show, glow/hide and glow/pulse drive the colour wash around the screen’s edge; the glow option lights it while the window is open or while the prompt is busy.
Custom content
if the app replaces the prompt through QuickEntryOptions.ContentFactory, the prompt routes answer 409 and the window routes still work.
const quickEntry = new QuickEntryBridge ();
await quickEntry. setPrompt ({ placeholder: "Ask Field App…" , suggestions: [{ text: "Sync now" , value: "sync" }] });
quickEntry. onSubmitted ( async ({ text , suggestion }) => {
await quickEntry. setPrompt ({ isBusy: true , busyText: "Thinking…" });
await quickEntry. setPrompt ({ isBusy: false , response: await answer (text, suggestion?.value) });
Tray /_bridge/tray12 requests · 2 events System tray and menu bar icons on Windows, macOS and Linux: create and update them, give them menus, show notifications, animate them, and hear clicks and menu choices. Android and iOS fail with 501. Ids are 1–64 letters, digits, ., _ or -.
GET /_bridge/ trayWhether tray icons are supported here, the limit, and the icons this page created. POST /_bridge/ trayCreates an icon, assigning an id when id is null. DELETE /_bridge/ trayRemoves every icon. GET /_bridge/ tray/{id} One icon. PUT /_bridge/ tray/{id} Creates the icon, or updates it: only properties that are set change, and an empty string clears tooltip, title or badge. DELETE /_bridge/ tray/{id} Removes an icon. PUT /_bridge/ tray/{id} /menuReplaces an icon's menu. DELETE /_bridge/ tray/{id} /menuRemoves an icon's menu. POST /_bridge/ tray/{id} /menu/showOpens an icon's menu, as though it had been clicked. POST /_bridge/ tray/{id} /notificationShows a notification from the icon. PUT /_bridge/ tray/{id} /animationCycles the icon through two or more frames. DELETE /_bridge/ tray/{id} /animationStops animating and restores the icon. Whether tray icons are supported here, the limit, and the icons this page created.
C# ITrayBridge.GetStatusAsyncTS TrayBridge.getStatus
Response 200 TrayStatus TrayStatus · 3 fields Creates an icon, assigning an id when id is null. Fails with 429 past the limit.
C# ITrayBridge.CreateAsyncTS TrayBridge.create
Body application/json · TrayIconInput TrayIconInput · 8 fields idstring | null tooltipstring | null titlestring | null visibleboolean | null templateImageboolean | null On macOS, let the menu bar tint the icon; use black with alpha. badgestring | null A short count or label drawn over the icon. iconTrayImage | null menuTrayMenuInput | null
Response 200 TrayIconInfo TrayIconInfo · 9 fields idstring tooltipstring | null titlestring | null visibleboolean templateImageboolean badgestring | null hasIconboolean animatingboolean menuTrayMenuItemInfo []
Errors 429 Too manyRemoves every icon.
C# ITrayBridge.RemoveAllAsyncTS TrayBridge.removeAll
Response 204 No contentOne icon. Fails with 404 when there is no such icon.
C# ITrayBridge.GetAsyncTS TrayBridge.get
Parameter In Type idpath string required
Response 200 TrayIconInfo TrayIconInfo · 9 fields idstring tooltipstring | null titlestring | null visibleboolean templateImageboolean badgestring | null hasIconboolean animatingboolean menuTrayMenuItemInfo []
Errors 404 Not foundCreates the icon, or updates it: only properties that are set change, and an empty string clears tooltip, title or badge. PUT by a fixed id lets a reloaded page adopt its icon rather than add another.
C# ITrayBridge.PutAsyncTS TrayBridge.put
Parameter In Type idpath string required
Body application/json · TrayIconInput TrayIconInput · 8 fields idstring | null tooltipstring | null titlestring | null visibleboolean | null templateImageboolean | null On macOS, let the menu bar tint the icon; use black with alpha. badgestring | null A short count or label drawn over the icon. iconTrayImage | null menuTrayMenuInput | null
Response 200 TrayIconInfo TrayIconInfo · 9 fields idstring tooltipstring | null titlestring | null visibleboolean templateImageboolean badgestring | null hasIconboolean animatingboolean menuTrayMenuItemInfo []
DELETE /_bridge/ tray/{id} # Removes an icon.
C# ITrayBridge.RemoveAsyncTS TrayBridge.remove
Parameter In Type idpath string required
Response 204 No contentPOST /_bridge/ tray/{id} /notification# Shows a notification from the icon.
C# ITrayBridge.NotifyAsyncTS TrayBridge.notify
Parameter In Type idpath string required
Body application/json · TrayNotification TrayNotification · 2 fields titlestring | null messagestring | null
Response 204 No contentPUT /_bridge/ tray/{id} /animation# Cycles the icon through two or more frames.
C# ITrayBridge.StartAnimationAsyncTS TrayBridge.startAnimation
Parameter In Type idpath string required
Body application/json · TrayAnimation TrayAnimation · 2 fields framesTrayImage [] | null 2–24 images. intervalMsnumber Time per frame; at least 50 ms.
Response 200 TrayIconInfo TrayIconInfo · 9 fields idstring tooltipstring | null titlestring | null visibleboolean templateImageboolean badgestring | null hasIconboolean animatingboolean menuTrayMenuItemInfo []
DELETE /_bridge/ tray/{id} /animation# Stops animating and restores the icon.
C# ITrayBridge.StopAnimationAsyncTS TrayBridge.stopAnimation
Parameter In Type idpath string required
Response 204 No contentQuickEntry /_bridge/quickentry11 requests · 6 events Quick entry: a prompt window that opens over other applications — from a global hotkey, a tray click or the page — in the style of Spotlight or a desktop assistant. The page configures the prompt, hears what is submitted, and writes the response back, and it keeps working with the app's own window closed: submissions also go to background.js. Windows, macOS and Linux; Android and iOS fail with 501.
GET /_bridge/ quickentryWhether quick entry works here, and what it is doing. PUT /_bridge/ quickentry/optionsChanges how the window opens. POST /_bridge/ quickentry/showOpens the window. POST /_bridge/ quickentry/hideCloses the window. POST /_bridge/ quickentry/toggleOpens the window if it is closed, and closes it if it is open. GET /_bridge/ quickentry/promptThe prompt. PUT /_bridge/ quickentry/promptChanges the prompt: its placeholder, suggestions, busy state and response. POST /_bridge/ quickentry/prompt/resetClears the entry, the response and the busy state. POST /_bridge/ quickentry/glow/showLights the glow around the screen's edge until hideGlow. POST /_bridge/ quickentry/glow/hidePOST /_bridge/ quickentry/glow/pulseLights the glow for a while, answering once it has gone out. Whether quick entry works here, and what it is doing.
C# IQuickEntryBridge.GetStatusAsyncTS QuickEntryBridge.getStatus
Response 200 QuickEntryStatus QuickEntryStatus · 8 fields supportedboolean False on Android and iOS, where every other call fails with 501. presentationQuickEntryPresentation How the window will actually open, with Auto resolved. isOpenboolean glowSupportedboolean glowVisibleboolean hotKeystring | null The global shortcut that toggles the window, when one is set. hotKeyRegisteredboolean False when the shortcut is set but another application already owns it. hasPromptboolean False when the app replaced the prompt with content of its own, which the prompt calls cannot drive.
PUT /_bridge/ quickentry/options# Changes how the window opens. A hotkey another application owns is reported, not an error.
C# IQuickEntryBridge.ConfigureAsyncTS QuickEntryBridge.configure
Body application/json · QuickEntryOptionsInput QuickEntryOptionsInput · 14 fields presentationQuickEntryPresentation | null hotKeystring | null A global shortcut such as Cmd+Opt+Space or Ctrl+Alt+Space; an empty string removes it. widthnumber | null Device-independent pixels, 200 to 4000. collapsedHeightnumber | null The height with nothing below the entry row. maxHeightnumber | null The window grows to fit suggestions and a response, up to this. placementQuickEntryPlacement | null topMarginRationumber | null For TopCenter: the top edge as a fraction of the screen, 0 to 1. bottomMarginRationumber | null For BottomCenter: the gap below as a fraction of the screen, 0 to 1. xnumber | null For Manual: screen coordinates from the top left. ynumber | null dismissOnFocusLostboolean | null Close when another application takes focus. dismissOnEscapeboolean | null activateOnShowboolean | null Take keyboard focus when opening. Off for a window that should not steal typing. glowQuickEntryGlowTrigger | null What lights the glow around the screen's edge.
Response 200 QuickEntryStatus QuickEntryStatus · 8 fields supportedboolean False on Android and iOS, where every other call fails with 501. presentationQuickEntryPresentation How the window will actually open, with Auto resolved. isOpenboolean glowSupportedboolean glowVisibleboolean hotKeystring | null The global shortcut that toggles the window, when one is set. hotKeyRegisteredboolean False when the shortcut is set but another application already owns it. hasPromptboolean False when the app replaced the prompt with content of its own, which the prompt calls cannot drive.
POST /_bridge/ quickentry/show# Opens the window.
C# IQuickEntryBridge.ShowAsyncTS QuickEntryBridge.show
Response 200 QuickEntryStatus QuickEntryStatus · 8 fields supportedboolean False on Android and iOS, where every other call fails with 501. presentationQuickEntryPresentation How the window will actually open, with Auto resolved. isOpenboolean glowSupportedboolean glowVisibleboolean hotKeystring | null The global shortcut that toggles the window, when one is set. hotKeyRegisteredboolean False when the shortcut is set but another application already owns it. hasPromptboolean False when the app replaced the prompt with content of its own, which the prompt calls cannot drive.
POST /_bridge/ quickentry/hide# Closes the window.
C# IQuickEntryBridge.HideAsyncTS QuickEntryBridge.hide
Response 200 QuickEntryStatus QuickEntryStatus · 8 fields supportedboolean False on Android and iOS, where every other call fails with 501. presentationQuickEntryPresentation How the window will actually open, with Auto resolved. isOpenboolean glowSupportedboolean glowVisibleboolean hotKeystring | null The global shortcut that toggles the window, when one is set. hotKeyRegisteredboolean False when the shortcut is set but another application already owns it. hasPromptboolean False when the app replaced the prompt with content of its own, which the prompt calls cannot drive.
POST /_bridge/ quickentry/toggle# Opens the window if it is closed, and closes it if it is open.
C# IQuickEntryBridge.ToggleAsyncTS QuickEntryBridge.toggle
Response 200 QuickEntryStatus QuickEntryStatus · 8 fields supportedboolean False on Android and iOS, where every other call fails with 501. presentationQuickEntryPresentation How the window will actually open, with Auto resolved. isOpenboolean glowSupportedboolean glowVisibleboolean hotKeystring | null The global shortcut that toggles the window, when one is set. hotKeyRegisteredboolean False when the shortcut is set but another application already owns it. hasPromptboolean False when the app replaced the prompt with content of its own, which the prompt calls cannot drive.
GET /_bridge/ quickentry/prompt# The prompt. Fails with 409 when the app replaced the prompt with content of its own.
C# IQuickEntryBridge.GetPromptAsyncTS QuickEntryBridge.getPrompt
Response 200 QuickEntryPrompt QuickEntryPrompt · 9 fields textstring What is typed in the entry right now. placeholderstring isBusyboolean Shows busyText and an animated orb in place of the entry, while the page works. busyTextstring responsestring | null Text shown under the entry — the answer to what was submitted. suggestionsQuickEntrySuggestion []showMicrophoneboolean Shows a microphone button, which raises quickentry.microphone. showSubmitButtonboolean clearOnSubmitboolean Empty the entry once a prompt is submitted.
Errors 409 ConflictPUT /_bridge/ quickentry/prompt# Changes the prompt: its placeholder, suggestions, busy state and response.
C# IQuickEntryBridge.SetPromptAsyncTS QuickEntryBridge.setPrompt
Body application/json · QuickEntryPromptInput QuickEntryPromptInput · 9 fields textstring | null placeholderstring | null isBusyboolean | null busyTextstring | null responsestring | null suggestionsQuickEntrySuggestion [] | null showMicrophoneboolean | null showSubmitButtonboolean | null clearOnSubmitboolean | null
Response 200 QuickEntryPrompt QuickEntryPrompt · 9 fields textstring What is typed in the entry right now. placeholderstring isBusyboolean Shows busyText and an animated orb in place of the entry, while the page works. busyTextstring responsestring | null Text shown under the entry — the answer to what was submitted. suggestionsQuickEntrySuggestion []showMicrophoneboolean Shows a microphone button, which raises quickentry.microphone. showSubmitButtonboolean clearOnSubmitboolean Empty the entry once a prompt is submitted.
POST /_bridge/ quickentry/prompt/reset# Clears the entry, the response and the busy state.
C# IQuickEntryBridge.ResetPromptAsyncTS QuickEntryBridge.resetPrompt
Response 200 QuickEntryPrompt QuickEntryPrompt · 9 fields textstring What is typed in the entry right now. placeholderstring isBusyboolean Shows busyText and an animated orb in place of the entry, while the page works. busyTextstring responsestring | null Text shown under the entry — the answer to what was submitted. suggestionsQuickEntrySuggestion []showMicrophoneboolean Shows a microphone button, which raises quickentry.microphone. showSubmitButtonboolean clearOnSubmitboolean Empty the entry once a prompt is submitted.
POST /_bridge/ quickentry/glow/show# Lights the glow around the screen's edge until hideGlow.
C# IQuickEntryBridge.ShowGlowAsyncTS QuickEntryBridge.showGlow
Response 204 No contentPOST /_bridge/ quickentry/glow/hide#
C# IQuickEntryBridge.HideGlowAsyncTS QuickEntryBridge.hideGlow
Response 204 No contentPOST /_bridge/ quickentry/glow/pulse# Lights the glow for a while, answering once it has gone out.
C# IQuickEntryBridge.PulseGlowAsyncTS QuickEntryBridge.pulseGlow
Body application/json · QuickEntryPulse QuickEntryPulse · 1 field durationMsnumber How long the glow stays lit: 100 ms to 30 s.
Response 204 No content
An icon was clicked. Also delivered to background.js when no page is listening.
C# ITrayBridge.OnClickAsyncTS TrayBridge.onClick
Payload TrayClick idstring The icon clicked. buttonTrayClickButton xnumber Screen coordinates, where the platform reports them. ynumber
EVENT quickentry.submitted# The user submitted a prompt. Also delivered to background.js when no page is listening.
C# IQuickEntryBridge.OnSubmittedAsyncTS QuickEntryBridge.onSubmitted
Payload QuickEntrySubmission textstring suggestionQuickEntrySuggestion | null The suggestion chosen to submit it; null when it was typed.
EVENT quickentry.suggestion# The user chose a suggestion, just before it is submitted.
C# IQuickEntryBridge.OnSuggestionAsyncTS QuickEntryBridge.onSuggestion
Payload QuickEntrySubmission textstring suggestionQuickEntrySuggestion | null The suggestion chosen to submit it; null when it was typed.
EVENT quickentry.cancelled# The user pressed Escape in the prompt.
C# IQuickEntryBridge.OnCancelledAsyncTS QuickEntryBridge.onCancelled
Payload QuickEntryPromptEvent EVENT quickentry.microphone# The user pressed the microphone button.
C# IQuickEntryBridge.OnMicrophoneAsyncTS QuickEntryBridge.onMicrophone
Payload QuickEntryPromptEvent The window opened, however it was opened.
C# IQuickEntryBridge.OnOpenedAsyncTS QuickEntryBridge.onOpened
Payload QuickEntryStatus supportedboolean False on Android and iOS, where every other call fails with 501. presentationQuickEntryPresentation How the window will actually open, with Auto resolved. isOpenboolean glowSupportedboolean glowVisibleboolean hotKeystring | null The global shortcut that toggles the window, when one is set. hotKeyRegisteredboolean False when the shortcut is set but another application already owns it. hasPromptboolean False when the app replaced the prompt with content of its own, which the prompt calls cannot drive.
The window closed, however it was dismissed.
C# IQuickEntryBridge.OnClosedAsyncTS QuickEntryBridge.onClosed
Payload QuickEntryStatus supportedboolean False on Android and iOS, where every other call fails with 501. presentationQuickEntryPresentation How the window will actually open, with Auto resolved. isOpenboolean glowSupportedboolean glowVisibleboolean hotKeystring | null The global shortcut that toggles the window, when one is set. hotKeyRegisteredboolean False when the shortcut is set but another application already owns it. hasPromptboolean False when the app replaced the prompt with content of its own, which the prompt calls cannot drive.
JSON property names are camelCase and enums travel as their names. A ? marks a property the payload may omit;| null one that may be null.
idstring tooltipstring | null titlestring | null visibleboolean templateImageboolean badgestring | null hasIconboolean animatingboolean menuTrayMenuItemInfo []
An icon to create, or the changes to one. Properties left null are left as they are.
id?string | null tooltip?string | null title?string | null visible?boolean | null templateImage?boolean | null On macOS, let the menu bar tint the icon; use black with alpha. badge?string | null A short count or label drawn over the icon. icon?TrayImage | null menu?TrayMenuInput | null
An image: a file the page could already read through the files bridge (root and path), or base64 bytes it built itself (data, with or without a data: URI prefix).
root?string | null path?string | null data?string | null
title?string | null message?string | null
frames?TrayImage [] | null 2–24 images. intervalMs?number Time per frame; at least 50 ms.
idstring The icon clicked. buttonTrayClickButton xnumber Screen coordinates, where the platform reports them. ynumber
"Primary""Secondary""Double"
What quick entry can do here and what it is doing.
supportedboolean False on Android and iOS, where every other call fails with 501. presentationQuickEntryPresentation How the window will actually open, with Auto resolved. isOpenboolean glowSupportedboolean glowVisibleboolean hotKeystring | null The global shortcut that toggles the window, when one is set. hotKeyRegisteredboolean False when the shortcut is set but another application already owns it. hasPromptboolean False when the app replaced the prompt with content of its own, which the prompt calls cannot drive.
QuickEntryPresentationenum # "Auto""InApp""Desktop"
QuickEntryOptionsInputobject # Changes to how the window opens. Properties left null are left as they are.
presentation?QuickEntryPresentation | null hotKey?string | null A global shortcut such as Cmd+Opt+Space or Ctrl+Alt+Space; an empty string removes it. width?number | null Device-independent pixels, 200 to 4000. collapsedHeight?number | null The height with nothing below the entry row. maxHeight?number | null The window grows to fit suggestions and a response, up to this. placement?QuickEntryPlacement | null topMarginRatio?number | null For TopCenter: the top edge as a fraction of the screen, 0 to 1. bottomMarginRatio?number | null For BottomCenter: the gap below as a fraction of the screen, 0 to 1. x?number | null For Manual: screen coordinates from the top left. y?number | null dismissOnFocusLost?boolean | null Close when another application takes focus. dismissOnEscape?boolean | null activateOnShow?boolean | null Take keyboard focus when opening. Off for a window that should not steal typing. glow?QuickEntryGlowTrigger | null What lights the glow around the screen's edge.
"TopCenter""BottomCenter""Center""NearCursor""Manual"
QuickEntryGlowTriggerenum # "None""WhileOpen""WhileBusy"
The prompt in the window.
textstring What is typed in the entry right now. placeholderstring isBusyboolean Shows busyText and an animated orb in place of the entry, while the page works. busyTextstring responsestring | null Text shown under the entry — the answer to what was submitted. suggestionsQuickEntrySuggestion []showMicrophoneboolean Shows a microphone button, which raises quickentry.microphone. showSubmitButtonboolean clearOnSubmitboolean Empty the entry once a prompt is submitted.
QuickEntrySuggestionobject # A row under the entry.
textstring What is written into the entry when it is chosen. descriptionstring | null A second line. glyphstring | null A leading emoji or icon-font character. valuestring | null Anything the page wants back when the suggestion is chosen.
QuickEntryPromptInputobject # Changes to the prompt. Properties left null are left as they are; an empty response removes the response and an empty suggestions list removes the suggestions. Everything but text is kept across a window that rebuilds its content on every open.
text?string | null placeholder?string | null isBusy?boolean | null busyText?string | null response?string | null suggestions?QuickEntrySuggestion [] | null showMicrophone?boolean | null showSubmitButton?boolean | null clearOnSubmit?boolean | null
durationMsnumber How long the glow stays lit: 100 ms to 30 s.
QuickEntrySubmissionobject # A prompt the user submitted.
textstring suggestionQuickEntrySuggestion | null The suggestion chosen to submit it; null when it was typed.
QuickEntryPromptEventobject # The entry's text when the user cancelled or pressed the microphone.
These routes sit under AppDeviceBridgePolicies.Bridges like every other bridge: callers on this device by default.
See Security .