PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.13
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.13
1.1.13 1.1.12 1.1.11 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 All 37 releases
← All changes | includes/render/assets.php +121 -54 1.1.5 → 1.1.13 View file →
@@ -87,14 +87,18 @@
87 87 wp_enqueue_style( 'openstation' );
88 88 wp_enqueue_style( 'os-windows' );
89 89 wp_enqueue_style( 'os-window-overview' );
90 90 wp_enqueue_style( 'os-dock' );
91 + wp_enqueue_style( 'os-dock-taskbar' );
91 92 wp_enqueue_style( 'os-dock-peek' );
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();
@@ -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
@@ -342,8 +346,16 @@
342 346 : array();
343 347 $server_file_openers = function_exists( 'openstation_build_file_openers_payload' )
344 348 ? openstation_build_file_openers_payload()
345 349 : array();
350 + // Entries in the menu payload carry dependency handles; their
351 + // payloads ride once in `scriptDepPayloads` (GH#892). The file
352 + // lists stay whole: no sync module reads them on the client, and
353 + // compacting them here was the only write to the map after the
354 + // menu payload froze its own, so a refresh could not match it.
355 + $script_dep_payloads = isset( $menu_payload['scriptDepPayloads'] )
356 + ? (array) $menu_payload['scriptDepPayloads']
357 + : array();
346 358 $user_file_associations = function_exists( 'openstation_get_user_file_associations' )
347 359 ? openstation_get_user_file_associations( get_current_user_id() )
348 360 : array();
349 361 $server_wallpaper_menu_items = function_exists( 'openstation_build_wallpaper_menu_items' )
@@ -498,9 +510,14 @@
498 510 * @type bool $fromPortal Whether the shell was reached via the portal.
499 511 * @type bool $fromPortalIntent Whether the portal redirect resolved from an explicit `?target=…` (user navigation intent) rather than the session's focused window or the default-window fallback. Distinguishes a bare `/openstation/` visit from a portal-redirected admin-bar click so the shell can honour the URL the user actually asked for.
500 512 * @type array $seenIntros Slugs of one-time announcements the user has dismissed (e.g. `['openstation-rebrand']`).
501 513 * @type string $seenIntrosUrl REST endpoint for the seen-intros surface — POST `/seen` to mark, DELETE the base to reset.
514 + * @type bool $shellTour Whether this site offers the first-boot shell tour (`openstation_show_shell_tour`). Whether this user already had it is `seenIntros` containing `shell-tour`.
515 + * @type string $shellTourBundleUrl URL of the lazy shell-tour bundle, injected on first use.
516 + * @type array $firstRun `{ installedAt, firstEnabledAt, enabledAt }`, epoch seconds, 0 when unknown — the first-run stamps, read-only.
502 517 * @type bool $rebrandNotice Whether to offer this user the one-off announcement explaining the rename from Desktop Mode to OpenStation. True only when migration 5 flagged this user as a Desktop Mode user from before the rename AND they haven't dismissed the `openstation-rebrand` intro. Only ever present in the shell config, so the announcement never reaches the classic admin.
518 + * @type array|null $usageFeedback What the one-time usage feedback prompt needs (`restUrl`), or `null` when this user is not owed it: the feature is off, they have had OpenStation on for fewer than seven days by the `openstation_enabled_at` stamp, or they already answered or dismissed the `usage-feedback` intro. Carries no user data.
519 + * @type string $usageFeedbackBundleUrl URL of the lazy `usage-feedback` bundle, the form the prompt opens.
503 520 * }
504 521 */
505 522 $config = apply_filters(
506 523 'openstation_shell_config',
@@ -507,9 +524,13 @@
507 524 array(
508 525 'currentPage' => esc_url( $current_page ),
509 526 'currentTitle' => $current_title,
510 527 'currentIcon' => sanitize_html_class( $menu_icon ),
511 - 'adminUrl' => esc_url( admin_url() ),
528 + // `self_admin_url()`: the base a window id is derived from,
529 + // and the URL the shell leaves for on exit. Both want the
530 + // admin the screen is in, which is the network one when the
531 + // network shell screen is what rendered.
532 + 'adminUrl' => esc_url( self_admin_url() ),
512 533 'homeUrl' => esc_url( home_url( '/' ) ),
513 534 // Decoded: the shell assigns this to `window.location`,
514 535 // where `&` would make `_wpnonce` arrive as
515 536 // `amp;_wpnonce` and fail the nonce check.
@@ -553,8 +574,11 @@
553 574 'serverDesktopThemes' => $server_desktop_themes,
554 575 'desktopIcons' => $desktop_icons,
555 576 'serverFileTypes' => $server_file_types,
556 577 'serverFileOpeners' => $server_file_openers,
578 + // Handle => dependency payload for every `scriptDeps` list in
579 + // this config; see `openstation_compact_script_deps()`.
580 + 'scriptDepPayloads' => (object) $script_dep_payloads,
557 581 'userFileAssociations' => $user_file_associations,
558 582 'filesUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/files' ) ),
559 583 // Pinned-notes REST base (`includes/notes/rest.php`). The
560 584 // notes layer boots only when this is present.
@@ -570,9 +594,17 @@
570 594 'coreNotices' => openstation_get_core_notices(),
571 595 'pluginNotices' => openstation_get_plugin_notices(),
572 596 'defaultWallpaper' => openstation_get_default_wallpaper(),
573 597 'session' => openstation_get_session( get_current_user_id() ),
574 - 'sessionUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/session' ) ),
598 + // The session route runs in the main site's blog context
599 + // whichever desktop posts to it, so the network screen's
600 + // URL says which session it is addressing — see
601 + // `openstation_rest_session_network()`.
602 + 'sessionUrl' => esc_url_raw(
603 + is_network_admin()
604 + ? add_query_arg( 'network', '1', rest_url( 'desktop-mode/v1/session' ) )
605 + : rest_url( 'desktop-mode/v1/session' )
606 + ),
575 607 'restUrl' => esc_url_raw( rest_url() ),
576 608 'mediaUrl' => esc_url_raw( rest_url( 'wp/v2/media' ) ),
577 609 'dropConfig' => $drop_config,
578 610 'defaultWindowUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/default-window' ) ),
@@ -595,15 +627,8 @@
595 627 // stub `<script>`-injects this URL the first time the user
596 628 // opens the assistant. Picking `.js` vs `.min.js` here keeps
597 629 // the SCRIPT_DEBUG gate server-side, matching iframeBridgeUrl.
598 630 'aiAssistantBundleUrl' => $lazy_bundle_url( 'ai-assistant' ),
599 - // URL of the OS Settings panel lazy bundle. Injected by
600 - // the main bundle's `OsSettings.renderPanel()` stub on
601 - // the user's first Settings open. Holds every section
602 - // renderer + the `<os-*>` components only the panel
603 - // uses, so nothing about Settings ships in
604 - // `desktop.min.js` for users who never open it.
605 - 'osSettingsPanelBundleUrl' => $lazy_bundle_url( 'os-settings-panel' ),
606 631 // URL of the shell-overlays lazy bundle. Pre-loaded by
607 632 // the main bundle after first paint so action-triggered
608 633 // overlays (toast, confirm dialog, context menus) feel
609 634 // instant the first time they fire.
@@ -658,12 +683,29 @@
658 683 // / `openNew()` call (both async); pre-loaded
659 684 // by the shell after first paint when no session is being
660 685 // restored and no `openCurrentPage` will fire.
661 686 'windowSystemBundleUrl' => $lazy_bundle_url( 'window-system' ),
687 + // URL of the lazy phone-layer bundle. Injected by the main
688 + // bundle only when the mode resolves to `mobile`, so a
689 + // desktop never fetches it. `mode` carries the preference
690 + // and breakpoints the first-paint head stamp already used,
691 + // plus the server's default tab-bar pins — see
692 + // `includes/mobile.php`.
693 + 'mobileBundleUrl' => $lazy_bundle_url( 'mobile' ),
694 + 'mode' => openstation_mode_config( get_current_user_id() ),
662 695 // URL of the item-visibility-menu lazy bundle — the
663 696 // right-click "hide from dock / desktop" menu. Injected by
664 697 // the main bundle's loader shim on the first right-click.
665 698 'itemVisibilityMenuBundleUrl' => $lazy_bundle_url( 'item-visibility-menu' ),
699 + // URL of the workspace-wizard lazy bundle — the modal
700 + // behind "Edit this workspace…". Injected by the main
701 + // bundle's loader shim on first open.
702 + 'workspaceWizardBundleUrl' => $lazy_bundle_url( 'workspace-wizard' ),
703 + // Server-side view of the workspace templates, so a plugin
704 + // can add or drop one from PHP. The client merges these
705 + // with its own built-ins by id — see
706 + // `src/workspaces/server-sync.ts`.
707 + 'workspacePresets' => openstation_workspace_presets(),
666 708 // URL of the release-card lazy bundle — the vinyl core-
667 709 // update announcement. Injected by `maybeShowUpdate()` only
668 710 // when a core update is actually pending.
669 711 'releaseCardBundleUrl' => $lazy_bundle_url( 'release-card' ),
@@ -680,8 +722,26 @@
680 722 // announcement yet. Same value that gated `os-announce`
681 723 // above; the dialog cannot paint without that stylesheet, so
682 724 // the two must not diverge.
683 725 'rebrandNotice' => $show_rebrand_notice,
726 + // The first-boot shell tour: whether this site offers it
727 + // (the `openstation_show_shell_tour` filter), and the lazy
728 + // bundle that runs it. Whether THIS user already had it is
729 + // `seenIntros` containing `shell-tour`; the shell reads that
730 + // itself so a reset can replay the tour without a new boot.
731 + 'shellTour' => openstation_should_offer_shell_tour( get_current_user_id() ),
732 + 'shellTourBundleUrl' => $lazy_bundle_url( 'shell-tour' ),
733 + // The first-run stamps, read-only, epoch seconds (0 when
734 + // unknown): when the plugin was installed, when anyone first
735 + // enabled it, and when this user did. See
736 + // `includes/first-run/stamps.php`.
737 + 'firstRun' => openstation_first_run_config( get_current_user_id() ),
738 + // Null for everyone but a user who has had OpenStation on
739 + // long enough and has not answered yet; the gate lives in
740 + // `includes/feedback/usage.php`. The form is a lazy bundle
741 + // fetched only when the user says yes to the prompt.
742 + 'usageFeedback' => function_exists( 'openstation_usage_feedback_config' ) ? openstation_usage_feedback_config() : null,
743 + 'usageFeedbackBundleUrl' => $lazy_bundle_url( 'usage-feedback' ),
684 744 'aiSearchUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/ai/search' ) ),
685 745 // AI assistant availability + per-user toggle. Drives whether the
686 746 // Cmd+K palette and admin-bar icon appear, and the setup placeholder.
687 747 'aiAssistant' => function_exists( 'openstation_ai_assistant_config' )
@@ -690,43 +750,29 @@
690 750 // Lets the Features tab re-check provider availability without a
691 751 // reload after a connector is configured in Settings → Connectors.
692 752 'aiStatusUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/ai/status' ) ),
693 753 'extendedOptions' => current_user_can( 'manage_options' ) ? openstation_get_extended_options() : null,
754 + // Agents offered as "Send to <agent>" on desktop and folder
755 + // tiles. See `src/desktop-files/agent-menu-items.ts`.
756 + 'agentsSendTo' => function_exists( 'openstation_agents_send_to_targets' ) ? openstation_agents_send_to_targets() : array(),
694 757 'extendedOptionsUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/extended-options' ) ),
695 758 // Site-wide games kill switch (Extended options). Exposed to
696 759 // every user — the shell skips the challenges Heartbeat
697 760 // channel when the framework is off.
698 761 'gamesEnabled' => openstation_games_enabled(),
699 - // Comments-window AI moderation toggle — surfaced at the
700 - // shell level so the OS Settings → Features tab can render
701 - // the toggle without depending on the Comments window
702 - // being registered for this user. URL is the same
703 - // endpoint the comments-window config exposes; state is
704 - // `null` for non-admins (the UI hides the row entirely).
705 - 'commentsAiUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/comments/ai-settings' ) ),
706 - // Non-null only for admins on a site where the Core AI stack is
707 - // present. Comment scoring routes through the AI Client (WP 7.0+),
708 - // so on older WordPress the whole row is hidden — same as the
709 - // assistant toggle — rather than shown disabled pointing at a
710 - // Settings → Connectors screen that doesn't exist there.
711 - 'commentsAi' => (
712 - current_user_can( 'manage_options' )
713 - && function_exists( 'openstation_ai_is_available' )
714 - && openstation_ai_is_available()
715 - )
716 - ? array(
717 - 'enabled' => function_exists( 'openstation_comments_ai_is_enabled' )
718 - ? openstation_comments_ai_is_enabled()
719 - : false,
720 - 'providerConfigured' => function_exists( 'openstation_comments_ai_provider_configured' )
721 - ? openstation_comments_ai_provider_configured()
722 - : false,
723 - )
724 - : null,
725 762 'currentUserIsAdmin' => current_user_can( 'manage_options' ),
763 + // Null on single-site installs; its `networkAdmin` null
764 + // without `manage_network`, which is what keeps the dock
765 + // tile from registering.
766 + 'multisite' => openstation_multisite_payload(),
726 767 'portalUrl' => esc_url( openstation_portal_url() ),
727 768 'fromPortal' => $from_portal,
728 769 'fromPortalIntent' => $from_portal_intent,
770 + // One-shot like the boot target: a switch from another
771 + // site's overview lands in this one's.
772 + 'landInOverview' => openstation_shell_lands_in_overview(),
773 + 'arrivalDirection' => openstation_shell_arrival_direction(),
774 + 'hopLinkOffer' => function_exists( 'openstation_network_link_offer' ) ? openstation_network_link_offer() : null,
729 775 'pwa' => array(
730 776 'manifestUrl' => esc_url_raw( openstation_pwa_manifest_url() ),
731 777 'swUrl' => esc_url_raw( openstation_pwa_sw_url() ),
732 778 // Extensionless retry target for hosts whose nginx 404s
@@ -731,8 +777,12 @@
731 777 'swUrl' => esc_url_raw( openstation_pwa_sw_url() ),
732 778 // Extensionless retry target for hosts whose nginx 404s
733 779 // virtual .js paths before WordPress runs (WordPress.com).
734 780 'swFallbackUrl' => esc_url_raw( openstation_pwa_sw_fallback_url() ),
781 + // The site's home path — the scope the registration
782 + // asks for, so a subdirectory network's sites each get
783 + // their own worker instead of fighting over the root.
784 + 'swScope' => openstation_pwa_sw_scope(),
735 785 // The worker's per-user flags, computed HERE rather than
736 786 // baked into the served `sw.js`.
737 787 //
738 788 // A service worker is origin-wide but these are per-user
@@ -738,12 +788,11 @@
738 788 // A service worker is origin-wide but these are per-user
739 789 // preferences, so putting them in the script bytes made
740 790 // the body differ between an anonymous and a logged-in
741 791 // request — and any in-scope logged-out navigation then
742 - // installed a "new" worker and tripped the shell's
743 - // `controllerchange` reload. The bytes are identical for
744 - // everyone now; the shell posts these to the worker at
745 - // boot.
792 + // installed a "new" worker, which at the time reloaded
793 + // the shell. The bytes are identical for everyone now;
794 + // the shell posts these to the worker at boot.
746 795 //
747 796 // Computed server-side, not read from the settings
748 797 // snapshot client-side, because
749 798 // `openstation_pwa_admin_asset_cache_enabled()` applies
@@ -752,8 +801,14 @@
752 801 'swConfig' => array(
753 802 'adminAssetCache' => (bool) openstation_pwa_admin_asset_cache_enabled(),
754 803 'windowPrewarm' => ! empty( openstation_get_os_settings( get_current_user_id() )['windowPrewarmEnabled'] ),
755 804 ),
805 + // The build this shell document belongs to. When a new
806 + // worker takes over mid-session the shell asks it for
807 + // the stamp it was served with and compares; only a
808 + // difference — the shell's own files changed on the
809 + // server — offers the user a reload. Never automatic.
810 + 'shellBuild' => openstation_shell_build_stamp(),
756 811 'stateUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/pwa-state' ) ),
757 812 'state' => openstation_pwa_get_user_state( get_current_user_id() ),
758 813 // Mirrors the manifest's `name` field — used by the
759 814 // install pill so the button reads "Install <site>"
@@ -790,19 +845,19 @@
790 845 : array(),
791 846 'deferredStyles' => openstation_build_deferred_styles(
792 847 array_merge(
793 848 array(
794 - 'os-settings',
795 849 'desktop-mode-ai-assistant',
796 850 'desktop-mode-bug-report',
797 - // WP Explorer's sheet. It rides that window as a
798 - // companion style, but the desktop FOLDER window
799 - // paints its preview pane with the same
800 - // `os-my-wordpress__*` classes and — being a
801 - // native window opened straight from JS — carries
802 - // no companion styles of its own. Without this,
803 - // the pane rendered unstyled until WP Explorer
804 - // had been opened once in the session.
851 + // The explorer's shared sheet. It rides the WP
852 + // Explorer APP as a companion style, but the
853 + // desktop FOLDER window paints its preview pane
854 + // with the same `os-my-wordpress__*` classes and
855 + // — being a native window opened straight from
856 + // JS — carries no companion styles of its own.
857 + // Without this, the pane rendered unstyled until
858 + // the explorer had been opened once in the
859 + // session.
805 860 'desktop-mode-my-wordpress',
806 861 ),
807 862 // The Games sheets. They also ride the hub window as
808 863 // companion styles, but a game is reachable without
@@ -955,8 +1010,21 @@
955 1010 'rel' => 'prefetch',
956 1011 ),
957 1012 );
958 1013
1014 + // The phone layer is needed at boot on a phone and never on a
1015 + // desktop; the server cannot see the viewport, so the user agent
1016 + // decides whether the hint is worth its bytes. A wrong guess costs
1017 + // one low-priority fetch, never a wrong layout — the stamp and the
1018 + // bundle loader read the real viewport.
1019 + if ( openstation_mode_hint_is_mobile( get_current_user_id() ) ) {
1020 + $hints[] = array(
1021 + 'href' => $build_url( 'assets/js/mobile' . $suffix . '.js' ),
1022 + 'as' => 'script',
1023 + 'rel' => 'prefetch',
1024 + );
1025 + }
1026 +
959 1027 /**
960 1028 * Filters the list of resource preload hints emitted in `<head>`.
961 1029 *
962 1030 * Each entry is a `{ 'href' => string, 'as' => string,
@@ -1073,9 +1141,8 @@
1073 1141 'os-openstation-layout',
1074 1142 'desktop-mode-ai-assistant',
1075 1143 'desktop-mode-bug-report',
1076 1144 'os-window-overview',
1077 - 'os-settings',
1078 1145 )
1079 1146 );
1080 1147
1081 1148 if ( ! in_array( $handle, (array) $deferred, true ) ) {