| @@ -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 ) ) { |