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/admin-bar.php +144 -179 0.9.31.1.10 View file →
@@ -1,32 +1,30 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode admin-bar toggle.
3 + * OpenStation admin-bar toggle.
4 4 *
5 - * Adds the "Switch to Desktop Mode" button to the admin bar's top-right
6 - * area and wires its click handler to the save-desktop-mode AJAX endpoint.
5 + * Adds the "Switch to OpenStation" button to the admin bar's top-right
6 + * area and wires its click handler to the save-openstation AJAX endpoint.
7 7 *
8 - * @package WPDesktopMode
8 + * @package OpenStation
9 9 */
10 10
11 11 defined( 'ABSPATH' ) || exit;
12 12
13 13 /**
14 - * Adds the desktop mode toggle to the admin bar.
14 + * Adds the OpenStation toggle to the admin bar.
15 15 *
16 - * Allows users to switch between classic admin and desktop mode,
16 + * Allows users to switch between classic admin and OpenStation,
17 17 * which renders admin screens in draggable, resizable windows.
18 18 *
19 - * @since 0.1.0
20 - *
21 19 * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
22 20 */
23 -function desktop_mode_admin_bar_toggle( $wp_admin_bar ) {
21 +function openstation_admin_bar_toggle( $wp_admin_bar ) {
24 22 if ( ! is_admin() || ! is_user_logged_in() ) {
25 23 return;
26 24 }
27 25
28 - // "Active" means "the user is *currently viewing* desktop mode",
26 + // "Active" means "the user is *currently viewing* OpenStation",
29 27 // not "the preference is enabled in user meta." These diverge on
30 28 // requests carrying the per-request classic override
31 29 // (`?desktop_mode_classic=1`) — the user's meta may be '1' but the
32 30 // page they're looking at right now is classic admin. If we went
@@ -31,25 +29,25 @@
31 29 // (`?desktop_mode_classic=1`) — the user's meta may be '1' but the
32 30 // page they're looking at right now is classic admin. If we went
33 31 // by meta alone, the toggle here would read "Switch to Classic
34 32 // Admin" on a page that's already classic, and the user's first
35 - // click would disable desktop mode entirely (redirecting to
33 + // click would disable OpenStation entirely (redirecting to
36 34 // classic admin) instead of taking them back into the shell.
37 35 // The second click would then re-enable it — a two-click trap.
38 - $is_active = desktop_mode_is_enabled() && ! desktop_mode_is_classic_request();
36 + $is_active = openstation_is_enabled() && ! openstation_is_classic_request();
39 37 $label = $is_active
40 38 ? __( 'Switch to Classic Admin', 'desktop-mode' )
41 - : __( 'Switch to Desktop Mode', 'desktop-mode' );
39 + : __( 'Switch to OpenStation', 'desktop-mode' );
42 40
43 41 $wp_admin_bar->add_node(
44 42 array(
45 43 'parent' => 'top-secondary',
46 - 'id' => 'desktop-mode-toggle',
44 + 'id' => 'os-toggle',
47 45 'title' => '<span class="ab-icon dashicons dashicons-desktop" aria-hidden="true"></span>'
48 46 . '<span class="ab-label">' . $label . '</span>',
49 47 'href' => '#',
50 48 'meta' => array(
51 - 'class' => $is_active ? 'desktop-mode-active' : '',
49 + 'class' => $is_active ? 'os-active' : '',
52 50 'tabindex' => 0,
53 51 'title' => $label,
54 52 ),
55 53 )
@@ -56,9 +54,9 @@
56 54 );
57 55
58 56 // Fullscreen toggle — sits next to "Switch to Classic Admin" so the
59 57 // shell can occupy the whole screen without browser chrome. Only
60 - // makes sense in desktop mode; kept out of classic where it would
58 + // makes sense in OpenStation; kept out of classic where it would
61 59 // just be a redundant browser-fullscreen shortcut.
62 60 if ( $is_active ) {
63 61 $wp_admin_bar->add_node(
64 62 array(
@@ -78,9 +76,9 @@
78 76 // Layout menu — only surfaced when the user is actually viewing
79 77 // the desktop shell (the actions don't make sense in classic
80 78 // admin, which has no windows to arrange). Parent renders as a
81 79 // dashicon with a hover-opened submenu; each child is routed to
82 - // `wp.desktop.windowManager.*` by the inline JS.
80 + // `wp.os.windowManager.*` by the inline JS.
83 81 //
84 82 // Sits next to Fullscreen so the two shell-state actions group
85 83 // visually. Ask AI, then the help/meta cluster (Keyboard shortcuts
86 84 // + Report a bug) follow, with the help/meta pair anchored to the
@@ -104,9 +102,9 @@
104 102 'id' => 'desktop-layout-cascade',
105 103 'title' => esc_html__( 'Cascade', 'desktop-mode' ),
106 104 'href' => '#',
107 105 'meta' => array(
108 - 'class' => 'desktop-mode-layout-action',
106 + 'class' => 'os-layout-action',
109 107 'title' => __( 'Lay all windows out from top-left, offset so every title bar stays visible.', 'desktop-mode' ),
110 108 ),
111 109 )
112 110 );
@@ -116,9 +114,9 @@
116 114 'id' => 'desktop-layout-overview',
117 115 'title' => esc_html__( 'Overview', 'desktop-mode' ),
118 116 'href' => '#',
119 117 'meta' => array(
120 - 'class' => 'desktop-mode-layout-action',
118 + 'class' => 'os-layout-action',
121 119 'title' => __( 'Zoom out to see every window at once. Click one to focus it.', 'desktop-mode' ),
122 120 ),
123 121 )
124 122 );
@@ -130,13 +128,13 @@
130 128 $wp_admin_bar->add_node(
131 129 array(
132 130 'parent' => 'desktop-layout-menu',
133 131 'id' => 'desktop-layout-snap',
134 - 'title' => '<span class="desktop-mode-layout-checkbox" aria-hidden="true">☐</span> '
132 + 'title' => '<span class="os-layout-checkbox" aria-hidden="true">☐</span> '
135 133 . esc_html__( 'Snap to grid', 'desktop-mode' ),
136 134 'href' => '#',
137 135 'meta' => array(
138 - 'class' => 'desktop-mode-layout-snap',
136 + 'class' => 'os-layout-snap',
139 137 'title' => __( 'Snap windows to a grid while dragging or resizing.', 'desktop-mode' ),
140 138 ),
141 139 )
142 140 );
@@ -146,9 +144,9 @@
146 144 'id' => 'desktop-layout-tile',
147 145 'title' => esc_html__( 'Tile all windows', 'desktop-mode' ),
148 146 'href' => '#',
149 147 'meta' => array(
150 - 'class' => 'desktop-mode-layout-action',
148 + 'class' => 'os-layout-action',
151 149 'title' => __( 'Pack every window into an evenly tiled grid that fills the desktop.', 'desktop-mode' ),
152 150 ),
153 151 )
154 152 );
@@ -157,9 +155,9 @@
157 155 * Filter the list of custom arrange-menu items contributed by
158 156 * plugins. Each entry becomes an additional node under the
159 157 * "Arrange" submenu, rendered with the same styling as the
160 158 * built-in Cascade / Overview / Tile items. Clicking a custom
161 - * item dispatches the JS action `desktop-mode.arrange.custom-action`
159 + * item dispatches the JS action `os.arrange.custom-action`
162 160 * with payload `{ id }` — plugins subscribe via
163 161 * `wp.hooks.addAction()` and run their own arrangement logic.
164 162 *
165 163 * Each item is an associative array:
@@ -172,13 +170,11 @@
172 170 * 10+ to append.
173 171 *
174 172 * Entries with missing/invalid `id` or `title` are dropped.
175 173 *
176 - * @since 0.6.2
177 - *
178 174 * @param array $items Existing custom items (default empty).
179 175 */
180 - $custom = apply_filters( 'desktop_mode_arrange_menu_items', array() );
176 + $custom = apply_filters( 'openstation_arrange_menu_items', array() );
181 177 if ( is_array( $custom ) ) {
182 178 // Stable sort by `position` (default 10 — after built-ins),
183 179 // preserving registration order within a tie.
184 180 $sortable = array();
@@ -222,9 +218,9 @@
222 218 'id' => 'desktop-layout-custom-' . $item['id'],
223 219 'title' => esc_html( $item['title'] ),
224 220 'href' => '#',
225 221 'meta' => array(
226 - 'class' => 'desktop-mode-layout-action desktop-mode-layout-custom',
222 + 'class' => 'os-layout-action os-layout-custom',
227 223 'title' => $item['description'],
228 224 ),
229 225 )
230 226 );
@@ -231,43 +227,19 @@
231 227 }
232 228 }
233 229 }
234 230
235 - // AI Assistant trigger — shown when desktop mode is active AND the
236 - // current user has AI features configured. Clicking (or pressing
237 - // Cmd+K anywhere) opens the spotlight-style AI overlay.
238 - if ( $is_active && function_exists( 'desktop_mode_ai_is_enabled' ) && desktop_mode_ai_is_enabled( get_current_user_id() ) ) {
239 - // Use dashicons-admin-comments (speech bubble) — same rendering
240 - // path as the toggle + arrange buttons, no SVG / HTML parsing
241 - // issues in the admin-bar context. The ⌘K badge is added via
242 - // a CSS ::after on the label so it never touches the DOM.
243 - $wp_admin_bar->add_node(
244 - array(
245 - 'parent' => 'top-secondary',
246 - 'id' => 'desktop-ai-assistant',
247 - 'title' => '<span class="ab-icon dashicons dashicons-admin-comments" aria-hidden="true"></span>'
248 - . '<span class="ab-label">' . esc_html__( 'Ask AI', 'desktop-mode' ) . '</span>',
249 - 'href' => '#',
250 - 'meta' => array(
251 - 'class' => 'desktop-ai-btn',
252 - 'title' => __( 'Open AI Assistant (Cmd+K)', 'desktop-mode' ),
253 - 'tabindex' => 0,
254 - ),
255 - )
256 - );
257 - }
258 -
259 - // "Keyboard shortcuts" trigger — shown only when desktop mode is
231 + // "Keyboard shortcuts" trigger — shown only when OpenStation is
260 232 // active. Clicking toggles the keyboard-shortcuts popover wired by
261 233 // assets/js/admin-bar.js (wireShortcutsPopover); the popover content
262 234 // is translated server-side and shipped via the `shortcuts` key of
263 - // the desktopModeAdminBar config blob below.
235 + // the openStationAdminBar config blob below.
264 236 if ( $is_active ) {
265 237 $wp_admin_bar->add_node(
266 238 array(
267 239 'parent' => 'top-secondary',
268 240 'id' => 'desktop-help',
269 - 'title' => '<span class="ab-icon desktop-mode-keyboard-icon" aria-hidden="true"></span>',
241 + 'title' => '<span class="ab-icon os-keyboard-icon" aria-hidden="true"></span>',
270 242 'href' => '#',
271 243 'meta' => array(
272 244 'class' => 'desktop-help-btn',
273 245 'title' => __( 'Keyboard shortcuts', 'desktop-mode' ),
@@ -276,10 +248,10 @@
276 248 )
277 249 );
278 250 }
279 251
280 - // "Report a bug" trigger — shown only when desktop mode is active.
281 - // Clicking dispatches a `desktop-mode-open-bug-report` document
252 + // "Report a bug" trigger — shown only when OpenStation is active.
253 + // Clicking dispatches a `os-open-bug-report` document
282 254 // CustomEvent that the shell listens for and answers by opening the
283 255 // Bug Report native window. PHP doesn't know the JS side exists; the
284 256 // event lets us add the button without coupling to any specific
285 257 // shell module. Anchored to the far right of the secondary bar so
@@ -301,40 +273,62 @@
301 273 )
302 274 );
303 275 }
304 276 }
305 -add_action( 'admin_bar_menu', 'desktop_mode_admin_bar_toggle', 190 );
277 +add_action( 'admin_bar_menu', 'openstation_admin_bar_toggle', 190 );
306 278
307 279 /**
308 - * Enqueues the CSS and JS for the desktop mode toggle.
280 + * Enqueues the CSS and JS for the OpenStation toggle.
309 281 *
310 282 * The CSS is inline, attached to the `admin-bar` style handle so it always
311 283 * ships with the admin bar itself — no matter which admin screen is showing.
312 284 * The JS is the external assets/js/admin-bar.js bundle, registered as
313 - * `desktop-mode-admin-bar` with `admin-bar` as a dependency; its config is
285 + * `os-admin-bar` with `admin-bar` as a dependency; its config is
314 286 * emitted as an inline JSON literal `before` the script.
315 - *
316 - * @since 0.1.0
317 287 */
318 -function desktop_mode_enqueue_toggle_assets() {
288 +function openstation_enqueue_toggle_assets() {
319 289 if ( ! is_admin() || ! is_user_logged_in() ) {
320 290 return;
321 291 }
322 292
293 + // Inside a chromeless window the admin bar is suppressed outright
294 + // (`show_admin_bar` + the `wp_admin_bar_render` removal in
295 + // helpers.php), so `#wpadminbar` never reaches the DOM. Every byte
296 + // below — the toggle bundle, its inline config, the node styling —
297 + // would load and run against markup that does not exist. Measured
298 + // on a live install: the toggle bundle alone was 17.7 KB, the
299 + // largest single asset in the admin-bar family a window loaded for
300 + // nothing. See `includes/render/chromeless-trim.php`, which drops
301 + // the rest of that family (core's `admin-bar`, host masterbar
302 + // extras) for the same reason.
303 + if ( openstation_is_chromeless_request() ) {
304 + return;
305 + }
306 +
307 + // The items are `display: flex`, never `inline-flex`. An
308 + // inline-level box sits on a line box the <li> lays out at its
309 + // 32px line-height and aligns on the baseline, so the line grows
310 + // by the descender: the item became 37px inside a 32px bar. Under
311 + // Core's float layout that overflow was invisible. On a host that
312 + // lays the secondary group out as a flex row (WordPress.com's Debug
313 + // Bar does, to order its own item first) every sibling stretched
314 + // to the tallest one and the group's background painted 5px into
315 + // the shell, under the windows' title bars. Block-level, the item
316 + // is exactly the bar. `Tests_OpenStation_AdminBarDesktopToggle`
317 + // pins it; `desktop.css` caps the group as well, for items we do
318 + // not own.
323 319 $css = '
324 - #wpadminbar #wp-admin-bar-desktop-mode-toggle > .ab-item,
320 + #wpadminbar #wp-admin-bar-os-toggle > .ab-item,
325 321 #wpadminbar #wp-admin-bar-desktop-layout-menu > .ab-item,
326 - #wpadminbar #wp-admin-bar-desktop-ai-assistant > .ab-item,
327 322 #wpadminbar #wp-admin-bar-desktop-fullscreen > .ab-item,
328 323 #wpadminbar #wp-admin-bar-desktop-bug-report > .ab-item,
329 324 #wpadminbar #wp-admin-bar-desktop-help > .ab-item {
330 - display: inline-flex;
325 + display: flex;
331 326 align-items: center;
332 327 gap: 6px;
333 328 }
334 - #wpadminbar #wp-admin-bar-desktop-mode-toggle .ab-icon,
329 + #wpadminbar #wp-admin-bar-os-toggle .ab-icon,
335 330 #wpadminbar #wp-admin-bar-desktop-layout-menu .ab-icon,
336 - #wpadminbar #wp-admin-bar-desktop-ai-assistant .ab-icon,
337 331 #wpadminbar #wp-admin-bar-desktop-fullscreen .ab-icon,
338 332 #wpadminbar #wp-admin-bar-desktop-bug-report .ab-icon,
339 333 #wpadminbar #wp-admin-bar-desktop-help .ab-icon {
340 334 float: none;
@@ -346,15 +340,15 @@
346 340 width: 20px;
347 341 height: 20px;
348 342 }
349 343
350 - #wp-admin-bar-desktop-mode-toggle .ab-icon.dashicons,
344 + #wp-admin-bar-os-toggle .ab-icon.dashicons,
351 345 #wp-admin-bar-desktop-layout-menu .ab-icon.dashicons {
352 346 font: normal 20px/1 dashicons;
353 347 -webkit-font-smoothing: antialiased;
354 348 -moz-osx-font-smoothing: grayscale;
355 349 }
356 - #wp-admin-bar-desktop-mode-toggle .ab-icon.dashicons::before {
350 + #wp-admin-bar-os-toggle .ab-icon.dashicons::before {
357 351 content: "\f472";
358 352 top: 0;
359 353 position: static;
360 354 }
@@ -363,9 +357,9 @@
363 357 content: "\f509";
364 358 top: 0;
365 359 position: static;
366 360 }
367 - #wp-admin-bar-desktop-mode-toggle.desktop-mode-active .ab-icon.dashicons::before {
361 + #wp-admin-bar-os-toggle.os-active .ab-icon.dashicons::before {
368 362 /* Inherit the admin-bar text color so the active state
369 363 matches the +New, Home, Comments dashicons. Previously
370 364 hardcoded #72aee6, which forced blue regardless of the
371 365 profile color scheme. */
@@ -371,60 +365,13 @@
371 365 profile color scheme. */
372 366 color: inherit;
373 367 }
374 368 @media screen and (max-width: 782px) {
375 - #wp-admin-bar-desktop-mode-toggle .ab-label {
369 + #wp-admin-bar-os-toggle .ab-label {
376 370 display: none;
377 371 }
378 372 }
379 373
380 - /* AI Assistant admin-bar button — same icon/label pattern as the
381 - desktop-mode-toggle; ⌘K badge added via CSS ::after so we keep
382 - the title HTML clean and avoid admin-bar sanitisation edge-cases. */
383 - #wp-admin-bar-desktop-ai-assistant .ab-icon.dashicons,
384 - #wp-admin-bar-desktop-ai-assistant .ab-icon.dashicons {
385 - font: normal 20px/1 dashicons;
386 - -webkit-font-smoothing: antialiased;
387 - -moz-osx-font-smoothing: grayscale;
388 - }
389 - #wp-admin-bar-desktop-ai-assistant .ab-icon.dashicons::before {
390 - content: "\f101";
391 - top: 0;
392 - position: static;
393 - /* See note on desktop-mode-toggle above — inherit so the
394 - icon matches the rest of the admin-bar dashicons
395 - across all WP profile color schemes. */
396 - color: inherit;
397 - }
398 - /* ⌘K badge rendered purely in CSS to the right of the label.
399 - inline-flex + align-items:center centers the glyph inside
400 - its own padding box; a 1px upward translate compensates
401 - for the optical sag from the admin-bar label baseline. */
402 - #wpadminbar #wp-admin-bar-desktop-ai-assistant .ab-label::after {
403 - content: "\2318K";
404 - display: inline-flex;
405 - align-items: center;
406 - justify-content: center;
407 - margin-inline-start: 5px;
408 - font-size: 10px;
409 - line-height: 1;
410 - padding: 2px 5px;
411 - background: rgba( 255, 255, 255, 0.1 );
412 - border: 1px solid rgba( 255, 255, 255, 0.18 );
413 - border-radius: 3px;
414 - color: rgba( 255, 255, 255, 0.55 );
415 - vertical-align: middle;
416 - font-weight: 400;
417 - letter-spacing: 0;
418 - position: relative;
419 - top: -1px;
420 - }
421 - @media screen and (max-width: 782px) {
422 - #wp-admin-bar-desktop-ai-assistant .ab-label {
423 - display: none;
424 - }
425 - }
426 -
427 374 /* Fullscreen admin-bar button — same dashicons rendering pattern
428 375 as the toggle. Icon swaps between fullscreen-alt (enter) and
429 376 fullscreen-exit-alt (exit) via the .is-fullscreen class set by
430 377 admin-bar.js on `fullscreenchange`. */
@@ -464,10 +411,10 @@
464 411 where pointer events get intercepted before reaching the
465 412 page. Hiding these two shifts the Fullscreen toggle further
466 413 from the corner so its own click lands. Toggled by a body
467 414 class admin-bar.js sets on `fullscreenchange`. */
468 - body.desktop-mode-browser-fs #wp-admin-bar-desktop-mode-toggle,
469 - body.desktop-mode-browser-fs #wp-admin-bar-desktop-bug-report {
415 + body.os-browser-fs #wp-admin-bar-os-toggle,
416 + body.os-browser-fs #wp-admin-bar-desktop-bug-report {
470 417 display: none;
471 418 }
472 419
473 420 /* Bug Report admin-bar button — same dashicons rendering pattern
@@ -491,9 +438,9 @@
491 438 glyph, so we paint a small inline SVG via CSS `mask` with
492 439 `background-color: currentColor` — that way the icon inherits
493 440 the admin-bar text color across every WP profile scheme
494 441 without us hardcoding a fill. */
495 - #wp-admin-bar-desktop-help .ab-icon.desktop-mode-keyboard-icon {
442 + #wp-admin-bar-desktop-help .ab-icon.os-keyboard-icon {
496 443 background-color: currentColor;
497 444 -webkit-mask: url( "data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 24 24\' fill=\'none\' stroke=\'black\' stroke-width=\'1.6\' stroke-linecap=\'round\' stroke-linejoin=\'round\'><rect x=\'2.5\' y=\'6\' width=\'19\' height=\'12\' rx=\'2.2\'/><path d=\'M6 10h0M10 10h0M14 10h0M18 10h0M6 14h0M10 14h4M18 14h0\'/></svg>" ) no-repeat center / 20px 20px;
498 445 mask: url( "data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 24 24\' fill=\'none\' stroke=\'black\' stroke-width=\'1.6\' stroke-linecap=\'round\' stroke-linejoin=\'round\'><rect x=\'2.5\' y=\'6\' width=\'19\' height=\'12\' rx=\'2.2\'/><path d=\'M6 10h0M10 10h0M14 10h0M18 10h0M6 14h0M10 14h4M18 14h0\'/></svg>" ) no-repeat center / 20px 20px;
499 446 }
@@ -501,13 +448,13 @@
501 448 /* Keyboard-shortcuts floating popover — anchored under the
502 449 keyboard button. Styled to match the Arrange submenu (light
503 450 bg on the otherwise-dark admin bar, soft shadow, rounded
504 451 corners). Built dynamically in admin-bar.js from translated
505 - content shipped via `desktopModeAdminBar.shortcuts`. */
452 + content shipped via `openStationAdminBar.shortcuts`. */
506 453 #wpadminbar #wp-admin-bar-desktop-help {
507 454 position: relative;
508 455 }
509 - #wpadminbar .desktop-mode-shortcuts-popover {
456 + #wpadminbar .os-shortcuts-popover {
510 457 display: none;
511 458 position: absolute;
512 459 top: 100%;
513 460 right: 0;
@@ -514,11 +461,11 @@
514 461 margin-top: 0;
515 462 padding: 12px 14px;
516 463 min-width: 460px;
517 464 max-width: min( 90vw, 720px );
518 - background: var( --desktop-mode-window-bg, #fff );
519 - color: var( --desktop-mode-text, #1d2327 );
520 - border: 1px solid var( --desktop-mode-window-border, #c3c4c7 );
465 + background: var( --os-ui-surface, var( --os-window-bg, #fff ) );
466 + color: var( --os-ui-fg, #1d2327 );
467 + border: 1px solid var( --os-ui-border, var( --os-window-border, #c3c4c7 ) );
521 468 border-radius: 8px;
522 469 box-shadow: 0 8px 24px rgba( 0, 0, 0, 0.18 ),
523 470 0 2px 6px rgba( 0, 0, 0, 0.08 );
524 471 z-index: 99999;
@@ -525,15 +472,15 @@
525 472 font-size: 12px;
526 473 line-height: 1.4;
527 474 text-align: left;
528 475 }
529 - #wpadminbar .desktop-mode-shortcuts-popover.is-open {
476 + #wpadminbar .os-shortcuts-popover.is-open {
530 477 display: block;
531 478 }
532 - #wpadminbar .desktop-mode-shortcuts-popover__section + .desktop-mode-shortcuts-popover__section {
479 + #wpadminbar .os-shortcuts-popover__section + .os-shortcuts-popover__section {
533 480 margin-top: 12px;
534 481 }
535 - #wpadminbar .desktop-mode-shortcuts-popover__heading {
482 + #wpadminbar .os-shortcuts-popover__heading {
536 483 margin: 0 0 6px;
537 484 padding: 0;
538 485 font-size: 11px;
539 486 font-weight: 600;
@@ -538,44 +485,44 @@
538 485 font-size: 11px;
539 486 font-weight: 600;
540 487 letter-spacing: 0.04em;
541 488 text-transform: uppercase;
542 - color: var( --desktop-mode-muted-fg, #50575e );
489 + color: var( --os-ui-fg-muted, #50575e );
543 490 line-height: 1.2;
544 491 }
545 - #wpadminbar .desktop-mode-shortcuts-popover__table {
492 + #wpadminbar .os-shortcuts-popover__table {
546 493 width: 100%;
547 494 border-collapse: collapse;
548 495 color: inherit;
549 496 }
550 - #wpadminbar .desktop-mode-shortcuts-popover__table th,
551 - #wpadminbar .desktop-mode-shortcuts-popover__table td {
497 + #wpadminbar .os-shortcuts-popover__table th,
498 + #wpadminbar .os-shortcuts-popover__table td {
552 499 padding: 6px 8px;
553 500 vertical-align: middle;
554 501 text-align: left;
555 - border-bottom: 1px solid rgba( 0, 0, 0, 0.06 );
502 + border-bottom: 1px solid var( --os-ui-border, rgba( 0, 0, 0, 0.06 ) );
556 503 color: inherit;
557 504 font-weight: 400;
558 505 font-size: 12px;
559 506 line-height: 1.4;
560 507 }
561 - #wpadminbar .desktop-mode-shortcuts-popover__table th {
508 + #wpadminbar .os-shortcuts-popover__table th {
562 509 font-weight: 600;
563 510 font-size: 11px;
564 - color: var( --desktop-mode-muted-fg, #50575e );
511 + color: var( --os-ui-fg-muted, #50575e );
565 512 letter-spacing: 0.02em;
566 513 }
567 - #wpadminbar .desktop-mode-shortcuts-popover__table tbody tr:last-child td {
514 + #wpadminbar .os-shortcuts-popover__table tbody tr:last-child td {
568 515 border-bottom: none;
569 516 }
570 - #wpadminbar .desktop-mode-shortcuts-popover__key-cell {
517 + #wpadminbar .os-shortcuts-popover__key-cell {
571 518 white-space: nowrap;
572 519 }
573 - #wpadminbar .desktop-mode-shortcuts-popover__note {
574 - color: var( --desktop-mode-muted-fg, #50575e );
520 + #wpadminbar .os-shortcuts-popover__note {
521 + color: var( --os-ui-fg-muted, #50575e );
575 522 font-size: 11px;
576 523 }
577 - #wpadminbar .desktop-mode-shortcuts-popover__list {
524 + #wpadminbar .os-shortcuts-popover__list {
578 525 margin: 0;
579 526 padding: 0;
580 527 list-style: none;
581 528 display: flex;
@@ -581,9 +528,9 @@
581 528 display: flex;
582 529 flex-direction: column;
583 530 gap: 4px;
584 531 }
585 - #wpadminbar .desktop-mode-shortcuts-popover__item {
532 + #wpadminbar .os-shortcuts-popover__item {
586 533 display: grid;
587 534 grid-template-columns: 120px 1fr;
588 535 align-items: center;
589 536 gap: 12px;
@@ -588,17 +535,17 @@
588 535 align-items: center;
589 536 gap: 12px;
590 537 padding: 4px 8px;
591 538 border-radius: 4px;
592 - background: rgba( 0, 0, 0, 0.03 );
539 + background: var( --os-ui-surface-sunken, rgba( 0, 0, 0, 0.03 ) );
593 540 }
594 - #wpadminbar .desktop-mode-shortcuts-popover__keys {
541 + #wpadminbar .os-shortcuts-popover__keys {
595 542 display: inline-flex;
596 543 align-items: center;
597 544 gap: 4px;
598 545 flex-wrap: wrap;
599 546 }
600 - #wpadminbar .desktop-mode-shortcuts-popover__kbd {
547 + #wpadminbar .os-shortcuts-popover__kbd {
601 548 display: inline-flex;
602 549 align-items: center;
603 550 justify-content: center;
604 551 min-width: 22px;
@@ -603,13 +550,13 @@
603 550 justify-content: center;
604 551 min-width: 22px;
605 552 height: 20px;
606 553 padding: 0 5px;
607 - border: 1px solid rgba( 0, 0, 0, 0.18 );
554 + border: 1px solid var( --os-ui-border-strong, rgba( 0, 0, 0, 0.18 ) );
608 555 border-bottom-width: 2px;
609 556 border-radius: 4px;
610 - background: #fff;
611 - color: var( --desktop-mode-text, #1d2327 );
557 + background: var( --os-ui-surface-elevated, #fff );
558 + color: var( --os-ui-fg, #1d2327 );
612 559 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
613 560 font-size: 11px;
614 561 font-weight: 600;
615 562 line-height: 1;
@@ -614,13 +561,13 @@
614 561 font-weight: 600;
615 562 line-height: 1;
616 563 white-space: nowrap;
617 564 }
618 - #wpadminbar .desktop-mode-shortcuts-popover__plus {
565 + #wpadminbar .os-shortcuts-popover__plus {
619 566 font-size: 10px;
620 - color: var( --desktop-mode-muted-fg, #50575e );
567 + color: var( --os-ui-fg-muted, #50575e );
621 568 }
622 - #wpadminbar .desktop-mode-shortcuts-popover__description {
569 + #wpadminbar .os-shortcuts-popover__description {
623 570 color: inherit;
624 571 }
625 572
626 573 /* Dock-style custom tooltip for icon-only admin-bar buttons.
@@ -662,21 +609,29 @@
662 609 #wpadminbar #wp-admin-bar-desktop-help.is-open .ab-item[ data-desktop-tooltip ]::after {
663 610 opacity: 0;
664 611 }
665 612
666 - /* Arrange submenu — aligned visually with the <wpd-menu> component
667 - (src/ui/components/wpd-menu/wpd-menu.styles.ts). The submenu
668 - flips from the native dark-on-dark admin bar to a light theme
669 - (white bg, dark text) so it matches the rest of our UI AND so
670 - the hover state stays legible (a light tint on a dark bg
671 - would render as invisible overlap with native admin-bar hover
672 - colors). Selectors prefixed with #wpadminbar to win the
673 - admin-bar specificity without !important. */
613 + /* Arrange submenu — aligned visually with the <os-menu> component
614 + (src/ui/components/os-menu/os-menu.styles.ts). The submenu
615 + breaks out of the native dark-on-dark admin bar and paints
616 + itself from the `--os-ui-*` panel palette instead, so it matches
617 + the rest of our UI and stays legible under any desktop theme.
618 +
619 + Surface and text MUST come from the same palette. They used to
620 + not: the background read `--os-window-bg` (which a
621 + theme sets) while the text read `--os-text` (which
622 + nothing has ever defined, so it always fell back to near-black).
623 + Any dark theme therefore rendered near-black text on its own
624 + near-black panel. Keep both sides on `--os-ui-surface` /
625 + `--os-ui-fg` and that class of bug cannot come back.
626 +
627 + Selectors prefixed with #wpadminbar to win the admin-bar
628 + specificity without !important. */
674 629 #wpadminbar #wp-admin-bar-desktop-layout-menu .ab-sub-wrapper {
675 - background: var( --desktop-mode-window-bg, #fff );
630 + background: var( --os-ui-surface, var( --os-window-bg, #fff ) );
676 631 padding: 4px;
677 632 min-width: 220px;
678 - border: 1px solid var( --desktop-mode-window-border, #c3c4c7 );
633 + border: 1px solid var( --os-ui-border, var( --os-window-border, #c3c4c7 ) );
679 634 border-radius: 8px;
680 635 box-shadow: 0 8px 24px rgba( 0, 0, 0, 0.18 ),
681 636 0 2px 6px rgba( 0, 0, 0, 0.08 );
682 637 }
@@ -695,9 +650,9 @@
695 650 min-height: 32px;
696 651 padding: 6px 10px;
697 652 font-size: 13px;
698 653 line-height: 1.3;
699 - color: var( --desktop-mode-text, #1d2327 );
654 + color: var( --os-ui-fg, #1d2327 );
700 655 background: transparent;
701 656 border-radius: 6px;
702 657 transition: background-color 0.12s ease, color 0.12s ease;
703 658 }
@@ -707,12 +662,12 @@
707 662 thanks to the extra id in our selector chain. */
708 663 #wpadminbar #wp-admin-bar-desktop-layout-menu .ab-sub-wrapper .ab-submenu > li > .ab-item:hover,
709 664 #wpadminbar #wp-admin-bar-desktop-layout-menu .ab-sub-wrapper .ab-submenu > li > .ab-item:focus,
710 665 #wpadminbar.nojq #wp-admin-bar-desktop-layout-menu .ab-sub-wrapper .ab-submenu > li:hover > .ab-item {
711 - background: rgba( 0, 0, 0, 0.06 );
712 - color: #000;
666 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
667 + color: var( --os-ui-fg, #000 );
713 668 }
714 - #wp-admin-bar-desktop-layout-snap .desktop-mode-layout-checkbox {
669 + #wp-admin-bar-desktop-layout-snap .os-layout-checkbox {
715 670 flex-shrink: 0;
716 671 display: inline-flex;
717 672 align-items: center;
718 673 justify-content: center;
@@ -720,9 +675,9 @@
720 675 height: 16px;
721 676 font-size: 14px;
722 677 line-height: 1;
723 678 }
724 - #wp-admin-bar-desktop-layout-snap[aria-checked="true"] .desktop-mode-layout-checkbox {
679 + #wp-admin-bar-desktop-layout-snap[aria-checked="true"] .os-layout-checkbox {
725 680 color: var( --wp-admin-theme-color, #2271b1 );
726 681 }
727 682 ';
728 683 wp_add_inline_style( 'admin-bar', $css );
@@ -730,18 +685,18 @@
730 685 // All PHP→JS values are emitted as JSON literals (never interpolated
731 686 // raw into the script body) so special characters, quotes, and
732 687 // unexpected shapes can't break the parser or be exploited.
733 688 // `active` must match the visual state shown on the toggle above —
734 - // i.e. "currently viewing desktop mode." Using `desktop_mode_is_enabled()`
689 + // i.e. "currently viewing OpenStation." Using `openstation_is_enabled()`
735 690 // alone would misclassify a classic-override request (meta = '1',
736 691 // URL carrying `desktop_mode_classic=1`) as active, causing the
737 692 // click handler to send `enabled=0` when the user actually wants
738 693 // to return to the shell.
739 694 wp_register_script(
740 - 'desktop-mode-admin-bar',
741 - DESKTOP_MODE_URL . 'assets/js/admin-bar.js',
695 + 'os-admin-bar',
696 + OPENSTATION_URL . 'assets/js/admin-bar.js',
742 697 array( 'admin-bar' ),
743 - DESKTOP_MODE_VERSION,
698 + OPENSTATION_VERSION,
744 699 true
745 700 );
746 701 // Emit the config as a JSON literal via wp_add_inline_script (not
747 702 // wp_localize_script — the latter casts booleans to '' / '1', which
@@ -747,15 +702,21 @@
747 702 // wp_localize_script — the latter casts booleans to '' / '1', which
748 703 // breaks the click handler's `!! cfg.active` check). 'before' runs
749 704 // before admin-bar.js so the global is ready when the IIFE fires.
750 705 wp_add_inline_script(
751 - 'desktop-mode-admin-bar',
752 - 'var desktopModeAdminBar = ' . wp_json_encode(
706 + 'os-admin-bar',
707 + 'var openStationAdminBar = ' . wp_json_encode(
753 708 array(
754 - 'nonce' => wp_create_nonce( 'save-desktop-mode' ),
755 - 'active' => desktop_mode_is_enabled() && ! desktop_mode_is_classic_request(),
756 - 'classicUrl' => esc_url_raw( admin_url() ),
757 - 'portalUrl' => esc_url_raw( desktop_mode_portal_url() ),
709 + 'nonce' => wp_create_nonce( 'save-openstation' ),
710 + 'active' => openstation_is_enabled() && ! openstation_is_classic_request(),
711 + // `self_admin_url()`: switching off from the network
712 + // admin returns there, not to the main site.
713 + 'classicUrl' => esc_url_raw( self_admin_url() ),
714 + 'portalUrl' => esc_url_raw( openstation_portal_url() ),
715 + // Passed back on the toggle's AJAX call: the handler
716 + // runs on `admin-ajax.php`, where `is_network_admin()`
717 + // is always false.
718 + 'network' => is_network_admin(),
758 719 'ajaxUrl' => esc_url_raw( admin_url( 'admin-ajax.php' ) ),
759 720 'i18n' => array(
760 721 'enterFullscreen' => __( 'Fullscreen', 'desktop-mode' ),
761 722 'exitFullscreen' => __( 'Exit fullscreen', 'desktop-mode' ),
@@ -761,8 +722,9 @@
761 722 'exitFullscreen' => __( 'Exit fullscreen', 'desktop-mode' ),
762 723 'enterTitle' => __( 'Enter fullscreen', 'desktop-mode' ),
763 724 'exitTitle' => __( 'Exit fullscreen', 'desktop-mode' ),
764 725 ),
726 +
765 727 /*
766 728 * Keyboard-shortcuts popover content. Translated once on
767 729 * the server and shipped to admin-bar.js, which renders
768 730 * the popover anchored under the keyboard button.
@@ -832,8 +794,12 @@
832 794 'keys' => array( '⌘/Ctrl', 'K' ),
833 795 'description' => __( 'Open the command palette / Ask AI overlay.', 'desktop-mode' ),
834 796 ),
835 797 array(
798 + 'keys' => array( '⌥/Alt', '⌘/Ctrl', 'W' ),
799 + 'description' => __( 'Close every open window on the current desktop (asks first).', 'desktop-mode' ),
800 + ),
801 + array(
836 802 'keys' => array( 'Esc' ),
837 803 'description' => __( 'Exit Overview (or Snap Overview) without changing window state.', 'desktop-mode' ),
838 804 ),
839 805 ),
@@ -842,8 +808,7 @@
842 808 )
843 809 ) . ';',
844 810 'before'
845 811 );
846 - wp_enqueue_script( 'desktop-mode-admin-bar' );
812 + wp_enqueue_script( 'os-admin-bar' );
847 813 }
848 -add_action( 'admin_enqueue_scripts', 'desktop_mode_enqueue_toggle_assets' );
849 -
814 +add_action( 'admin_enqueue_scripts', 'openstation_enqueue_toggle_assets' );