-
Notifications
You must be signed in to change notification settings - Fork 41
example desktop host
Experimental — see Native Desktop Host.
When the user is running the OpenStation Desktop app, every window's ⋯ menu offers "Send to your Mac". Picking it takes the window out of the OpenStation desk and gives it to the real one.
Your plugin might care: a window whose content polls could stand down while the shell is not the thing showing it, an analytics integration might want to record it, a companion widget might want to show where things went.
wp.os.electron is published by the Electron Adapter extension and
is simply absent in a browser — there is no always-present stub to
branch on. So:
wp.os.ready( () => {
const electron = wp.os.electron;
if ( ! electron?.isAvailable() ) {
return; // Plain browser, or the adapter is not installed.
}
const info = electron.getInfo();
console.log( `Hosted by OpenStation Desktop ${ info.appVersion } on ${ info.osLabel }` );
} );document.addEventListener( 'os-desktop-host-freed', ( event ) => {
if ( 'my-plugin-dashboard' === event.detail.windowId ) {
// It is out on the real desktop now — its content is live in a
// native window, so anything the shell was doing on its behalf
// (a badge poller, a preview refresh) can stand down.
stopPolling();
}
} );
document.addEventListener( 'os-desktop-host-docked', ( event ) => {
if ( 'my-plugin-dashboard' === event.detail.windowId ) {
startPolling();
}
} );Both fire regardless of who initiated the change — the ⋯ menu, your
own call to free(), or the user simply closing the native window.
That is deliberate: "freed" is one fact with two writers, and both land
on the same event.
An "Open in its own window" button in your plugin's UI:
async function popOut( windowId ) {
const electron = wp.os.electron;
if ( ! electron?.isAvailable() ) {
// No native host. Fall back to the browser affordance the ⋯
// menu already offers: open the page in a normal browser tab.
wp.os.windowManager.getById( windowId )?.detach();
return;
}
if ( electron.isFreed( windowId ) ) {
return; // Already out there — free() would just focus it.
}
await electron.free( windowId );
}Do not hard-code "Mac". The app reports its own OS name, and a platform added to the app should not need your plugin republished:
button.textContent = wp.os.electron.getSendLabel(); // "Send to your Windows PC"A freed window loads either the chromeless admin page or the shell in solo mode. Either way, code running there can tell:
if ( wp.os.electron?.isFreedWindow() ) {
// This page IS a native window. It has an OS frame, so hide any
// "open in new window" affordance of your own — it is already one.
myPopOutButton.hidden = true;
}Restrict native hosts to editors and above:
add_filter(
'openstation_electron_enabled',
function ( $enabled, $user_id ) {
return user_can( $user_id, 'edit_others_posts' );
},
10,
2
);Or widen the liveness pulse on constrained hosting — the app re-reads the interval from every response, so this lands within one beat with no client update:
add_filter( 'openstation_electron_heartbeat_interval', fn() => 300 );Anything that would surface a freed window inside the shell — a dock
click, the window switcher, your own wp.os.openWindow() call — raises
the native window instead. You never need to check isFreed()
before opening a window; the adapter enforces it on the framework's own
lifecycle hooks.
This wiki is generated from the docs/ directory — edits made here are overwritten by the next sync.
To change a page, open a pull request against docs/.
Guides
- Development guide
- Releasing openstation
- Agents security model
- API Index
- Architecture
- Bridge protocol — wiring overview
- <os-*> component reference
- Native Desktop Host — Experimental
- Desktop themes
- Dock customization — two registries, one mental model
- The event-driven framework
- Files on the Desktop
- Folder sharing
- Getting Started
- Hooks Reference
- Icons
- JavaScript Reference
- The Living Tree — algorithm definition
- Mio
- Native Windows & Framework Interop
- Plugin compatibility layer
- Progressive Web App (PWA)
- Station Home
- Using openstation from your own plugin
Migration notes
- Migration: built-in activity channels move to the os/ namespace
- Migration: window, wallpaper and widget bundles load on demand
- Migration — the navigation model
- Migration: a native window's tabs move to the window chrome
All examples
- AI Agents — extend and invoke from a plugin
- wp.os.ai.ask() — programmatic AI Copilot
- Tune the AI model config
- Custom arrange-menu action
- Open a child window its owner can't cover
- Style a specific admin page inside the iframe
- Code Blue — register your plugin's log file
- Open a file in the Code editor (deep-link from any window)
- Connect to a window — title-bar button + iframe pub/sub
- Content changes — live-refresh every window listing your type
- Custom window chrome (Experimental)
- Register a custom unfocused-window effect
- Example: render a data table
- Real file storage — react to uploads, gate policy, share from PHP
- React to a window being set free onto the real desktop
- Cross-window devtools — instrumentation primitives
- Add a dock item with a badge
- Decorate the dock without forking the renderer
- Replace the dock rail entirely
- Retune the Drafts widget's AI writing assistant
- Gate OpenStation by role
- Iframe-initiated window opens
- Build a feed reader without the bookkeeping
- Inject data into openStationConfig
- Render a list without losing clicks — renderKeyedList()
- Example: layout primitives (body → panel → row → col)
- Use <os-*> components from a plugin that ships as a zip
- Restyle and drive Mio
- Add an action that works on a whole selection
- WP Explorer — custom post types and their folder
- Add an action button to a WP Explorer preview pane
- Example: native Posts window
- Example: native window with tabs
- Native windows
- Customize note → post conversion
- Send a notification
- OAuth relay — connect to an external service
- OS-file drop
- <os-flyout> — window-scoped sliding card
- Plugins window — extras
- Track who's around — wp.os.presence
- Example: progress bar
- PWA install — surface your own button
- React to window events
- Example: extend the Trash
- Register a slash-command
- Register a desktop theme from a plugin
- Register a game
- Example: register a desktop icon (Jorvy)
- Register a wallpaper
- Register a widget
- Related entities — extend the title bar's "Related" menu
- The native-window render ctx
- Programmatic folder sharing
- Share state across multi-bundle plugins — wp.os.createSharedStore()
- Example: loading spinner
- Add an opt-in card to Station Home
- Accept drops on your desktop icon
- Give a tile two icons, one per state
- Add a row to a window's ⋯ menu
- Example: window activity & the status ring
- Window controls
- Subscribe to window lifecycle events
- Window links — relate windows and restyle the ties (Experimental)
- Window loading state — spinner overlay & ready signal
- Show a banner at the top of a window
- Pulse a window's icon — Window.requestAttention()
- Register a custom window reveal
- Window slots
- Window themes
- Native window with bundle-bound config