-
Notifications
You must be signed in to change notification settings - Fork 41
example window with config
Status: Stable
Most non-trivial native windows need session-bound config — REST URLs, an auth nonce, capability flags. This page shows the recommended way to ship that config to your bundle so it lands reliably on both the eager and lazy load paths.
A native window's script loads the first time the window opens (and on
mid-session plugin activation, for a window that opted into
preload_script), by appending a raw <script src="…"> tag to the
document head. That bypasses wp_print_scripts() entirely, which means data
attached to the handle via wp_localize_script /
wp_add_inline_script / wp_set_script_translations would be silently
dropped on the lazy path. The shell harvests that data into
the payload and re-injects it inline alongside the lazy <script> tag,
preserving the standard WordPress contract — but the 'config' arg
below is the discoverable, supported way to ship config and is the one
we recommend for new windows.
<?php
add_action( 'init', function () {
wp_register_script(
'my-plugin-cron',
plugin_dir_url( __FILE__ ) . 'assets/js/cron.min.js',
array( 'wp-i18n', 'openstation' ),
'1.0.0',
true
);
}, 20 );
add_action( 'init', function () {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
openstation_register_window( 'my-plugin-cron', array(
'title' => __( 'Cron Jobs', 'my-plugin' ),
'icon' => 'dashicons-clock',
'template' => 'my_plugin_render_cron_template',
'script' => 'my-plugin-cron',
// Anything serializable. Lands on both eager AND lazy
// load paths — no admin-template hooks required.
'config' => array(
'restNonce' => wp_create_nonce( 'wp_rest' ),
'eventsUrl' => esc_url_raw( rest_url( 'my-plugin/v1/events' ) ),
'schedulesUrl' => esc_url_raw( rest_url( 'my-plugin/v1/schedules' ) ),
),
) );
}, 20 );PHP window ids pass through sanitize_key() — lowercase letters, digits,
- and _ only; everything else (including /) is stripped. Pick an id
that survives sanitization unchanged, or the JS lookups below won't match.
In the bundle:
( function () {
const cfg = wp.os.getWindowConfig( 'my-plugin-cron' );
if ( ! cfg ) {
// Plugin not registered (capability gate, hook order, etc.) —
// bail rather than throwing.
return;
}
window.openStationNativeWindows ??= {};
window.openStationNativeWindows[ 'my-plugin-cron' ] = async ( body ) => {
const events = await fetch( cfg.eventsUrl, {
headers: { 'X-WP-Nonce': cfg.restNonce },
} ).then( ( r ) => r.json() );
// … render `events` into `body` …
};
} )();If you already attach config via wp_localize_script( $handle, $name, $data )
on a handle declared as 'script' of openstation_register_window(),
that path also lands on both eager and lazy — the shell
harvests the handle's extra data into the payload and re-injects it
before the lazy <script> tag. So wp_localize_script keeps working,
but the new 'config' arg is more discoverable and avoids the
"localized variable name" bookkeeping.
When you suspect config didn't reach the page, ask the framework directly:
wp.os.debug.window( 'my-plugin-cron' )
// → { id, scriptHandle, scriptUrl, loadPath: 'eager'|'lazy'|'unknown',
// tagInDom, configPresent, extras: { … } }loadPath tells you whether the script came in eagerly via
wp_print_scripts or lazily via loadVendorScript. configPresent
reflects whether window.openStationWindowConfig[ id ] is set. extras
counts the inline snippets the shell injected for you.
-
docs/examples/native-windows.md— the bare registration recipe (no config). -
docs/architecture.md— the lazy vs eager load-path contract. -
docs/javascript-reference.md— the fullwp.os.*surface.
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