WP OpenStation
A WordPress plugin that reimagines /wp-admin as a desktop operating system. Admin screens open as draggable, resizable, minimizable windows on a desktop, with a left-edge dock built from the admin menu. Purely opt-in per user — the classic admin stays untouched for everyone else, and deactivating the plugin restores vanilla Core exactly.
Zero Core patches. Every feature is wired through public WordPress hooks.
Demo
Contents
- Quick install - Development setupCurrent State
- Station Home
- Per-user opt-in
desktop_mode_mode user meta. A dedicated /openstation/ portal URL auto-enables OpenStation for first-time visitors (gated by openstation_portal_auto_enable) and the admin_init redirect sends opted-in users from /wp-admin/ to the portal (openstation_admin_redirect_to_portal).
- Desktop shell
/wp-admin: wallpaper area, unified dock (placement picked in OpenStation Preferences — left / right / bottom, default bottom), right-column widget layer, and full windowing system. openstation_mode_init, openstation_shell_before / _after, and the openstation_shell_config filter are the main extension points.
- Window system — iframe + native
?openstation_chromeless=1 (chromeless mode). Native windows render directly in the parent DOM via openstation_register_window() / wp.os.registerWindow() — multi-tab native windows are supported through openstation_register_window_tab(). Both types share drag, resize, minimize, maximize, close, fullscreen, and detach-to-new-tab.
- Dock
openstation_dock_placement ('hidden'). Per-item multi-window support via openstation_dock_item_multi. Letter-badge icon fallback for plugins without icon art.
- Virtual desktops (“Spaces”)
- Arrange & snap
cascade(), tile() and setSnapEnabled() on wp.os.windowManager. Tile grid dimensions and snap cell size are both filterable.
- Wallpaper registry
openstation_register_wallpaper() / wp.os.registerWallpaper()). CSS presets + canvas (WebGL/2D) wallpapers with collision-aware surface data (wp.os.getWallpaperSurfaces()) for snow/rain/physics effects. In-panel renderEditor callback for custom controls, shared vendor-module loader (pixijs pre-registered).
- Widgets
openstation_register_widget() / wp.os.registerWidget(). Built-in clock. User placement persists per-user in localStorage.
- Desktop icons
openstation_register_icon() — targets a registered native window or an admin URL.
- AI Assistant + slash commands
search_posts / search_pages / search_comments tools run WordPress's native keyword search. Admin-configured API key + model picker. No content is analyzed in the background: every AI call is one a user asked for. wp.os.registerCommand() adds slash commands with autocomplete (suggest()), confirm dialogs (ctx.confirm()), and full lifecycle hooks (before-run / after-run / error). Built-in /open [window] is extensible via os.open-command.items.
- Palette registry
wp.os.registerPalette()) — the AI assistant is palette 0 by default; additional plugin overlays share the shortcut.
- Cross-frame drag bridge
- Toast notifications
component. Plugins register their own tone/icon via the openstation_toast_types filter. Iframe pages raise a toast through the os-notification bridge message — it survives the iframe's own lifecycle.
- OpenStation Preferences
apps/os-settings/): wallpaper picker (with HD-only media filter), accent color swatches + custom gradient editor, desktop layout and dock controls, themes, window effects, navigation placement, feature switches and the component reference. Persisted via /desktop-mode/v1/os-settings.
- Session persistence
/desktop-mode/v1/session and restored without layout flicker. Viewport-shrink clamping keeps off-screen windows reachable.
- Mobile — the phone layer
wp.os.mode reports desktop | tablet | mobile; a head stamp makes the first paint right; a phone restores one window and hands the desktop its session back untouched. Lazy bundle, wallpaper suspended, widgets skipped. See docs/mobile.md.
- postMessage bridge
iframe-error, iframe-network).
- UI component library
web components (os-button, os-menu, os-panel, os-range-field, os-swatch, os-toast, os-tabs, …) available to plugin authors — rendered server-side via openstation_component() or imported in TS.
- i18n
wp.i18n (__, _x, _n, sprintf) directly — no shell-specific re-export.
- Component registration API
openstation_register_* functions for windows, widgets, wallpapers, icons, and window tabs. All return true / WP_Error with documented error codes.
- Public hook API
docs/hooks-reference.md and docs/javascript-reference.md.
Still ahead
- Mobile, the next pass — the phone layer ships (
docs/mobile.md: home-screen grid, full-screen apps, app switcher, edge-swipe back, bottom tab bar,wp.os.mode); still ahead are pull-to-refresh and a pass on real devices. - Tablet hybrid — split view, slide-over, horizontal dock, on the
wp.os.modeprimitive that already reports'tablet'. - Cross-window drag & drop (the North Star) — extend the current drag bridge to Media → Gutenberg block insertion, with pluggable mime-type negotiation.
- Polish — color-scheme-aware variables across all shell surfaces, View Transitions API animations, full a11y audit (ARIA, focus traps, keyboard nav).
- …and a whole lot more hooks, filters, and actions — every new surface lands with its own extension points, so this list keeps growing.
docs/architecture.md for how the pieces fit together and docs/hooks-reference.md for the hook surface (current and planned).
Repository layout
.
├── desktop-mode.php # bootstrap: header, constants, require_once of includes/
├── includes/ # PHP subsystems
│ ├── helpers.php admin-bar.php ajax.php
│ ├── assets.php render.php portal.php
│ ├── session.php default-window.php components.php
│ ├── os-settings.php extended-options.php
│ ├── accents.php wallpapers.php toast-types.php
│ ├── media-query.php
│ └── ai-copilot/ # AI assistant (OpenAI client, analysis, search, jobs)
├── apps/ # App Framework apps: <name>.os.php (window, state, actions,
│ # data) + optional <name>.os.ts (client view) + <name>.css;
│ # Code Blue lives here — see docs/app-framework.md
├── assets/ # hand-authored CSS + JS build output
│ ├── css/ desktop.css, windows.css, dock.css, chromeless.css, variables.css
│ │ variables.css carries the OpenStation palette — every design
│ │ token the shell and the <os-*> kit read, declared once
│ ├── fonts/ Geist + Geist Mono (variable, SIL OFL 1.1)
│ ├── wallpapers/ the four brand desks: galaxy (default), space,
│ │ holomesh, pulsemesh
│ ├── desktop-themes/legacy/
│ │ the built-in "Desktop Mode (Legacy)" theme — a frozen snapshot of
│ │ every token at its pre-brand value; never regenerated.
│ │ Zip it with npm run package:legacy-theme
│ └── js/ Vite bundles (gitignored; regenerate with npm run build) — only
│ admin-bar.js and media-library-enhanced.js are hand-written and tracked
├── src/ # TypeScript source — compiled by Vite
│ ├── desktop.ts / dock.ts / hooks.ts / commands.ts / palette-registry.ts
│ ├── ai-assistant/ + drag-bridge.ts / toast.ts / desktop-icons.ts
│ ├── native-windows.ts / built-in-commands.ts / public-api.ts / types.ts
│ ├── window/ # Window class — DOM, pointer, tabs, iframe bridge
│ ├── window-manager/ # stack, desktops, arrange, snap, overview
│ ├── wallpapers/ # registry, layer, surfaces, server sync, vendor loader
│ ├── widgets/ # registry, layer, frame, picker, storage
│ ├── settings/ # OpenStation Preferences panel sections
│ ├── ui/ # <os-*> web components
│ ├── modules/ # vendor-script lazy-loader
│ └── plugins/ # built-in demos (animated-logo-wallpaper)
├── docs/ # developer-facing docs (source of truth for plugin authors)
├── extensions/ # bundled sibling plugins (see "Bundled extensions" below)
├── tests/ # PHPUnit + Vitest
├── languages/ # .po / .mo (es shipped)
├── bin/ # package-zip helpers
├── package.json # devDeps (vite, typescript, vitest)
├── vite.config.js # Vite lib-mode: src/desktop.ts → assets/js/desktop[.min].js (IIFE)
├── vitest.config.ts
└── tsconfig.json
Bundled extensions
The extensions/ directory hosts sibling plugins that build on OpenStation's public APIs. Each one is a standalone WordPress plugin (Requires Plugins: desktop-mode) installed from its own zip — run ./bin/package-extensions.sh to build one per extension under dist/; see docs/RELEASE.md for the full packaging steps. (extensions/base/ is a shared base library for extension authors, not an installable plugin.)
- Code Editor (
desktop-mode-code-editor) — a Monaco-backed Code editor native window for browsing and editing files insidewp-content. Editing requires theedit_pluginscapability and is disabled entirely whenDISALLOW_FILE_EDITis set. - Cron Manager (
desktop-mode-cron-manager) — a Cron Jobs native window for browsing, editing, deleting, and running WP-Cron events. Gated bymanage_options. - Popup Siege (
desktop-mode-popup-siege) — a 90-second Breakout-style archive rescue game with OpenStation leaderboards, play-time tracking, and score-to-beat challenges. The deterministic game runtime is lazy-loaded on first play. - SOL Inbound Monologue (
desktop-mode-feed-buddy) — an AIM-era RSS/Atom reader with a movable buddy-list widget, a native conversation-style reader window, per-user subscriptions and unread state, safe server-side feed discovery, and optional synthesized chimes. - phpMyAdmin (
desktop-mode-phpmyadmin) — embeds a bundled phpMyAdmin install as a native window. Local environments only: the window registers solely whenwp_get_environment_type()is'local', because the bundled phpMyAdmin runs withauth_type=configand reuses the WordPress DB credentials — any visitor who finds the URL gets full DB access. Themanage_optionscheck only hides the shortcut from lower-privilege users; it does not gate the underlying URL.
How to run it
Quick install
Just want to try it? Grab the pre-built zip and upload it to any WordPress — Studio by WordPress.com, wp-env, or a hosted site. No Node, no build step.
- Download
openstation.zipfrom the latest release (or pick a specific version from the releases page). - In WP Admin: Plugins → Add New → Upload Plugin, choose the zip, and activate.
- Click the desktop icon in the admin bar's top-right corner. The admin reloads inside the desktop shell. Click the same icon again to return to classic admin.
Development setup
For hacking on the plugin: clone the repo, run the build in watch mode, and load it into a local WordPress via symlink so every save is one browser refresh away.
1. Install dependencies
npm install
2. Build the TypeScript bundle
The plugin uses Vite in library mode. esbuild handles transpile and minify, so builds finish in ~70 ms per bundle.
Full build — runs the PixiJS vendor-copy step plus every build:* target defined in package.json (one Vite bundle each; the scripts block in package.json and the entry map in vite.config.js are the authoritative target list):
npm run build
Writes one assets/js/ / .min.js pair per target, including:
assets/js/desktop.js/.min.js— main shell bundle (loaded based onSCRIPT_DEBUG).assets/js/iframe-bridge.js/.min.js— opt-in bridge that gives any same-origin iframe access towp.os.iframe.*.assets/js/posts-window.js/.min.js— Native Posts window (thereplacement for theedit.phpiframe; opt-in per user via OpenStation Preferences → Features).
npm run dev
Leave it running in a separate terminal; refresh the browser after each save. Set define( 'SCRIPT_DEBUG', true ) in wp-config.php so WordPress picks up the unminified bundle during development.
3. Load into a local WordPress
You need a running WordPress to load the plugin into. Pick whichever is easier.
##### Studio, wp-env, or a hosted WP
Run npm run package to build a zip from HEAD (with correct 0644 / 0755 permissions), then follow the Quick install steps 2–3 to upload and activate it. Re-package and re-upload after each change.
If you changed source, runnpm run buildbeforenpm run package— the Vite output is gitignored, andbin/package.shsplices the built files into the zip from your working tree.
npm run package:legacy-themebuildsdist/desktop-mode-legacy-theme.zip— the built-in Legacy desktop theme as an installable theme ZIP. The plugin registers Legacy from code either way; the zip is for handing it to someone else or forking it into a theme of your own.
##### Clone wordpress-develop and symlink
Gives you the full dev loop: npm run dev rebuilds on save, a browser refresh picks it up.
# clone Core's Docker-based dev host alongside this repo
git clone https://github.com/WordPress/wordpress-develop.git
cd wordpress-develop
npm install
symlink this plugin into the WP plugins directory
ln -s "$(pwd)/../alcazaba-plugin" src/wp-content/plugins/desktop-mode
boot + install WordPress
npm run env:start # nginx + PHP + MySQL in Docker
npm run env:install # installs WordPress
Site: http://localhost:8889
Admin: http://localhost:8889/wp-admin/
Credentials: admin / password
Stop the environment with npm run env:stop (from the wordpress-develop directory). Activate the plugin per Quick install steps 2–3.
Requirements
- WordPress 6.0+
- PHP 7.4+
For plugin authors
This plugin is built to be extended. Every significant behavior is hookable — drop an icon on the desktop, add a dock item, gate OpenStation by role, react to window events, or register a native window, all from your own plugin with zero patches here.
See docs/ — the developer documentation index.
Quick links:
- Getting Started — the five-minute tour for plugin authors.
- Architecture — how the pieces fit together.
- Hooks Reference — every action and filter we fire, with signatures and examples.
- JavaScript Reference — CustomEvents,
window.wp.osAPI, and the iframepostMessagebridge. - Examples — copy-paste recipes.
License
GPLv2 or later. See LICENSE.