PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.1
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.1
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
desktop-mode / includes / assets.php

assets.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.1, at includes/assets.php

633 lines 24.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation asset registration.
4 *
5 * Registers all openstation CSS and JS handles with WordPress so they can
6 * be enqueued from anywhere in the plugin (or by third parties).
7 *
8 * @package OpenStation
9 */
10
11 defined( 'ABSPATH' ) || exit;
12
13 /**
14 * Cache-buster for a stylesheet that `@import`s sub-sheets: the max
15 * `filemtime` across the file AND everything it (transitively) imports.
16 * Sub-sheet URLs carry no `?ver=` of their own, so the parent's stamp is
17 * the only cache key the browser ever sees for the subtree — it must
18 * move when any member changes.
19 *
20 * @param string $relative Stylesheet path relative to the plugin dir.
21 * @param string $fallback Version to use when the file is missing.
22 * @return string Version string.
23 */
24 function openstation_css_subtree_version( $relative, $fallback ) {
25 $root = OPENSTATION_DIR . $relative;
26 if ( ! file_exists( $root ) ) {
27 return (string) $fallback;
28 }
29 $max = (int) filemtime( $root );
30 $queue = array( $root );
31 $seen = array( $root => true );
32 while ( ! empty( $queue ) ) {
33 $file = array_pop( $queue );
34 $css = (string) file_get_contents( $file );
35 if ( ! preg_match_all( '/@import\s+url\(\s*["\']?([^"\')\s]+)["\']?\s*\)/i', $css, $matches ) ) {
36 continue;
37 }
38 foreach ( $matches[1] as $import ) {
39 $path = dirname( $file ) . '/' . $import;
40 if ( ! file_exists( $path ) || isset( $seen[ $path ] ) ) {
41 continue;
42 }
43 $seen[ $path ] = true;
44 $max = max( $max, (int) filemtime( $path ) );
45 $queue[] = $path;
46 }
47 }
48 return (string) $max;
49 }
50
51 /**
52 * Registers the OpenStation CSS and JS handles.
53 */
54 function openstation_register_assets() {
55 $version = OPENSTATION_VERSION;
56 $suffix = openstation_asset_suffix();
57
58 // `filemtime`-stamped version for built bundles. The plugin-wide
59 // `OPENSTATION_VERSION` is bumped per release, but the bundles
60 // iterate faster — without a per-file mtime stamp, two clients
61 // loading the same `?ver=…` URL can be served different bytes
62 // (whichever build was on disk at upload time). Stamping with the
63 // file's modification time guarantees the URL changes whenever the
64 // file does, so "is my fix deployed?" is answerable from the
65 // network tab. Falls back to `$version` when the file is missing
66 // (test envs that import this file before the build runs).
67 $built_version = static function ( $relative ) use ( $version ) {
68 $path = OPENSTATION_DIR . $relative;
69 return file_exists( $path ) ? (string) filemtime( $path ) : $version;
70 };
71
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.
80 wp_register_style(
81 'os-variables',
82 OPENSTATION_URL . 'assets/css/variables.css',
83 array(),
84 $built_version( 'assets/css/variables.css' )
85 );
86 // `filemtime`-stamped so the `<link rel="stylesheet">` URL matches the
87 // `<link rel="preload" as="style">` hint emitted by
88 // `openstation_print_preload_hints()` (which stamps with filemtime).
89 // Registering this with the plain `$version` instead produced two
90 // different `?ver=` query strings for the same file, so the browser
91 // never matched the preload to the stylesheet and logged "preloaded
92 // but not used within a few seconds from the window's load event".
93 wp_register_style(
94 'openstation',
95 OPENSTATION_URL . 'assets/css/desktop.css',
96 array( 'os-variables' ),
97 $built_version( 'assets/css/desktop.css' )
98 );
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` and `os-settings` are deliberately NOT in this
127 * chain — they are registered below, after `os-windows`,
128 * so they can load deferred. Adding a sheet here means splicing it
129 * into the chain, not appending an unrelated dependency: order is
130 * the contract.
131 */
132 $window_sheets = array(
133 'os-window-chrome' => 'assets/css/window-chrome.css',
134 'os-window-states' => 'assets/css/window-states.css',
135 'os-effects' => 'assets/css/effects.css',
136 'os-window-links' => 'assets/css/window-links.css',
137 );
138 $previous = array( 'os-variables', 'dashicons' );
139 foreach ( $window_sheets as $handle => $relative ) {
140 wp_register_style(
141 $handle,
142 OPENSTATION_URL . $relative,
143 $previous,
144 $built_version( $relative )
145 );
146 $previous = array( $handle );
147 }
148
149 // Entry point. Depends on the tail of the chain above, so
150 // enqueuing this one handle still pulls in every critical window
151 // sheet — the behaviour callers had when they were `@import`s.
152 wp_register_style(
153 'os-windows',
154 OPENSTATION_URL . 'assets/css/windows.css',
155 $previous,
156 $built_version( 'assets/css/windows.css' )
157 );
158 // These two load DEFERRED (see `openstation_defer_non_critical_styles()`):
159 // the UI they style — the OS Settings panel and the window
160 // overview — is lazy-loaded JS that can never be on screen at
161 // first paint, so ~47 KB of CSS has no business blocking render.
162 // They depend on `os-windows` so they print after it,
163 // preserving the cascade position they had as `@import`s.
164 wp_register_style(
165 'os-window-overview',
166 OPENSTATION_URL . 'assets/css/window-overview.css',
167 array( 'os-windows' ),
168 $built_version( 'assets/css/window-overview.css' )
169 );
170 wp_register_style(
171 'os-settings',
172 OPENSTATION_URL . 'assets/css/os-settings.css',
173 array( 'os-windows' ),
174 $built_version( 'assets/css/os-settings.css' )
175 );
176 // Solo mode — one window, no desk around it. Loads last so it can
177 // hide surfaces the sheets above declared, and only enqueues on a
178 // solo request, so a normal shell never pays for it.
179 wp_register_style(
180 'os-solo',
181 OPENSTATION_URL . 'assets/css/solo.css',
182 array( 'os-windows' ),
183 $built_version( 'assets/css/solo.css' )
184 );
185 wp_register_style(
186 'os-dock',
187 OPENSTATION_URL . 'assets/css/dock.css',
188 array( 'os-variables', 'dashicons' ),
189 $built_version( 'assets/css/dock.css' )
190 );
191 wp_register_style(
192 'os-dock-peek',
193 OPENSTATION_URL . 'assets/css/dock-peek.css',
194 array( 'os-dock' ),
195 $built_version( 'assets/css/dock-peek.css' )
196 );
197 // The notch — the shell's top-centre voice and the site
198 // assistant's front door. Scoped to `.os-notch`.
199 wp_register_style(
200 'os-notch',
201 OPENSTATION_URL . 'assets/css/notch.css',
202 array( 'os-variables' ),
203 $built_version( 'assets/css/notch.css' )
204 );
205 // Keyboard-shortcuts window. Scoped to `.os-shortcuts`, so it is
206 // inert until the System menu opens the window, and unconditional
207 // for the same reason the layout sheet is: the window can be
208 // opened at any moment and a deferred sheet would paint it raw.
209 wp_register_style(
210 'os-shortcuts',
211 OPENSTATION_URL . 'assets/css/shortcuts.css',
212 array( 'os-variables' ),
213 $built_version( 'assets/css/shortcuts.css' )
214 );
215 // The OpenStation desktop layout — the core/plugin seam on the rail
216 // plus the constellation hover-submenu flyout. Both surfaces are
217 // scoped (`[data-os-layout="openstation"]` / `.os-constellation`),
218 // so the sheet is inert in every other layout and is loaded
219 // unconditionally rather than gated on the user's current pick:
220 // the layout is switchable live from OpenStation Preferences, and a
221 // conditional enqueue would leave the first switch unstyled.
222 wp_register_style(
223 'os-openstation-layout',
224 OPENSTATION_URL . 'assets/css/openstation-layout.css',
225 array( 'os-dock' ),
226 $built_version( 'assets/css/openstation-layout.css' )
227 );
228 // `filemtime`-stamped — the chromeless overrides iterate faster
229 // than the plugin-wide version bumps (per-page compat shims and
230 // page-title-action exceptions land in patches), and a stale
231 // cached copy means the user sees yesterday's rules. Without the
232 // stamp, the browser keeps `?ver=0.8.1` valid for the whole
233 // release cycle.
234 wp_register_style(
235 'os-chromeless',
236 OPENSTATION_URL . 'assets/css/chromeless.css',
237 array( 'openstation' ),
238 $built_version( 'assets/css/chromeless.css' )
239 );
240
241 // `filemtime`-stamped — the AI assistant surface (error states,
242 // inline affordances) iterates faster than plugin version bumps.
243 // Without an mtime stamp the browser keeps `?ver=<plugin-version>`
244 // valid for the whole release cycle and the user keeps seeing
245 // yesterday's CSS even after a hard reload.
246 wp_register_style(
247 'desktop-mode-ai-assistant',
248 OPENSTATION_URL . 'assets/css/ai-assistant.css',
249 array( 'os-variables' ),
250 $built_version( 'assets/css/ai-assistant.css' )
251 );
252
253 wp_register_style(
254 'desktop-mode-bug-report',
255 OPENSTATION_URL . 'assets/css/bug-report.css',
256 array( 'os-variables' ),
257 $built_version( 'assets/css/bug-report.css' )
258 );
259
260 // `filemtime` instead of the plugin-wide `$version` for the
261 // recycle-bin CSS — this file iterates faster than the bundle
262 // and we never want a stale CSS cache to mask a real fix.
263 $recycle_bin_css = OPENSTATION_DIR . 'assets/css/recycle-bin.css';
264 wp_register_style(
265 'desktop-mode-recycle-bin',
266 OPENSTATION_URL . 'assets/css/recycle-bin.css',
267 array( 'os-variables', 'dashicons' ),
268 file_exists( $recycle_bin_css ) ? (string) filemtime( $recycle_bin_css ) : $version
269 );
270
271 // `filemtime` for the native Posts window CSS — same rationale as
272 // the recycle-bin CSS: bundle iterates faster than the plugin
273 // version and stale caches are worse than the cost of a 304.
274 $posts_window_css = OPENSTATION_DIR . 'assets/css/posts-window.css';
275 wp_register_style(
276 'os-posts-window',
277 OPENSTATION_URL . 'assets/css/posts-window.css',
278 array( 'os-variables', 'dashicons' ),
279 file_exists( $posts_window_css ) ? (string) filemtime( $posts_window_css ) : $version
280 );
281
282 // Native Plugins window CSS — same `filemtime`-cache-bust posture
283 // as the Posts/Recycle Bin styles.
284 $plugins_window_css = OPENSTATION_DIR . 'assets/css/plugins-window.css';
285 wp_register_style(
286 'os-plugins-window',
287 OPENSTATION_URL . 'assets/css/plugins-window.css',
288 array( 'os-variables', 'dashicons' ),
289 file_exists( $plugins_window_css ) ? (string) filemtime( $plugins_window_css ) : $version
290 );
291
292 // Native Comments window CSS — same `filemtime` posture.
293 $comments_window_css = OPENSTATION_DIR . 'assets/css/comments-window.css';
294 wp_register_style(
295 'os-comments-window',
296 OPENSTATION_URL . 'assets/css/comments-window.css',
297 array( 'os-variables', 'dashicons' ),
298 file_exists( $comments_window_css ) ? (string) filemtime( $comments_window_css ) : $version
299 );
300
301 // Files-on-the-Desktop tile + layer styles. `filemtime` for the
302 // same reason as the recycle-bin / posts-window CSS: this file
303 // iterates faster than the plugin version, and a stale cache
304 // would mask a real fix.
305 $desktop_files_css = OPENSTATION_DIR . 'assets/css/desktop-files.css';
306 wp_register_style(
307 'os-files',
308 OPENSTATION_URL . 'assets/css/desktop-files.css',
309 array( 'os-variables', 'dashicons' ),
310 file_exists( $desktop_files_css ) ? (string) filemtime( $desktop_files_css ) : $version
311 );
312
313 // Games hub window (launcher grid, scoreboard, challenges) +
314 // per-game styles. Same `filemtime` cache-bust posture as the
315 // other fast-iterating feature stylesheets.
316 $games_css = OPENSTATION_DIR . 'assets/css/games.css';
317 wp_register_style(
318 'desktop-mode-games',
319 OPENSTATION_URL . 'assets/css/games.css',
320 array( 'os-variables', 'dashicons' ),
321 file_exists( $games_css ) ? (string) filemtime( $games_css ) : $version
322 );
323 $game_inkfall_css = OPENSTATION_DIR . 'assets/css/game-inkfall.css';
324 wp_register_style(
325 'os-game-inkfall',
326 OPENSTATION_URL . 'assets/css/game-inkfall.css',
327 array( 'os-variables' ),
328 file_exists( $game_inkfall_css ) ? (string) filemtime( $game_inkfall_css ) : $version
329 );
330 $game_alphabet_soup_css = OPENSTATION_DIR . 'assets/css/game-alphabet-soup.css';
331 wp_register_style(
332 'os-game-alphabet-soup',
333 OPENSTATION_URL . 'assets/css/game-alphabet-soup.css',
334 array( 'os-variables' ),
335 file_exists( $game_alphabet_soup_css ) ? (string) filemtime( $game_alphabet_soup_css ) : $version
336 );
337
338 // Pinned-notes layer styles (paper, pushpin, pastel tokens, pin
339 // animations). Same `filemtime` cache-bust posture as the other
340 // fast-iterating feature stylesheets above.
341 $notes_css = OPENSTATION_DIR . 'assets/css/notes.css';
342 wp_register_style(
343 'os-notes',
344 OPENSTATION_URL . 'assets/css/notes.css',
345 array( 'os-variables', 'dashicons' ),
346 file_exists( $notes_css ) ? (string) filemtime( $notes_css ) : $version
347 );
348
349 // Announcement-dialog styles (scrim, hero card, buttons). Registered
350 // always, enqueued only for a user who is actually owed the
351 // announcement — see the gate in `openstation_enqueue_assets()`. It
352 // is not lazy-loaded on top of that: on the one boot where it does
353 // go out, the dialog opens from the main bundle a second later, and
354 // a fetch racing that would show an unstyled card.
355 $announce_css = OPENSTATION_DIR . 'assets/css/announce.css';
356 wp_register_style(
357 'os-announce',
358 OPENSTATION_URL . 'assets/css/announce.css',
359 array( 'os-variables' ),
360 file_exists( $announce_css ) ? (string) filemtime( $announce_css ) : $version
361 );
362
363 // Scripts.
364 //
365 // `wp-hooks` — the shell exposes a WordPress-style filter/action
366 // API (`window.wp.hooks`) to third-party plugins.
367 // `wp-i18n` — the TS `__()` / `_x()` / `sprintf()` wrappers in
368 // `src/i18n.ts` delegate to `window.wp.i18n` for translation
369 // lookups. Both handles are core-shipped but only pre-enqueued
370 // when Gutenberg-adjacent deps pull them in, so we list them
371 // explicitly to guarantee load order.
372 wp_register_script(
373 'openstation',
374 OPENSTATION_URL . 'assets/js/desktop' . $suffix . '.js',
375 // `heartbeat` + `jquery` — the recycle-bin badge module
376 // (loaded as part of this bundle) opts into the WordPress
377 // Heartbeat API so the count tile / desktop-icon badge
378 // stays in sync even when the bin window is closed.
379 array( 'wp-hooks', 'wp-i18n', 'heartbeat', 'jquery' ),
380 $built_version( 'assets/js/desktop' . $suffix . '.js' ),
381 // Footer + defer: the shell boots on DOMContentLoaded anyway,
382 // so deferring frees the parser instead of blocking at the
383 // footer print point. Both inline payloads attached to this
384 // handle (`__openStationMenuCommands`, the jazz-quote version
385 // stamp) are `'before'`-position, which WP keeps as blocking
386 // inline ahead of a deferred tag — order is preserved. On WP
387 // < 6.3 the array collapses to a truthy `$in_footer`, same as
388 // before.
389 array(
390 'in_footer' => true,
391 'strategy' => 'defer',
392 )
393 );
394
395 // `os-iframe-bridge` — opt-in iframe-side bridge that
396 // provides `wp.os.iframe.publish/subscribe/onConnection/
397 // requestConnection` to any same-origin iframe that enqueues it.
398 // Same code is also injected inline by the chromeless bridge
399 // (so chromeless wp-admin pages don't need a separate enqueue)
400 // and auto-injected when a native window opts in via
401 // `iframeContent: { bridge: true }`. Plugins targeting their
402 // own iframe pages just enqueue this handle.
403 wp_register_script(
404 'os-iframe-bridge',
405 OPENSTATION_URL . 'assets/js/iframe-bridge' . $suffix . '.js',
406 array(),
407 $built_version( 'assets/js/iframe-bridge' . $suffix . '.js' ),
408 true
409 );
410
411 // `os-gutenberg-drop-receiver` — iframe-side bundle
412 // enqueued only on the Block Editor screens (`post.php` /
413 // `post-new.php`) for openstation users. Listens for
414 // `os-drop` postMessages from the parent shell (see
415 // `src/drag/iframe-drop-targets.ts`) and inserts the matching
416 // Gutenberg block via `wp.data.dispatch('core/block-editor')`.
417 // Depends on `wp-blocks` + `wp-data` so the editor stores are
418 // guaranteed enqueued before the receiver runs its first message
419 // handler.
420 wp_register_script(
421 'os-gutenberg-drop-receiver',
422 OPENSTATION_URL . 'assets/js/gutenberg-drop-receiver' . $suffix . '.js',
423 array( 'wp-blocks', 'wp-data' ),
424 $built_version( 'assets/js/gutenberg-drop-receiver' . $suffix . '.js' ),
425 true
426 );
427
428 // `desktop-mode-recycle-bin` — small bundle for the Recycle Bin
429 // native window. Lazy-loaded by the native-window sync the first
430 // time the bin opens; registers a render callback on
431 // `window.openStationNativeWindows['desktop-mode-recycle-bin']`.
432 $recycle_bin_js = OPENSTATION_DIR . 'assets/js/recycle-bin' . $suffix . '.js';
433 wp_register_script(
434 'desktop-mode-recycle-bin',
435 OPENSTATION_URL . 'assets/js/recycle-bin' . $suffix . '.js',
436 // `heartbeat` + `jquery` — the bin opts in to the WordPress
437 // Heartbeat API while its window is open as the catch-all
438 // real-time channel for deletes that don't render an admin
439 // footer (REST/AJAX/other tabs/WP-CLI). See
440 // `src/recycle-bin/realtime.ts` for the subscriber.
441 array( 'wp-i18n', 'heartbeat', 'jquery' ),
442 file_exists( $recycle_bin_js ) ? (string) filemtime( $recycle_bin_js ) : $version,
443 true
444 );
445 wp_set_script_translations(
446 'desktop-mode-recycle-bin',
447 'desktop-mode',
448 OPENSTATION_DIR . 'languages'
449 );
450
451 // `desktop-mode-games` — bundle for the Games hub native window
452 // (launcher grid, scoreboard, challenges client). Lazy-loaded by
453 // the native-window sync the first time the hub opens; registers a
454 // render callback on
455 // `window.openStationNativeWindows['desktop-mode-games']`.
456 // `heartbeat` + `jquery` — the challenges client rides the
457 // WordPress Heartbeat bus for live delivery.
458 $games_js = OPENSTATION_DIR . 'assets/js/games' . $suffix . '.js';
459 wp_register_script(
460 'desktop-mode-games',
461 OPENSTATION_URL . 'assets/js/games' . $suffix . '.js',
462 array( 'wp-i18n', 'heartbeat', 'jquery' ),
463 file_exists( $games_js ) ? (string) filemtime( $games_js ) : $version,
464 true
465 );
466 wp_set_script_translations(
467 'desktop-mode-games',
468 'desktop-mode',
469 OPENSTATION_DIR . 'languages'
470 );
471
472 // `os-game-inkfall` — the Inkfall game bundle. Loaded
473 // lazily by the games framework on first launch; publishes the
474 // game def on `window.openStationGames.inkfall`.
475 $game_inkfall_js = OPENSTATION_DIR . 'assets/js/game-inkfall' . $suffix . '.js';
476 wp_register_script(
477 'os-game-inkfall',
478 OPENSTATION_URL . 'assets/js/game-inkfall' . $suffix . '.js',
479 array( 'wp-i18n' ),
480 file_exists( $game_inkfall_js ) ? (string) filemtime( $game_inkfall_js ) : $version,
481 true
482 );
483 wp_set_script_translations(
484 'os-game-inkfall',
485 'desktop-mode',
486 OPENSTATION_DIR . 'languages'
487 );
488
489 // `os-game-alphabet-soup` — the Alphabet Soup game
490 // bundle. Loaded lazily by the games framework on first launch;
491 // publishes the game def on
492 // `window.openStationGames['alphabet-soup']`.
493 $game_alphabet_soup_js = OPENSTATION_DIR . 'assets/js/game-alphabet-soup' . $suffix . '.js';
494 wp_register_script(
495 'os-game-alphabet-soup',
496 OPENSTATION_URL . 'assets/js/game-alphabet-soup' . $suffix . '.js',
497 array( 'wp-i18n' ),
498 file_exists( $game_alphabet_soup_js ) ? (string) filemtime( $game_alphabet_soup_js ) : $version,
499 true
500 );
501 wp_set_script_translations(
502 'os-game-alphabet-soup',
503 'desktop-mode',
504 OPENSTATION_DIR . 'languages'
505 );
506
507 // `os-posts-window` — small bundle for the native Posts
508 // window. Lazy-loaded by the native-window sync the first time the
509 // window opens (via the dock-click swap when the user opts in);
510 // registers a render callback on
511 // `window.openStationNativeWindows['desktop-mode-posts']`.
512 $posts_window_js = OPENSTATION_DIR . 'assets/js/posts-window' . $suffix . '.js';
513 wp_register_script(
514 'os-posts-window',
515 OPENSTATION_URL . 'assets/js/posts-window' . $suffix . '.js',
516 array( 'wp-i18n' ),
517 file_exists( $posts_window_js ) ? (string) filemtime( $posts_window_js ) : $version,
518 true
519 );
520 wp_set_script_translations(
521 'os-posts-window',
522 'desktop-mode',
523 OPENSTATION_DIR . 'languages'
524 );
525
526 // `os-plugins-window` — small bundle for the native
527 // Plugins window. Lazy-loaded by the native-window sync the first
528 // time the window opens (via the dock-click swap when the user
529 // opts in); registers a render callback on
530 // `window.openStationNativeWindows['desktop-mode-plugins']`.
531 $plugins_window_js = OPENSTATION_DIR . 'assets/js/plugins-window' . $suffix . '.js';
532 wp_register_script(
533 'os-plugins-window',
534 OPENSTATION_URL . 'assets/js/plugins-window' . $suffix . '.js',
535 array( 'wp-i18n' ),
536 file_exists( $plugins_window_js ) ? (string) filemtime( $plugins_window_js ) : $version,
537 true
538 );
539 wp_set_script_translations(
540 'os-plugins-window',
541 'desktop-mode',
542 OPENSTATION_DIR . 'languages'
543 );
544
545 // `os-comments-window` — small bundle for the native
546 // Comments window. Lazy-loaded by the native-window sync the first
547 // time the window opens (via the dock-click swap when the user
548 // opts in); registers a render callback on
549 // `window.openStationNativeWindows['desktop-mode-comments']`.
550 $comments_window_js = OPENSTATION_DIR . 'assets/js/comments-window' . $suffix . '.js';
551 wp_register_script(
552 'os-comments-window',
553 OPENSTATION_URL . 'assets/js/comments-window' . $suffix . '.js',
554 array( 'wp-i18n' ),
555 file_exists( $comments_window_js ) ? (string) filemtime( $comments_window_js ) : $version,
556 true
557 );
558 wp_set_script_translations(
559 'os-comments-window',
560 'desktop-mode',
561 OPENSTATION_DIR . 'languages'
562 );
563
564 // `os-animated-logo-wallpaper` — built-in PixiJS canvas
565 // wallpaper, moved out of `desktop.min.js`. The wallpaper
566 // `server-sync` loads this handle when the user selects the
567 // `wp-animated-logo` wallpaper (or opens OS Settings → Wallpaper
568 // and the picker pulls every registered canvas def in). The
569 // bundle's only side effect is publishing the `WallpaperDef` on
570 // `window.openStationWallpapers['wp-animated-logo']`.
571 $animated_logo_js = OPENSTATION_DIR . 'assets/js/animated-logo-wallpaper' . $suffix . '.js';
572 wp_register_script(
573 'os-animated-logo-wallpaper',
574 OPENSTATION_URL . 'assets/js/animated-logo-wallpaper' . $suffix . '.js',
575 array( 'wp-hooks' ),
576 file_exists( $animated_logo_js ) ? (string) filemtime( $animated_logo_js ) : $version,
577 true
578 );
579
580 // `os-snow-wallpaper` — built-in PixiJS canvas
581 // wallpaper: snowfall that accumulates on window tops and melts
582 // away. Same lazy-load path as the animated logo: the wallpaper
583 // `server-sync` injects this handle when the user selects the
584 // `wp-snow` wallpaper (or opens OS Settings → Wallpaper and the
585 // picker pulls the def in). The bundle's only side effect is
586 // publishing the `WallpaperDef` on
587 // `window.openStationWallpapers['wp-snow']`.
588 $snow_js = OPENSTATION_DIR . 'assets/js/snow-wallpaper' . $suffix . '.js';
589 wp_register_script(
590 'os-snow-wallpaper',
591 OPENSTATION_URL . 'assets/js/snow-wallpaper' . $suffix . '.js',
592 array( 'wp-hooks', 'wp-i18n' ),
593 file_exists( $snow_js ) ? (string) filemtime( $snow_js ) : $version,
594 true
595 );
596 wp_set_script_translations(
597 'os-snow-wallpaper',
598 'desktop-mode',
599 OPENSTATION_DIR . 'languages'
600 );
601
602 // `desktop-mode-ai-assistant` — AI Copilot spotlight overlay,
603 // moved out of `desktop.min.js`. The main bundle ships a
604 // stub matching the public `wp.os.ai` contract; the stub
605 // `<script>`-injects this handle the first time the user opens
606 // the assistant (Cmd+K or admin-bar button).
607 $ai_assistant_js = OPENSTATION_DIR . 'assets/js/ai-assistant' . $suffix . '.js';
608 wp_register_script(
609 'desktop-mode-ai-assistant',
610 OPENSTATION_URL . 'assets/js/ai-assistant' . $suffix . '.js',
611 array( 'wp-hooks', 'wp-i18n' ),
612 file_exists( $ai_assistant_js ) ? (string) filemtime( $ai_assistant_js ) : $version,
613 true
614 );
615 wp_set_script_translations(
616 'desktop-mode-ai-assistant',
617 'desktop-mode',
618 OPENSTATION_DIR . 'languages'
619 );
620
621 // Wire the translation bundle to this script handle. WP looks
622 // for `languages/os-{locale}-os.json` and
623 // injects its `locale_data` into `wp.i18n` just before the
624 // script runs — so every `__()` call resolves to the right
625 // language without any runtime fetch.
626 wp_set_script_translations(
627 'openstation',
628 'desktop-mode',
629 OPENSTATION_DIR . 'languages'
630 );
631 }
632 add_action( 'init', 'openstation_register_assets' );
633