PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.8
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.8
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 0.9.8, at includes/assets.php

579 lines 23.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Desktop Mode asset registration.
4 *
5 * Registers all desktop-mode CSS and JS handles with WordPress so they can
6 * be enqueued from anywhere in the plugin (or by third parties).
7 *
8 * @package WPDesktopMode
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 desktop_mode_css_subtree_version( $relative, $fallback ) {
25 $root = DESKTOP_MODE_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 desktop mode CSS and JS handles.
53 */
54 function desktop_mode_register_assets() {
55 $version = DESKTOP_MODE_VERSION;
56 $suffix = desktop_mode_asset_suffix();
57
58 // `filemtime`-stamped version for built bundles. The plugin-wide
59 // `DESKTOP_MODE_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 = DESKTOP_MODE_DIR . $relative;
69 return file_exists( $path ) ? (string) filemtime( $path ) : $version;
70 };
71
72
73 // Styles.
74 // `filemtime`-stamped, NOT the plugin-wide `$version`. This file
75 // is the token catalogue every other sheet resolves `var()`s
76 // against, and it changes whenever the palette does — which is a
77 // lot more often than the plugin version is bumped. With a static
78 // stamp the browser holds the old palette until a hard reload,
79 // and the symptom is maddening: a themed shell where some
80 // surfaces update and others don't.
81 wp_register_style(
82 'desktop-mode-variables',
83 DESKTOP_MODE_URL . 'assets/css/variables.css',
84 array(),
85 $built_version( 'assets/css/variables.css' )
86 );
87 // `filemtime`-stamped so the `<link rel="stylesheet">` URL matches the
88 // `<link rel="preload" as="style">` hint emitted by
89 // `desktop_mode_print_preload_hints()` (which stamps with filemtime).
90 // Registering this with the plain `$version` instead produced two
91 // different `?ver=` query strings for the same file, so the browser
92 // never matched the preload to the stylesheet and logged "preloaded
93 // but not used within a few seconds from the window's load event".
94 wp_register_style(
95 'desktop-mode',
96 DESKTOP_MODE_URL . 'assets/css/desktop.css',
97 array( 'desktop-mode-variables' ),
98 $built_version( 'assets/css/desktop.css' )
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 `desktop-mode-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 * `desktop_mode_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 * `desktop-mode-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 `desktop-mode-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 'desktop-mode-window-chrome' => 'assets/css/window-chrome.css',
134 'desktop-mode-window-states' => 'assets/css/window-states.css',
135 'desktop-mode-effects' => 'assets/css/effects.css',
136 'desktop-mode-window-links' => 'assets/css/window-links.css',
137 );
138 $previous = array( 'desktop-mode-variables', 'dashicons' );
139 foreach ( $window_sheets as $handle => $relative ) {
140 wp_register_style(
141 $handle,
142 DESKTOP_MODE_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 'desktop-mode-windows',
154 DESKTOP_MODE_URL . 'assets/css/windows.css',
155 $previous,
156 $built_version( 'assets/css/windows.css' )
157 );
158 // These two load DEFERRED (see `desktop_mode_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 `desktop-mode-windows` so they print after it,
163 // preserving the cascade position they had as `@import`s.
164 wp_register_style(
165 'desktop-mode-window-overview',
166 DESKTOP_MODE_URL . 'assets/css/window-overview.css',
167 array( 'desktop-mode-windows' ),
168 $built_version( 'assets/css/window-overview.css' )
169 );
170 wp_register_style(
171 'desktop-mode-os-settings',
172 DESKTOP_MODE_URL . 'assets/css/os-settings.css',
173 array( 'desktop-mode-windows' ),
174 $built_version( 'assets/css/os-settings.css' )
175 );
176 wp_register_style(
177 'desktop-mode-dock',
178 DESKTOP_MODE_URL . 'assets/css/dock.css',
179 array( 'desktop-mode-variables', 'dashicons' ),
180 $built_version( 'assets/css/dock.css' )
181 );
182 wp_register_style(
183 'desktop-mode-dock-peek',
184 DESKTOP_MODE_URL . 'assets/css/dock-peek.css',
185 array( 'desktop-mode-dock' ),
186 $built_version( 'assets/css/dock-peek.css' )
187 );
188 // `filemtime`-stamped — the chromeless overrides iterate faster
189 // than the plugin-wide version bumps (per-page compat shims and
190 // page-title-action exceptions land in patches), and a stale
191 // cached copy means the user sees yesterday's rules. Without the
192 // stamp, the browser keeps `?ver=0.8.1` valid for the whole
193 // release cycle.
194 wp_register_style(
195 'desktop-mode-chromeless',
196 DESKTOP_MODE_URL . 'assets/css/chromeless.css',
197 array( 'desktop-mode' ),
198 $built_version( 'assets/css/chromeless.css' )
199 );
200
201 // `filemtime`-stamped — the AI assistant surface (error states,
202 // inline affordances) iterates faster than plugin version bumps.
203 // Without an mtime stamp the browser keeps `?ver=<plugin-version>`
204 // valid for the whole release cycle and the user keeps seeing
205 // yesterday's CSS even after a hard reload.
206 wp_register_style(
207 'desktop-mode-ai-assistant',
208 DESKTOP_MODE_URL . 'assets/css/ai-assistant.css',
209 array( 'desktop-mode-variables' ),
210 $built_version( 'assets/css/ai-assistant.css' )
211 );
212
213 wp_register_style(
214 'desktop-mode-bug-report',
215 DESKTOP_MODE_URL . 'assets/css/bug-report.css',
216 array( 'desktop-mode-variables' ),
217 $built_version( 'assets/css/bug-report.css' )
218 );
219
220 // `filemtime` instead of the plugin-wide `$version` for the
221 // recycle-bin CSS — this file iterates faster than the bundle
222 // and we never want a stale CSS cache to mask a real fix.
223 $recycle_bin_css = DESKTOP_MODE_DIR . 'assets/css/recycle-bin.css';
224 wp_register_style(
225 'desktop-mode-recycle-bin',
226 DESKTOP_MODE_URL . 'assets/css/recycle-bin.css',
227 array( 'desktop-mode-variables', 'dashicons' ),
228 file_exists( $recycle_bin_css ) ? (string) filemtime( $recycle_bin_css ) : $version
229 );
230
231 // `filemtime` for the native Posts window CSS — same rationale as
232 // the recycle-bin CSS: bundle iterates faster than the plugin
233 // version and stale caches are worse than the cost of a 304.
234 $posts_window_css = DESKTOP_MODE_DIR . 'assets/css/posts-window.css';
235 wp_register_style(
236 'desktop-mode-posts-window',
237 DESKTOP_MODE_URL . 'assets/css/posts-window.css',
238 array( 'desktop-mode-variables', 'dashicons' ),
239 file_exists( $posts_window_css ) ? (string) filemtime( $posts_window_css ) : $version
240 );
241
242 // Native Plugins window CSS — same `filemtime`-cache-bust posture
243 // as the Posts/Recycle Bin styles.
244 $plugins_window_css = DESKTOP_MODE_DIR . 'assets/css/plugins-window.css';
245 wp_register_style(
246 'desktop-mode-plugins-window',
247 DESKTOP_MODE_URL . 'assets/css/plugins-window.css',
248 array( 'desktop-mode-variables', 'dashicons' ),
249 file_exists( $plugins_window_css ) ? (string) filemtime( $plugins_window_css ) : $version
250 );
251
252 // Native Comments window CSS — same `filemtime` posture.
253 $comments_window_css = DESKTOP_MODE_DIR . 'assets/css/comments-window.css';
254 wp_register_style(
255 'desktop-mode-comments-window',
256 DESKTOP_MODE_URL . 'assets/css/comments-window.css',
257 array( 'desktop-mode-variables', 'dashicons' ),
258 file_exists( $comments_window_css ) ? (string) filemtime( $comments_window_css ) : $version
259 );
260
261 // Files-on-the-Desktop tile + layer styles. `filemtime` for the
262 // same reason as the recycle-bin / posts-window CSS: this file
263 // iterates faster than the plugin version, and a stale cache
264 // would mask a real fix.
265 $desktop_files_css = DESKTOP_MODE_DIR . 'assets/css/desktop-files.css';
266 wp_register_style(
267 'desktop-mode-files',
268 DESKTOP_MODE_URL . 'assets/css/desktop-files.css',
269 array( 'desktop-mode-variables', 'dashicons' ),
270 file_exists( $desktop_files_css ) ? (string) filemtime( $desktop_files_css ) : $version
271 );
272
273 // Games hub window (launcher grid, scoreboard, challenges) +
274 // per-game styles. Same `filemtime` cache-bust posture as the
275 // other fast-iterating feature stylesheets.
276 $games_css = DESKTOP_MODE_DIR . 'assets/css/games.css';
277 wp_register_style(
278 'desktop-mode-games',
279 DESKTOP_MODE_URL . 'assets/css/games.css',
280 array( 'desktop-mode-variables', 'dashicons' ),
281 file_exists( $games_css ) ? (string) filemtime( $games_css ) : $version
282 );
283 $game_inkfall_css = DESKTOP_MODE_DIR . 'assets/css/game-inkfall.css';
284 wp_register_style(
285 'desktop-mode-game-inkfall',
286 DESKTOP_MODE_URL . 'assets/css/game-inkfall.css',
287 array( 'desktop-mode-variables' ),
288 file_exists( $game_inkfall_css ) ? (string) filemtime( $game_inkfall_css ) : $version
289 );
290 $game_alphabet_soup_css = DESKTOP_MODE_DIR . 'assets/css/game-alphabet-soup.css';
291 wp_register_style(
292 'desktop-mode-game-alphabet-soup',
293 DESKTOP_MODE_URL . 'assets/css/game-alphabet-soup.css',
294 array( 'desktop-mode-variables' ),
295 file_exists( $game_alphabet_soup_css ) ? (string) filemtime( $game_alphabet_soup_css ) : $version
296 );
297
298 // Pinned-notes layer styles (paper, pushpin, pastel tokens, pin
299 // animations). Same `filemtime` cache-bust posture as the other
300 // fast-iterating feature stylesheets above.
301 $notes_css = DESKTOP_MODE_DIR . 'assets/css/notes.css';
302 wp_register_style(
303 'desktop-mode-notes',
304 DESKTOP_MODE_URL . 'assets/css/notes.css',
305 array( 'desktop-mode-variables', 'dashicons' ),
306 file_exists( $notes_css ) ? (string) filemtime( $notes_css ) : $version
307 );
308
309 // Scripts.
310 //
311 // `wp-hooks` — the shell exposes a WordPress-style filter/action
312 // API (`window.wp.hooks`) to third-party plugins.
313 // `wp-i18n` — the TS `__()` / `_x()` / `sprintf()` wrappers in
314 // `src/i18n.ts` delegate to `window.wp.i18n` for translation
315 // lookups. Both handles are core-shipped but only pre-enqueued
316 // when Gutenberg-adjacent deps pull them in, so we list them
317 // explicitly to guarantee load order.
318 wp_register_script(
319 'desktop-mode',
320 DESKTOP_MODE_URL . 'assets/js/desktop' . $suffix . '.js',
321 // `heartbeat` + `jquery` — the recycle-bin badge module
322 // (loaded as part of this bundle) opts into the WordPress
323 // Heartbeat API so the count tile / desktop-icon badge
324 // stays in sync even when the bin window is closed.
325 array( 'wp-hooks', 'wp-i18n', 'heartbeat', 'jquery' ),
326 $built_version( 'assets/js/desktop' . $suffix . '.js' ),
327 // Footer + defer: the shell boots on DOMContentLoaded anyway,
328 // so deferring frees the parser instead of blocking at the
329 // footer print point. Both inline payloads attached to this
330 // handle (`__desktopModeMenuCommands`, the jazz-quote version
331 // stamp) are `'before'`-position, which WP keeps as blocking
332 // inline ahead of a deferred tag — order is preserved. On WP
333 // < 6.3 the array collapses to a truthy `$in_footer`, same as
334 // before.
335 array(
336 'in_footer' => true,
337 'strategy' => 'defer',
338 )
339 );
340
341 // `desktop-mode-iframe-bridge` — opt-in iframe-side bridge that
342 // provides `wp.desktop.iframe.publish/subscribe/onConnection/
343 // requestConnection` to any same-origin iframe that enqueues it.
344 // Same code is also injected inline by the chromeless bridge
345 // (so chromeless wp-admin pages don't need a separate enqueue)
346 // and auto-injected when a native window opts in via
347 // `iframeContent: { bridge: true }`. Plugins targeting their
348 // own iframe pages just enqueue this handle.
349 wp_register_script(
350 'desktop-mode-iframe-bridge',
351 DESKTOP_MODE_URL . 'assets/js/iframe-bridge' . $suffix . '.js',
352 array(),
353 $built_version( 'assets/js/iframe-bridge' . $suffix . '.js' ),
354 true
355 );
356
357 // `desktop-mode-gutenberg-drop-receiver` — iframe-side bundle
358 // enqueued only on the Block Editor screens (`post.php` /
359 // `post-new.php`) for desktop-mode users. Listens for
360 // `desktop-mode-drop` postMessages from the parent shell (see
361 // `src/drag/iframe-drop-targets.ts`) and inserts the matching
362 // Gutenberg block via `wp.data.dispatch('core/block-editor')`.
363 // Depends on `wp-blocks` + `wp-data` so the editor stores are
364 // guaranteed enqueued before the receiver runs its first message
365 // handler.
366 wp_register_script(
367 'desktop-mode-gutenberg-drop-receiver',
368 DESKTOP_MODE_URL . 'assets/js/gutenberg-drop-receiver' . $suffix . '.js',
369 array( 'wp-blocks', 'wp-data' ),
370 $built_version( 'assets/js/gutenberg-drop-receiver' . $suffix . '.js' ),
371 true
372 );
373
374 // `desktop-mode-recycle-bin` — small bundle for the Recycle Bin
375 // native window. Lazy-loaded by the native-window sync the first
376 // time the bin opens; registers a render callback on
377 // `window.desktopModeNativeWindows['desktop-mode-recycle-bin']`.
378 $recycle_bin_js = DESKTOP_MODE_DIR . 'assets/js/recycle-bin' . $suffix . '.js';
379 wp_register_script(
380 'desktop-mode-recycle-bin',
381 DESKTOP_MODE_URL . 'assets/js/recycle-bin' . $suffix . '.js',
382 // `heartbeat` + `jquery` — the bin opts in to the WordPress
383 // Heartbeat API while its window is open as the catch-all
384 // real-time channel for deletes that don't render an admin
385 // footer (REST/AJAX/other tabs/WP-CLI). See
386 // `src/recycle-bin/realtime.ts` for the subscriber.
387 array( 'wp-i18n', 'heartbeat', 'jquery' ),
388 file_exists( $recycle_bin_js ) ? (string) filemtime( $recycle_bin_js ) : $version,
389 true
390 );
391 wp_set_script_translations(
392 'desktop-mode-recycle-bin',
393 'desktop-mode',
394 DESKTOP_MODE_DIR . 'languages'
395 );
396
397 // `desktop-mode-games` — bundle for the Games hub native window
398 // (launcher grid, scoreboard, challenges client). Lazy-loaded by
399 // the native-window sync the first time the hub opens; registers a
400 // render callback on
401 // `window.desktopModeNativeWindows['desktop-mode-games']`.
402 // `heartbeat` + `jquery` — the challenges client rides the
403 // WordPress Heartbeat bus for live delivery.
404 $games_js = DESKTOP_MODE_DIR . 'assets/js/games' . $suffix . '.js';
405 wp_register_script(
406 'desktop-mode-games',
407 DESKTOP_MODE_URL . 'assets/js/games' . $suffix . '.js',
408 array( 'wp-i18n', 'heartbeat', 'jquery' ),
409 file_exists( $games_js ) ? (string) filemtime( $games_js ) : $version,
410 true
411 );
412 wp_set_script_translations(
413 'desktop-mode-games',
414 'desktop-mode',
415 DESKTOP_MODE_DIR . 'languages'
416 );
417
418 // `desktop-mode-game-inkfall` — the Inkfall game bundle. Loaded
419 // lazily by the games framework on first launch; publishes the
420 // game def on `window.desktopModeGames.inkfall`.
421 $game_inkfall_js = DESKTOP_MODE_DIR . 'assets/js/game-inkfall' . $suffix . '.js';
422 wp_register_script(
423 'desktop-mode-game-inkfall',
424 DESKTOP_MODE_URL . 'assets/js/game-inkfall' . $suffix . '.js',
425 array( 'wp-i18n' ),
426 file_exists( $game_inkfall_js ) ? (string) filemtime( $game_inkfall_js ) : $version,
427 true
428 );
429 wp_set_script_translations(
430 'desktop-mode-game-inkfall',
431 'desktop-mode',
432 DESKTOP_MODE_DIR . 'languages'
433 );
434
435 // `desktop-mode-game-alphabet-soup` — the Alphabet Soup game
436 // bundle. Loaded lazily by the games framework on first launch;
437 // publishes the game def on
438 // `window.desktopModeGames['alphabet-soup']`.
439 $game_alphabet_soup_js = DESKTOP_MODE_DIR . 'assets/js/game-alphabet-soup' . $suffix . '.js';
440 wp_register_script(
441 'desktop-mode-game-alphabet-soup',
442 DESKTOP_MODE_URL . 'assets/js/game-alphabet-soup' . $suffix . '.js',
443 array( 'wp-i18n' ),
444 file_exists( $game_alphabet_soup_js ) ? (string) filemtime( $game_alphabet_soup_js ) : $version,
445 true
446 );
447 wp_set_script_translations(
448 'desktop-mode-game-alphabet-soup',
449 'desktop-mode',
450 DESKTOP_MODE_DIR . 'languages'
451 );
452
453 // `desktop-mode-posts-window` — small bundle for the native Posts
454 // window. Lazy-loaded by the native-window sync the first time the
455 // window opens (via the dock-click swap when the user opts in);
456 // registers a render callback on
457 // `window.desktopModeNativeWindows['desktop-mode-posts']`.
458 $posts_window_js = DESKTOP_MODE_DIR . 'assets/js/posts-window' . $suffix . '.js';
459 wp_register_script(
460 'desktop-mode-posts-window',
461 DESKTOP_MODE_URL . 'assets/js/posts-window' . $suffix . '.js',
462 array( 'wp-i18n' ),
463 file_exists( $posts_window_js ) ? (string) filemtime( $posts_window_js ) : $version,
464 true
465 );
466 wp_set_script_translations(
467 'desktop-mode-posts-window',
468 'desktop-mode',
469 DESKTOP_MODE_DIR . 'languages'
470 );
471
472 // `desktop-mode-plugins-window` — small bundle for the native
473 // Plugins window. Lazy-loaded by the native-window sync the first
474 // time the window opens (via the dock-click swap when the user
475 // opts in); registers a render callback on
476 // `window.desktopModeNativeWindows['desktop-mode-plugins']`.
477 $plugins_window_js = DESKTOP_MODE_DIR . 'assets/js/plugins-window' . $suffix . '.js';
478 wp_register_script(
479 'desktop-mode-plugins-window',
480 DESKTOP_MODE_URL . 'assets/js/plugins-window' . $suffix . '.js',
481 array( 'wp-i18n' ),
482 file_exists( $plugins_window_js ) ? (string) filemtime( $plugins_window_js ) : $version,
483 true
484 );
485 wp_set_script_translations(
486 'desktop-mode-plugins-window',
487 'desktop-mode',
488 DESKTOP_MODE_DIR . 'languages'
489 );
490
491 // `desktop-mode-comments-window` — small bundle for the native
492 // Comments window. Lazy-loaded by the native-window sync the first
493 // time the window opens (via the dock-click swap when the user
494 // opts in); registers a render callback on
495 // `window.desktopModeNativeWindows['desktop-mode-comments']`.
496 $comments_window_js = DESKTOP_MODE_DIR . 'assets/js/comments-window' . $suffix . '.js';
497 wp_register_script(
498 'desktop-mode-comments-window',
499 DESKTOP_MODE_URL . 'assets/js/comments-window' . $suffix . '.js',
500 array( 'wp-i18n' ),
501 file_exists( $comments_window_js ) ? (string) filemtime( $comments_window_js ) : $version,
502 true
503 );
504 wp_set_script_translations(
505 'desktop-mode-comments-window',
506 'desktop-mode',
507 DESKTOP_MODE_DIR . 'languages'
508 );
509
510 // `desktop-mode-animated-logo-wallpaper` — built-in PixiJS canvas
511 // wallpaper, moved out of `desktop.min.js`. The wallpaper
512 // `server-sync` loads this handle when the user selects the
513 // `wp-animated-logo` wallpaper (or opens OS Settings → Wallpaper
514 // and the picker pulls every registered canvas def in). The
515 // bundle's only side effect is publishing the `WallpaperDef` on
516 // `window.desktopModeWallpapers['wp-animated-logo']`.
517 $animated_logo_js = DESKTOP_MODE_DIR . 'assets/js/animated-logo-wallpaper' . $suffix . '.js';
518 wp_register_script(
519 'desktop-mode-animated-logo-wallpaper',
520 DESKTOP_MODE_URL . 'assets/js/animated-logo-wallpaper' . $suffix . '.js',
521 array( 'wp-hooks' ),
522 file_exists( $animated_logo_js ) ? (string) filemtime( $animated_logo_js ) : $version,
523 true
524 );
525
526 // `desktop-mode-snow-wallpaper` — built-in PixiJS canvas
527 // wallpaper: snowfall that accumulates on window tops and melts
528 // away. Same lazy-load path as the animated logo: the wallpaper
529 // `server-sync` injects this handle when the user selects the
530 // `wp-snow` wallpaper (or opens OS Settings → Wallpaper and the
531 // picker pulls the def in). The bundle's only side effect is
532 // publishing the `WallpaperDef` on
533 // `window.desktopModeWallpapers['wp-snow']`.
534 $snow_js = DESKTOP_MODE_DIR . 'assets/js/snow-wallpaper' . $suffix . '.js';
535 wp_register_script(
536 'desktop-mode-snow-wallpaper',
537 DESKTOP_MODE_URL . 'assets/js/snow-wallpaper' . $suffix . '.js',
538 array( 'wp-hooks', 'wp-i18n' ),
539 file_exists( $snow_js ) ? (string) filemtime( $snow_js ) : $version,
540 true
541 );
542 wp_set_script_translations(
543 'desktop-mode-snow-wallpaper',
544 'desktop-mode',
545 DESKTOP_MODE_DIR . 'languages'
546 );
547
548 // `desktop-mode-ai-assistant` — AI Copilot spotlight overlay,
549 // moved out of `desktop.min.js`. The main bundle ships a
550 // stub matching the public `wp.desktop.ai` contract; the stub
551 // `<script>`-injects this handle the first time the user opens
552 // the assistant (Cmd+K or admin-bar button).
553 $ai_assistant_js = DESKTOP_MODE_DIR . 'assets/js/ai-assistant' . $suffix . '.js';
554 wp_register_script(
555 'desktop-mode-ai-assistant',
556 DESKTOP_MODE_URL . 'assets/js/ai-assistant' . $suffix . '.js',
557 array( 'wp-hooks', 'wp-i18n' ),
558 file_exists( $ai_assistant_js ) ? (string) filemtime( $ai_assistant_js ) : $version,
559 true
560 );
561 wp_set_script_translations(
562 'desktop-mode-ai-assistant',
563 'desktop-mode',
564 DESKTOP_MODE_DIR . 'languages'
565 );
566
567 // Wire the translation bundle to this script handle. WP looks
568 // for `languages/desktop-mode-{locale}-desktop-mode.json` and
569 // injects its `locale_data` into `wp.i18n` just before the
570 // script runs — so every `__()` call resolves to the right
571 // language without any runtime fetch.
572 wp_set_script_translations(
573 'desktop-mode',
574 'desktop-mode',
575 DESKTOP_MODE_DIR . 'languages'
576 );
577 }
578 add_action( 'init', 'desktop_mode_register_assets' );
579