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 -191 0.9.51.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,48 +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 - // Rendered whenever the AI APIs are available; its visibility is driven by
239 - // the `desktop-mode-ai-enabled` body class (set server-side in
240 - // body-classes.php, toggled live by the shell when the user flips the
241 - // assistant toggle) so no reload is needed to show/hide it.
242 - if ( $is_active
243 - && function_exists( 'desktop_mode_ai_is_available' ) && desktop_mode_ai_is_available() ) {
244 - // Use dashicons-admin-comments (speech bubble) — same rendering
245 - // path as the toggle + arrange buttons, no SVG / HTML parsing
246 - // issues in the admin-bar context. The ⌘K badge is added via
247 - // a CSS ::after on the label so it never touches the DOM.
248 - $wp_admin_bar->add_node(
249 - array(
250 - 'parent' => 'top-secondary',
251 - 'id' => 'desktop-ai-assistant',
252 - 'title' => '<span class="ab-icon dashicons dashicons-admin-comments" aria-hidden="true"></span>'
253 - . '<span class="ab-label">' . esc_html__( 'Ask AI', 'desktop-mode' ) . '</span>',
254 - 'href' => '#',
255 - 'meta' => array(
256 - 'class' => 'desktop-ai-btn',
257 - 'title' => __( 'Open AI Assistant (Cmd+K)', 'desktop-mode' ),
258 - 'tabindex' => 0,
259 - ),
260 - )
261 - );
262 - }
263 -
264 - // "Keyboard shortcuts" trigger — shown only when desktop mode is
231 + // "Keyboard shortcuts" trigger — shown only when OpenStation is
265 232 // active. Clicking toggles the keyboard-shortcuts popover wired by
266 233 // assets/js/admin-bar.js (wireShortcutsPopover); the popover content
267 234 // is translated server-side and shipped via the `shortcuts` key of
268 - // the desktopModeAdminBar config blob below.
235 + // the openStationAdminBar config blob below.
269 236 if ( $is_active ) {
270 237 $wp_admin_bar->add_node(
271 238 array(
272 239 'parent' => 'top-secondary',
273 240 'id' => 'desktop-help',
274 - '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>',
275 242 'href' => '#',
276 243 'meta' => array(
277 244 'class' => 'desktop-help-btn',
278 245 'title' => __( 'Keyboard shortcuts', 'desktop-mode' ),
@@ -281,10 +248,10 @@
281 248 )
282 249 );
283 250 }
284 251
285 - // "Report a bug" trigger — shown only when desktop mode is active.
286 - // 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
287 254 // CustomEvent that the shell listens for and answers by opening the
288 255 // Bug Report native window. PHP doesn't know the JS side exists; the
289 256 // event lets us add the button without coupling to any specific
290 257 // shell module. Anchored to the far right of the secondary bar so
@@ -306,40 +273,62 @@
306 273 )
307 274 );
308 275 }
309 276 }
310 -add_action( 'admin_bar_menu', 'desktop_mode_admin_bar_toggle', 190 );
277 +add_action( 'admin_bar_menu', 'openstation_admin_bar_toggle', 190 );
311 278
312 279 /**
313 - * Enqueues the CSS and JS for the desktop mode toggle.
280 + * Enqueues the CSS and JS for the OpenStation toggle.
314 281 *
315 282 * The CSS is inline, attached to the `admin-bar` style handle so it always
316 283 * ships with the admin bar itself — no matter which admin screen is showing.
317 284 * The JS is the external assets/js/admin-bar.js bundle, registered as
318 - * `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
319 286 * emitted as an inline JSON literal `before` the script.
320 - *
321 - * @since 0.1.0
322 287 */
323 -function desktop_mode_enqueue_toggle_assets() {
288 +function openstation_enqueue_toggle_assets() {
324 289 if ( ! is_admin() || ! is_user_logged_in() ) {
325 290 return;
326 291 }
327 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.
328 319 $css = '
329 - #wpadminbar #wp-admin-bar-desktop-mode-toggle > .ab-item,
320 + #wpadminbar #wp-admin-bar-os-toggle > .ab-item,
330 321 #wpadminbar #wp-admin-bar-desktop-layout-menu > .ab-item,
331 - #wpadminbar #wp-admin-bar-desktop-ai-assistant > .ab-item,
332 322 #wpadminbar #wp-admin-bar-desktop-fullscreen > .ab-item,
333 323 #wpadminbar #wp-admin-bar-desktop-bug-report > .ab-item,
334 324 #wpadminbar #wp-admin-bar-desktop-help > .ab-item {
335 - display: inline-flex;
325 + display: flex;
336 326 align-items: center;
337 327 gap: 6px;
338 328 }
339 - #wpadminbar #wp-admin-bar-desktop-mode-toggle .ab-icon,
329 + #wpadminbar #wp-admin-bar-os-toggle .ab-icon,
340 330 #wpadminbar #wp-admin-bar-desktop-layout-menu .ab-icon,
341 - #wpadminbar #wp-admin-bar-desktop-ai-assistant .ab-icon,
342 331 #wpadminbar #wp-admin-bar-desktop-fullscreen .ab-icon,
343 332 #wpadminbar #wp-admin-bar-desktop-bug-report .ab-icon,
344 333 #wpadminbar #wp-admin-bar-desktop-help .ab-icon {
345 334 float: none;
@@ -351,22 +340,15 @@
351 340 width: 20px;
352 341 height: 20px;
353 342 }
354 343
355 - /* Ask AI button shows only when the per-user assistant toggle is on.
356 - The body class is set server-side and toggled live by the shell, so
357 - enabling/disabling the assistant needs no reload. */
358 - body:not( .desktop-mode-ai-enabled ) #wpadminbar #wp-admin-bar-desktop-ai-assistant {
359 - display: none;
360 - }
361 -
362 - #wp-admin-bar-desktop-mode-toggle .ab-icon.dashicons,
344 + #wp-admin-bar-os-toggle .ab-icon.dashicons,
363 345 #wp-admin-bar-desktop-layout-menu .ab-icon.dashicons {
364 346 font: normal 20px/1 dashicons;
365 347 -webkit-font-smoothing: antialiased;
366 348 -moz-osx-font-smoothing: grayscale;
367 349 }
368 - #wp-admin-bar-desktop-mode-toggle .ab-icon.dashicons::before {
350 + #wp-admin-bar-os-toggle .ab-icon.dashicons::before {
369 351 content: "\f472";
370 352 top: 0;
371 353 position: static;
372 354 }
@@ -375,9 +357,9 @@
375 357 content: "\f509";
376 358 top: 0;
377 359 position: static;
378 360 }
379 - #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 {
380 362 /* Inherit the admin-bar text color so the active state
381 363 matches the +New, Home, Comments dashicons. Previously
382 364 hardcoded #72aee6, which forced blue regardless of the
383 365 profile color scheme. */
@@ -383,60 +365,13 @@
383 365 profile color scheme. */
384 366 color: inherit;
385 367 }
386 368 @media screen and (max-width: 782px) {
387 - #wp-admin-bar-desktop-mode-toggle .ab-label {
369 + #wp-admin-bar-os-toggle .ab-label {
388 370 display: none;
389 371 }
390 372 }
391 373
392 - /* AI Assistant admin-bar button — same icon/label pattern as the
393 - desktop-mode-toggle; ⌘K badge added via CSS ::after so we keep
394 - the title HTML clean and avoid admin-bar sanitisation edge-cases. */
395 - #wp-admin-bar-desktop-ai-assistant .ab-icon.dashicons,
396 - #wp-admin-bar-desktop-ai-assistant .ab-icon.dashicons {
397 - font: normal 20px/1 dashicons;
398 - -webkit-font-smoothing: antialiased;
399 - -moz-osx-font-smoothing: grayscale;
400 - }
401 - #wp-admin-bar-desktop-ai-assistant .ab-icon.dashicons::before {
402 - content: "\f101";
403 - top: 0;
404 - position: static;
405 - /* See note on desktop-mode-toggle above — inherit so the
406 - icon matches the rest of the admin-bar dashicons
407 - across all WP profile color schemes. */
408 - color: inherit;
409 - }
410 - /* ⌘K badge rendered purely in CSS to the right of the label.
411 - inline-flex + align-items:center centers the glyph inside
412 - its own padding box; a 1px upward translate compensates
413 - for the optical sag from the admin-bar label baseline. */
414 - #wpadminbar #wp-admin-bar-desktop-ai-assistant .ab-label::after {
415 - content: "\2318K";
416 - display: inline-flex;
417 - align-items: center;
418 - justify-content: center;
419 - margin-inline-start: 5px;
420 - font-size: 10px;
421 - line-height: 1;
422 - padding: 2px 5px;
423 - background: rgba( 255, 255, 255, 0.1 );
424 - border: 1px solid rgba( 255, 255, 255, 0.18 );
425 - border-radius: 3px;
426 - color: rgba( 255, 255, 255, 0.55 );
427 - vertical-align: middle;
428 - font-weight: 400;
429 - letter-spacing: 0;
430 - position: relative;
431 - top: -1px;
432 - }
433 - @media screen and (max-width: 782px) {
434 - #wp-admin-bar-desktop-ai-assistant .ab-label {
435 - display: none;
436 - }
437 - }
438 -
439 374 /* Fullscreen admin-bar button — same dashicons rendering pattern
440 375 as the toggle. Icon swaps between fullscreen-alt (enter) and
441 376 fullscreen-exit-alt (exit) via the .is-fullscreen class set by
442 377 admin-bar.js on `fullscreenchange`. */
@@ -476,10 +411,10 @@
476 411 where pointer events get intercepted before reaching the
477 412 page. Hiding these two shifts the Fullscreen toggle further
478 413 from the corner so its own click lands. Toggled by a body
479 414 class admin-bar.js sets on `fullscreenchange`. */
480 - body.desktop-mode-browser-fs #wp-admin-bar-desktop-mode-toggle,
481 - 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 {
482 417 display: none;
483 418 }
484 419
485 420 /* Bug Report admin-bar button — same dashicons rendering pattern
@@ -503,9 +438,9 @@
503 438 glyph, so we paint a small inline SVG via CSS `mask` with
504 439 `background-color: currentColor` — that way the icon inherits
505 440 the admin-bar text color across every WP profile scheme
506 441 without us hardcoding a fill. */
507 - #wp-admin-bar-desktop-help .ab-icon.desktop-mode-keyboard-icon {
442 + #wp-admin-bar-desktop-help .ab-icon.os-keyboard-icon {
508 443 background-color: currentColor;
509 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;
510 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;
511 446 }
@@ -513,13 +448,13 @@
513 448 /* Keyboard-shortcuts floating popover — anchored under the
514 449 keyboard button. Styled to match the Arrange submenu (light
515 450 bg on the otherwise-dark admin bar, soft shadow, rounded
516 451 corners). Built dynamically in admin-bar.js from translated
517 - content shipped via `desktopModeAdminBar.shortcuts`. */
452 + content shipped via `openStationAdminBar.shortcuts`. */
518 453 #wpadminbar #wp-admin-bar-desktop-help {
519 454 position: relative;
520 455 }
521 - #wpadminbar .desktop-mode-shortcuts-popover {
456 + #wpadminbar .os-shortcuts-popover {
522 457 display: none;
523 458 position: absolute;
524 459 top: 100%;
525 460 right: 0;
@@ -526,11 +461,11 @@
526 461 margin-top: 0;
527 462 padding: 12px 14px;
528 463 min-width: 460px;
529 464 max-width: min( 90vw, 720px );
530 - background: var( --desktop-mode-window-bg, #fff );
531 - color: var( --desktop-mode-text, #1d2327 );
532 - 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 ) );
533 468 border-radius: 8px;
534 469 box-shadow: 0 8px 24px rgba( 0, 0, 0, 0.18 ),
535 470 0 2px 6px rgba( 0, 0, 0, 0.08 );
536 471 z-index: 99999;
@@ -537,15 +472,15 @@
537 472 font-size: 12px;
538 473 line-height: 1.4;
539 474 text-align: left;
540 475 }
541 - #wpadminbar .desktop-mode-shortcuts-popover.is-open {
476 + #wpadminbar .os-shortcuts-popover.is-open {
542 477 display: block;
543 478 }
544 - #wpadminbar .desktop-mode-shortcuts-popover__section + .desktop-mode-shortcuts-popover__section {
479 + #wpadminbar .os-shortcuts-popover__section + .os-shortcuts-popover__section {
545 480 margin-top: 12px;
546 481 }
547 - #wpadminbar .desktop-mode-shortcuts-popover__heading {
482 + #wpadminbar .os-shortcuts-popover__heading {
548 483 margin: 0 0 6px;
549 484 padding: 0;
550 485 font-size: 11px;
551 486 font-weight: 600;
@@ -550,44 +485,44 @@
550 485 font-size: 11px;
551 486 font-weight: 600;
552 487 letter-spacing: 0.04em;
553 488 text-transform: uppercase;
554 - color: var( --desktop-mode-muted-fg, #50575e );
489 + color: var( --os-ui-fg-muted, #50575e );
555 490 line-height: 1.2;
556 491 }
557 - #wpadminbar .desktop-mode-shortcuts-popover__table {
492 + #wpadminbar .os-shortcuts-popover__table {
558 493 width: 100%;
559 494 border-collapse: collapse;
560 495 color: inherit;
561 496 }
562 - #wpadminbar .desktop-mode-shortcuts-popover__table th,
563 - #wpadminbar .desktop-mode-shortcuts-popover__table td {
497 + #wpadminbar .os-shortcuts-popover__table th,
498 + #wpadminbar .os-shortcuts-popover__table td {
564 499 padding: 6px 8px;
565 500 vertical-align: middle;
566 501 text-align: left;
567 - 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 ) );
568 503 color: inherit;
569 504 font-weight: 400;
570 505 font-size: 12px;
571 506 line-height: 1.4;
572 507 }
573 - #wpadminbar .desktop-mode-shortcuts-popover__table th {
508 + #wpadminbar .os-shortcuts-popover__table th {
574 509 font-weight: 600;
575 510 font-size: 11px;
576 - color: var( --desktop-mode-muted-fg, #50575e );
511 + color: var( --os-ui-fg-muted, #50575e );
577 512 letter-spacing: 0.02em;
578 513 }
579 - #wpadminbar .desktop-mode-shortcuts-popover__table tbody tr:last-child td {
514 + #wpadminbar .os-shortcuts-popover__table tbody tr:last-child td {
580 515 border-bottom: none;
581 516 }
582 - #wpadminbar .desktop-mode-shortcuts-popover__key-cell {
517 + #wpadminbar .os-shortcuts-popover__key-cell {
583 518 white-space: nowrap;
584 519 }
585 - #wpadminbar .desktop-mode-shortcuts-popover__note {
586 - color: var( --desktop-mode-muted-fg, #50575e );
520 + #wpadminbar .os-shortcuts-popover__note {
521 + color: var( --os-ui-fg-muted, #50575e );
587 522 font-size: 11px;
588 523 }
589 - #wpadminbar .desktop-mode-shortcuts-popover__list {
524 + #wpadminbar .os-shortcuts-popover__list {
590 525 margin: 0;
591 526 padding: 0;
592 527 list-style: none;
593 528 display: flex;
@@ -593,9 +528,9 @@
593 528 display: flex;
594 529 flex-direction: column;
595 530 gap: 4px;
596 531 }
597 - #wpadminbar .desktop-mode-shortcuts-popover__item {
532 + #wpadminbar .os-shortcuts-popover__item {
598 533 display: grid;
599 534 grid-template-columns: 120px 1fr;
600 535 align-items: center;
601 536 gap: 12px;
@@ -600,17 +535,17 @@
600 535 align-items: center;
601 536 gap: 12px;
602 537 padding: 4px 8px;
603 538 border-radius: 4px;
604 - background: rgba( 0, 0, 0, 0.03 );
539 + background: var( --os-ui-surface-sunken, rgba( 0, 0, 0, 0.03 ) );
605 540 }
606 - #wpadminbar .desktop-mode-shortcuts-popover__keys {
541 + #wpadminbar .os-shortcuts-popover__keys {
607 542 display: inline-flex;
608 543 align-items: center;
609 544 gap: 4px;
610 545 flex-wrap: wrap;
611 546 }
612 - #wpadminbar .desktop-mode-shortcuts-popover__kbd {
547 + #wpadminbar .os-shortcuts-popover__kbd {
613 548 display: inline-flex;
614 549 align-items: center;
615 550 justify-content: center;
616 551 min-width: 22px;
@@ -615,13 +550,13 @@
615 550 justify-content: center;
616 551 min-width: 22px;
617 552 height: 20px;
618 553 padding: 0 5px;
619 - border: 1px solid rgba( 0, 0, 0, 0.18 );
554 + border: 1px solid var( --os-ui-border-strong, rgba( 0, 0, 0, 0.18 ) );
620 555 border-bottom-width: 2px;
621 556 border-radius: 4px;
622 - background: #fff;
623 - color: var( --desktop-mode-text, #1d2327 );
557 + background: var( --os-ui-surface-elevated, #fff );
558 + color: var( --os-ui-fg, #1d2327 );
624 559 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
625 560 font-size: 11px;
626 561 font-weight: 600;
627 562 line-height: 1;
@@ -626,13 +561,13 @@
626 561 font-weight: 600;
627 562 line-height: 1;
628 563 white-space: nowrap;
629 564 }
630 - #wpadminbar .desktop-mode-shortcuts-popover__plus {
565 + #wpadminbar .os-shortcuts-popover__plus {
631 566 font-size: 10px;
632 - color: var( --desktop-mode-muted-fg, #50575e );
567 + color: var( --os-ui-fg-muted, #50575e );
633 568 }
634 - #wpadminbar .desktop-mode-shortcuts-popover__description {
569 + #wpadminbar .os-shortcuts-popover__description {
635 570 color: inherit;
636 571 }
637 572
638 573 /* Dock-style custom tooltip for icon-only admin-bar buttons.
@@ -674,21 +609,29 @@
674 609 #wpadminbar #wp-admin-bar-desktop-help.is-open .ab-item[ data-desktop-tooltip ]::after {
675 610 opacity: 0;
676 611 }
677 612
678 - /* Arrange submenu — aligned visually with the <wpd-menu> component
679 - (src/ui/components/wpd-menu/wpd-menu.styles.ts). The submenu
680 - flips from the native dark-on-dark admin bar to a light theme
681 - (white bg, dark text) so it matches the rest of our UI AND so
682 - the hover state stays legible (a light tint on a dark bg
683 - would render as invisible overlap with native admin-bar hover
684 - colors). Selectors prefixed with #wpadminbar to win the
685 - 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. */
686 629 #wpadminbar #wp-admin-bar-desktop-layout-menu .ab-sub-wrapper {
687 - background: var( --desktop-mode-window-bg, #fff );
630 + background: var( --os-ui-surface, var( --os-window-bg, #fff ) );
688 631 padding: 4px;
689 632 min-width: 220px;
690 - border: 1px solid var( --desktop-mode-window-border, #c3c4c7 );
633 + border: 1px solid var( --os-ui-border, var( --os-window-border, #c3c4c7 ) );
691 634 border-radius: 8px;
692 635 box-shadow: 0 8px 24px rgba( 0, 0, 0, 0.18 ),
693 636 0 2px 6px rgba( 0, 0, 0, 0.08 );
694 637 }
@@ -707,9 +650,9 @@
707 650 min-height: 32px;
708 651 padding: 6px 10px;
709 652 font-size: 13px;
710 653 line-height: 1.3;
711 - color: var( --desktop-mode-text, #1d2327 );
654 + color: var( --os-ui-fg, #1d2327 );
712 655 background: transparent;
713 656 border-radius: 6px;
714 657 transition: background-color 0.12s ease, color 0.12s ease;
715 658 }
@@ -719,12 +662,12 @@
719 662 thanks to the extra id in our selector chain. */
720 663 #wpadminbar #wp-admin-bar-desktop-layout-menu .ab-sub-wrapper .ab-submenu > li > .ab-item:hover,
721 664 #wpadminbar #wp-admin-bar-desktop-layout-menu .ab-sub-wrapper .ab-submenu > li > .ab-item:focus,
722 665 #wpadminbar.nojq #wp-admin-bar-desktop-layout-menu .ab-sub-wrapper .ab-submenu > li:hover > .ab-item {
723 - background: rgba( 0, 0, 0, 0.06 );
724 - color: #000;
666 + background: var( --os-ui-hover, rgba( 0, 0, 0, 0.06 ) );
667 + color: var( --os-ui-fg, #000 );
725 668 }
726 - #wp-admin-bar-desktop-layout-snap .desktop-mode-layout-checkbox {
669 + #wp-admin-bar-desktop-layout-snap .os-layout-checkbox {
727 670 flex-shrink: 0;
728 671 display: inline-flex;
729 672 align-items: center;
730 673 justify-content: center;
@@ -732,9 +675,9 @@
732 675 height: 16px;
733 676 font-size: 14px;
734 677 line-height: 1;
735 678 }
736 - #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 {
737 680 color: var( --wp-admin-theme-color, #2271b1 );
738 681 }
739 682 ';
740 683 wp_add_inline_style( 'admin-bar', $css );
@@ -742,18 +685,18 @@
742 685 // All PHP→JS values are emitted as JSON literals (never interpolated
743 686 // raw into the script body) so special characters, quotes, and
744 687 // unexpected shapes can't break the parser or be exploited.
745 688 // `active` must match the visual state shown on the toggle above —
746 - // i.e. "currently viewing desktop mode." Using `desktop_mode_is_enabled()`
689 + // i.e. "currently viewing OpenStation." Using `openstation_is_enabled()`
747 690 // alone would misclassify a classic-override request (meta = '1',
748 691 // URL carrying `desktop_mode_classic=1`) as active, causing the
749 692 // click handler to send `enabled=0` when the user actually wants
750 693 // to return to the shell.
751 694 wp_register_script(
752 - 'desktop-mode-admin-bar',
753 - DESKTOP_MODE_URL . 'assets/js/admin-bar.js',
695 + 'os-admin-bar',
696 + OPENSTATION_URL . 'assets/js/admin-bar.js',
754 697 array( 'admin-bar' ),
755 - DESKTOP_MODE_VERSION,
698 + OPENSTATION_VERSION,
756 699 true
757 700 );
758 701 // Emit the config as a JSON literal via wp_add_inline_script (not
759 702 // wp_localize_script — the latter casts booleans to '' / '1', which
@@ -759,15 +702,21 @@
759 702 // wp_localize_script — the latter casts booleans to '' / '1', which
760 703 // breaks the click handler's `!! cfg.active` check). 'before' runs
761 704 // before admin-bar.js so the global is ready when the IIFE fires.
762 705 wp_add_inline_script(
763 - 'desktop-mode-admin-bar',
764 - 'var desktopModeAdminBar = ' . wp_json_encode(
706 + 'os-admin-bar',
707 + 'var openStationAdminBar = ' . wp_json_encode(
765 708 array(
766 - 'nonce' => wp_create_nonce( 'save-desktop-mode' ),
767 - 'active' => desktop_mode_is_enabled() && ! desktop_mode_is_classic_request(),
768 - 'classicUrl' => esc_url_raw( admin_url() ),
769 - '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(),
770 719 'ajaxUrl' => esc_url_raw( admin_url( 'admin-ajax.php' ) ),
771 720 'i18n' => array(
772 721 'enterFullscreen' => __( 'Fullscreen', 'desktop-mode' ),
773 722 'exitFullscreen' => __( 'Exit fullscreen', 'desktop-mode' ),
@@ -773,8 +722,9 @@
773 722 'exitFullscreen' => __( 'Exit fullscreen', 'desktop-mode' ),
774 723 'enterTitle' => __( 'Enter fullscreen', 'desktop-mode' ),
775 724 'exitTitle' => __( 'Exit fullscreen', 'desktop-mode' ),
776 725 ),
726 +
777 727 /*
778 728 * Keyboard-shortcuts popover content. Translated once on
779 729 * the server and shipped to admin-bar.js, which renders
780 730 * the popover anchored under the keyboard button.
@@ -844,8 +794,12 @@
844 794 'keys' => array( '⌘/Ctrl', 'K' ),
845 795 'description' => __( 'Open the command palette / Ask AI overlay.', 'desktop-mode' ),
846 796 ),
847 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(
848 802 'keys' => array( 'Esc' ),
849 803 'description' => __( 'Exit Overview (or Snap Overview) without changing window state.', 'desktop-mode' ),
850 804 ),
851 805 ),
@@ -854,8 +808,7 @@
854 808 )
855 809 ) . ';',
856 810 'before'
857 811 );
858 - wp_enqueue_script( 'desktop-mode-admin-bar' );
812 + wp_enqueue_script( 'os-admin-bar' );
859 813 }
860 -add_action( 'admin_enqueue_scripts', 'desktop_mode_enqueue_toggle_assets' );
861 -
814 +add_action( 'admin_enqueue_scripts', 'openstation_enqueue_toggle_assets' );