Help us improve
Share bugs, ideas, or general feedback.
From agency-slack
The window.desktop preload bridge in slack-desktop — architecture, namespaces, IPC patterns, and step-by-step guide for adding new methods.
npx claudepluginhub autisticaf/agency-slackHow this skill is triggered — by the user, by Claude, or both
Slash command
/agency-slack:desktop-preloadThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The preload bridge (`window.desktop`) is the contract between the Electron shell and the hosted webapp at `app.slack.com`. Changes here affect both repos.
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Provides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Share bugs, ideas, or general feedback.
The preload bridge (window.desktop) is the contract between the Electron shell and the hosted webapp at app.slack.com. Changes here affect both repos.
Two communication directions:
window.desktop.namespace.method(), preload translates to ipcRenderer.invoke() / ipcRenderer.send(), main process handles with ipcMain.handle() / ipcMain.on()webContents.send(IpcMainMessage.*), preload receives via ipcRenderer.on(), forwards to window.desktopDelegate.callback()Entry point: src/preload/bootstrap-util/initialize-main-preload.ts
getDesktopPreloadObject(store) builds the full namespace objectwindow.desktop (and legacy window.winssb)contextBridge.exposeInMainWorld('desktop', ...) exposes to isolated webapp contextwindow.desktop.setGlobalDelegate(delegate)Component windows (notifications, settings, etc.) get a smaller API via src/preload/component-preload-entry-point.ts.
window.desktopDefined in src/common/interfaces/preload-api/desktop-interface.ts:
| Namespace | Purpose |
|---|---|
accessibility | Screen reader support |
app | App lifecycle, version, preferences |
calls | Huddle/call integration |
clipboard | Read/write clipboard |
contextMenu | Right-click context menus |
contextMenus | Menu registration |
diagnostics | Logging, debug info |
dock | macOS dock badge/bounce |
downloads | File download management |
localFileSearch | OS file search |
menus | Application menu bar |
notice | Native notifications |
recentFiles | OS recent files list |
redux | Direct Redux store access |
search | Search index integration |
screen | Screen/display info |
callsScreenshare | Screen sharing for calls |
spellCheckerV3 | Spell checking |
stats | Telemetry/metrics |
teams | Multi-workspace management |
window | Window management |
workspaces | Workspace switching |
system | OS-level integration |
Plus windowChrome (Windows-only / debug) — not part of public DesktopInterface.
27 interface files in src/common/interfaces/preload-api/.
Defined in src/common/constants/ipc-channel-names.ts:
IpcRendererMessage — ~100+ channels for renderer-to-main (e.g., WRITE_STRING_TO_CLIPBOARD, GET_SCREEN_PREVIEW_THUMBNAILS)IpcMainMessage — ~12 channels for main-to-renderer (e.g., FORWARD_DEEP_LINK, START_SEARCH, RELOAD_DOCUMENT)The webapp registers a DesktopDelegate callback object. When the main process needs to notify the renderer, the preload forwards to the delegate:
// Preload listener (register-preload-handlers.ts)
ipcRenderer.on(IpcMainMessage.START_SEARCH, () => {
window.desktopDelegate?.startSearch();
});
DesktopDelegate interface (src/common/interfaces/preload-api/delegate.ts) defines ~25 callbacks: handleDeepLinkWithArgs, startSearch, closeCall, openDialog, focusNextWorkspace, reload, showUpdateBanner, etc.
Interface (src/common/interfaces/preload-api/clipboard.ts):
export interface ClipboardInterface {
writeString: (text: string) => Promise<void>;
writeImage: (base64EncodedPngOrJpg: string) => Promise<void>;
}
Preload implementation (src/preload/desktop-interface/clipboard.ts):
export const createClipboardInterface = (): ClipboardInterface => ({
writeString: (text) => ipcInvoke(IpcRendererMessage.WRITE_STRING_TO_CLIPBOARD, text),
writeImage: (base64) => ipcInvoke(IpcRendererMessage.WRITE_IMAGE_TO_CLIPBOARD, base64),
});
Main handler (registered in src/browser/register-main-process-handlers.ts):
ipcHandle(IpcRendererMessage.WRITE_STRING_TO_CLIPBOARD, (_, text) => {
clipboard.writeText(text);
});
Follow these steps in order:
src/common/constants/ipc-channel-names.ts — add to IpcRendererMessage (renderer->main) or IpcMainMessage (main->renderer).
Add to an existing interface in src/common/interfaces/preload-api/ or create a new file. If creating a new namespace, add it to DesktopInterface in desktop-interface.ts.
src/common/interfaces/ipc-renderer-interface-args.ts — map your new IpcRendererMessage to the parameter types.
src/browser/ipc-validator/ipc-renderer-interface.ts — add your channel with Joi validators for each argument. This protects against malicious IPC calls.
src/preload/desktop-interface/<namespace>.ts — call ipcInvoke (request/response) or ipcSend (fire-and-forget).
src/browser/register-main-process-handlers.ts — use ipcHandle(channel, handler) for invoke-style or ipcListen(channel, handler) for send-style. Both wrappers validate origin + arguments automatically.
ipcRenderer.on(IpcMainMessage.*) listener in src/preload/desktop-interface/register-preload-handlers.tsDesktopDelegate in src/common/interfaces/preload-api/delegate.tsUpdate @slack/desktop/is-api-available in the webapp (per the comment at line 149 of initialize-main-preload.ts).
validateEventOrigin in src/browser/ipc-from-renderer.ts checks that the IPC sender is from a valid Slack URL origin| File | Purpose |
|---|---|
src/preload/bootstrap-util/initialize-main-preload.ts | Bridge initialization (main windows) |
src/preload/component-preload-entry-point.ts | Bridge initialization (component windows) |
src/preload/desktop-interface/get-desktop-preload-object.ts | Desktop object factory |
src/common/interfaces/preload-api/desktop-interface.ts | Master DesktopInterface definition |
src/common/interfaces/preload-api/*.ts | All 27 namespace interface files |
src/common/interfaces/preload-api/delegate.ts | DesktopDelegate (main->renderer callbacks) |
src/preload/types/global-augment.ts | Global Window type augmentation |
src/common/constants/ipc-channel-names.ts | IPC channel name enums |
src/common/interfaces/ipc-renderer-interface-args.ts | IPC argument type mappings |
src/preload/ipc-to-main.ts | Typed ipcInvoke/ipcSend helpers |
src/preload/desktop-interface/register-preload-handlers.ts | Main->renderer listener registration |
src/browser/register-main-process-handlers.ts | Main-process handler registration |
src/browser/ipc-from-renderer.ts | ipcHandle/ipcListen wrappers + origin validation |
src/browser/ipc-validator/ipc-renderer-interface.ts | Joi argument validators |