-
Notifications
You must be signed in to change notification settings - Fork 41
example mio customization
Status: Experimental. Full reference: mio.md.
Mio is the soft-body companion that floats over the wallpaper. Users switch it on by right-clicking the desk. A plugin can restyle it, re-tune its physics, react to it being picked up, and turn it on or off programmatically.
The server-side filter runs once per shell render and is the right home for site-wide identity: brand colours, a bigger or smaller companion, heavier or lighter physics.
Return a partial array — anything you leave out keeps the reference design. Every value is re-clamped in the browser, so an out-of-range number produces a plain-looking Mio rather than a broken shell.
<?php
/**
* Plugin Name: My Desktop Extension
*/
defined( 'ABSPATH' ) || exit;
add_filter( 'openstation_mio_config', function ( $config ) {
// Brand colours: a teal-to-green ring instead of magenta-to-violet.
$config['appearance']['hueStart'] = 170;
$config['appearance']['hueSpan'] = 50;
$config['appearance']['glow'] = 1.4;
// A companion that commits to windows from further away.
$config['physics']['magnetStrength'] = 3400;
$config['physics']['magnetRange'] = 340;
// Firmer jelly: less wobble on impact, calmer while idle.
$config['physics']['damping'] = 8;
$config['physics']['idleWobble'] = 0.04;
return $config;
} );Colours accept integers (0x05050a) or CSS hex strings ('#05050a'). The full key/range table is in mio.md.
setConfig() merges over whatever is currently in force and applies live — useful for anything that depends on browser state rather than site state.
wp.os.ready( () => {
// Big and calm on a wall-mounted kiosk; the default elsewhere.
if ( window.innerWidth > 2200 ) {
wp.os.mio.setConfig( {
appearance: { radius: 90, glow: 1.6 },
physics: { magnetStrength: 1400, floatAmplitude: 20 },
} );
}
} );The two look similar and mean different things:
| Persists? | Takes | |
|---|---|---|
setConfig( { appearance, physics } ) |
No | Anything in the config, including the spring constants. |
setStyle( flatBag ) |
Yes, to the user's account | Appearance keys and the look-physics keys, in one flat bag. |
setStyle() is what "Make it yours" writes on every control movement, so calling it is changing the user's saved companion — on every device they log into. Reach for it only when you are acting on the user's behalf; reach for setConfig() when your plugin wants Mio to look a certain way for a moment.
// Give the user a Mio to match their brand, and stop it wandering.
wp.os.mio.setStyle( {
hueStart: 170,
hueSpan: 60,
shapePreset: 'star',
shapeShuffle: 0,
idleWobble: 0.12,
} );Keys outside the two whitelists are dropped rather than applied, so passing a whole MioPhysics will not let you set stiffnesses this way — that is setConfig()'s job, and it is deliberately the one that doesn't persist.
If you need the last word before Mio ever mounts — including on the very first frame — use the filter instead. It runs on top of the PHP config and is re-sanitized afterwards.
wp.hooks.addFilter(
'os.mio.config',
'my-plugin/mio',
( config ) => ( {
...config,
appearance: { ...config.appearance, eyeScale: 0.34 },
} )
);wp.hooks.addAction(
'os.mio.dropped',
'my-plugin/mio',
( { position } ) => {
// The user parked it somewhere. Positions are viewport
// coordinates and are already persisted by the shell.
myPluginRecordPreferredCorner( position );
}
);
wp.hooks.addAction(
'os.mio.enabled',
'my-plugin/mio',
() => wp.os.showToast( { message: 'Say hi 👋' } )
);Available actions: enabled, disabled, mounted, unmounted, grabbed, dropped, displaced (a window opened on top of it and it hopped clear), shape-changed ({ shape, from } — the silhouette shuffle picked a new shape).
enable() persists the preference exactly as Mio's dock tile does, and lazy-loads the Mio bundle. Only do this in response to something the user asked for — silently switching on an animated companion is not a good welcome.
wp.os.registerCommand( {
slug: 'mio',
label: 'Toggle Mio',
icon: 'dashicons-buddicons-replies',
run: () => wp.os.mio.toggle(),
} );The toggle is an ordinary system tile with id: 'os-mio-toggle', so the dock's decoration hooks reach it like any other:
wp.hooks.addFilter(
'os.dock.tile-class',
'my-plugin/mio',
( classes, ctx ) =>
ctx.item?.id === 'os-mio-toggle'
? [ ...classes, 'my-plugin-mio-tile' ]
: classes
);Users who don't want a desk companion hide the tile from OpenStation Preferences → Navigation; it opts into that list via SystemDockItem.placeable, like the System and Overview tiles do. There is nothing to filter out server-side — a shell whose user never switches Mio on downloads none of the simulation.
-
Don't reach into the layer's DOM.
#os-mioand its<canvas>are owned by the shell and rebuilt on every toggle. Everything supported is onwp.os.mio. - Don't make the layer interactive. It spans the whole shell; anything you make clickable there swallows clicks meant for the window underneath.
-
Don't assume it's mounted. It is off by default and lazy-loaded.
getPosition()returnsnullwhen off, andsetPosition()is a no-op. -
Don't use
setStyle()for a temporary adjustment. It saves to the user's account, so a look you set "just for this page" follows them to every browser they log into.setConfig()is the one that doesn't persist.
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