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/render/assets.php +112 -72 1.1.41.1.10 View file →
@@ -73,9 +73,9 @@
73 73 do_action( 'openstation_chromeless_styles' );
74 74 return;
75 75 }
76 76
77 - if ( ! openstation_is_enabled() || openstation_is_classic_request() ) {
77 + if ( ! openstation_is_shell_request() ) {
78 78 return;
79 79 }
80 80
81 81 // CSS. Only the sheets that paint surfaces present at boot — the
@@ -89,12 +89,16 @@
89 89 wp_enqueue_style( 'os-window-overview' );
90 90 wp_enqueue_style( 'os-dock' );
91 91 wp_enqueue_style( 'os-dock-peek' );
92 92 wp_enqueue_style( 'os-notch' );
93 + wp_enqueue_style( 'os-workspaces' );
93 94 wp_enqueue_style( 'os-shortcuts' );
94 95 wp_enqueue_style( 'os-openstation-layout' );
95 96 wp_enqueue_style( 'os-files' );
96 97 wp_enqueue_style( 'os-notes' );
98 + // Unconditional like the layout sheet: a live crossing into the
99 + // phone band must not find the phone layer unstyled.
100 + wp_enqueue_style( 'os-mobile' );
97 101
98 102 // Solo mode — a single window freed into a native OS window by the
99 103 // desktop host. Same shell, everything but that one window hidden.
100 104 $solo_window = openstation_solo_window_id();
@@ -199,9 +203,9 @@
199 203 );
200 204 }
201 205
202 206 // Pass configuration to JavaScript.
203 - global $title, $pagenow, $parent_file, $menu;
207 + global $title, $parent_file, $menu;
204 208
205 209 $menu_icon = 'dashicons-admin-generic';
206 210 if ( ! empty( $parent_file ) && ! empty( $menu ) ) {
207 211 foreach ( $menu as $item ) {
@@ -217,11 +221,11 @@
217 221 // plugin-contributed top-level routes. `openstation_dock_placement`
218 222 // is the per-item filter escape hatch for hiding. Shared with the
219 223 // REST menu endpoint so live refreshes (post plugin-activation)
220 224 // produce the same ordering as the boot payload.
221 - $menu_payload = openstation_build_menu_payload();
222 - $dock_items = $menu_payload['dockItems'];
223 - $native_windows = isset( $menu_payload['nativeWindows'] )
225 + $menu_payload = openstation_build_menu_payload();
226 + $dock_items = $menu_payload['dockItems'];
227 + $native_windows = isset( $menu_payload['nativeWindows'] )
224 228 ? $menu_payload['nativeWindows']
225 229 : array();
226 230
227 231 // The BOOT page prints every registry window's template as a real
@@ -329,9 +333,9 @@
329 333 $desktop_theme_row['cssDeferred'] = true;
330 334 }
331 335 }
332 336 unset( $desktop_theme_row );
333 - $desktop_icons = isset( $menu_payload['desktopIcons'] )
337 + $desktop_icons = isset( $menu_payload['desktopIcons'] )
334 338 ? $menu_payload['desktopIcons']
335 339 : array();
336 340
337 341 // Files-on-the-Desktop payload (Phase 0+1). Plugin-registered
@@ -432,18 +436,33 @@
432 436 OPENSTATION_URL . 'assets/js/' . $base . $suffix . '.js?ver=' . $ver
433 437 );
434 438 };
435 439
436 - // Build the current page URL from $pagenow + $_GET. Strip the portal
437 - // markers so the derived window ID matches what the dock would produce
438 - // for the same page — otherwise auto-opening the entry window and
439 - // clicking the same dock icon would create a duplicate.
440 - $current_query = $_GET; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
441 - unset( $current_query[ OPENSTATION_PORTAL_FLAG ], $current_query[ OPENSTATION_PORTAL_INTENT_FLAG ] );
442 - $current_page = admin_url( $pagenow ) . ( ! empty( $current_query ) ? '?' . http_build_query( $current_query ) : '' );
440 + // The page the shell opens first. On the shell screen it is the
441 + // validated `target` query arg (else the session's focused window,
442 + // the default window, the Dashboard); on a solo boot it is the
443 + // request's own URL. Either way the frozen portal flags are gone
444 + // from it, so the derived window id matches what the dock would
445 + // produce for the same page — otherwise auto-opening the entry
446 + // window and clicking the same dock icon would create a duplicate.
447 + $boot_target = openstation_shell_boot_target();
448 + $current_page = $boot_target['url'];
449 + $from_portal = $boot_target['fromPortal'];
450 + $from_portal_intent = $boot_target['fromPortalIntent'];
443 451
444 - $from_portal = ! empty( $_GET[ OPENSTATION_PORTAL_FLAG ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
445 - $from_portal_intent = ! empty( $_GET[ OPENSTATION_PORTAL_INTENT_FLAG ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
452 + // On the shell screen `$title` and `$parent_file` describe the
453 + // screen ("OpenStation", no menu), not the page about to open. The
454 + // dock entry for that page is the identity the entry window folds
455 + // into, so its title and icon are the right first paint; the iframe
456 + // reports its own title once it lands either way.
457 + $current_title = wp_strip_all_tags( (string) $title );
458 + if ( openstation_is_shell_screen_request() ) {
459 + $boot_meta = openstation_shell_boot_target_meta( $current_page, $dock_items );
460 + $current_title = wp_strip_all_tags( $boot_meta['title'] );
461 + if ( '' !== $boot_meta['icon'] ) {
462 + $menu_icon = $boot_meta['icon'];
463 + }
464 + }
446 465
447 466 /**
448 467 * Filters the desktop shell configuration passed to JavaScript.
449 468 *
@@ -490,11 +509,15 @@
490 509 $config = apply_filters(
491 510 'openstation_shell_config',
492 511 array(
493 512 'currentPage' => esc_url( $current_page ),
494 - 'currentTitle' => wp_strip_all_tags( $title ),
513 + 'currentTitle' => $current_title,
495 514 'currentIcon' => sanitize_html_class( $menu_icon ),
496 - 'adminUrl' => esc_url( admin_url() ),
515 + // `self_admin_url()`: the base a window id is derived from,
516 + // and the URL the shell leaves for on exit. Both want the
517 + // admin the screen is in, which is the network one when the
518 + // network shell screen is what rendered.
519 + 'adminUrl' => esc_url( self_admin_url() ),
497 520 'homeUrl' => esc_url( home_url( '/' ) ),
498 521 // Decoded: the shell assigns this to `window.location`,
499 522 // where `&` would make `_wpnonce` arrive as
500 523 // `amp;_wpnonce` and fail the nonce check.
@@ -555,9 +578,17 @@
555 578 'coreNotices' => openstation_get_core_notices(),
556 579 'pluginNotices' => openstation_get_plugin_notices(),
557 580 'defaultWallpaper' => openstation_get_default_wallpaper(),
558 581 'session' => openstation_get_session( get_current_user_id() ),
559 - 'sessionUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/session' ) ),
582 + // The session route runs in the main site's blog context
583 + // whichever desktop posts to it, so the network screen's
584 + // URL says which session it is addressing — see
585 + // `openstation_rest_session_network()`.
586 + 'sessionUrl' => esc_url_raw(
587 + is_network_admin()
588 + ? add_query_arg( 'network', '1', rest_url( 'desktop-mode/v1/session' ) )
589 + : rest_url( 'desktop-mode/v1/session' )
590 + ),
560 591 'restUrl' => esc_url_raw( rest_url() ),
561 592 'mediaUrl' => esc_url_raw( rest_url( 'wp/v2/media' ) ),
562 593 'dropConfig' => $drop_config,
563 594 'defaultWindowUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/default-window' ) ),
@@ -580,15 +611,8 @@
580 611 // stub `<script>`-injects this URL the first time the user
581 612 // opens the assistant. Picking `.js` vs `.min.js` here keeps
582 613 // the SCRIPT_DEBUG gate server-side, matching iframeBridgeUrl.
583 614 'aiAssistantBundleUrl' => $lazy_bundle_url( 'ai-assistant' ),
584 - // URL of the OS Settings panel lazy bundle. Injected by
585 - // the main bundle's `OsSettings.renderPanel()` stub on
586 - // the user's first Settings open. Holds every section
587 - // renderer + the `<os-*>` components only the panel
588 - // uses, so nothing about Settings ships in
589 - // `desktop.min.js` for users who never open it.
590 - 'osSettingsPanelBundleUrl' => $lazy_bundle_url( 'os-settings-panel' ),
591 615 // URL of the shell-overlays lazy bundle. Pre-loaded by
592 616 // the main bundle after first paint so action-triggered
593 617 // overlays (toast, confirm dialog, context menus) feel
594 618 // instant the first time they fire.
@@ -643,12 +667,29 @@
643 667 // / `openNew()` call (both async); pre-loaded
644 668 // by the shell after first paint when no session is being
645 669 // restored and no `openCurrentPage` will fire.
646 670 'windowSystemBundleUrl' => $lazy_bundle_url( 'window-system' ),
671 + // URL of the lazy phone-layer bundle. Injected by the main
672 + // bundle only when the mode resolves to `mobile`, so a
673 + // desktop never fetches it. `mode` carries the preference
674 + // and breakpoints the first-paint head stamp already used,
675 + // plus the server's default tab-bar pins — see
676 + // `includes/mobile.php`.
677 + 'mobileBundleUrl' => $lazy_bundle_url( 'mobile' ),
678 + 'mode' => openstation_mode_config( get_current_user_id() ),
647 679 // URL of the item-visibility-menu lazy bundle — the
648 680 // right-click "hide from dock / desktop" menu. Injected by
649 681 // the main bundle's loader shim on the first right-click.
650 682 'itemVisibilityMenuBundleUrl' => $lazy_bundle_url( 'item-visibility-menu' ),
683 + // URL of the workspace-wizard lazy bundle — the modal
684 + // behind "Edit this workspace…". Injected by the main
685 + // bundle's loader shim on first open.
686 + 'workspaceWizardBundleUrl' => $lazy_bundle_url( 'workspace-wizard' ),
687 + // Server-side view of the workspace templates, so a plugin
688 + // can add or drop one from PHP. The client merges these
689 + // with its own built-ins by id — see
690 + // `src/workspaces/server-sync.ts`.
691 + 'workspacePresets' => openstation_workspace_presets(),
651 692 // URL of the release-card lazy bundle — the vinyl core-
652 693 // update announcement. Injected by `maybeShowUpdate()` only
653 694 // when a core update is actually pending.
654 695 'releaseCardBundleUrl' => $lazy_bundle_url( 'release-card' ),
@@ -680,38 +721,21 @@
680 721 // Site-wide games kill switch (Extended options). Exposed to
681 722 // every user — the shell skips the challenges Heartbeat
682 723 // channel when the framework is off.
683 724 'gamesEnabled' => openstation_games_enabled(),
684 - // Comments-window AI moderation toggle — surfaced at the
685 - // shell level so the OS Settings → Features tab can render
686 - // the toggle without depending on the Comments window
687 - // being registered for this user. URL is the same
688 - // endpoint the comments-window config exposes; state is
689 - // `null` for non-admins (the UI hides the row entirely).
690 - 'commentsAiUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/comments/ai-settings' ) ),
691 - // Non-null only for admins on a site where the Core AI stack is
692 - // present. Comment scoring routes through the AI Client (WP 7.0+),
693 - // so on older WordPress the whole row is hidden — same as the
694 - // assistant toggle — rather than shown disabled pointing at a
695 - // Settings → Connectors screen that doesn't exist there.
696 - 'commentsAi' => (
697 - current_user_can( 'manage_options' )
698 - && function_exists( 'openstation_ai_is_available' )
699 - && openstation_ai_is_available()
700 - )
701 - ? array(
702 - 'enabled' => function_exists( 'openstation_comments_ai_is_enabled' )
703 - ? openstation_comments_ai_is_enabled()
704 - : false,
705 - 'providerConfigured' => function_exists( 'openstation_comments_ai_provider_configured' )
706 - ? openstation_comments_ai_provider_configured()
707 - : false,
708 - )
709 - : null,
710 725 'currentUserIsAdmin' => current_user_can( 'manage_options' ),
726 + // Null on single-site installs; its `networkAdmin` null
727 + // without `manage_network`, which is what keeps the dock
728 + // tile from registering.
729 + 'multisite' => openstation_multisite_payload(),
711 730 'portalUrl' => esc_url( openstation_portal_url() ),
712 731 'fromPortal' => $from_portal,
713 732 'fromPortalIntent' => $from_portal_intent,
733 + // One-shot like the boot target: a switch from another
734 + // site's overview lands in this one's.
735 + 'landInOverview' => openstation_shell_lands_in_overview(),
736 + 'arrivalDirection' => openstation_shell_arrival_direction(),
737 + 'hopLinkOffer' => function_exists( 'openstation_network_link_offer' ) ? openstation_network_link_offer() : null,
714 738 'pwa' => array(
715 739 'manifestUrl' => esc_url_raw( openstation_pwa_manifest_url() ),
716 740 'swUrl' => esc_url_raw( openstation_pwa_sw_url() ),
717 741 // Extensionless retry target for hosts whose nginx 404s
@@ -716,8 +740,12 @@
716 740 'swUrl' => esc_url_raw( openstation_pwa_sw_url() ),
717 741 // Extensionless retry target for hosts whose nginx 404s
718 742 // virtual .js paths before WordPress runs (WordPress.com).
719 743 'swFallbackUrl' => esc_url_raw( openstation_pwa_sw_fallback_url() ),
744 + // The site's home path — the scope the registration
745 + // asks for, so a subdirectory network's sites each get
746 + // their own worker instead of fighting over the root.
747 + 'swScope' => openstation_pwa_sw_scope(),
720 748 // The worker's per-user flags, computed HERE rather than
721 749 // baked into the served `sw.js`.
722 750 //
723 751 // A service worker is origin-wide but these are per-user
@@ -723,12 +751,11 @@
723 751 // A service worker is origin-wide but these are per-user
724 752 // preferences, so putting them in the script bytes made
725 753 // the body differ between an anonymous and a logged-in
726 754 // request — and any in-scope logged-out navigation then
727 - // installed a "new" worker and tripped the shell's
728 - // `controllerchange` reload. The bytes are identical for
729 - // everyone now; the shell posts these to the worker at
730 - // boot.
755 + // installed a "new" worker, which at the time reloaded
756 + // the shell. The bytes are identical for everyone now;
757 + // the shell posts these to the worker at boot.
731 758 //
732 759 // Computed server-side, not read from the settings
733 760 // snapshot client-side, because
734 761 // `openstation_pwa_admin_asset_cache_enabled()` applies
@@ -737,8 +764,14 @@
737 764 'swConfig' => array(
738 765 'adminAssetCache' => (bool) openstation_pwa_admin_asset_cache_enabled(),
739 766 'windowPrewarm' => ! empty( openstation_get_os_settings( get_current_user_id() )['windowPrewarmEnabled'] ),
740 767 ),
768 + // The build this shell document belongs to. When a new
769 + // worker takes over mid-session the shell asks it for
770 + // the stamp it was served with and compares; only a
771 + // difference — the shell's own files changed on the
772 + // server — offers the user a reload. Never automatic.
773 + 'shellBuild' => openstation_shell_build_stamp(),
741 774 'stateUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/pwa-state' ) ),
742 775 'state' => openstation_pwa_get_user_state( get_current_user_id() ),
743 776 // Mirrors the manifest's `name` field — used by the
744 777 // install pill so the button reads "Install <site>"
@@ -775,19 +808,19 @@
775 808 : array(),
776 809 'deferredStyles' => openstation_build_deferred_styles(
777 810 array_merge(
778 811 array(
779 - 'os-settings',
780 812 'desktop-mode-ai-assistant',
781 813 'desktop-mode-bug-report',
782 - // WP Explorer's sheet. It rides that window as a
783 - // companion style, but the desktop FOLDER window
784 - // paints its preview pane with the same
785 - // `os-my-wordpress__*` classes and — being a
786 - // native window opened straight from JS — carries
787 - // no companion styles of its own. Without this,
788 - // the pane rendered unstyled until WP Explorer
789 - // had been opened once in the session.
814 + // The explorer's shared sheet. It rides the WP
815 + // Explorer APP as a companion style, but the
816 + // desktop FOLDER window paints its preview pane
817 + // with the same `os-my-wordpress__*` classes and
818 + // — being a native window opened straight from
819 + // JS — carries no companion styles of its own.
820 + // Without this, the pane rendered unstyled until
821 + // the explorer had been opened once in the
822 + // session.
790 823 'desktop-mode-my-wordpress',
791 824 ),
792 825 // The Games sheets. They also ride the hub window as
793 826 // companion styles, but a game is reachable without
@@ -843,9 +876,9 @@
843 876 * before the callback fires. On WP 6.9 (function exists, no default
844 877 * hook) the `remove_action()` is a harmless no-op.
845 878 */
846 879 function openstation_defer_core_command_palette() {
847 - if ( ! openstation_is_enabled() || openstation_is_chromeless_request() || openstation_is_classic_request() ) {
880 + if ( ! openstation_is_shell_request() ) {
848 881 return;
849 882 }
850 883 remove_action( 'admin_enqueue_scripts', 'wp_enqueue_command_palette_assets' );
851 884 }
@@ -895,14 +928,9 @@
895 928 * supply absolute URLs through the filter; in that case the consumer
896 929 * is responsible for the `crossorigin` semantics.
897 930 */
898 931 function openstation_print_preload_hints() {
899 - if (
900 - ! is_admin()
901 - || ! openstation_is_enabled()
902 - || openstation_is_chromeless_request()
903 - || openstation_is_classic_request()
904 - ) {
932 + if ( ! openstation_is_shell_request() ) {
905 933 return;
906 934 }
907 935
908 936 $suffix = openstation_asset_suffix();
@@ -945,8 +973,21 @@
945 973 'rel' => 'prefetch',
946 974 ),
947 975 );
948 976
977 + // The phone layer is needed at boot on a phone and never on a
978 + // desktop; the server cannot see the viewport, so the user agent
979 + // decides whether the hint is worth its bytes. A wrong guess costs
980 + // one low-priority fetch, never a wrong layout — the stamp and the
981 + // bundle loader read the real viewport.
982 + if ( openstation_mode_hint_is_mobile( get_current_user_id() ) ) {
983 + $hints[] = array(
984 + 'href' => $build_url( 'assets/js/mobile' . $suffix . '.js' ),
985 + 'as' => 'script',
986 + 'rel' => 'prefetch',
987 + );
988 + }
989 +
949 990 /**
950 991 * Filters the list of resource preload hints emitted in `<head>`.
951 992 *
952 993 * Each entry is a `{ 'href' => string, 'as' => string,
@@ -1063,9 +1104,8 @@
1063 1104 'os-openstation-layout',
1064 1105 'desktop-mode-ai-assistant',
1065 1106 'desktop-mode-bug-report',
1066 1107 'os-window-overview',
1067 - 'os-settings',
1068 1108 )
1069 1109 );
1070 1110
1071 1111 if ( ! in_array( $handle, (array) $deferred, true ) ) {