` tag (`openstation_render_native_window_templates()`, // admin_footer @ 20 — before footer scripts, so the tags are in // the DOM before the shell boots and `ensureTemplate()` adopts // them by id). The payload's `templateHtml` copy exists for the // MID-SESSION path — a bridge or probe payload delivering a // window whose plugin activated after the page rendered — so on // the boot config it is ~27 KB of the same markup twice. Strip it // here, and only here: the bridge and probe payloads keep theirs. foreach ( $native_windows as &$native_window_row ) { if ( is_array( $native_window_row ) ) { $native_window_row['templateHtml'] = ''; } } unset( $native_window_row ); $native_window_script_data = isset( $menu_payload['nativeWindowScriptData'] ) ? $menu_payload['nativeWindowScriptData'] : array(); $server_widgets = isset( $menu_payload['serverWidgets'] ) ? $menu_payload['serverWidgets'] : array(); $server_wallpapers = isset( $menu_payload['serverWallpapers'] ) ? $menu_payload['serverWallpapers'] : array(); $server_command_scripts = isset( $menu_payload['serverCommandScripts'] ) ? $menu_payload['serverCommandScripts'] : array(); $server_commands = isset( $menu_payload['serverCommands'] ) ? $menu_payload['serverCommands'] : array(); $server_settings_tab_scripts = isset( $menu_payload['serverSettingsTabScripts'] ) ? $menu_payload['serverSettingsTabScripts'] : array(); $server_settings_tabs = isset( $menu_payload['serverSettingsTabs'] ) ? $menu_payload['serverSettingsTabs'] : array(); $server_dock_rail_renderer_scripts = isset( $menu_payload['serverDockRailRendererScripts'] ) ? $menu_payload['serverDockRailRendererScripts'] : array(); $server_titlebar_button_scripts = isset( $menu_payload['serverTitleBarButtonScripts'] ) ? $menu_payload['serverTitleBarButtonScripts'] : array(); $server_window_action_scripts = isset( $menu_payload['serverWindowActionScripts'] ) ? $menu_payload['serverWindowActionScripts'] : array(); $server_window_theme_scripts = isset( $menu_payload['serverWindowThemeScripts'] ) ? $menu_payload['serverWindowThemeScripts'] : array(); $server_window_themes = isset( $menu_payload['serverWindowThemes'] ) ? $menu_payload['serverWindowThemes'] : array(); $server_window_control_scripts = isset( $menu_payload['serverWindowControlScripts'] ) ? $menu_payload['serverWindowControlScripts'] : array(); $server_window_controls = isset( $menu_payload['serverWindowControls'] ) ? $menu_payload['serverWindowControls'] : array(); $server_window_slot_scripts = isset( $menu_payload['serverWindowSlotScripts'] ) ? $menu_payload['serverWindowSlotScripts'] : array(); $server_window_slots = isset( $menu_payload['serverWindowSlots'] ) ? $menu_payload['serverWindowSlots'] : array(); $server_window_chrome_scripts = isset( $menu_payload['serverWindowChromeScripts'] ) ? $menu_payload['serverWindowChromeScripts'] : array(); $server_window_chromes = isset( $menu_payload['serverWindowChromes'] ) ? $menu_payload['serverWindowChromes'] : array(); $server_window_notices = isset( $menu_payload['serverWindowNotices'] ) ? $menu_payload['serverWindowNotices'] : array(); $server_games = isset( $menu_payload['serverGames'] ) ? $menu_payload['serverGames'] : array(); // Boot-time copy of the desktop-theme library. Without it the // shell's registry seeds EMPTY, and the consequences are subtle // rather than obvious: PHP has already applied the user's theme // server-side (stylesheet + shell attribute), but the client // can't resolve the slug to an entry, so it believes nothing is // active. Themed ICONS never paint, and switching back to the // system default no-ops the first time — `applyDesktopTheme()` // dedupes on an `activeId` that was never set. $server_desktop_themes = isset( $menu_payload['serverDesktopThemes'] ) ? $menu_payload['serverDesktopThemes'] : array(); // Slim the theme library for BOOT: `cssText` and `tokens` are // each ~20 KB per theme, and neither is read at boot — the ACTIVE // theme's stylesheet is server-delivered (see // `openstation_enqueue_desktop_theme_style()`, whose stamp // `bootAlreadyApplied()` detects), and an inactive theme's CSS // only matters at the moment the user picks it in the Preferences // picker — which fetches the full entries from // `GET desktop-mode/v1/desktop-themes` (`ensureFullDesktopThemes()` // client-side). `cssDeferred` marks the gap so the shell can tell // a slimmed entry from a theme that genuinely ships no CSS. // Bridge and probe payloads keep full entries. foreach ( $server_desktop_themes as &$desktop_theme_row ) { if ( is_array( $desktop_theme_row ) ) { $desktop_theme_row['cssText'] = ''; $desktop_theme_row['tokens'] = new stdClass(); $desktop_theme_row['cssDeferred'] = true; } } unset( $desktop_theme_row ); $desktop_icons = isset( $menu_payload['desktopIcons'] ) ? $menu_payload['desktopIcons'] : array(); // Files-on-the-Desktop payload (Phase 0+1). Plugin-registered // file types and openers ship as metadata only; the JS side // holds the executable handlers and resolves on double-click. $server_file_types = function_exists( 'openstation_build_file_types_payload' ) ? openstation_build_file_types_payload() : array(); $server_file_openers = function_exists( 'openstation_build_file_openers_payload' ) ? openstation_build_file_openers_payload() : array(); $user_file_associations = function_exists( 'openstation_get_user_file_associations' ) ? openstation_get_user_file_associations( get_current_user_id() ) : array(); $server_wallpaper_menu_items = function_exists( 'openstation_build_wallpaper_menu_items' ) ? openstation_build_wallpaper_menu_items() : array(); /* * OS-file drop config — what the browser drop manager will * accept when the user drags a file from their native desktop * onto any surface inside OpenStation (wallpaper, a folder, * a window, or a chromeless iframe). The allowed-mimes list is * the user-scoped `get_allowed_mime_types()` (already capability * gated by WordPress); the size cap is `wp_max_upload_size()`. * * Both are filterable so plugins can narrow or widen the set — * e.g. a media-only plugin can restrict drops to images, or a * docs plugin can opt PDFs in for a specific role. */ $drop_allowed_mimes_map = current_user_can( 'upload_files' ) ? get_allowed_mime_types( get_current_user_id() ) : array(); /** * Filter the allowed-mime map used by the OS-file drop manager. * * @param array $mimes_map `ext => mime-type` map (same shape `get_allowed_mime_types()` returns). * @param int $user_id The current user id. */ $drop_allowed_mimes_map = apply_filters( 'openstation_drop_allowed_mimes', $drop_allowed_mimes_map, get_current_user_id() ); $drop_allowed_mimes_map = is_array( $drop_allowed_mimes_map ) ? $drop_allowed_mimes_map : array(); $drop_allowed_mimes = array_values( array_unique( array_values( $drop_allowed_mimes_map ) ) ); $drop_max_size = (int) wp_max_upload_size(); /** * Filter the per-file size cap (in bytes) used by the OS-file * drop manager. Returning `0` disables the client-side cap — * the server still enforces its own. * * @param int $max_size Default `wp_max_upload_size()`. * @param int $user_id The current user id. */ $drop_max_size = (int) apply_filters( 'openstation_drop_max_size', $drop_max_size, get_current_user_id() ); /** * Filter the master OS-file drop enable gate. Lets plugins * disable the drop manager by role / capability beyond the * default `upload_files` check (e.g. only for admins, or * only on specific multisite blogs). * * @param bool $enabled Default — `current_user_can( 'upload_files' )`. * @param int $user_id The current user id. */ $drop_enabled = (bool) apply_filters( 'openstation_drop_enabled', current_user_can( 'upload_files' ), get_current_user_id() ); $drop_config = array( 'enabled' => $drop_enabled, 'allowedMimes' => $drop_allowed_mimes, 'extToMime' => $drop_allowed_mimes_map, 'maxSize' => $drop_max_size, ); // Lazy-bundle URL builder. Each lazy-loaded bundle (AI Assistant, // OS Settings panel, shell-overlays, window-system) // is `