`, and * `wp.os.games.launch()` is documented for plugin authors. Each of * those reaches `launchGame()` in the shell bundle without the hub * window ever existing, and the game then paints unstyled. * * @param string[]|null $set Handles to store, when registering. * @return string[] The stored handles. */ function openstation_games_style_handles( $set = null ) { static $handles = array(); if ( null !== $set ) { $handles = array_values( array_unique( array_filter( array_map( 'strval', (array) $set ) ) ) ); } return $handles; } /** * The shared gamepad SVG used by the window icon and the desktop * icon. * * Drawn in `currentColor`, which is what makes it a silhouette: * `isSilhouetteSvg()` in `src/icon.ts` looks for exactly that token * and, finding it, paints the art as a CSS mask instead of a * background-image, so it takes whatever colour the surface is * already using for text. * * This icon used to be the only fixed-colour one in the shell, in * four hues (`#6c5ce7`, white, `#ffd166`, `#06d6a0`) that belong to * no palette the plugin ships. Two things followed from that. It * could not invert on a light title bar or a light desktop theme, * because a background-image has no colour to inherit. And under a * desktop theme's icon tint it collapsed outright: that path runs * `applyIconMask()`, which keeps only the artwork's alpha, and every * pixel of the old gamepad was opaque, so the d-pad and the buttons * vanished into a featureless blob. * * Redrawn as an outlined body with the controls knocked into it, the * detail is carried by negative space rather than by colour, so it * survives both. Held to five elements because it renders as small as * 20px in the dock: body, two d-pad bars, two buttons. * * @return string Raw `` markup. */ function openstation_games_icon_svg() { return '' // The body: outlined, so what sits inside it reads as detail // rather than as more fill. . '' // D-pad, two crossed bars sharing a centre. . '' . '' // Face buttons, offset on the diagonal the way a real pad has // them. Two, not four: four would not separate at 20px. . '' . '' . ''; } /** * Whether the current user can use desktop games. * * @return bool */ function openstation_games_user_can_use() { $can = is_user_logged_in() && current_user_can( 'read' ); /** * Filter whether the current user can see the Games window, * icon, and play games. * * @param bool $can Default: logged-in + `read` capability. */ return (bool) apply_filters( 'openstation_games_user_can_use', $can ); } /** * Echoes the Games window's template body. * * The `data-os-games-*` hooks are the contract the JS * render callback relies on — keep them intact (or rename via the * filter) when customizing the layout. */ function openstation_games_render_template() { ob_start(); ?>
__( 'Games', 'desktop-mode' ), 'icon' => $icon_uri, 'template' => 'openstation_games_render_template', 'script' => 'desktop-mode-games', // Companion styles load with the games bundle on first open. // Every game window (`os-game-`) is opened by // `launchGame()`, which lives in that bundle — so a sheet // riding it here is guaranteed in the tab before any game // paints. Built-in games append their own via the // `openstation_games_window_args` filter below. 'styles' => array( 'desktop-mode-games' ), 'width' => 900, 'height' => 600, 'min_width' => 560, 'min_height' => 400, // No dock tile from the window registration: the desktop icon // below is this app's one launcher. Registering both used to // mint two entries for one app, each with its own default, and // the dock painted a tile while Preferences said "On the // desktop". The navigation model collapses them either way — // this keeps Games matching how My WordPress and Corkboard // already register. 'placement' => 'none', ); /** * Filter the args used to register the Games native window. * * @param array $window_args Args passed to `openstation_register_window()`. */ $window_args = (array) apply_filters( 'openstation_games_window_args', $window_args ); // Remember the finished style list — base sheet plus whatever the // built-in games and any plugin appended through the filter above. // A game window does NOT always arrive through the hub: the // challenge toast's "Accept & Play" is designed to work with the // hub closed, solo mode boots straight into // `?openstation_solo=os-game-`, and `wp.os.games.launch()` is // documented for plugins. In each of those, `launchGame()` runs // from the SHELL bundle's copy, the hub window never opens, and its // companion sheets never load — the HUD paints as raw text. The // shell needs the list to inject them on demand. openstation_games_style_handles( $window_args['styles'] ); $registered = openstation_register_window( 'desktop-mode-games', $window_args ); if ( is_wp_error( $registered ) ) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log( '[openstation] Games window registration failed: ' . $registered->get_error_message() ); return; } $icon_args = array( 'title' => __( 'Games', 'desktop-mode' ), 'icon_svg' => openstation_games_icon_svg(), 'window' => 'desktop-mode-games', 'position' => 85, ); /** * Filter the args used to register the Games desktop icon. * * @param array $icon_args Args passed to `openstation_register_icon()`. */ $icon_args = (array) apply_filters( 'openstation_games_icon_args', $icon_args ); openstation_register_icon( 'desktop-mode-games', $icon_args ); } add_action( 'init', 'openstation_games_register_window', 20 ); /** * Localize REST endpoints for the JS bundle. * * The bundle reads its config off `window.openStationGamesConfig` * and never hardcodes URLs. */ function openstation_games_localize_config() { if ( ! openstation_games_user_can_use() ) { return; } wp_localize_script( 'desktop-mode-games', 'openStationGamesConfig', array( 'restNonce' => wp_create_nonce( 'wp_rest' ), 'gamesUrlBase' => esc_url_raw( rest_url( 'desktop-mode/v1/games' ) ), 'challengesUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/games/challenges' ) ), 'usersSearchUrl' => esc_url_raw( rest_url( 'desktop-mode/v1/games/users/search' ) ), ) ); } // Priority 5 for the same reason as the recycle bin: the lazy-load payload is // built at priority 10, and localize data attached after that never reaches a // bundle that loads on window open. add_action( 'admin_enqueue_scripts', 'openstation_games_localize_config', 5 );