PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
← All changes | includes/assets.php +321 -244 0.9.61.1.10 View file →
@@ -1,12 +1,12 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode asset registration.
3 + * OpenStation asset registration.
4 4 *
5 - * Registers all desktop-mode CSS and JS handles with WordPress so they can
5 + * Registers all openstation CSS and JS handles with WordPress so they can
6 6 * be enqueued from anywhere in the plugin (or by third parties).
7 7 *
8 - * @package WPDesktopMode
8 + * @package OpenStation
9 9 */
10 10
11 11 defined( 'ABSPATH' ) || exit;
12 12
@@ -16,16 +16,14 @@
16 16 * Sub-sheet URLs carry no `?ver=` of their own, so the parent's stamp is
17 17 * the only cache key the browser ever sees for the subtree — it must
18 18 * move when any member changes.
19 19 *
20 - * @since 0.9.4
21 - *
22 20 * @param string $relative Stylesheet path relative to the plugin dir.
23 21 * @param string $fallback Version to use when the file is missing.
24 22 * @return string Version string.
25 23 */
26 -function desktop_mode_css_subtree_version( $relative, $fallback ) {
27 - $root = DESKTOP_MODE_DIR . $relative;
24 +function openstation_css_subtree_version( $relative, $fallback ) {
25 + $root = OPENSTATION_DIR . $relative;
28 26 if ( ! file_exists( $root ) ) {
29 27 return (string) $fallback;
30 28 }
31 29 $max = (int) filemtime( $root );
@@ -50,18 +48,16 @@
50 48 return (string) $max;
51 49 }
52 50
53 51 /**
54 - * Registers the desktop mode CSS and JS handles.
55 - *
56 - * @since 0.1.0
52 + * Registers the OpenStation CSS and JS handles.
57 53 */
58 -function desktop_mode_register_assets() {
59 - $version = DESKTOP_MODE_VERSION;
60 - $suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
54 +function openstation_register_assets() {
55 + $version = OPENSTATION_VERSION;
56 + $suffix = openstation_asset_suffix();
61 57
62 58 // `filemtime`-stamped version for built bundles. The plugin-wide
63 - // `DESKTOP_MODE_VERSION` is bumped per release, but the bundles
59 + // `OPENSTATION_VERSION` is bumped per release, but the bundles
64 60 // iterate faster — without a per-file mtime stamp, two clients
65 61 // loading the same `?ver=…` URL can be served different bytes
66 62 // (whichever build was on disk at upload time). Stamping with the
67 63 // file's modification time guarantees the URL changes whenever the
@@ -68,68 +64,184 @@
68 64 // file does, so "is my fix deployed?" is answerable from the
69 65 // network tab. Falls back to `$version` when the file is missing
70 66 // (test envs that import this file before the build runs).
71 67 $built_version = static function ( $relative ) use ( $version ) {
72 - $path = DESKTOP_MODE_DIR . $relative;
68 + $path = OPENSTATION_DIR . $relative;
73 69 return file_exists( $path ) ? (string) filemtime( $path ) : $version;
74 70 };
75 71
76 -
77 72 // Styles.
73 + // `filemtime`-stamped, NOT the plugin-wide `$version`. This file
74 + // is the token catalogue every other sheet resolves `var()`s
75 + // against, and it changes whenever the palette does — which is a
76 + // lot more often than the plugin version is bumped. With a static
77 + // stamp the browser holds the old palette until a hard reload,
78 + // and the symptom is maddening: a themed shell where some
79 + // surfaces update and others don't.
78 80 wp_register_style(
79 - 'desktop-mode-variables',
80 - DESKTOP_MODE_URL . 'assets/css/variables.css',
81 + 'os-variables',
82 + OPENSTATION_URL . 'assets/css/variables.css',
81 83 array(),
82 - $version
84 + $built_version( 'assets/css/variables.css' )
83 85 );
84 86 // `filemtime`-stamped so the `<link rel="stylesheet">` URL matches the
85 87 // `<link rel="preload" as="style">` hint emitted by
86 - // `desktop_mode_print_preload_hints()` (which stamps with filemtime).
88 + // `openstation_print_preload_hints()` (which stamps with filemtime).
87 89 // Registering this with the plain `$version` instead produced two
88 90 // different `?ver=` query strings for the same file, so the browser
89 91 // never matched the preload to the stylesheet and logged "preloaded
90 92 // but not used within a few seconds from the window's load event".
91 93 wp_register_style(
92 - 'desktop-mode',
93 - DESKTOP_MODE_URL . 'assets/css/desktop.css',
94 - array( 'desktop-mode-variables' ),
94 + 'openstation',
95 + OPENSTATION_URL . 'assets/css/desktop.css',
96 + array( 'os-variables' ),
95 97 $built_version( 'assets/css/desktop.css' )
96 98 );
97 - // `filemtime`-stamped — window-chrome iteration (drop overlays,
98 - // new drag affordances, third-party-plugin compat) lands faster
99 - // than plugin version bumps. Without an mtime stamp the browser
100 - // keeps `?ver=<plugin-version>` valid for the whole release cycle
101 - // and the user keeps seeing yesterday's CSS even after a hard
102 - // reload.
103 - //
104 - // The stamp covers the whole `@import` SUBTREE, not just the parent
105 - // file: `windows.css` imports `window-chrome.css`,
106 - // `os-settings.css`, … and those sub-sheet URLs carry no `?ver=` of
107 - // their own. Stamping only the parent meant an edit to a sub-sheet
108 - // never changed any URL the browser knew about, so users kept
109 - // yesterday's rules until an unrelated parent edit came along
110 - // (exactly how the wallpaper-description card shipped with stale
111 - // layout). The subtree max-mtime busts the parent whenever ANY
112 - // sub-sheet changes; the re-parsed parent then re-requests the
113 - // sub-sheets, whose fresh mtimes fail heuristic-cache reuse.
99 +
100 + /*
101 + * Window styles — one handle per sheet, chained by dependency.
102 + *
103 + * These used to be `@import url( … )`ed from `windows.css` under
104 + * the single `os-windows` handle. That was a standing
105 + * cache bug: an `@import` URL carries no `?ver=`, so a changed
106 + * sub-sheet had no URL for the browser to invalidate. Stamping the
107 + * PARENT with the subtree's max mtime (which is what
108 + * `openstation_css_subtree_version()` was for) made the browser
109 + * re-fetch `windows.css` and then request each sub-sheet at an
110 + * unchanged URL — free to be served from its heuristic cache. The
111 + * result was edits not landing until a hard refresh, and rules
112 + * being relocated into `windows.css` purely to dodge it.
113 + *
114 + * Now every sheet is separately registered with its own
115 + * `filemtime` stamp, so each has a real cache key.
116 + *
117 + * THE DEPENDENCY CHAIN IS LOAD-BEARING. WordPress prints
118 + * dependencies before dependents, so chaining each sheet to the
119 + * previous one reproduces the order the `@import` block had, and
120 + * `os-windows` (which depends on the last link) still
121 + * prints after them and still wins ties:
122 + *
123 + * window-chrome → window-states → effects → window-links
124 + * → windows
125 + *
126 + * `window-overview` is deliberately NOT in this chain — it is
127 + * registered below, after `os-windows`, so it can load deferred.
128 + * Adding a sheet here means splicing it into the chain, not
129 + * appending an unrelated dependency: order is the contract.
130 + * (The Preferences window's sheet is not here at all: it rides
131 + * the `apps/os-settings/` app as a first-open companion style.)
132 + */
133 + $window_sheets = array(
134 + 'os-window-chrome' => 'assets/css/window-chrome.css',
135 + 'os-window-states' => 'assets/css/window-states.css',
136 + 'os-effects' => 'assets/css/effects.css',
137 + 'os-window-links' => 'assets/css/window-links.css',
138 + );
139 + $previous = array( 'os-variables', 'dashicons' );
140 + foreach ( $window_sheets as $handle => $relative ) {
141 + wp_register_style(
142 + $handle,
143 + OPENSTATION_URL . $relative,
144 + $previous,
145 + $built_version( $relative )
146 + );
147 + $previous = array( $handle );
148 + }
149 +
150 + // Entry point. Depends on the tail of the chain above, so
151 + // enqueuing this one handle still pulls in every critical window
152 + // sheet — the behaviour callers had when they were `@import`s.
114 153 wp_register_style(
115 - 'desktop-mode-windows',
116 - DESKTOP_MODE_URL . 'assets/css/windows.css',
117 - array( 'desktop-mode-variables', 'dashicons' ),
118 - desktop_mode_css_subtree_version( 'assets/css/windows.css', $version )
154 + 'os-windows',
155 + OPENSTATION_URL . 'assets/css/windows.css',
156 + $previous,
157 + $built_version( 'assets/css/windows.css' )
119 158 );
159 + // Loads DEFERRED (see `openstation_defer_non_critical_styles()`):
160 + // the UI it styles — the window overview — is lazy-loaded JS that
161 + // can never be on screen at first paint, so its CSS has no
162 + // business blocking render. It depends on `os-windows` so it
163 + // prints after it, preserving the cascade position it had as an
164 + // `@import`.
120 165 wp_register_style(
121 - 'desktop-mode-dock',
122 - DESKTOP_MODE_URL . 'assets/css/dock.css',
123 - array( 'desktop-mode-variables', 'dashicons' ),
166 + 'os-window-overview',
167 + OPENSTATION_URL . 'assets/css/window-overview.css',
168 + array( 'os-windows' ),
169 + $built_version( 'assets/css/window-overview.css' )
170 + );
171 + // Solo mode — one window, no desk around it. Loads last so it can
172 + // hide surfaces the sheets above declared, and only enqueues on a
173 + // solo request, so a normal shell never pays for it.
174 + wp_register_style(
175 + 'os-solo',
176 + OPENSTATION_URL . 'assets/css/solo.css',
177 + array( 'os-windows' ),
178 + $built_version( 'assets/css/solo.css' )
179 + );
180 + wp_register_style(
181 + 'os-dock',
182 + OPENSTATION_URL . 'assets/css/dock.css',
183 + array( 'os-variables', 'dashicons' ),
124 184 $built_version( 'assets/css/dock.css' )
125 185 );
126 186 wp_register_style(
127 - 'desktop-mode-dock-peek',
128 - DESKTOP_MODE_URL . 'assets/css/dock-peek.css',
129 - array( 'desktop-mode-dock' ),
130 - $version
187 + 'os-dock-peek',
188 + OPENSTATION_URL . 'assets/css/dock-peek.css',
189 + array( 'os-dock' ),
190 + $built_version( 'assets/css/dock-peek.css' )
131 191 );
192 + // The phone layer. Every rule is scoped to
193 + // `html[data-os-mode="mobile"]`, so the sheet is inert on a
194 + // desktop and the first paint on a phone is already right (the
195 + // head stamp writes the attribute before any stylesheet applies).
196 + // Depends on the dock and window sheets because it overrides them.
197 + wp_register_style(
198 + 'os-mobile',
199 + OPENSTATION_URL . 'assets/css/mobile.css',
200 + array( 'os-variables', 'dashicons', 'os-dock', 'os-windows' ),
201 + $built_version( 'assets/css/mobile.css' )
202 + );
203 + // The notch — the shell's top-centre voice and the site
204 + // assistant's front door. Scoped to `.os-notch`.
205 + wp_register_style(
206 + 'os-notch',
207 + OPENSTATION_URL . 'assets/css/notch.css',
208 + array( 'os-variables' ),
209 + $built_version( 'assets/css/notch.css' )
210 + );
211 + // The workspace wizard. Scoped to `.os-workspace-wizard`, so it is
212 + // inert until the user opens it from the overview bar's `+` or a
213 + // tile's Edit. Those controls are styled with the rest of the bar
214 + // in `window-overview.css`.
215 + wp_register_style(
216 + 'os-workspaces',
217 + OPENSTATION_URL . 'assets/css/workspaces.css',
218 + array( 'os-variables', 'dashicons' ),
219 + $built_version( 'assets/css/workspaces.css' )
220 + );
221 + // Keyboard-shortcuts window. Scoped to `.os-shortcuts`, so it is
222 + // inert until the System menu opens the window, and unconditional
223 + // for the same reason the layout sheet is: the window can be
224 + // opened at any moment and a deferred sheet would paint it raw.
225 + wp_register_style(
226 + 'os-shortcuts',
227 + OPENSTATION_URL . 'assets/css/shortcuts.css',
228 + array( 'os-variables' ),
229 + $built_version( 'assets/css/shortcuts.css' )
230 + );
231 + // The OpenStation desktop layout — the core/plugin seam on the rail
232 + // plus the constellation hover-submenu flyout. Both surfaces are
233 + // scoped (`[data-os-layout="openstation"]` / `.os-constellation`),
234 + // so the sheet is inert in every other layout and is loaded
235 + // unconditionally rather than gated on the user's current pick:
236 + // the layout is switchable live from OpenStation Preferences, and a
237 + // conditional enqueue would leave the first switch unstyled.
238 + wp_register_style(
239 + 'os-openstation-layout',
240 + OPENSTATION_URL . 'assets/css/openstation-layout.css',
241 + array( 'os-dock' ),
242 + $built_version( 'assets/css/openstation-layout.css' )
243 + );
132 244 // `filemtime`-stamped — the chromeless overrides iterate faster
133 245 // than the plugin-wide version bumps (per-page compat shims and
134 246 // page-title-action exceptions land in patches), and a stale
135 247 // cached copy means the user sees yesterday's rules. Without the
@@ -135,11 +247,11 @@
135 247 // cached copy means the user sees yesterday's rules. Without the
136 248 // stamp, the browser keeps `?ver=0.8.1` valid for the whole
137 249 // release cycle.
138 250 wp_register_style(
139 - 'desktop-mode-chromeless',
140 - DESKTOP_MODE_URL . 'assets/css/chromeless.css',
141 - array( 'desktop-mode' ),
251 + 'os-chromeless',
252 + OPENSTATION_URL . 'assets/css/chromeless.css',
253 + array( 'openstation' ),
142 254 $built_version( 'assets/css/chromeless.css' )
143 255 );
144 256
145 257 // `filemtime`-stamped — the AI assistant surface (error states,
@@ -148,70 +260,40 @@
148 260 // valid for the whole release cycle and the user keeps seeing
149 261 // yesterday's CSS even after a hard reload.
150 262 wp_register_style(
151 263 'desktop-mode-ai-assistant',
152 - DESKTOP_MODE_URL . 'assets/css/ai-assistant.css',
153 - array( 'desktop-mode-variables' ),
264 + OPENSTATION_URL . 'assets/css/ai-assistant.css',
265 + array( 'os-variables' ),
154 266 $built_version( 'assets/css/ai-assistant.css' )
155 267 );
156 268
157 269 wp_register_style(
158 270 'desktop-mode-bug-report',
159 - DESKTOP_MODE_URL . 'assets/css/bug-report.css',
160 - array( 'desktop-mode-variables' ),
161 - $version
271 + OPENSTATION_URL . 'assets/css/bug-report.css',
272 + array( 'os-variables' ),
273 + $built_version( 'assets/css/bug-report.css' )
162 274 );
163 275
164 276 // `filemtime` instead of the plugin-wide `$version` for the
165 277 // recycle-bin CSS — this file iterates faster than the bundle
166 278 // and we never want a stale CSS cache to mask a real fix.
167 - $recycle_bin_css = DESKTOP_MODE_DIR . 'assets/css/recycle-bin.css';
279 + $recycle_bin_css = OPENSTATION_DIR . 'assets/css/recycle-bin.css';
168 280 wp_register_style(
169 281 'desktop-mode-recycle-bin',
170 - DESKTOP_MODE_URL . 'assets/css/recycle-bin.css',
171 - array( 'desktop-mode-variables', 'dashicons' ),
282 + OPENSTATION_URL . 'assets/css/recycle-bin.css',
283 + array( 'os-variables', 'dashicons' ),
172 284 file_exists( $recycle_bin_css ) ? (string) filemtime( $recycle_bin_css ) : $version
173 285 );
174 286
175 - // `filemtime` for the native Posts window CSS — same rationale as
176 - // the recycle-bin CSS: bundle iterates faster than the plugin
177 - // version and stale caches are worse than the cost of a 304.
178 - $posts_window_css = DESKTOP_MODE_DIR . 'assets/css/posts-window.css';
179 - wp_register_style(
180 - 'desktop-mode-posts-window',
181 - DESKTOP_MODE_URL . 'assets/css/posts-window.css',
182 - array( 'desktop-mode-variables', 'dashicons' ),
183 - file_exists( $posts_window_css ) ? (string) filemtime( $posts_window_css ) : $version
184 - );
185 -
186 - // Native Plugins window CSS — same `filemtime`-cache-bust posture
187 - // as the Posts/Recycle Bin styles.
188 - $plugins_window_css = DESKTOP_MODE_DIR . 'assets/css/plugins-window.css';
189 - wp_register_style(
190 - 'desktop-mode-plugins-window',
191 - DESKTOP_MODE_URL . 'assets/css/plugins-window.css',
192 - array( 'desktop-mode-variables', 'dashicons' ),
193 - file_exists( $plugins_window_css ) ? (string) filemtime( $plugins_window_css ) : $version
194 - );
195 -
196 - // Native Comments window CSS — same `filemtime` posture.
197 - $comments_window_css = DESKTOP_MODE_DIR . 'assets/css/comments-window.css';
198 - wp_register_style(
199 - 'desktop-mode-comments-window',
200 - DESKTOP_MODE_URL . 'assets/css/comments-window.css',
201 - array( 'desktop-mode-variables', 'dashicons' ),
202 - file_exists( $comments_window_css ) ? (string) filemtime( $comments_window_css ) : $version
203 - );
204 -
205 287 // Files-on-the-Desktop tile + layer styles. `filemtime` for the
206 - // same reason as the recycle-bin / posts-window CSS: this file
288 + // same reason as the recycle-bin CSS: this file
207 289 // iterates faster than the plugin version, and a stale cache
208 290 // would mask a real fix.
209 - $desktop_files_css = DESKTOP_MODE_DIR . 'assets/css/desktop-files.css';
291 + $desktop_files_css = OPENSTATION_DIR . 'assets/css/desktop-files.css';
210 292 wp_register_style(
211 - 'desktop-mode-files',
212 - DESKTOP_MODE_URL . 'assets/css/desktop-files.css',
213 - array( 'desktop-mode-variables', 'dashicons' ),
293 + 'os-files',
294 + OPENSTATION_URL . 'assets/css/desktop-files.css',
295 + array( 'os-variables', 'dashicons' ),
214 296 file_exists( $desktop_files_css ) ? (string) filemtime( $desktop_files_css ) : $version
215 297 );
216 298
217 299 // Games hub window (launcher grid, scoreboard, challenges) +
@@ -216,41 +298,58 @@
216 298
217 299 // Games hub window (launcher grid, scoreboard, challenges) +
218 300 // per-game styles. Same `filemtime` cache-bust posture as the
219 301 // other fast-iterating feature stylesheets.
220 - $games_css = DESKTOP_MODE_DIR . 'assets/css/games.css';
302 + $games_css = OPENSTATION_DIR . 'assets/css/games.css';
221 303 wp_register_style(
222 304 'desktop-mode-games',
223 - DESKTOP_MODE_URL . 'assets/css/games.css',
224 - array( 'desktop-mode-variables', 'dashicons' ),
305 + OPENSTATION_URL . 'assets/css/games.css',
306 + array( 'os-variables', 'dashicons' ),
225 307 file_exists( $games_css ) ? (string) filemtime( $games_css ) : $version
226 308 );
227 - $game_inkfall_css = DESKTOP_MODE_DIR . 'assets/css/game-inkfall.css';
309 + $game_inkfall_css = OPENSTATION_DIR . 'assets/css/game-inkfall.css';
228 310 wp_register_style(
229 - 'desktop-mode-game-inkfall',
230 - DESKTOP_MODE_URL . 'assets/css/game-inkfall.css',
231 - array( 'desktop-mode-variables' ),
311 + 'os-game-inkfall',
312 + OPENSTATION_URL . 'assets/css/game-inkfall.css',
313 + array( 'os-variables' ),
232 314 file_exists( $game_inkfall_css ) ? (string) filemtime( $game_inkfall_css ) : $version
233 315 );
234 - $game_alphabet_soup_css = DESKTOP_MODE_DIR . 'assets/css/game-alphabet-soup.css';
316 + $game_alphabet_soup_css = OPENSTATION_DIR . 'assets/css/game-alphabet-soup.css';
235 317 wp_register_style(
236 - 'desktop-mode-game-alphabet-soup',
237 - DESKTOP_MODE_URL . 'assets/css/game-alphabet-soup.css',
238 - array( 'desktop-mode-variables' ),
318 + 'os-game-alphabet-soup',
319 + OPENSTATION_URL . 'assets/css/game-alphabet-soup.css',
320 + array( 'os-variables' ),
239 321 file_exists( $game_alphabet_soup_css ) ? (string) filemtime( $game_alphabet_soup_css ) : $version
240 322 );
241 323
242 324 // Pinned-notes layer styles (paper, pushpin, pastel tokens, pin
243 325 // animations). Same `filemtime` cache-bust posture as the other
244 - // fast-iterating feature stylesheets above.
245 - $notes_css = DESKTOP_MODE_DIR . 'assets/css/notes.css';
326 + // fast-iterating feature stylesheets above. Depends on `os-files`
327 + // because a pinned note dresses the canonical `.os-file-tile`
328 + // chrome — anything that restyles a tile has to print after the
329 + // file that declares one.
330 + $notes_css = OPENSTATION_DIR . 'assets/css/notes.css';
246 331 wp_register_style(
247 - 'desktop-mode-notes',
248 - DESKTOP_MODE_URL . 'assets/css/notes.css',
249 - array( 'desktop-mode-variables', 'dashicons' ),
332 + 'os-notes',
333 + OPENSTATION_URL . 'assets/css/notes.css',
334 + array( 'os-variables', 'dashicons', 'os-files' ),
250 335 file_exists( $notes_css ) ? (string) filemtime( $notes_css ) : $version
251 336 );
252 337
338 + // Announcement-dialog styles (scrim, hero card, buttons). Registered
339 + // always, enqueued only for a user who is actually owed the
340 + // announcement — see the gate in `openstation_enqueue_assets()`. It
341 + // is not lazy-loaded on top of that: on the one boot where it does
342 + // go out, the dialog opens from the main bundle a second later, and
343 + // a fetch racing that would show an unstyled card.
344 + $announce_css = OPENSTATION_DIR . 'assets/css/announce.css';
345 + wp_register_style(
346 + 'os-announce',
347 + OPENSTATION_URL . 'assets/css/announce.css',
348 + array( 'os-variables' ),
349 + file_exists( $announce_css ) ? (string) filemtime( $announce_css ) : $version
350 + );
351 +
253 352 // Scripts.
254 353 //
255 354 // `wp-hooks` — the shell exposes a WordPress-style filter/action
256 355 // API (`window.wp.hooks`) to third-party plugins.
@@ -259,10 +358,10 @@
259 358 // lookups. Both handles are core-shipped but only pre-enqueued
260 359 // when Gutenberg-adjacent deps pull them in, so we list them
261 360 // explicitly to guarantee load order.
262 361 wp_register_script(
263 - 'desktop-mode',
264 - DESKTOP_MODE_URL . 'assets/js/desktop' . $suffix . '.js',
362 + 'openstation',
363 + OPENSTATION_URL . 'assets/js/desktop' . $suffix . '.js',
265 364 // `heartbeat` + `jquery` — the recycle-bin badge module
266 365 // (loaded as part of this bundle) opts into the WordPress
267 366 // Heartbeat API so the count tile / desktop-icon badge
268 367 // stays in sync even when the bin window is closed.
@@ -267,13 +366,24 @@
267 366 // Heartbeat API so the count tile / desktop-icon badge
268 367 // stays in sync even when the bin window is closed.
269 368 array( 'wp-hooks', 'wp-i18n', 'heartbeat', 'jquery' ),
270 369 $built_version( 'assets/js/desktop' . $suffix . '.js' ),
271 - true
370 + // Footer + defer: the shell boots on DOMContentLoaded anyway,
371 + // so deferring frees the parser instead of blocking at the
372 + // footer print point. Both inline payloads attached to this
373 + // handle (`__openStationMenuCommands`, the jazz-quote version
374 + // stamp) are `'before'`-position, which WP keeps as blocking
375 + // inline ahead of a deferred tag — order is preserved. On WP
376 + // < 6.3 the array collapses to a truthy `$in_footer`, same as
377 + // before.
378 + array(
379 + 'in_footer' => true,
380 + 'strategy' => 'defer',
381 + )
272 382 );
273 383
274 - // `desktop-mode-iframe-bridge` — opt-in iframe-side bridge that
275 - // provides `wp.desktop.iframe.publish/subscribe/onConnection/
384 + // `os-iframe-bridge` — opt-in iframe-side bridge that
385 + // provides `wp.os.iframe.publish/subscribe/onConnection/
276 386 // requestConnection` to any same-origin iframe that enqueues it.
277 387 // Same code is also injected inline by the chromeless bridge
278 388 // (so chromeless wp-admin pages don't need a separate enqueue)
279 389 // and auto-injected when a native window opts in via
@@ -279,19 +389,60 @@
279 389 // and auto-injected when a native window opts in via
280 390 // `iframeContent: { bridge: true }`. Plugins targeting their
281 391 // own iframe pages just enqueue this handle.
282 392 wp_register_script(
283 - 'desktop-mode-iframe-bridge',
284 - DESKTOP_MODE_URL . 'assets/js/iframe-bridge' . $suffix . '.js',
393 + 'os-iframe-bridge',
394 + OPENSTATION_URL . 'assets/js/iframe-bridge' . $suffix . '.js',
285 395 array(),
286 396 $built_version( 'assets/js/iframe-bridge' . $suffix . '.js' ),
287 397 true
288 398 );
289 399
290 - // `desktop-mode-gutenberg-drop-receiver` — iframe-side bundle
400 + // `os-deactivation-feedback` — the dialog that asks one optional
401 + // question when an admin deactivates OpenStation. Enqueued on
402 + // `plugins.php` (classic, chromeless and network admin) by
403 + // `includes/feedback/deactivation.php`, and lazy-loaded by the
404 + // native Plugins app from its config block. Plain DOM on purpose:
405 + // the classic screen has no `<os-*>` kit.
406 + wp_register_script(
407 + 'os-deactivation-feedback',
408 + OPENSTATION_URL . 'assets/js/deactivation-feedback' . $suffix . '.js',
409 + array( 'wp-i18n' ),
410 + $built_version( 'assets/js/deactivation-feedback' . $suffix . '.js' ),
411 + true
412 + );
413 + wp_set_script_translations(
414 + 'os-deactivation-feedback',
415 + 'desktop-mode',
416 + OPENSTATION_DIR . 'languages'
417 + );
418 + $feedback_css = OPENSTATION_DIR . 'assets/css/deactivation-feedback.css';
419 + wp_register_style(
420 + 'os-deactivation-feedback',
421 + OPENSTATION_URL . 'assets/css/deactivation-feedback.css',
422 + array(),
423 + file_exists( $feedback_css ) ? (string) filemtime( $feedback_css ) : $version
424 + );
425 +
426 + // `os-chromeless-bridge` — the iframe side of every window,
427 + // enqueued on `admin_footer` by
428 + // `openstation_chromeless_bridge_script()` with its per-request
429 + // data attached as a `before` inline block. Registered here (not
430 + // there) so the handle exists before the enqueue and so the asset
431 + // guard's OPENSTATION_URL snapshot protects it like every other
432 + // bundle of ours.
433 + wp_register_script(
434 + 'os-chromeless-bridge',
435 + OPENSTATION_URL . 'assets/js/chromeless-bridge' . $suffix . '.js',
436 + array(),
437 + $built_version( 'assets/js/chromeless-bridge' . $suffix . '.js' ),
438 + true
439 + );
440 +
441 + // `os-gutenberg-drop-receiver` — iframe-side bundle
291 442 // enqueued only on the Block Editor screens (`post.php` /
292 - // `post-new.php`) for desktop-mode users. Listens for
293 - // `desktop-mode-drop` postMessages from the parent shell (see
443 + // `post-new.php`) for openstation users. Listens for
444 + // `os-drop` postMessages from the parent shell (see
294 445 // `src/drag/iframe-drop-targets.ts`) and inserts the matching
295 446 // Gutenberg block via `wp.data.dispatch('core/block-editor')`.
296 447 // Depends on `wp-blocks` + `wp-data` so the editor stores are
297 448 // guaranteed enqueued before the receiver runs its first message
@@ -296,49 +447,32 @@
296 447 // Depends on `wp-blocks` + `wp-data` so the editor stores are
297 448 // guaranteed enqueued before the receiver runs its first message
298 449 // handler.
299 450 wp_register_script(
300 - 'desktop-mode-gutenberg-drop-receiver',
301 - DESKTOP_MODE_URL . 'assets/js/gutenberg-drop-receiver' . $suffix . '.js',
451 + 'os-gutenberg-drop-receiver',
452 + OPENSTATION_URL . 'assets/js/gutenberg-drop-receiver' . $suffix . '.js',
302 453 array( 'wp-blocks', 'wp-data' ),
303 454 $built_version( 'assets/js/gutenberg-drop-receiver' . $suffix . '.js' ),
304 455 true
305 456 );
306 457
307 - // `desktop-mode-recycle-bin` — small bundle for the Recycle Bin
308 - // native window. Lazy-loaded by the native-window sync the first
309 - // time the bin opens; registers a render callback on
310 - // `window.desktopModeNativeWindows['desktop-mode-recycle-bin']`.
311 - $recycle_bin_js = DESKTOP_MODE_DIR . 'assets/js/recycle-bin' . $suffix . '.js';
312 - wp_register_script(
313 - 'desktop-mode-recycle-bin',
314 - DESKTOP_MODE_URL . 'assets/js/recycle-bin' . $suffix . '.js',
315 - // `heartbeat` + `jquery` — the bin opts in to the WordPress
316 - // Heartbeat API while its window is open as the catch-all
317 - // real-time channel for deletes that don't render an admin
318 - // footer (REST/AJAX/other tabs/WP-CLI). See
319 - // `src/recycle-bin/realtime.ts` for the subscriber.
320 - array( 'wp-i18n', 'heartbeat', 'jquery' ),
321 - file_exists( $recycle_bin_js ) ? (string) filemtime( $recycle_bin_js ) : $version,
322 - true
323 - );
324 - wp_set_script_translations(
325 - 'desktop-mode-recycle-bin',
326 - 'desktop-mode',
327 - DESKTOP_MODE_DIR . 'languages'
328 - );
458 + // The Recycle Bin window is an App Framework app (`apps/trash/`);
459 + // its client view rides the app-bundle pipeline, so no dedicated
460 + // script handle. The `desktop-mode-recycle-bin` STYLE handle above
461 + // stays — the drag-to-trash drop-target highlight must be present
462 + // at boot (dropping on the closed bin's dock tile), not window-open.
329 463
330 464 // `desktop-mode-games` — bundle for the Games hub native window
331 465 // (launcher grid, scoreboard, challenges client). Lazy-loaded by
332 466 // the native-window sync the first time the hub opens; registers a
333 467 // render callback on
334 - // `window.desktopModeNativeWindows['desktop-mode-games']`.
468 + // `window.openStationNativeWindows['desktop-mode-games']`.
335 469 // `heartbeat` + `jquery` — the challenges client rides the
336 470 // WordPress Heartbeat bus for live delivery.
337 - $games_js = DESKTOP_MODE_DIR . 'assets/js/games' . $suffix . '.js';
471 + $games_js = OPENSTATION_DIR . 'assets/js/games' . $suffix . '.js';
338 472 wp_register_script(
339 473 'desktop-mode-games',
340 - DESKTOP_MODE_URL . 'assets/js/games' . $suffix . '.js',
474 + OPENSTATION_URL . 'assets/js/games' . $suffix . '.js',
341 475 array( 'wp-i18n', 'heartbeat', 'jquery' ),
342 476 file_exists( $games_js ) ? (string) filemtime( $games_js ) : $version,
343 477 true
344 478 );
@@ -344,120 +478,63 @@
344 478 );
345 479 wp_set_script_translations(
346 480 'desktop-mode-games',
347 481 'desktop-mode',
348 - DESKTOP_MODE_DIR . 'languages'
482 + OPENSTATION_DIR . 'languages'
349 483 );
350 484
351 - // `desktop-mode-game-inkfall` — the Inkfall game bundle. Loaded
485 + // `os-game-inkfall` — the Inkfall game bundle. Loaded
352 486 // lazily by the games framework on first launch; publishes the
353 - // game def on `window.desktopModeGames.inkfall`.
354 - $game_inkfall_js = DESKTOP_MODE_DIR . 'assets/js/game-inkfall' . $suffix . '.js';
487 + // game def on `window.openStationGames.inkfall`.
488 + $game_inkfall_js = OPENSTATION_DIR . 'assets/js/game-inkfall' . $suffix . '.js';
355 489 wp_register_script(
356 - 'desktop-mode-game-inkfall',
357 - DESKTOP_MODE_URL . 'assets/js/game-inkfall' . $suffix . '.js',
490 + 'os-game-inkfall',
491 + OPENSTATION_URL . 'assets/js/game-inkfall' . $suffix . '.js',
358 492 array( 'wp-i18n' ),
359 493 file_exists( $game_inkfall_js ) ? (string) filemtime( $game_inkfall_js ) : $version,
360 494 true
361 495 );
362 496 wp_set_script_translations(
363 - 'desktop-mode-game-inkfall',
497 + 'os-game-inkfall',
364 498 'desktop-mode',
365 - DESKTOP_MODE_DIR . 'languages'
499 + OPENSTATION_DIR . 'languages'
366 500 );
367 501
368 - // `desktop-mode-game-alphabet-soup` — the Alphabet Soup game
502 + // `os-game-alphabet-soup` — the Alphabet Soup game
369 503 // bundle. Loaded lazily by the games framework on first launch;
370 504 // publishes the game def on
371 - // `window.desktopModeGames['alphabet-soup']`.
372 - $game_alphabet_soup_js = DESKTOP_MODE_DIR . 'assets/js/game-alphabet-soup' . $suffix . '.js';
505 + // `window.openStationGames['alphabet-soup']`.
506 + $game_alphabet_soup_js = OPENSTATION_DIR . 'assets/js/game-alphabet-soup' . $suffix . '.js';
373 507 wp_register_script(
374 - 'desktop-mode-game-alphabet-soup',
375 - DESKTOP_MODE_URL . 'assets/js/game-alphabet-soup' . $suffix . '.js',
508 + 'os-game-alphabet-soup',
509 + OPENSTATION_URL . 'assets/js/game-alphabet-soup' . $suffix . '.js',
376 510 array( 'wp-i18n' ),
377 511 file_exists( $game_alphabet_soup_js ) ? (string) filemtime( $game_alphabet_soup_js ) : $version,
378 512 true
379 513 );
380 514 wp_set_script_translations(
381 - 'desktop-mode-game-alphabet-soup',
515 + 'os-game-alphabet-soup',
382 516 'desktop-mode',
383 - DESKTOP_MODE_DIR . 'languages'
517 + OPENSTATION_DIR . 'languages'
384 518 );
385 519
386 - // `desktop-mode-posts-window` — small bundle for the native Posts
387 - // window. Lazy-loaded by the native-window sync the first time the
388 - // window opens (via the dock-click swap when the user opts in);
389 - // registers a render callback on
390 - // `window.desktopModeNativeWindows['desktop-mode-posts']`.
391 - $posts_window_js = DESKTOP_MODE_DIR . 'assets/js/posts-window' . $suffix . '.js';
392 - wp_register_script(
393 - 'desktop-mode-posts-window',
394 - DESKTOP_MODE_URL . 'assets/js/posts-window' . $suffix . '.js',
395 - array( 'wp-i18n' ),
396 - file_exists( $posts_window_js ) ? (string) filemtime( $posts_window_js ) : $version,
397 - true
398 - );
399 - wp_set_script_translations(
400 - 'desktop-mode-posts-window',
401 - 'desktop-mode',
402 - DESKTOP_MODE_DIR . 'languages'
403 - );
404 -
405 - // `desktop-mode-plugins-window` — small bundle for the native
406 - // Plugins window. Lazy-loaded by the native-window sync the first
407 - // time the window opens (via the dock-click swap when the user
408 - // opts in); registers a render callback on
409 - // `window.desktopModeNativeWindows['desktop-mode-plugins']`.
410 - $plugins_window_js = DESKTOP_MODE_DIR . 'assets/js/plugins-window' . $suffix . '.js';
411 - wp_register_script(
412 - 'desktop-mode-plugins-window',
413 - DESKTOP_MODE_URL . 'assets/js/plugins-window' . $suffix . '.js',
414 - array( 'wp-i18n' ),
415 - file_exists( $plugins_window_js ) ? (string) filemtime( $plugins_window_js ) : $version,
416 - true
417 - );
418 - wp_set_script_translations(
419 - 'desktop-mode-plugins-window',
420 - 'desktop-mode',
421 - DESKTOP_MODE_DIR . 'languages'
422 - );
423 -
424 - // `desktop-mode-comments-window` — small bundle for the native
425 - // Comments window. Lazy-loaded by the native-window sync the first
426 - // time the window opens (via the dock-click swap when the user
427 - // opts in); registers a render callback on
428 - // `window.desktopModeNativeWindows['desktop-mode-comments']`.
429 - $comments_window_js = DESKTOP_MODE_DIR . 'assets/js/comments-window' . $suffix . '.js';
430 - wp_register_script(
431 - 'desktop-mode-comments-window',
432 - DESKTOP_MODE_URL . 'assets/js/comments-window' . $suffix . '.js',
433 - array( 'wp-i18n' ),
434 - file_exists( $comments_window_js ) ? (string) filemtime( $comments_window_js ) : $version,
435 - true
436 - );
437 - wp_set_script_translations(
438 - 'desktop-mode-comments-window',
439 - 'desktop-mode',
440 - DESKTOP_MODE_DIR . 'languages'
441 - );
442 -
443 - // `desktop-mode-animated-logo-wallpaper` — built-in PixiJS canvas
444 - // wallpaper, moved out of `desktop.min.js` in 0.8.4. The wallpaper
520 + // `os-animated-logo-wallpaper` — built-in PixiJS canvas
521 + // wallpaper, moved out of `desktop.min.js`. The wallpaper
445 522 // `server-sync` loads this handle when the user selects the
446 523 // `wp-animated-logo` wallpaper (or opens OS Settings → Wallpaper
447 524 // and the picker pulls every registered canvas def in). The
448 525 // bundle's only side effect is publishing the `WallpaperDef` on
449 - // `window.desktopModeWallpapers['wp-animated-logo']`.
450 - $animated_logo_js = DESKTOP_MODE_DIR . 'assets/js/animated-logo-wallpaper' . $suffix . '.js';
526 + // `window.openStationWallpapers['wp-animated-logo']`.
527 + $animated_logo_js = OPENSTATION_DIR . 'assets/js/animated-logo-wallpaper' . $suffix . '.js';
451 528 wp_register_script(
452 - 'desktop-mode-animated-logo-wallpaper',
453 - DESKTOP_MODE_URL . 'assets/js/animated-logo-wallpaper' . $suffix . '.js',
529 + 'os-animated-logo-wallpaper',
530 + OPENSTATION_URL . 'assets/js/animated-logo-wallpaper' . $suffix . '.js',
454 531 array( 'wp-hooks' ),
455 532 file_exists( $animated_logo_js ) ? (string) filemtime( $animated_logo_js ) : $version,
456 533 true
457 534 );
458 535
459 - // `desktop-mode-snow-wallpaper` — built-in PixiJS canvas
536 + // `os-snow-wallpaper` — built-in PixiJS canvas
460 537 // wallpaper: snowfall that accumulates on window tops and melts
461 538 // away. Same lazy-load path as the animated logo: the wallpaper
462 539 // `server-sync` injects this handle when the user selects the
463 540 // `wp-snow` wallpaper (or opens OS Settings → Wallpaper and the
@@ -462,32 +539,32 @@
462 539 // `server-sync` injects this handle when the user selects the
463 540 // `wp-snow` wallpaper (or opens OS Settings → Wallpaper and the
464 541 // picker pulls the def in). The bundle's only side effect is
465 542 // publishing the `WallpaperDef` on
466 - // `window.desktopModeWallpapers['wp-snow']`.
467 - $snow_js = DESKTOP_MODE_DIR . 'assets/js/snow-wallpaper' . $suffix . '.js';
543 + // `window.openStationWallpapers['wp-snow']`.
544 + $snow_js = OPENSTATION_DIR . 'assets/js/snow-wallpaper' . $suffix . '.js';
468 545 wp_register_script(
469 - 'desktop-mode-snow-wallpaper',
470 - DESKTOP_MODE_URL . 'assets/js/snow-wallpaper' . $suffix . '.js',
546 + 'os-snow-wallpaper',
547 + OPENSTATION_URL . 'assets/js/snow-wallpaper' . $suffix . '.js',
471 548 array( 'wp-hooks', 'wp-i18n' ),
472 549 file_exists( $snow_js ) ? (string) filemtime( $snow_js ) : $version,
473 550 true
474 551 );
475 552 wp_set_script_translations(
476 - 'desktop-mode-snow-wallpaper',
553 + 'os-snow-wallpaper',
477 554 'desktop-mode',
478 - DESKTOP_MODE_DIR . 'languages'
555 + OPENSTATION_DIR . 'languages'
479 556 );
480 557
481 558 // `desktop-mode-ai-assistant` — AI Copilot spotlight overlay,
482 - // moved out of `desktop.min.js` in 0.8.4. The main bundle ships a
483 - // stub matching the public `wp.desktop.ai` contract; the stub
559 + // moved out of `desktop.min.js`. The main bundle ships a
560 + // stub matching the public `wp.os.ai` contract; the stub
484 561 // `<script>`-injects this handle the first time the user opens
485 562 // the assistant (Cmd+K or admin-bar button).
486 - $ai_assistant_js = DESKTOP_MODE_DIR . 'assets/js/ai-assistant' . $suffix . '.js';
563 + $ai_assistant_js = OPENSTATION_DIR . 'assets/js/ai-assistant' . $suffix . '.js';
487 564 wp_register_script(
488 565 'desktop-mode-ai-assistant',
489 - DESKTOP_MODE_URL . 'assets/js/ai-assistant' . $suffix . '.js',
566 + OPENSTATION_URL . 'assets/js/ai-assistant' . $suffix . '.js',
490 567 array( 'wp-hooks', 'wp-i18n' ),
491 568 file_exists( $ai_assistant_js ) ? (string) filemtime( $ai_assistant_js ) : $version,
492 569 true
493 570 );
@@ -493,19 +570,19 @@
493 570 );
494 571 wp_set_script_translations(
495 572 'desktop-mode-ai-assistant',
496 573 'desktop-mode',
497 - DESKTOP_MODE_DIR . 'languages'
574 + OPENSTATION_DIR . 'languages'
498 575 );
499 576
500 577 // Wire the translation bundle to this script handle. WP looks
501 - // for `languages/desktop-mode-{locale}-desktop-mode.json` and
578 + // for `languages/os-{locale}-os.json` and
502 579 // injects its `locale_data` into `wp.i18n` just before the
503 580 // script runs — so every `__()` call resolves to the right
504 581 // language without any runtime fetch.
505 582 wp_set_script_translations(
583 + 'openstation',
506 584 'desktop-mode',
507 - 'desktop-mode',
508 - DESKTOP_MODE_DIR . 'languages'
585 + OPENSTATION_DIR . 'languages'
509 586 );
510 587 }
511 -add_action( 'init', 'desktop_mode_register_assets' );
588 +add_action( 'init', 'openstation_register_assets' );