[], "js" => [], "icons" => [], ]; /** * Auto-enqueue enabled * * @var bool */ private static $autoEnqueue = true; /** * Assets enqueued flag * * @var bool */ private static $assetsEnqueued = false; /** * Vite dev server URL * * @var string */ private static $viteDevServer = "http://localhost:5173"; /** * Vite manifest data * * @var array|null */ private static $viteManifest = null; /** * Dev mode cache * * @var bool|null */ private static $isDevMode = null; /** * Register the asset service */ public static function register() { // Hook into WordPress asset enqueuing add_action("wp_enqueue_scripts", [self::class, "enqueue_assets"], 20); add_action( "admin_enqueue_scripts", [self::class, "enqueue_admin_assets"], 20, ); add_action("wp_enqueue_scripts", [self::class, "enqueue_vite_assets"], 5); add_action("wp_head", [self::class, "output_vite_dev"], 5); add_filter("wp_preload_resources", [self::class, "preload_vite_fonts"]); add_action("enqueue_block_editor_assets", [ self::class, "enqueue_block_editor_assets", ]); // Load Vite manifest self::load_vite_manifest(); } /** * Register CSS file * * @param string $handle Handle name * @param string|null $src Source path * @param array $deps Dependencies * @param string|bool|null $ver Version * @param string $media Media type */ public static function css( $handle, $src = null, $deps = [], $ver = null, $media = "all", ) { // Auto-detect source if not provided if ($src === null) { $src = self::auto_detect_css_path($handle); } // Convert relative path to full URL if ($src && !self::is_url($src)) { $src = self::get_asset_url($src); } self::$assets["css"][$handle] = [ "src" => $src, "deps" => $deps, "ver" => $ver ?: HITHTO_VERSION, "media" => $media, ]; } /** * Register JavaScript file * * @param string $handle Handle name * @param string|null $src Source path * @param array $deps Dependencies * @param string|bool|null $ver Version * @param bool $in_footer Load in footer */ public static function js( $handle, $src = null, $deps = [], $ver = null, $in_footer = true, ) { // Auto-detect source if not provided if ($src === null) { $src = self::auto_detect_js_path($handle); } // Convert relative path to full URL if ($src && !self::is_url($src)) { $src = self::get_asset_url($src); } self::$assets["js"][$handle] = [ "src" => $src, "deps" => $deps, "ver" => $ver ?: HITHTO_VERSION, "in_footer" => $in_footer, ]; } /** * Register icon set * * @param string $name Icon set name * @param string $url Icon set URL */ public static function register_icon_set(string $name, string $url) { self::$assets["icons"][$name] = $url; } /** * Get asset URL * * @param string $path Asset path * @return string Asset URL */ public static function url($path) { if (self::is_url($path)) { return $path; } // Remove leading slash if present $path = ltrim($path, "/"); return HITHTO_THEME_URL . "/public/" . $path; } /** * Enqueue all registered assets */ public static function enqueue_assets(): void { if (!self::$autoEnqueue || self::$assetsEnqueued) { return; } // Enqueue CSS files foreach (self::$assets["css"] as $handle => $asset) { if ($asset["src"]) { wp_enqueue_style( $handle, $asset["src"], $asset["deps"], $asset["ver"], $asset["media"], ); } } // Enqueue JavaScript files foreach (self::$assets["js"] as $handle => $asset) { if ($asset["src"]) { wp_enqueue_script( $handle, $asset["src"], $asset["deps"], $asset["ver"], $asset["in_footer"], ); } } self::$assetsEnqueued = true; } /** * Enqueue admin assets * * @param string $hook Current admin page hook */ public static function enqueue_admin_assets($hook = ''): void { // Always enqueue admin-specific assets wp_enqueue_style( "toolkit-admin-css", HITHTO_URL . "/admin/assets/css/toolkit-admin.css", [], HITHTO_VERSION, ); wp_enqueue_style( "toolkit-icomoon-style", HITHTO_URL . "/admin/assets/css/toolkit-icomoon.css", [], HITHTO_VERSION, ); /** * Theme assets registered with AssetService::css()/js() are front-end only. * A theme can opt in to load them on some admin pages: * * add_filter( 'hithto_enqueue_theme_assets_in_admin', fn( $load, $hook ) => 'post.php' === $hook, 10, 2 ); * * @param bool $load Whether to load the theme assets on this admin page. Default false. * @param string $hook Current admin page hook. */ if (apply_filters('hithto_enqueue_theme_assets_in_admin', false, $hook)) { self::enqueue_assets(); } } /** * Enqueue block editor assets */ public static function enqueue_block_editor_assets() { if (file_exists(HITHTO_THEME_PATH . "/public/css/blocks.css")) { wp_enqueue_style( "hithto-block-styles", HITHTO_THEME_URL . "/public/css/blocks.css", ["wp-edit-blocks"], filemtime(HITHTO_THEME_PATH . "/public/css/blocks.css"), ); } } /** * Enqueue Vite production assets (dev server assets are printed by output_vite_dev) */ public static function enqueue_vite_assets() { if (self::is_dev_mode() || !isset(self::$viteManifest["src/javascript/app.js"])) { return; } $entry = self::$viteManifest["src/javascript/app.js"]; $baseUrl = HITHTO_THEME_URL . "/public/"; // CSS (file names are hashed by Vite, no version needed) foreach ($entry["css"] ?? [] as $index => $cssFile) { wp_enqueue_style("hithto-vite-app-" . $index, $baseUrl . $cssFile, [], null); } // Configuration for JavaScript, printed in before the app module wp_register_script("hithto-toolkit-config", false, [], HITHTO_VERSION, false); wp_enqueue_script("hithto-toolkit-config"); wp_add_inline_script( "hithto-toolkit-config", "window.toolkitConfig = " . wp_json_encode([ "ajaxUrl" => admin_url("admin-ajax.php"), "debug" => defined("WP_DEBUG") && WP_DEBUG, ]) . ";", ); // App entry, loaded as