-
Notifications
You must be signed in to change notification settings - Fork 41
example custom unfocus effect
An unfocus effect is a visual treatment applied to every window
that isn't the focused one — the family of effects surfaced in
OpenStation Preferences → Effects → "Unfocused windows". The plugin ships three
built-ins — darken (dims), frost (frosted-glass blur), and
grayscale (drains colour) — each registered through the exact same
public hook a plugin would use. This page shows a plugin adding its own.
Status: Experimental.
The framework owns when the chosen effect runs — it watches focus
changes, the user's selection, and window state — and toggles your
effect on every unfocused window's root element (.os-window). Your
def owns what the effect is: either a CSS class to toggle (the cheap
path) or apply/clear callbacks for anything a static class can't
express.
Three kinds of window are exempt, whatever effect is selected:
- Minimized windows — nothing is on screen to treat.
-
Split-view tiles (
snapped-left/snapped-right) — snapping is the "work on these side by side" gesture, so the half that doesn't hold focus stays untouched. The exemption is per-window and partner-blind: a tile is exempt whether or not the opposite half is filled. -
Windows hosting a
<canvas>in the parent document — a CSSfilterover a live WebGL surface can cost the context, which would crash the native Pixi scenes.
The user picks among registered effects (plus a "None" option) in
OpenStation Preferences; the choice persists per-user as the unfocusEffect
setting.
Ship a stylesheet rule and register an effect that names its class:
wp.os.ready( () => {
wp.os.registerUnfocusEffect( {
id: 'acme/blur',
label: 'Blur',
description: 'Softly blur windows you are not working in.',
className: 'acme-window--blur',
owner: 'acme-effects', // live-unregister on deactivate
} );
} );/* The framework adds this class to unfocused windows while
* `acme/blur` is the selected effect, and removes it on focus. */
.acme-window--blur {
filter: blur( 2px );
}To get a smooth fade as focus moves, the framework already includes
filter in the shared window transition — so a filter-based effect
animates both ways for free (and collapses to instant under
prefers-reduced-motion). For other properties, add your own
transition to the class.
When a static class isn't enough (you need to compute per-window state, attach a canvas, etc.), provide callbacks instead:
wp.os.registerUnfocusEffect( {
id: 'acme/grayscale-fade',
label: 'Grayscale',
apply: ( el ) => {
el.style.filter = 'grayscale(1)';
},
clear: ( el ) => {
el.style.filter = '';
},
owner: 'acme-effects',
} );apply receives the window root when it becomes unfocused under your
effect; clear must undo whatever apply did and is called when the
window regains focus or the user switches effects. You can provide
both a className and callbacks — the framework removes the class for
you and also calls clear.
So a plugin activated mid-session shows up in the selector without a page reload, opt the script in from PHP:
add_action( 'admin_enqueue_scripts', function () {
wp_register_script(
'acme-effects',
plugins_url( 'js/effects.js', __FILE__ ),
array( 'openstation' ),
'1.0.0',
true
);
wp_enqueue_script( 'acme-effects' );
} );
openstation_register_unfocus_effect_script( 'acme-effects' );The handle you pass here should match the owner on your
registerUnfocusEffect calls — that's what lets the framework
live-unregister your effect when the plugin is deactivated.
-
Reserved id.
'none'is the selector's "no effect" sentinel and cannot be used as an effect id. -
Namespacing. Ids accept
vendor/sub-id([a-z0-9_/-]+). The persisted setting preserves the slash, so a namespaced id round-trips cleanly. -
Reading the selection.
wp.os.getOsSettings().unfocusEffectis the active effect id (or'none'). -
Filter. The raw
os.unfocus-effectsJS filter receives the registry array on every read — reorder, remove, or conditionally swap effects, mirroringos.wallpapers.
See also: registerUnfocusEffect in the JavaScript reference and openstation_register_unfocus_effect_script in the hooks reference.
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