PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.3
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.3
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.3, at includes/assets.php

636 lines 24.9 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. Depends on `os-files`
341 // because a pinned note dresses the canonical `.os-file-tile`
342 // chrome — anything that restyles a tile has to print after the
343 // file that declares one.
344 $notes_css = OPENSTATION_DIR . 'assets/css/notes.css';
345 wp_register_style(
346 'os-notes',
347 OPENSTATION_URL . 'assets/css/notes.css',
348 array( 'os-variables', 'dashicons', 'os-files' ),
349 file_exists( $notes_css ) ? (string) filemtime( $notes_css ) : $version
350 );
351
352 // Announcement-dialog styles (scrim, hero card, buttons). Registered
353 // always, enqueued only for a user who is actually owed the
354 // announcement — see the gate in `openstation_enqueue_assets()`. It
355 // is not lazy-loaded on top of that: on the one boot where it does
356 // go out, the dialog opens from the main bundle a second later, and
357 // a fetch racing that would show an unstyled card.
358 $announce_css = OPENSTATION_DIR . 'assets/css/announce.css';
359 wp_register_style(
360 'os-announce',
361 OPENSTATION_URL . 'assets/css/announce.css',
362 array( 'os-variables' ),
363 file_exists( $announce_css ) ? (string) filemtime( $announce_css ) : $version
364 );
365
366 // Scripts.
367 //
368 // `wp-hooks` — the shell exposes a WordPress-style filter/action
369 // API (`window.wp.hooks`) to third-party plugins.
370 // `wp-i18n` — the TS `__()` / `_x()` / `sprintf()` wrappers in
371 // `src/i18n.ts` delegate to `window.wp.i18n` for translation
372 // lookups. Both handles are core-shipped but only pre-enqueued
373 // when Gutenberg-adjacent deps pull them in, so we list them
374 // explicitly to guarantee load order.
375 wp_register_script(
376 'openstation',
377 OPENSTATION_URL . 'assets/js/desktop' . $suffix . '.js',
378 // `heartbeat` + `jquery` — the recycle-bin badge module
379 // (loaded as part of this bundle) opts into the WordPress
380 // Heartbeat API so the count tile / desktop-icon badge
381 // stays in sync even when the bin window is closed.
382 array( 'wp-hooks', 'wp-i18n', 'heartbeat', 'jquery' ),
383 $built_version( 'assets/js/desktop' . $suffix . '.js' ),
384 // Footer + defer: the shell boots on DOMContentLoaded anyway,
385 // so deferring frees the parser instead of blocking at the
386 // footer print point. Both inline payloads attached to this
387 // handle (`__openStationMenuCommands`, the jazz-quote version
388 // stamp) are `'before'`-position, which WP keeps as blocking
389 // inline ahead of a deferred tag — order is preserved. On WP
390 // < 6.3 the array collapses to a truthy `$in_footer`, same as
391 // before.
392 array(
393 'in_footer' => true,
394 'strategy' => 'defer',
395 )
396 );
397
398 // `os-iframe-bridge` — opt-in iframe-side bridge that
399 // provides `wp.os.iframe.publish/subscribe/onConnection/
400 // requestConnection` to any same-origin iframe that enqueues it.
401 // Same code is also injected inline by the chromeless bridge
402 // (so chromeless wp-admin pages don't need a separate enqueue)
403 // and auto-injected when a native window opts in via
404 // `iframeContent: { bridge: true }`. Plugins targeting their
405 // own iframe pages just enqueue this handle.
406 wp_register_script(
407 'os-iframe-bridge',
408 OPENSTATION_URL . 'assets/js/iframe-bridge' . $suffix . '.js',
409 array(),
410 $built_version( 'assets/js/iframe-bridge' . $suffix . '.js' ),
411 true
412 );
413
414 // `os-gutenberg-drop-receiver` — iframe-side bundle
415 // enqueued only on the Block Editor screens (`post.php` /
416 // `post-new.php`) for openstation users. Listens for
417 // `os-drop` postMessages from the parent shell (see
418 // `src/drag/iframe-drop-targets.ts`) and inserts the matching
419 // Gutenberg block via `wp.data.dispatch('core/block-editor')`.
420 // Depends on `wp-blocks` + `wp-data` so the editor stores are
421 // guaranteed enqueued before the receiver runs its first message
422 // handler.
423 wp_register_script(
424 'os-gutenberg-drop-receiver',
425 OPENSTATION_URL . 'assets/js/gutenberg-drop-receiver' . $suffix . '.js',
426 array( 'wp-blocks', 'wp-data' ),
427 $built_version( 'assets/js/gutenberg-drop-receiver' . $suffix . '.js' ),
428 true
429 );
430
431 // `desktop-mode-recycle-bin` — small bundle for the Recycle Bin
432 // native window. Lazy-loaded by the native-window sync the first
433 // time the bin opens; registers a render callback on
434 // `window.openStationNativeWindows['desktop-mode-recycle-bin']`.
435 $recycle_bin_js = OPENSTATION_DIR . 'assets/js/recycle-bin' . $suffix . '.js';
436 wp_register_script(
437 'desktop-mode-recycle-bin',
438 OPENSTATION_URL . 'assets/js/recycle-bin' . $suffix . '.js',
439 // `heartbeat` + `jquery` — the bin opts in to the WordPress
440 // Heartbeat API while its window is open as the catch-all
441 // real-time channel for deletes that don't render an admin
442 // footer (REST/AJAX/other tabs/WP-CLI). See
443 // `src/recycle-bin/realtime.ts` for the subscriber.
444 array( 'wp-i18n', 'heartbeat', 'jquery' ),
445 file_exists( $recycle_bin_js ) ? (string) filemtime( $recycle_bin_js ) : $version,
446 true
447 );
448 wp_set_script_translations(
449 'desktop-mode-recycle-bin',
450 'desktop-mode',
451 OPENSTATION_DIR . 'languages'
452 );
453
454 // `desktop-mode-games` — bundle for the Games hub native window
455 // (launcher grid, scoreboard, challenges client). Lazy-loaded by
456 // the native-window sync the first time the hub opens; registers a
457 // render callback on
458 // `window.openStationNativeWindows['desktop-mode-games']`.
459 // `heartbeat` + `jquery` — the challenges client rides the
460 // WordPress Heartbeat bus for live delivery.
461 $games_js = OPENSTATION_DIR . 'assets/js/games' . $suffix . '.js';
462 wp_register_script(
463 'desktop-mode-games',
464 OPENSTATION_URL . 'assets/js/games' . $suffix . '.js',
465 array( 'wp-i18n', 'heartbeat', 'jquery' ),
466 file_exists( $games_js ) ? (string) filemtime( $games_js ) : $version,
467 true
468 );
469 wp_set_script_translations(
470 'desktop-mode-games',
471 'desktop-mode',
472 OPENSTATION_DIR . 'languages'
473 );
474
475 // `os-game-inkfall` — the Inkfall game bundle. Loaded
476 // lazily by the games framework on first launch; publishes the
477 // game def on `window.openStationGames.inkfall`.
478 $game_inkfall_js = OPENSTATION_DIR . 'assets/js/game-inkfall' . $suffix . '.js';
479 wp_register_script(
480 'os-game-inkfall',
481 OPENSTATION_URL . 'assets/js/game-inkfall' . $suffix . '.js',
482 array( 'wp-i18n' ),
483 file_exists( $game_inkfall_js ) ? (string) filemtime( $game_inkfall_js ) : $version,
484 true
485 );
486 wp_set_script_translations(
487 'os-game-inkfall',
488 'desktop-mode',
489 OPENSTATION_DIR . 'languages'
490 );
491
492 // `os-game-alphabet-soup` — the Alphabet Soup game
493 // bundle. Loaded lazily by the games framework on first launch;
494 // publishes the game def on
495 // `window.openStationGames['alphabet-soup']`.
496 $game_alphabet_soup_js = OPENSTATION_DIR . 'assets/js/game-alphabet-soup' . $suffix . '.js';
497 wp_register_script(
498 'os-game-alphabet-soup',
499 OPENSTATION_URL . 'assets/js/game-alphabet-soup' . $suffix . '.js',
500 array( 'wp-i18n' ),
501 file_exists( $game_alphabet_soup_js ) ? (string) filemtime( $game_alphabet_soup_js ) : $version,
502 true
503 );
504 wp_set_script_translations(
505 'os-game-alphabet-soup',
506 'desktop-mode',
507 OPENSTATION_DIR . 'languages'
508 );
509
510 // `os-posts-window` — small bundle for the native Posts
511 // window. Lazy-loaded by the native-window sync the first time the
512 // window opens (via the dock-click swap when the user opts in);
513 // registers a render callback on
514 // `window.openStationNativeWindows['desktop-mode-posts']`.
515 $posts_window_js = OPENSTATION_DIR . 'assets/js/posts-window' . $suffix . '.js';
516 wp_register_script(
517 'os-posts-window',
518 OPENSTATION_URL . 'assets/js/posts-window' . $suffix . '.js',
519 array( 'wp-i18n' ),
520 file_exists( $posts_window_js ) ? (string) filemtime( $posts_window_js ) : $version,
521 true
522 );
523 wp_set_script_translations(
524 'os-posts-window',
525 'desktop-mode',
526 OPENSTATION_DIR . 'languages'
527 );
528
529 // `os-plugins-window` — small bundle for the native
530 // Plugins window. Lazy-loaded by the native-window sync the first
531 // time the window opens (via the dock-click swap when the user
532 // opts in); registers a render callback on
533 // `window.openStationNativeWindows['desktop-mode-plugins']`.
534 $plugins_window_js = OPENSTATION_DIR . 'assets/js/plugins-window' . $suffix . '.js';
535 wp_register_script(
536 'os-plugins-window',
537 OPENSTATION_URL . 'assets/js/plugins-window' . $suffix . '.js',
538 array( 'wp-i18n' ),
539 file_exists( $plugins_window_js ) ? (string) filemtime( $plugins_window_js ) : $version,
540 true
541 );
542 wp_set_script_translations(
543 'os-plugins-window',
544 'desktop-mode',
545 OPENSTATION_DIR . 'languages'
546 );
547
548 // `os-comments-window` — small bundle for the native
549 // Comments window. Lazy-loaded by the native-window sync the first
550 // time the window opens (via the dock-click swap when the user
551 // opts in); registers a render callback on
552 // `window.openStationNativeWindows['desktop-mode-comments']`.
553 $comments_window_js = OPENSTATION_DIR . 'assets/js/comments-window' . $suffix . '.js';
554 wp_register_script(
555 'os-comments-window',
556 OPENSTATION_URL . 'assets/js/comments-window' . $suffix . '.js',
557 array( 'wp-i18n' ),
558 file_exists( $comments_window_js ) ? (string) filemtime( $comments_window_js ) : $version,
559 true
560 );
561 wp_set_script_translations(
562 'os-comments-window',
563 'desktop-mode',
564 OPENSTATION_DIR . 'languages'
565 );
566
567 // `os-animated-logo-wallpaper` — built-in PixiJS canvas
568 // wallpaper, moved out of `desktop.min.js`. The wallpaper
569 // `server-sync` loads this handle when the user selects the
570 // `wp-animated-logo` wallpaper (or opens OS Settings → Wallpaper
571 // and the picker pulls every registered canvas def in). The
572 // bundle's only side effect is publishing the `WallpaperDef` on
573 // `window.openStationWallpapers['wp-animated-logo']`.
574 $animated_logo_js = OPENSTATION_DIR . 'assets/js/animated-logo-wallpaper' . $suffix . '.js';
575 wp_register_script(
576 'os-animated-logo-wallpaper',
577 OPENSTATION_URL . 'assets/js/animated-logo-wallpaper' . $suffix . '.js',
578 array( 'wp-hooks' ),
579 file_exists( $animated_logo_js ) ? (string) filemtime( $animated_logo_js ) : $version,
580 true
581 );
582
583 // `os-snow-wallpaper` — built-in PixiJS canvas
584 // wallpaper: snowfall that accumulates on window tops and melts
585 // away. Same lazy-load path as the animated logo: the wallpaper
586 // `server-sync` injects this handle when the user selects the
587 // `wp-snow` wallpaper (or opens OS Settings → Wallpaper and the
588 // picker pulls the def in). The bundle's only side effect is
589 // publishing the `WallpaperDef` on
590 // `window.openStationWallpapers['wp-snow']`.
591 $snow_js = OPENSTATION_DIR . 'assets/js/snow-wallpaper' . $suffix . '.js';
592 wp_register_script(
593 'os-snow-wallpaper',
594 OPENSTATION_URL . 'assets/js/snow-wallpaper' . $suffix . '.js',
595 array( 'wp-hooks', 'wp-i18n' ),
596 file_exists( $snow_js ) ? (string) filemtime( $snow_js ) : $version,
597 true
598 );
599 wp_set_script_translations(
600 'os-snow-wallpaper',
601 'desktop-mode',
602 OPENSTATION_DIR . 'languages'
603 );
604
605 // `desktop-mode-ai-assistant` — AI Copilot spotlight overlay,
606 // moved out of `desktop.min.js`. The main bundle ships a
607 // stub matching the public `wp.os.ai` contract; the stub
608 // `<script>`-injects this handle the first time the user opens
609 // the assistant (Cmd+K or admin-bar button).
610 $ai_assistant_js = OPENSTATION_DIR . 'assets/js/ai-assistant' . $suffix . '.js';
611 wp_register_script(
612 'desktop-mode-ai-assistant',
613 OPENSTATION_URL . 'assets/js/ai-assistant' . $suffix . '.js',
614 array( 'wp-hooks', 'wp-i18n' ),
615 file_exists( $ai_assistant_js ) ? (string) filemtime( $ai_assistant_js ) : $version,
616 true
617 );
618 wp_set_script_translations(
619 'desktop-mode-ai-assistant',
620 'desktop-mode',
621 OPENSTATION_DIR . 'languages'
622 );
623
624 // Wire the translation bundle to this script handle. WP looks
625 // for `languages/os-{locale}-os.json` and
626 // injects its `locale_data` into `wp.i18n` just before the
627 // script runs — so every `__()` call resolves to the right
628 // language without any runtime fetch.
629 wp_set_script_translations(
630 'openstation',
631 'desktop-mode',
632 OPENSTATION_DIR . 'languages'
633 );
634 }
635 add_action( 'init', 'openstation_register_assets' );
636