-
Notifications
You must be signed in to change notification settings - Fork 41
example window slot
The title bar is composed of named slots — regions plugins can replace, augment, or empty per-window. This is Layer 3 of the four-layer window-chrome customization framework. See Window themes for Layer 1 and Window controls for Layer 2.
The slot host elements live inside the title bar with data-slot="<name>" attributes; CSS targets them via .os-window__slot--<name>.
[ before-titlebar ] ← above the title bar (banners, status strips)
[ before-icon ][ icon ][ title ][ after-title ]
… screen-meta + custom buttons + menu …
[ before-controls ][ controls* ][ after-controls ]
[ after-titlebar ] ← below the title bar (progress bars, contextual rows)
controls* is owned by Layer 2 (registerWindowControl); the slot painter does not touch it. Use the Window controls APIs for that cluster.
before-titlebar and after-titlebar render OUTSIDE the title-bar flex row — perfect for full-width banners that shouldn't compete with sibling slots for horizontal space.
wp.os.applyWindowSlot( 'edit-post', 'icon', {
html: '🎨',
} );html is sandboxed via textContent — markup is rendered as text, not parsed. For rich content register a render callback instead.
wp.os.applyWindowSlot( 'my-plugin/sync', 'before-titlebar', {
render: ( host ) => {
host.style.background = 'linear-gradient(90deg, #38bdf8, #818cf8)';
host.style.color = '#fff';
host.style.padding = '4px 12px';
host.textContent = '⏳ Syncing 14 items…';
// Optional: return a teardown the framework calls on re-paint / close.
const t = setInterval( () => host.textContent = `⏳ ${ Date.now() }`, 1000 );
return () => clearInterval( t );
},
} );wp.os.applyWindowSlot( 'my-plugin/dashboard', 'title', null );null empties the slot AND suppresses any matching global slot renderers — explicit "render nothing".
When the same slot decoration should apply to many windows, register globally with a match predicate:
plugin.php
add_action( 'admin_enqueue_scripts', function () {
wp_register_script(
'my-decorator',
plugins_url( 'decorator.js', __FILE__ ),
array( 'openstation' ),
'1.0.0', true
);
wp_enqueue_script( 'my-decorator' );
} );
openstation_register_window_slot_script( 'my-decorator' );decorator.js
wp.os.whenReady( () => {
wp.os.registerWindowSlot( {
id: 'my-decorator/title-star',
slot: 'title',
replace: false, // append, don't wipe the default title
match: ( win ) => win.config.url?.includes( 'post.php' ) ?? false,
owner: 'my-decorator', // for live unregister on deactivation
render: ( host ) => {
const star = document.createElement( 'span' );
star.textContent = ' ★';
star.title = 'Editing a post';
host.appendChild( star );
return () => star.remove();
},
} );
} );replace: false means "append my content after whatever's already in the slot." Plugins that want to fully replace the slot (default content + earlier renderers) leave replace unset (defaults to true) and the framework clears the host before invoking their render.
Native windows can declare slot overrides inline:
wp.os.registerWindow( {
id: 'my-plugin/dashboard',
title: 'Dashboard',
icon: 'dashicons-dashboard',
width: 800, height: 500,
minWidth: 320, minHeight: 200,
appearance: {
slots: {
'before-titlebar': {
html: 'BETA',
},
'after-titlebar': {
render: ( host ) => {
const bar = document.createElement( 'div' );
bar.style.height = '2px';
bar.style.background = '#22c55e';
host.appendChild( bar );
},
},
},
},
render: ( body ) => { body.textContent = 'Hello'; },
} );| Hook | Type | Signature | Purpose |
|---|---|---|---|
openstation_window_slot_script_registered |
action | ( string $handle ) |
Fires after openstation_register_window_slot_script() succeeds. |
openstation_window_slot_registered |
action | ( string $id, array $entry ) |
Fires after openstation_register_window_slot() stores metadata. |
| Hook | Type | Signature | Purpose |
|---|---|---|---|
os.window.chrome.slot |
filter | ( host, { windowId, slot, config } ) => host |
Mutate a slot's host element after content settles. Stable. |
os.window.chrome.applied |
action | ( { windowId, layer } ) |
Fires with layer: 'slots' after a paint. Stable. |
| Function | Purpose |
|---|---|
wp.os.registerWindowSlot( def ) |
Register a global slot renderer. Throws on validation failure. |
wp.os.unregisterWindowSlot( id ) |
Drop by id. |
wp.os.listWindowSlots() |
Snapshot of registered renderers. |
wp.os.applyWindowSlot( windowId, slot, config ) |
Per-window override at runtime. Pass undefined for config to clear. |
WindowConfig.appearance.slots |
Per-window declaration at registration time. |
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