-
Notifications
You must be signed in to change notification settings - Fork 41
migration list apps
The six native list windows are now .os.php apps under apps/ — apps/posts/, apps/pages/, apps/users/, apps/user-edit/, apps/plugins/, apps/comments/ — driven by the App Framework instead of three PHP modules plus three TypeScript bundles. Every window keeps its frozen id (desktop-mode-posts, desktop-mode-pages, desktop-mode-users, desktop-mode-user-edit, desktop-mode-plugins, desktop-mode-comments), its title, size, gate, opt-in setting and URL remap, so dock tiles, session restore, desktop shortcuts and the nativeXEnabled toggles carry over untouched. Same windows, same rows, same styling on a desk and on a phone; new plumbing.
| Surface | Replacement |
|---|---|
Filters openstation_posts_window_args, openstation_pages_window_args, openstation_users_window_args, openstation_user_edit_window_args, openstation_plugins_window_args, openstation_comments_window_args
|
openstation_app_manifest — $manifest['width'], ['icon'], ['title'], ['config'] (the former config blob, now the config extra), … for the matching $id; openstation_app_window_args for the registration args. |
Filters openstation_*_window_template_html (all six) |
There is no server template. Each body is a client view; the data-os-posts-*, data-os-users-*, data-os-plugins-* and data-os-comments-* hooks still mark the same elements, and the old root classes (desktop-mode-posts, desktop-mode-users, desktop-mode-plugins, os-comments) are still on the roots for third-party CSS. |
PHP openstation_*_window_render_template() / _register_window() / _default_query_args() callers of openstation_register_window()
|
The apps register themselves through the framework. openstation_*_window_default_query_args() still exist (the openstation_*_window_query_args filters are unchanged) and now feed each app's data(). |
Script handles os-posts-window, os-plugins-window, os-comments-window; bundles assets/js/{posts,plugins,comments}-window[.min].js; style handles of the same names; assets/css/{posts,plugins,comments}-window.css
|
The shared openstation-app-runtime bundle plus each app's companion client view (assets/js/apps/<name>[.min].js) and stylesheet (apps/<dir>/<name>.css, registered on first open). The list layout Posts, Pages and Users share (.os-app-list__*) is in assets/css/app-runtime.css; apps/user-edit/ points at apps/users/users.css. |
wp.os.getWindowConfig( id ) keys postsUrl, usersUrl, commentsUrl, bulkUrl, replyUrl, createUserUrl, insightsUrlBase, pluginsUrl, queryArgs, … |
The list is the app's data(); the static facts an app still needs ride wp.os.getWindowConfig( id ).extra (ctx.extra in the client view): mode, editPostUrlBase, newPostUrl, defaultOrderby / defaultOrder (what a cleared column sort returns to), frontPageId / postsPageId / pageTemplates (Pages), the capability flags and role / locale / colour-scheme / contact-method maps from one memoised openstation_users_profile_facts() (Users and User Edit share it), caps, ajaxUrl, ajaxNonce, updatesNonce, autoUpdatesEnabled, selfPluginFile, adminUrl (Plugins), canModerate, canEditComments (Comments). |
The shared stores desktop-mode/user-edit/target (setUserEditTarget() / readUserEditTarget() / subscribeUserEditTarget() in src/posts-window/user-edit-target.ts), desktop-mode/comments/post-filter (setCommentsPostFilter()), desktop-mode/plugins-window/tab-target (setPluginsWindowTab()) |
Open-time params. wp.os.openWindow( 'desktop-mode-user-edit', { params: { userId } } ) — or openUserEditWindow( userId, { source, fallback } ) from src/open-targets/user-edit-window.ts, the door WP Explorer and the agents roster use; wp.os.openWindow( 'desktop-mode-comments', { params: { post } } ) (0 clears the scope); wp.os.openWindow( 'desktop-mode-plugins', { params: { tab: 'installed' | 'browse' | 'featured' } } ). The URL remaps pass the same params. Each app reads them on mount; a live singleton retargets through the new reopen lifecycle action; params ride the session, so a reload restores the same person, scope or tab. |
The whole src/posts-window/, src/plugins-window/, src/comments-window/ directories and includes/{posts,pages,users,user-edit,plugins,comments}-window/
|
The apps own their code: apps/<dir>/<name>.os.php + parts/*.php (permissions, REST fields and routes, AJAX handlers — the same global function names), apps/<dir>/<name>.os.ts + parts/*.ts (cell renderers, columns, the canvases, the profile component, the plugin views, the conversation view). The <os-user-profile> element lives in apps/users/parts/ and is shared by the Users and User Edit bundles. |
openstation_comments_window_after_bulk firing only from the REST route |
It also fires from the Comments app's moderate action, since both run openstation_comments_window_moderate(). |
The Users default _fields carrying openstation_assignable_roles, url, description; the openstation_user_stats field feeding the table |
The app computes the page's content counts in two grouped queries (openstation_users_window_stats_for()) and reads the assignable roles off its config extra. Every REST field stays registered; openstation_users_window_query_args adds them back for a consumer that reads them off the rows. |
<os-user-profile> compiled into the Users and User Edit bundles, resolving its config from window.openStationWindowConfig
|
One companion bundle, assets/js/apps/user-profile[.min].js (script handle openstation-user-profile, registered by apps/users/parts/profile-script.php and appended to both windows through openstation_app_window_args). The element takes config / fetch / toast properties from the app that mounts it and falls back to the shell when unfed; the Users app mounts it only when the Profile tab is picked. |
Filter openstation_comments_window_reply_editor
|
Gone. The composer is a plain <os-textarea>; the filter never selected anything else ('rich' and 'gutenberg' were never implemented). |
The Comments default _fields carrying openstation_replies_count and openstation_can_moderate
|
The app computes the page's reply counts in one grouped query and ships the moderation cap in its config extra. Both REST fields stay registered; openstation_comments_window_query_args adds them back for a consumer that reads them off the rows. |
os.plugin.changed broadcasts with source: 'plugins-window'
|
source: 'plugins-app', carrying the changed plugins' ids. os.post.changed keeps source: 'posts-window'; a Pages trash now announces page, not post. |
The ?openstation_force_refresh=1 flag on the Plugins window's Refresh (openstation_plugins_window_force_refresh_requested()) |
Gone. Refresh is the app's reload action, which forces the update check through openstation_plugins_window_prime_updates_once( true ); the openstation_plugins_window_refresh_updates filter still receives $force. |
One dm_pwsz_<md5> size transient per plugin |
One dm_pwsz_map transient for the whole install, read once per request and invalidated on upgrader_process_complete / deleted_plugin. |
The Browse request's updated sort and tag filter; banners, contributors, donate_link in the wp.org calls |
Removed — nothing in the window offered them. openstation_plugins_window_browse_args no longer sees a tag in its raw params. |
The dock drop-target id os-plugins-window/dock
|
desktop-mode-plugins/dock. |
A plugin delete dispatch reaching Core's controller on a network |
Refused, like the admin-ajax half already was. openstation_plugins_window_caps() is the screen gate — Core's site plugins screen offers no Delete, the files are the network admin's — and a super admin holds delete_plugins, so the action enforces the gate rather than the bare capability. |
- The open. A window paints its frame the moment it opens — the client view's
placeholder(app-framework.md), with the table's skeleton or the rail's ghost rows untilmountlands the page — and the shell prefetches every app bundle and sheet in idle time after boot, so a first open waits on neither the download nor the shell's overlay. With "Prewarm windows on hover" on, a hover over the tile sends the window's firstmountahead of the click (wp.os.prewarmWindow()), and the rows are there when it opens. - The gates:
openstation_{posts,pages,users,plugins,comments}_window_user_can_register/_user_can_use,openstation_user_edit_window_user_can_register,openstation_users_window_assignable_roles. - The query-args filters:
openstation_{posts,pages,users,comments}_window_query_args,openstation_pages_window_template_labels— the same_fields/_embed/post_typereach the same Core REST controllers, in-process, so everyregister_rest_field(), therest_post_querytags-AND filter and theopenstation_*fields land on the rows exactly as before. - Every REST route under
/desktop-mode/v1:/term-counts,/tag-cooccurrence,/users*,/users/{id}/*,/comments/*— seeincludes/rest/README.mdfor the new handler files. The app actions share one PHP function per operation with them. - Every other
openstation_plugins_window_*filter and action (_browse_args,_browse_response,_info_response,_review_parser,_icon_url,_local_icon_candidates,_refresh_updates,_auto_updates_enabled,_installed, the featured-slugs pair), thewp_ajax_openstation_plugins_*handlers,openstation_plugins_window_count_visible_updates()(the dock badge). -
openstation_comments_window_spam_score,openstation_comments_ai_is_enabled,_toggled, the AI moderation option,openstation_users_window_user_created,_login_recorded,openstation_user_edit_window_insights,OPENSTATION_LAST_LOGIN_META_KEY. - The JS extension points of the Posts window: the
openstation.postsWindow.columns/statusSegments/bulkActions/toolbarTrailingfilters, theopenstation.postsWindow.opened/dataLoadedactions, theos-posts-window-opened/os-posts-window-data-loadedCustomEvents, thePostsWindowContext/BulkAction/StatusSegmentshapes (now exported fromapps/posts/parts/types.ts), theos.post.changedandos.term.changedbroadcasts, thenativePostsHiddenColumnssetting behind "Show columns". - The
wporg-plugindrag payload and the dock drop target, the plugins update queue and icon fallback chain, the upload dialog, the self-deactivate reload. - The phone layouts: the status picker, the card list, the bottom bulk bar, the folded Categories / Tags editors, the one-pane Comments view.
The six windows were 35,359 lines — 24,489 of TypeScript across three bundles, 7,517 of PHP registration / template / REST glue, 3,353 of CSS (the deleted files at trunk, counted with wc -l) — and each carried its own copy of the same plumbing: a config blob, a REST client for the list, pager DOM, toolbar wiring, a shared store to smuggle a deep link past openById(). As apps they are 25,737 lines: 24,816 under the six apps/<dir>/ directories (every file except *.test.ts; PHP, TypeScript and CSS alike) plus the 921 net lines the framework grew (git diff --numstat against trunk over includes/framework/, src/app-runtime/, app-runtime.css and the shell seams) to absorb that plumbing for every future list too: openstation_app_rest() / openstation_app_rest_page() (in-process Core REST with _fields and _embed, so a data() serves the exact rows a browser fetched), the reopen lifecycle and per-session params (the deep-link contract), App::config( callable ) (per-viewer config resolved when the manifest is built), the own-echo skip on watch() (a window's announce never costs it a second round trip), the list-window furniture (statusControl(), pager(), mountMenuCheckboxes(), createListTableSync()) and the shared .os-app-list__* layout with its phone rules. The test surface grew from 2,247 lines (the deleted vitest and registration suites) to 5,998 (app vitest 2,829, app PHPUnit 2,444, framework 725).
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
- The App Framework — a window in one PHP file
- Architecture
- Bridge protocol — wiring overview
- <os-*> component reference
- Data model — where OpenStation keeps its data
- 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
- Window-scoped MIO
- Mio
- Mobile — the phone layer
- Multisite
- Native Windows & Framework Interop
- OpenStation Network
- Plugin compatibility layer
- Progressive Web App (PWA)
- Station Home
- Using openstation from your own plugin
- Workspaces
Migration notes
- Migration: built-in activity channels move to the os/ namespace
- Migration — Code Blue becomes an App Framework app
- Migration: window, wallpaper and widget bundles load on demand
- Migration — Posts, Pages, Users, User Edit, Plugins and Comments become App Framework apps
- Migration — the navigation model
- Migration — OpenStation Preferences becomes an App Framework app
- Performance settings move to Extended options
- Presence storage migration
- Migration — the Recycle Bin becomes an App Framework app
- Migration — the shell boots from its own screen
- Migration — Station Home becomes an App Framework app
- Migration: a native window's tabs move to the window chrome
- Migration — WP Explorer becomes the my-wordpress app
- Migration — WordPress package globals are no longer ambient
More
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
- Repairable form edits with MIO
- Register a window companion
- Pin your app to the phone tab bar, and react to the mode
- Add an action that works on a whole selection
- WP Explorer — custom post types and their folder
- WP Explorer — add a column to the list view
- 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
- Ship a window as an .os.php app
- 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
- Revisions in their own window — extend or redirect "View revisions"
- Programmatic folder sharing
- Share state across multi-bundle plugins — wp.os.createSharedStore()
- Example: loading spinner
- Add an opt-in card to Station Home
- Observe stored-file cleanup failures
- 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
- Place something where the user can reach it — wp.os.workArea
- Ship a workspace template