1, 'flake' => 4, 'yuanbao' => 1, 'coin' => 1, 'santa_hat' => 1, 'candy_cane' => 1, 'christmas_sock' => 1, 'christmas_tree' => 1, 'reindeer' => 1, 'christmas_berry' => 1, ); $saved_weights = get_option('yoone_snow_shape_weights', $default_weights); if (!is_array($saved_weights)) { $saved_weights = array(); } // 合并用户保存的权重与默认值 并规范为非负整数 $shape_weights = array(); foreach ($default_weights as $k => $v) { $val = isset($saved_weights[$k]) ? intval($saved_weights[$k]) : intval($v); if ($val < 0) { $val = 0; } $shape_weights[$k] = $val; } // 读取尺寸组合设置 包含最小与最大半径 如未设置则回退到旧选项与默认值 $size_group = get_option('yoone_snow_size', array('min' => 1.0, 'max' => 3.0)); $radius_min_val = isset($size_group['min']) ? floatval($size_group['min']) : floatval(get_option('yoone_snow_radius_min', 1.0)); $radius_max_val = isset($size_group['max']) ? floatval($size_group['max']) : floatval(get_option('yoone_snow_radius_max', 3.0)); if ($radius_min_val < 0) { $radius_min_val = 0.0; } if ($radius_max_val < $radius_min_val) { $radius_max_val = $radius_min_val; } wp_localize_script($script_handle, 'YooneSnowSettings', array( 'selectedShapes' => $mixed_items_sanitized, 'mediaItems' => $media_urls, 'displayDurationSeconds' => intval(get_option('yoone_snow_home_duration', 0)), 'maxCount' => intval(get_option('yoone_snow_max_count', 0)), 'radiusMin' => $radius_min_val, 'radiusMax' => $radius_max_val, 'driftMin' => floatval(get_option('yoone_snow_drift_min', 0.4)), 'driftMax' => floatval(get_option('yoone_snow_drift_max', 1.0)), 'swingMin' => floatval(get_option('yoone_snow_swing_min', 0.2)), 'swingMax' => floatval(get_option('yoone_snow_swing_max', 1.0)), 'assetsMap' => array( 'santa_hat' => plugins_url('assets/圣诞雪帽.svg', __FILE__), 'candy_cane' => plugins_url('assets/圣诞拐杖.svg', __FILE__), 'christmas_sock' => plugins_url('assets/圣诞袜子.svg', __FILE__), 'christmas_tree' => plugins_url('assets/圣诞树.svg', __FILE__), 'reindeer' => plugins_url('assets/圣诞麋鹿.svg', __FILE__), 'christmas_berry' => plugins_url('assets/圣诞果.svg', __FILE__), ), 'shapeWeights' => $shape_weights, 'mediaWeights' => $media_weights_by_url, 'emojiItems' => (function(){ // 读取已保存的 emoji 列表 返回字符串数组 $items = get_option('yoone_snow_emoji_items', array()); if (!is_array($items)) { $items = array(); } $clean = array(); foreach ($items as $it) { $s = trim((string)$it); if ($s !== '') { $clean[] = $s; } } return $clean; })(), 'emojiWeights' => (function(){ // 读取已保存的 emoji 权重 映射为字符到权重 $map = get_option('yoone_snow_emoji_weights', array()); if (!is_array($map)) { $map = array(); } $clean = array(); foreach ($map as $k => $v) { $key = trim((string)$k); $num = intval($v); if ($key !== '') { if ($num < 0) { $num = 0; } $clean[$key] = $num; } } return $clean; })(), 'textItems' => (function(){ $items = get_option('yoone_snow_text_items', array()); if (!is_array($items)) { $items = array(); } $clean = array(); foreach ($items as $it) { $s = trim((string)$it); if ($s !== '') { $clean[] = $s; } } return $clean; })(), 'textWeights' => (function(){ $map = get_option('yoone_snow_text_weights', array()); if (!is_array($map)) { $map = array(); } $clean = array(); foreach ($map as $k => $v) { $key = trim((string)$k); $num = intval($v); if ($key !== '') { if ($num < 0) { $num = 0; } $clean[$key] = $num; } } return $clean; })(), )); } // 在后台设置页面加载媒体库脚本和交互脚本 用于选择 SVG 或图片 function yoone_snow_admin_enqueue($hook) { // 条件判断 仅在插件设置页面加载脚本 保持性能 if ($hook !== 'settings_page_yoone_snow') { return; } // 加载媒体库脚本 以便使用 wp.media 选择器 wp_enqueue_media(); // 在后台也注册并加载 shapes 渲染脚本 以供预览复用 // 形状索引与工具脚本 提供全局渲染器与图像加载能力 $shape_index_handle = 'yoone-snow-shapes-index'; $ver_shapes_index = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/index.js'); wp_register_script($shape_index_handle, plugins_url('js/shapes/index.js', __FILE__), array(), $ver_shapes_index ? (string)$ver_shapes_index : '1.1.0', true); $shape_utils_handle = 'yoone-snow-shapes-utils'; $ver_shapes_utils = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/utils.js'); wp_register_script($shape_utils_handle, plugins_url('js/shapes/utils.js', __FILE__), array($shape_index_handle), $ver_shapes_utils ? (string)$ver_shapes_utils : '1.1.0', true); // 各具体形状脚本 注册为依赖以保证顺序 $shape_dot_handle = 'yoone-snow-shapes-dot'; $ver_shapes_dot = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/dot.js'); wp_register_script($shape_dot_handle, plugins_url('js/shapes/dot.js', __FILE__), array($shape_index_handle), $ver_shapes_dot ? (string)$ver_shapes_dot : '1.1.0', true); $shape_flake_handle = 'yoone-snow-shapes-flake'; $ver_shapes_flake = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/flake.js'); wp_register_script($shape_flake_handle, plugins_url('js/shapes/flake.js', __FILE__), array($shape_index_handle), $ver_shapes_flake ? (string)$ver_shapes_flake : '1.1.0', true); $shape_yuanbao_handle = 'yoone-snow-shapes-yuanbao'; $ver_shapes_yuanbao = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/yuanbao.js'); wp_register_script($shape_yuanbao_handle, plugins_url('js/shapes/yuanbao.js', __FILE__), array($shape_index_handle), $ver_shapes_yuanbao ? (string)$ver_shapes_yuanbao : '1.1.0', true); $shape_coin_handle = 'yoone-snow-shapes-coin'; $ver_shapes_coin = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/coin.js'); wp_register_script($shape_coin_handle, plugins_url('js/shapes/coin.js', __FILE__), array($shape_index_handle), $ver_shapes_coin ? (string)$ver_shapes_coin : '1.1.0', true); $shape_santa_handle = 'yoone-snow-shapes-santa-hat'; $ver_shapes_santa = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/santa_hat.js'); wp_register_script($shape_santa_handle, plugins_url('js/shapes/santa_hat.js', __FILE__), array($shape_utils_handle), $ver_shapes_santa ? (string)$ver_shapes_santa : '1.1.0', true); $shape_cane_handle = 'yoone-snow-shapes-candy-cane'; $ver_shapes_cane = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/candy_cane.js'); wp_register_script($shape_cane_handle, plugins_url('js/shapes/candy_cane.js', __FILE__), array($shape_utils_handle), $ver_shapes_cane ? (string)$ver_shapes_cane : '1.1.0', true); $shape_sock_handle = 'yoone-snow-shapes-christmas-sock'; $ver_shapes_sock = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/christmas_sock.js'); wp_register_script($shape_sock_handle, plugins_url('js/shapes/christmas_sock.js', __FILE__), array($shape_utils_handle), $ver_shapes_sock ? (string)$ver_shapes_sock : '1.1.0', true); $shape_tree_handle = 'yoone-snow-shapes-christmas-tree'; $ver_shapes_tree = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/christmas_tree.js'); wp_register_script($shape_tree_handle, plugins_url('js/shapes/christmas_tree.js', __FILE__), array($shape_utils_handle), $ver_shapes_tree ? (string)$ver_shapes_tree : '1.1.0', true); $shape_reindeer_handle = 'yoone-snow-shapes-reindeer'; $ver_shapes_reindeer = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/reindeer.js'); wp_register_script($shape_reindeer_handle, plugins_url('js/shapes/reindeer.js', __FILE__), array($shape_utils_handle), $ver_shapes_reindeer ? (string)$ver_shapes_reindeer : '1.1.0', true); $shape_berry_handle = 'yoone-snow-shapes-christmas-berry'; $ver_shapes_berry = @filemtime(plugin_dir_path(__FILE__) . 'js/shapes/christmas_berry.js'); wp_register_script($shape_berry_handle, plugins_url('js/shapes/christmas_berry.js', __FILE__), array($shape_utils_handle), $ver_shapes_berry ? (string)$ver_shapes_berry : '1.1.0', true); // 在后台本页入队 shapes 相关脚本 以便预览复用前端实现 wp_enqueue_script($shape_index_handle); wp_enqueue_script($shape_utils_handle); wp_enqueue_script($shape_dot_handle); wp_enqueue_script($shape_flake_handle); wp_enqueue_script($shape_yuanbao_handle); wp_enqueue_script($shape_coin_handle); wp_enqueue_script($shape_santa_handle); wp_enqueue_script($shape_cane_handle); wp_enqueue_script($shape_sock_handle); wp_enqueue_script($shape_tree_handle); wp_enqueue_script($shape_reindeer_handle); wp_enqueue_script($shape_berry_handle); // 为后台也提供 YooneSnowSettings 的 assetsMap 以满足形状脚本的资源需求 wp_localize_script($shape_utils_handle, 'YooneSnowSettings', array( 'assetsMap' => array( 'santa_hat' => plugins_url('assets/圣诞雪帽.svg', __FILE__), 'candy_cane' => plugins_url('assets/圣诞拐杖.svg', __FILE__), 'christmas_sock' => plugins_url('assets/圣诞袜子.svg', __FILE__), 'christmas_tree' => plugins_url('assets/圣诞树.svg', __FILE__), 'reindeer' => plugins_url('assets/圣诞麋鹿.svg', __FILE__), 'christmas_berry' => plugins_url('assets/圣诞果.svg', __FILE__), ) )); // 注册并加载后台交互脚本 该脚本复用 shapes 渲染器进行预览 $admin_script_handle = 'yoone-snow-admin-media'; // 设置 admin 脚本依赖所有形状脚本 以保证渲染器已就绪 wp_register_script( $admin_script_handle, plugins_url('js/admin-media.js', __FILE__), array( $shape_index_handle, $shape_utils_handle, $shape_dot_handle, $shape_flake_handle, $shape_yuanbao_handle, $shape_coin_handle, $shape_santa_handle, $shape_cane_handle, $shape_sock_handle, $shape_tree_handle, $shape_reindeer_handle, $shape_berry_handle ), (@filemtime(plugin_dir_path(__FILE__) . 'js/admin-media.js') ? (string)@filemtime(plugin_dir_path(__FILE__) . 'js/admin-media.js') : '1.1.0'), true ); // 传递资源映射用于后台形状预览 保持与前端一致 wp_localize_script($admin_script_handle, 'YooneSnowAdmin', array( 'assetsMap' => array( 'santa_hat' => plugins_url('assets/圣诞雪帽.svg', __FILE__), 'candy_cane' => plugins_url('assets/圣诞拐杖.svg', __FILE__), 'christmas_sock' => plugins_url('assets/圣诞袜子.svg', __FILE__), 'christmas_tree' => plugins_url('assets/圣诞树.svg', __FILE__), 'reindeer' => plugins_url('assets/圣诞麋鹿.svg', __FILE__), 'christmas_berry' => plugins_url('assets/圣诞果.svg', __FILE__), ), 'shapeLabels' => array( 'dot' => esc_html__('Dot', 'yoone-snow'), 'flake' => esc_html__('Snowflake', 'yoone-snow'), 'yuanbao' => esc_html__('Yuanbao', 'yoone-snow'), 'coin' => esc_html__('Coin', 'yoone-snow'), 'santa_hat' => esc_html__('Santa Hat', 'yoone-snow'), 'candy_cane' => esc_html__('Candy Cane', 'yoone-snow'), 'christmas_sock' => esc_html__('Christmas Sock', 'yoone-snow'), 'christmas_tree' => esc_html__('Christmas Tree', 'yoone-snow'), 'reindeer' => esc_html__('Reindeer', 'yoone-snow'), 'christmas_berry' => esc_html__('Christmas Berry', 'yoone-snow'), ), 'i18n' => array( 'cancel' => esc_html__('Cancel', 'yoone-snow'), 'remove' => esc_html__('Remove', 'yoone-snow'), 'select_images_or_svg' => esc_html__('Select images or SVG', 'yoone-snow'), 'type' => esc_html__('Type', 'yoone-snow'), 'default' => esc_html__('Default', 'yoone-snow'), 'emoji' => esc_html__('Emoji', 'yoone-snow'), 'media' => esc_html__('Media', 'yoone-snow'), 'text' => esc_html__('Text', 'yoone-snow'), 'select_shape' => esc_html__('Select shape', 'yoone-snow'), 'select_emoji' => esc_html__('Select emoji', 'yoone-snow'), 'add_images' => esc_html__('Add Images', 'yoone-snow'), 'type_text' => esc_html__('Type text', 'yoone-snow'), 'type_emoji_or_alias' => esc_html__('Type emoji or alias', 'yoone-snow'), 'add_shapes_all_in_one' => esc_html__('Add shapes by type all in one list', 'yoone-snow'), 'shapes' => esc_html__('Shapes', 'yoone-snow') ) )); wp_enqueue_script($admin_script_handle); } function yoone_snow_render_overlay() { if (!yoone_snow_is_enabled()) { return; } static $yoone_snow_rendered = false; if ($yoone_snow_rendered) { return; } $yoone_snow_rendered = true; echo ''; } add_action('wp_enqueue_scripts', 'yoone_snow_enqueue_assets'); add_action('wp_body_open', 'yoone_snow_render_overlay'); add_action('wp_footer', 'yoone_snow_render_overlay', 100); // 注册设置页面和设置项 用于选择雪花形状 function yoone_snow_register_settings() { // 移除形状下拉选项 仅保留复选集合设置 // 注册 mixed 形状集合设置项 默认包含 dot 和 flake register_setting('yoone_snow_options', 'yoone_snow_mixed_items', array( 'type' => 'array', 'sanitize_callback' => function($value) { // 将输入统一为数组 并过滤到允许的集合 $allowed = array('dot','flake','yuanbao','coin','santa_hat','candy_cane','christmas_sock','christmas_tree','reindeer','christmas_berry'); if (is_string($value)) { $value = array_filter(array_map('trim', explode(',', $value))); } if (!is_array($value)) { $value = array('dot','flake'); } $filtered = array_values(array_unique(array_intersect($value, $allowed))); return !empty($filtered) ? $filtered : array('dot','flake'); }, 'default' => array('dot','flake'), )); // 添加设置分区 标题为 Snow Settings add_settings_section( 'yoone_snow_section', esc_html__('Snow Settings', 'yoone-snow'), function() { echo '

' . esc_html__('Configure snow appearance', 'yoone-snow') . '

'; }, 'yoone_snow' ); // 移除下拉字段 保留复选框作为唯一选择入口 add_settings_field( 'yoone_snow_mixed_items', esc_html__('Shapes', 'yoone-snow'), function() { // 读取默认形状列表 用于初始渲染卡片 $current_list = get_option('yoone_snow_mixed_items', array('dot','flake')); if (is_string($current_list)) { $current_list = array_filter(array_map('trim', explode(',', $current_list))); } if (!is_array($current_list)) { $current_list = array('dot','flake'); } $options = array( 'dot' => esc_html__('Dot', 'yoone-snow'), 'flake' => esc_html__('Snowflake', 'yoone-snow'), 'yuanbao' => esc_html__('Yuanbao', 'yoone-snow'), 'coin' => esc_html__('Coin', 'yoone-snow'), 'santa_hat' => esc_html__('Santa Hat', 'yoone-snow'), 'candy_cane' => esc_html__('Candy Cane', 'yoone-snow'), 'christmas_sock' => esc_html__('Christmas Sock', 'yoone-snow'), 'christmas_tree' => esc_html__('Christmas Tree', 'yoone-snow'), 'reindeer' => esc_html__('Reindeer', 'yoone-snow'), 'christmas_berry' => esc_html__('Christmas Berry', 'yoone-snow'), ); // 形状描述映射 用于在界面提示 使用国际化函数 $shape_descriptions = array( 'dot' => __('Basic dot shape simple and lightweight', 'yoone-snow'), 'flake' => __('Snowflake shape more decorative', 'yoone-snow'), 'yuanbao' => __('Yuanbao shape festive theme', 'yoone-snow'), 'coin' => __('Coin shape festive theme', 'yoone-snow'), 'santa_hat' => __('Santa hat shape seasonal theme', 'yoone-snow'), 'candy_cane' => __('Candy cane shape seasonal theme', 'yoone-snow'), 'christmas_sock' => __('Christmas sock shape seasonal theme', 'yoone-snow'), 'christmas_tree' => __('Christmas tree shape seasonal theme', 'yoone-snow'), 'reindeer' => __('Reindeer shape seasonal theme', 'yoone-snow'), 'christmas_berry' => __('Christmas berry shape seasonal theme', 'yoone-snow'), ); // 读取已选择的媒体与 emoji 列表 用于统一卡片渲染 $current_media = get_option('yoone_snow_media_items', array()); if (!is_array($current_media)) { $current_media = array(); } $current_emojis = get_option('yoone_snow_emoji_items', array()); if (!is_array($current_emojis)) { $current_emojis = array(); } $current_texts = get_option('yoone_snow_text_items', array()); if (!is_array($current_texts)) { $current_texts = array(); } // 读取当前权重值 用于在卡片中预填 $shape_weights_current = get_option('yoone_snow_shape_weights', array()); if (!is_array($shape_weights_current)) { $shape_weights_current = array(); } $media_weights_current = get_option('yoone_snow_media_weights', array()); if (!is_array($media_weights_current)) { $media_weights_current = array(); } $emoji_weights_current = get_option('yoone_snow_emoji_weights', array()); if (!is_array($emoji_weights_current)) { $emoji_weights_current = array(); } $text_weights_current = get_option('yoone_snow_text_weights', array()); if (!is_array($text_weights_current)) { $text_weights_current = array(); } echo '
'; // 渲染默认形状卡片 foreach ($current_list as $key) { if (!isset($options[$key])) { continue; } echo '
'; echo '
'; echo '' . esc_html($options[$key]) . ''; $shapeWeightVal = isset($shape_weights_current[$key]) ? intval($shape_weights_current[$key]) : 1; echo ''; echo ''; echo ''; echo '
'; } // 渲染 emoji 卡片 foreach ($current_emojis as $emoji_char) { $label = trim((string)$emoji_char); if ($label === '') { continue; } echo '
'; echo '
' . esc_html($label) . '
'; $emojiWeightVal = isset($emoji_weights_current[$label]) ? intval($emoji_weights_current[$label]) : 1; echo ''; echo ''; echo ''; echo '
'; } // 渲染媒体卡片 foreach ($current_media as $attachment_id) { $url = wp_get_attachment_thumb_url($attachment_id); if (!$url) { $url = wp_get_attachment_url($attachment_id); } $safe_id = intval($attachment_id); $safe_url = esc_url($url); echo '
'; echo 'media'; $mediaWeightVal = isset($media_weights_current[$safe_id]) ? intval($media_weights_current[$safe_id]) : 1; echo ''; echo ''; echo ''; echo '
'; } // 文本卡片 foreach ($current_texts as $text_item) { $label = trim((string)$text_item); if ($label === '') { continue; } echo '
'; echo '
' . esc_html($label) . '
'; $textWeightVal = isset($text_weights_current[$label]) ? intval($text_weights_current[$label]) : 1; echo ''; echo ''; echo ''; echo '
'; } echo '
'; echo '
'; echo ''; echo '
'; echo '
'; echo ''; echo ''; echo ''; echo '
'; echo ''; echo ''; echo ''; // 权重说明 提示用户权重影响概率且为非负整数 echo '

' . esc_html__('Add shapes by type all in one list', 'yoone-snow') . '

'; echo '
'; echo '
'; echo '

' . esc_html__('Weight controls relative probability Weight is a non negative integer Weight 0 disables a shape Probability equals shape weight divided by the sum of all shape weights Example dot 1 flake 4 flake has about four times the chance of dot', 'yoone-snow') . '

'; }, 'yoone_snow', 'yoone_snow_section' ); // 移除單獨 emoji 字段 渲染與交互已整合到 Shapes 字段 register_setting('yoone_snow_options', 'yoone_snow_shape_weights', array( 'type' => 'array', 'sanitize_callback' => function($value) { $allowed = array('dot','flake','yuanbao','coin','santa_hat','candy_cane','christmas_sock','christmas_tree','reindeer','christmas_berry'); $defaults = array( 'dot' => 1, 'flake' => 4, 'yuanbao' => 1, 'coin' => 1, 'santa_hat' => 1, 'candy_cane' => 1, 'christmas_sock' => 1, 'christmas_tree' => 1, 'reindeer' => 1, 'christmas_berry' => 1, ); if (!is_array($value)) { $value = array(); } $clean = array(); foreach ($allowed as $key) { $num = isset($value[$key]) ? intval($value[$key]) : (isset($defaults[$key]) ? intval($defaults[$key]) : 1); if ($num < 0) { $num = 0; } $clean[$key] = $num; } return $clean; }, 'default' => array( 'dot' => 1, 'flake' => 4, 'yuanbao' => 1, 'coin' => 1, 'santa_hat' => 1, 'candy_cane' => 1, 'christmas_sock' => 1, 'christmas_tree' => 1, 'reindeer' => 1, 'christmas_berry' => 1, ), )); // 形状权重字段将移动到媒体形状字段之后 以满足界面顺序需求 // 注册媒体形状集合 设置项保存为附件 ID 数组 register_setting('yoone_snow_options', 'yoone_snow_media_items', array( 'type' => 'array', 'sanitize_callback' => function($value) { // 将输入统一为整数 ID 数组 并过滤无效值 if (is_string($value)) { $value = array_filter(array_map('trim', explode(',', $value))); } if (!is_array($value)) { $value = array(); } $ids = array(); foreach ($value as $item) { $id = intval($item); if ($id > 0) { $ids[] = $id; } } return array_values(array_unique($ids)); }, 'default' => array(), )); // 移除單獨 Media 字段 渲染與交互已整合到 Shapes 字段 // 注册媒体权重设置项 将附件 ID 映射到权重数值 默认 1 register_setting('yoone_snow_options', 'yoone_snow_media_weights', array( 'type' => 'array', 'sanitize_callback' => function($value) { // 将输入统一为附件 ID 到非负整数权重的映射 if (!is_array($value)) { $value = array(); } $clean = array(); foreach ($value as $id => $num) { $aid = intval($id); $weight = intval($num); if ($aid > 0) { if ($weight < 0) { $weight = 0; } $clean[$aid] = $weight; } } return $clean; }, 'default' => array(), )); // 移除單獨 Weights 字段 權重輸入已置於 Shapes 字段下方 // 注册 emoji 列表设置项 保存为字符串数组 register_setting('yoone_snow_options', 'yoone_snow_emoji_items', array( 'type' => 'array', 'sanitize_callback' => function($value) { // 将输入统一为非空字符串数组 并去重 if (is_string($value)) { $value = array_filter(array_map('trim', explode(',', $value))); } if (!is_array($value)) { $value = array(); } $out = array(); foreach ($value as $item) { $s = trim((string)$item); if ($s !== '') { $out[] = $s; } } return array_values(array_unique($out)); }, 'default' => array(), )); // 注册 emoji 权重设置项 映射字符到非负整数权重 register_setting('yoone_snow_options', 'yoone_snow_emoji_weights', array( 'type' => 'array', 'sanitize_callback' => function($value) { if (!is_array($value)) { $value = array(); } $clean = array(); foreach ($value as $ch => $num) { $key = trim((string)$ch); $weight = intval($num); if ($key !== '') { if ($weight < 0) { $weight = 0; } $clean[$key] = $weight; } } return $clean; }, 'default' => array(), )); register_setting('yoone_snow_options', 'yoone_snow_text_items', array( 'type' => 'array', 'sanitize_callback' => function($value) { if (is_string($value)) { $value = array_filter(array_map('trim', explode(',', $value))); } if (!is_array($value)) { $value = array(); } $out = array(); foreach ($value as $item) { $s = trim((string)$item); if ($s !== '') { $out[] = $s; } } return array_values(array_unique($out)); }, 'default' => array(), )); register_setting('yoone_snow_options', 'yoone_snow_text_weights', array( 'type' => 'array', 'sanitize_callback' => function($value) { if (!is_array($value)) { $value = array(); } $clean = array(); foreach ($value as $ch => $num) { $key = trim((string)$ch); $weight = intval($num); if ($key !== '') { if ($weight < 0) { $weight = 0; } $clean[$key] = $weight; } } return $clean; }, 'default' => array(), )); // 注册首页显示时长设置 项为整数秒 0 表示无限 register_setting('yoone_snow_options', 'yoone_snow_home_duration', array( 'type' => 'integer', 'sanitize_callback' => function($value) { // 将输入转换为非负整数 单位为秒 0 表示无限 $num = intval($value); if ($num < 0) { $num = 0; } return $num; }, 'default' => 0, )); // 添加首页显示时长字段 输入为数字最小值为 0 add_settings_field( 'yoone_snow_home_duration', esc_html__('Display Duration Seconds', 'yoone-snow'), function() { // 读取当前设置值 并渲染数字输入框 $current = intval(get_option('yoone_snow_home_duration', 0)); echo ''; echo '

' . esc_html__('Duration in seconds for snow on home 0 means infinite', 'yoone-snow') . '

'; }, 'yoone_snow', 'yoone_snow_section' ); // 注册目标在屏最大数量设置项 0 表示自动根据视口面积 register_setting('yoone_snow_options', 'yoone_snow_max_count', array( 'type' => 'integer', 'sanitize_callback' => function($value) { $num = intval($value); if ($num < 0) { $num = 0; } // 上限保护 防止过大影响性能 if ($num > 1000) { $num = 1000; } return $num; }, 'default' => 0, )); // 添加输入字段 用于设置在屏最大数量 0 表示自动 add_settings_field( 'yoone_snow_max_count', esc_html__('Max Snowflakes On Screen', 'yoone-snow'), function() { $current = intval(get_option('yoone_snow_max_count', 0)); echo ''; echo '

' . esc_html__('0 means auto based on viewport area upper bound 1000', 'yoone-snow') . '

'; }, 'yoone_snow', 'yoone_snow_section' ); // 尺寸组合设置 使用单一选项 snow size 存储最小与最大半径 register_setting('yoone_snow_options', 'yoone_snow_size', array( 'type' => 'array', 'sanitize_callback' => function($value) { // 将输入统一为包含 min 与 max 的数值数组 并保证非负与 max 不小于 min if (!is_array($value)) { $value = array(); } $min = isset($value['min']) ? floatval($value['min']) : 1.0; $max = isset($value['max']) ? floatval($value['max']) : 3.0; if ($min < 0) { $min = 0.0; } if ($max < $min) { $max = $min; } return array('min' => $min, 'max' => $max); }, 'default' => array('min' => 1.0, 'max' => 3.0), )); add_settings_field( 'yoone_snow_size', esc_html__('Snow Size', 'yoone-snow'), function() { // 渲染组合输入 使用同一选项保存最小与最大半径 $grp = get_option('yoone_snow_size', array('min' => 1.0, 'max' => 3.0)); $min = isset($grp['min']) ? floatval($grp['min']) : 1.0; $max = isset($grp['max']) ? floatval($grp['max']) : 3.0; echo ''; echo ''; echo '

' . esc_html__('Random radius in [min max] single option', 'yoone-snow') . '

'; }, 'yoone_snow', 'yoone_snow_section' ); // 漂移速度与摆动幅度的随机范围设置 保持独立选项 register_setting('yoone_snow_options', 'yoone_snow_drift_min', array( 'type' => 'number', 'sanitize_callback' => function($value) { $num = floatval($value); if ($num < 0) { $num = 0; } return $num; }, 'default' => 0.4, )); register_setting('yoone_snow_options', 'yoone_snow_drift_max', array( 'type' => 'number', 'sanitize_callback' => function($value) { $min = floatval(get_option('yoone_snow_drift_min', 0.4)); $num = floatval($value); if ($num < $min) { $num = $min; } return $num; }, 'default' => 1.0, )); add_settings_field( 'yoone_snow_drift_range', esc_html__('Drift Speed Random Range', 'yoone-snow'), function() { $min = floatval(get_option('yoone_snow_drift_min', 0.4)); $max = floatval(get_option('yoone_snow_drift_max', 1.0)); echo ''; echo ''; echo '

' . esc_html__('Random vertical drift speed base in [min max]', 'yoone-snow') . '

'; }, 'yoone_snow', 'yoone_snow_section' ); register_setting('yoone_snow_options', 'yoone_snow_swing_min', array( 'type' => 'number', 'sanitize_callback' => function($value) { $num = floatval($value); if ($num < 0) { $num = 0; } return $num; }, 'default' => 0.2, )); register_setting('yoone_snow_options', 'yoone_snow_swing_max', array( 'type' => 'number', 'sanitize_callback' => function($value) { $min = floatval(get_option('yoone_snow_swing_min', 0.2)); $num = floatval($value); if ($num < $min) { $num = $min; } return $num; }, 'default' => 1.0, )); add_settings_field( 'yoone_snow_swing_range', esc_html__('Swing Amplitude Random Range', 'yoone-snow'), function() { $min = floatval(get_option('yoone_snow_swing_min', 0.2)); $max = floatval(get_option('yoone_snow_swing_max', 1.0)); echo ''; echo ''; echo '

' . esc_html__('Random horizontal swing amplitude base in [min max] before offset scale', 'yoone-snow') . '

'; }, 'yoone_snow', 'yoone_snow_section' ); // 路由显示模式设置 register_setting('yoone_snow_options', 'yoone_snow_display_routes_mode', array( 'type' => 'string', 'sanitize_callback' => function($value) { $val = strtolower(trim((string)$value)); $allowed = array('home','all','match'); return in_array($val, $allowed, true) ? $val : 'home'; }, 'default' => 'home', )); add_settings_field( 'yoone_snow_display_routes_mode', esc_html__('Display Routes Mode', 'yoone-snow'), function() { $current = get_option('yoone_snow_display_routes_mode', 'home'); echo ''; echo ''; echo ''; echo '

' . esc_html__('Default Home Only choose All Pages to enable globally choose Match URL Path to enable only for matched routes', 'yoone-snow') . '

'; }, 'yoone_snow', 'yoone_snow_section' ); // 路由匹配规则列表设置 register_setting('yoone_snow_options', 'yoone_snow_display_routes', array( 'type' => 'array', 'sanitize_callback' => function($value) { if (is_string($value)) { $value = preg_split('/\\r?\\n/', $value); } if (!is_array($value)) { $value = array(); } $out = array(); foreach ($value as $item) { $s = trim((string)$item); if ($s !== '') { $out[] = $s; } } return array_values(array_unique($out)); }, 'default' => array(), )); add_settings_field( 'yoone_snow_display_routes', esc_html__('Match Routes', 'yoone-snow'), function() { $current = get_option('yoone_snow_display_routes', array()); if (is_string($current)) { $current = preg_split('/\\r?\\n/', $current); } if (!is_array($current)) { $current = array(); } $text = implode("\n", array_map('strval', $current)); $placeholder = esc_attr__("Example /about\n/blog/*\n/shop", 'yoone-snow'); echo ''; echo '

' . esc_html__('One rule per line compare against request path example exact path /about prefix match with trailing star /blog/*', 'yoone-snow') . '

'; }, 'yoone_snow', 'yoone_snow_section' ); } // 添加设置页面到后台菜单 条目在设置菜单下 function yoone_snow_add_settings_page() { add_options_page( esc_html__('Yoone Snow', 'yoone-snow'), esc_html__('Yoone Snow', 'yoone-snow'), 'manage_options', 'yoone_snow', function() { // 渲染设置页面 表单提交到 options.php echo '
'; echo '

' . esc_html__('Yoone Snow', 'yoone-snow') . '

'; echo '
'; settings_fields('yoone_snow_options'); do_settings_sections('yoone_snow'); submit_button(); echo '
'; echo '
'; } ); } // 在 admin 初始化时注册设置 在 admin 菜单挂载页面 add_action('admin_init', 'yoone_snow_register_settings'); add_action('admin_menu', 'yoone_snow_add_settings_page'); add_action('admin_enqueue_scripts', 'yoone_snow_admin_enqueue'); // 在插件列表行添加 Settings 链接 指向设置页面 function yoone_snow_plugin_action_links($links) { // 构造设置页面链接 使用 admin_url 保证后台路径正确 $settingsUrl = admin_url('options-general.php?page=yoone_snow'); $settingsLink = '' . esc_html__('Settings', 'yoone-snow') . ''; // 将设置链接插入到最前面 便于用户点击 array_unshift($links, $settingsLink); return $links; } // 绑定到当前插件的 action links 钩子 使用 plugin_basename 计算插件标识 add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'yoone_snow_plugin_action_links'); function yoone_snow_load_textdomain() { load_plugin_textdomain('yoone-snow', false, dirname(plugin_basename(__FILE__)) . '/languages'); } add_action('plugins_loaded', 'yoone_snow_load_textdomain');