| @@ -74,8 +74,85 @@ | ||
| 74 | 74 | } |
| 75 | 75 | add_filter( 'openstation_dock_item', 'openstation_inject_appearance_tabs', 10, 2 ); |
| 76 | 76 | |
| 77 | 77 | /** |
| 78 | + * Renders the orientation header for the chromeless Themes workspace. | |
| 79 | + * | |
| 80 | + * Core's page heading is intentionally hidden inside OpenStation because the | |
| 81 | + * window chrome already names the screen. The Themes page still needs a little | |
| 82 | + * more context than a generic list screen, though: a theme changes the public | |
| 83 | + * face of the site, and Core's single-theme state otherwise opens directly on | |
| 84 | + * an unexplained details panel. | |
| 85 | + * | |
| 86 | + * The markup is emitted through `admin_notices`, which places it immediately | |
| 87 | + * before the page's `.wrap`. Core continues to own the theme cards, details | |
| 88 | + * overlay, actions, AJAX updates, and keyboard behavior. | |
| 89 | + * | |
| 90 | + * The screen is identified by `get_current_screen()`, not `$pagenow`. Every | |
| 91 | + * page registered with `add_theme_page()` also reports `themes.php` as its | |
| 92 | + * `$pagenow`, but its body class comes from the hook suffix | |
| 93 | + * (`appearance_page_<slug>`) and so matches none of the workspace CSS, which | |
| 94 | + * is scoped to `.themes-php`. Gating on `$pagenow` would emit this header | |
| 95 | + * unstyled on top of unrelated Appearance screens. The screen ID also keeps | |
| 96 | + * network admin (`themes-network`, a different list table entirely) out. | |
| 97 | + */ | |
| 98 | +function openstation_render_themes_workspace_intro() { | |
| 99 | + if ( ! openstation_is_chromeless_request() ) { | |
| 100 | + return; | |
| 101 | + } | |
| 102 | + | |
| 103 | + $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; | |
| 104 | + if ( ! $screen || 'themes' !== $screen->id ) { | |
| 105 | + return; | |
| 106 | + } | |
| 107 | + | |
| 108 | + /* | |
| 109 | + * Mirrors how `wp_prepare_themes_for_js()` builds its own list, without | |
| 110 | + * paying for the preparation itself. Core already called that function | |
| 111 | + * on this request before `admin-header.php` ran, and calling it again | |
| 112 | + * would re-resolve every screenshot, tag and author URL and fire | |
| 113 | + * `pre_prepare_themes_for_js` / `wp_prepare_themes_for_js` a second | |
| 114 | + * time — so any third-party callback hanging off them would run twice | |
| 115 | + * per page load. `wp_get_themes()` is cached, so this is a lookup. | |
| 116 | + */ | |
| 117 | + if ( current_user_can( 'switch_themes' ) ) { | |
| 118 | + $themes = wp_get_themes( array( 'allowed' => true ) ); | |
| 119 | + $stylesheet = get_stylesheet(); | |
| 120 | + if ( ! isset( $themes[ $stylesheet ] ) ) { | |
| 121 | + $themes[ $stylesheet ] = wp_get_theme(); | |
| 122 | + } | |
| 123 | + $theme_count = count( $themes ); | |
| 124 | + } else { | |
| 125 | + $theme_count = 1; | |
| 126 | + } | |
| 127 | + ?> | |
| 128 | + <section class="openstation-themes-intro" aria-labelledby="openstation-themes-intro-title"> | |
| 129 | + <div class="openstation-themes-intro__copy"> | |
| 130 | + <p class="openstation-themes-intro__eyebrow"><?php esc_html_e( 'Site appearance', 'desktop-mode' ); ?></p> | |
| 131 | + <h1 id="openstation-themes-intro-title"><?php esc_html_e( 'Choose how your site greets the world.', 'desktop-mode' ); ?></h1> | |
| 132 | + <p><?php esc_html_e( 'Themes shape your site’s look. Switching keeps your posts and pages in place.', 'desktop-mode' ); ?></p> | |
| 133 | + </div> | |
| 134 | + <?php | |
| 135 | + /* | |
| 136 | + * Left readable rather than labelled: `aria-label` is ignored on a | |
| 137 | + * `<p>` (the paragraph role prohibits an author-supplied name), so | |
| 138 | + * pairing it with `aria-hidden` children hid the count from | |
| 139 | + * assistive tech entirely — and Core's own `.title-count` inside | |
| 140 | + * the page `<h1>` is display:none in chromeless mode, making this | |
| 141 | + * the only count on the screen. Read in DOM order the two spans | |
| 142 | + * announce as "3 Themes installed" on their own. | |
| 143 | + */ | |
| 144 | + ?> | |
| 145 | + <p class="openstation-themes-intro__count"> | |
| 146 | + <strong><?php echo esc_html( number_format_i18n( $theme_count ) ); ?></strong> | |
| 147 | + <span><?php echo esc_html( _n( 'Theme installed', 'Themes installed', $theme_count, 'desktop-mode' ) ); ?></span> | |
| 148 | + </p> | |
| 149 | + </section> | |
| 150 | + <?php | |
| 151 | +} | |
| 152 | +add_action( 'admin_notices', 'openstation_render_themes_workspace_intro', 0 ); | |
| 153 | + | |
| 154 | +/** | |
| 78 | 155 | * Fixes the visible "active tab" state inside chromeless |
| 79 | 156 | * theme-install.php iframes. |
| 80 | 157 | * |
| 81 | 158 | * Background: WP's installer JS uses a Backbone router whose pattern |