From 08e63c07f4452b147a071c3ab76f1d4c62a6a6f6 Mon Sep 17 00:00:00 2001
From: unknown
Date: Tue, 21 Jul 2026 08:35:59 +0800
Subject: [PATCH 1/3] feat(widgets): VideoPlayer data-aksrt-video shared player
shell
Route non-embed videos through video-player.js with poster/type/autoplay
loop/muted attrs; keep platform iframe embeds; admin empty warning.
---
assets/js/video-player.js | 6 +++-
src/AKSRT/Widgets/WidgetVideoPlayer.php | 48 +++++++++++++++----------
2 files changed, 35 insertions(+), 19 deletions(-)
diff --git a/assets/js/video-player.js b/assets/js/video-player.js
index fe52c533..8f0e461f 100644
--- a/assets/js/video-player.js
+++ b/assets/js/video-player.js
@@ -65,6 +65,8 @@
this.poster = this.el.dataset.poster || '';
this.type = this.el.dataset.type || detectType(this.src);
this.autoplay = this.el.dataset.autoplay === 'true';
+ this.loop = this.el.dataset.loop === 'true';
+ this.muted = this.el.dataset.muted === 'true' || this.autoplay;
this.init();
}
@@ -76,8 +78,10 @@
this.video.controls = true;
this.video.playsInline = true;
this.video.preload = this.autoplay ? 'auto' : 'metadata';
+ if (this.loop) this.video.loop = true;
+ if (this.muted) this.video.muted = true;
if (this.poster) this.video.poster = this.poster;
- this.video.style.cssText = 'display:block;width:100%;height:auto;max-height:70vh;';
+ this.video.style.cssText = 'display:block;width:100%;height:100%;object-fit:contain;max-height:none;';
// Play overlay
this.overlay = document.createElement('div');
diff --git a/src/AKSRT/Widgets/WidgetVideoPlayer.php b/src/AKSRT/Widgets/WidgetVideoPlayer.php
index d20a2d0f..fde4bf8c 100644
--- a/src/AKSRT/Widgets/WidgetVideoPlayer.php
+++ b/src/AKSRT/Widgets/WidgetVideoPlayer.php
@@ -1,6 +1,6 @@
__('侧栏/流体区嵌入视频播放器。', 'aksrt')]
+ [
+ 'description' => __('侧栏/流体区嵌入视频(共享 aksrt-video 播放器)。', 'aksrt'),
+ 'classname' => 'widget_aksrt_video_player',
+ ]
);
}
@@ -31,6 +34,12 @@ class WidgetVideoPlayer extends \WP_Widget {
$aspect = (string) ($instance['aspect_ratio'] ?? '');
if ($video_url === '') {
+ if (current_user_can('manage_options')) {
+ echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo '' . esc_html__('视频播放器:', 'aksrt') . '' .
+ esc_html__('请配置视频 URL 或移出该模块', 'aksrt') . '
';
+ echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
return;
}
@@ -47,29 +56,32 @@ class WidgetVideoPlayer extends \WP_Widget {
}
$embed = $this->get_embed_url($video_url);
- $cls = 'aksrt-video-widget' . ($hide_controller ? ' controller-hide' : '');
+ $cls = 'aksrt-video-widget relative-h radius8' . ($hide_controller ? ' controller-hide' : '');
echo '';
+
if ($embed !== '') {
$src = $embed;
if ($autoplay) {
$src = add_query_arg(['autoplay' => '1', 'mute' => '1'], $src);
}
- echo '
';
+ echo '
';
} else {
- $attrs = ' controls playsinline';
- if ($autoplay) {
- $attrs .= ' autoplay';
+ // Shared player stack (assets/js/video-player.js).
+ $type = 'mp4';
+ if (preg_match('/\.m3u8(\?|$)/i', $video_url)) {
+ $type = 'hls';
+ } elseif (preg_match('/\.flv(\?|$)/i', $video_url)) {
+ $type = 'flv';
}
- if ($loop) {
- $attrs .= ' loop';
- }
- if ($muted) {
- $attrs .= ' muted';
- }
- if ($poster !== '') {
- $attrs .= ' poster="' . esc_url($poster) . '"';
- }
- echo '
';
+ printf(
+ '
',
+ esc_url($video_url),
+ esc_attr($type),
+ $poster !== '' ? ' data-poster="' . esc_url($poster) . '"' : '',
+ $autoplay ? ' data-autoplay="true"' : '',
+ $loop ? ' data-loop="true"' : '',
+ $muted ? ' data-muted="true"' : ''
+ );
}
echo '
';
@@ -97,7 +109,7 @@ class WidgetVideoPlayer extends \WP_Widget {
-
From af8f97ad3c39ffe85bbd1ebbe706461aa70e675b Mon Sep 17 00:00:00 2001
From: unknown
Date: Tue, 21 Jul 2026 08:36:00 +0800
Subject: [PATCH 2/3] feat(widgets): BookmarkLinks via aksrt_links_box
bigcard/popover
Delegate render to shared links helper for card/bigcard/image/simple
styles, popover data attrs, empty admin guidance.
---
src/AKSRT/Widgets/WidgetBookmarkLinks.php | 67 +++++++++++------------
1 file changed, 32 insertions(+), 35 deletions(-)
diff --git a/src/AKSRT/Widgets/WidgetBookmarkLinks.php b/src/AKSRT/Widgets/WidgetBookmarkLinks.php
index e3d900e0..ad69399c 100644
--- a/src/AKSRT/Widgets/WidgetBookmarkLinks.php
+++ b/src/AKSRT/Widgets/WidgetBookmarkLinks.php
@@ -1,6 +1,6 @@
esc_html__('WordPress 链接(书签)列表,适合友链/导航。', 'aksrt'),
+ 'description' => esc_html__('WordPress 链接(书签)列表:card / bigcard / image / simple。', 'aksrt'),
'classname' => 'widget_aksrt_bookmark_links',
]
);
@@ -30,12 +30,12 @@ class WidgetBookmarkLinks extends \WP_Widget {
}
$order = strtoupper((string) ($instance['links_order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC';
$type = sanitize_key((string) ($instance['type'] ?? 'card'));
- if (!in_array($type, ['card', 'image', 'simple'], true)) {
+ if (!in_array($type, ['card', 'bigcard', 'image', 'simple'], true)) {
$type = 'card';
}
$go_link = !empty($instance['go_link']);
$blank = !isset($instance['blank']) || !empty($instance['blank']);
- $nofollow = !empty($instance['nofollow']);
+ $nofollow = !isset($instance['nofollow']) || !empty($instance['nofollow']);
$show_box = !empty($instance['show_box']);
$bm_args = [
@@ -49,6 +49,13 @@ class WidgetBookmarkLinks extends \WP_Widget {
}
$bookmarks = get_bookmarks($bm_args);
if (!$bookmarks) {
+ if (current_user_can('manage_options')) {
+ echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo function_exists('aksrt_get_widget_null')
+ ? aksrt_get_widget_null(__('暂无链接,请在「链接」中添加', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ : '' . esc_html__('暂无链接', 'aksrt') . '
';
+ echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
return;
}
@@ -58,37 +65,22 @@ class WidgetBookmarkLinks extends \WP_Widget {
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
- $wrap = 'aksrt-bookmark-links style-' . $type . ($show_box ? ' has-box' : '');
+ $wrap = 'aksrt-bookmark-links links-box style-' . $type . ($show_box ? ' has-box' : '');
echo '';
@@ -116,6 +108,7 @@ class WidgetBookmarkLinks extends \WP_Widget {
@@ -137,7 +130,7 @@ class WidgetBookmarkLinks extends \WP_Widget {
>
- >
+ >
>
>
@@ -145,13 +138,17 @@ class WidgetBookmarkLinks extends \WP_Widget {
}
public function update($new_instance, $old_instance): array {
+ $type = sanitize_key((string) ($new_instance['type'] ?? 'card'));
+ if (!in_array($type, ['card', 'bigcard', 'image', 'simple'], true)) {
+ $type = 'card';
+ }
return [
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
'links_cats' => sanitize_text_field((string) ($new_instance['links_cats'] ?? '')),
'links_limit' => max(0, min(100, (int) ($new_instance['links_limit'] ?? 12))),
'links_orderby' => sanitize_key((string) ($new_instance['links_orderby'] ?? 'name')),
'links_order' => strtoupper((string) ($new_instance['links_order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC',
- 'type' => in_array(($new_instance['type'] ?? ''), ['card', 'image', 'simple'], true) ? $new_instance['type'] : 'card',
+ 'type' => $type,
'blank' => !empty($new_instance['blank']) ? 1 : 0,
'nofollow' => !empty($new_instance['nofollow']) ? 1 : 0,
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
From 0cf3d440e231541ed400fba080f556187e3725eb Mon Sep 17 00:00:00 2001
From: unknown
Date: Tue, 21 Jul 2026 08:36:00 +0800
Subject: [PATCH 3/3] feat(widgets): Stats count-up + product oneline
swiper-nav
Wire data-aksrt-countup for numeric stats; oneline products use
swiper-scroll slides with prev/next; batch12 CSS for video/links/oneline.
---
assets/css/frontend/widget-batch5.css | 129 ++++++++++++++++++++++++
src/AKSRT/Widgets/WidgetProductList.php | 66 +++++++-----
src/AKSRT/Widgets/WidgetStats.php | 17 +++-
3 files changed, 188 insertions(+), 24 deletions(-)
diff --git a/assets/css/frontend/widget-batch5.css b/assets/css/frontend/widget-batch5.css
index 6d0a9e8c..a51c6701 100644
--- a/assets/css/frontend/widget-batch5.css
+++ b/assets/css/frontend/widget-batch5.css
@@ -1303,3 +1303,132 @@
.aksrt-faq__main { grid-template-columns: 1fr; }
.aksrt-faq__a { padding-left: 14px; }
}
+
+/* ---- batch12: video shell + bookmark links styles + product oneline swiper ---- */
+.aksrt-video-widget {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+ border-radius: 10px;
+ background: #000;
+}
+.aksrt-video-widget.radius8 { border-radius: 10px; }
+.aksrt-video-widget__frame,
+.aksrt-video-widget__player,
+.aksrt-video-widget .aksrt-video {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+}
+.aksrt-video-widget__frame {
+ border: 0;
+ display: block;
+}
+.aksrt-video-widget.controller-hide .aksrt-video video::-webkit-media-controls {
+ opacity: 0;
+ transition: opacity .2s ease;
+}
+.aksrt-video-widget.controller-hide:hover .aksrt-video video::-webkit-media-controls {
+ opacity: 1;
+}
+
+.aksrt-bookmark-links.links-box {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+.aksrt-bookmark-links.style-card .author-minicard,
+.aksrt-bookmark-links.style-bigcard .author-minicard {
+ flex: 1 1 calc(50% - 12px);
+ min-width: 140px;
+ background: var(--surface-2, #f8fafc);
+ border: 1px solid var(--color-line, #e5e7eb);
+ padding: 12px;
+ box-sizing: border-box;
+}
+.aksrt-bookmark-links.style-bigcard .author-minicard {
+ flex: 1 1 100%;
+ text-align: center;
+}
+.aksrt-bookmark-links .avatar-img img,
+.aksrt-bookmark-links .link-img img {
+ width: 42px;
+ height: 42px;
+ border-radius: 50%;
+ object-fit: cover;
+}
+.aksrt-bookmark-links.style-bigcard .avatar-img img {
+ width: 64px;
+ height: 64px;
+}
+.aksrt-bookmark-links.style-image {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+}
+.aksrt-bookmark-links.style-image .link-only-img img {
+ width: 48px;
+ height: 48px;
+ border-radius: 10px;
+ object-fit: cover;
+}
+.aksrt-bookmark-links.style-simple {
+ display: block;
+ line-height: 1.9;
+ font-size: 13px;
+}
+.aksrt-bookmark-links.style-simple a {
+ color: inherit;
+ text-decoration: none;
+ margin-right: 8px;
+}
+.aksrt-bookmark-links.style-simple a:hover {
+ color: var(--color-primary, #3b82f6);
+}
+.aksrt-bookmark-links.has-box {
+ padding: 12px;
+ border: 1px solid var(--color-line, #e5e7eb);
+ border-radius: 12px;
+ background: var(--surface-1, #fff);
+}
+
+.aksrt-product-oneline-swiper {
+ position: relative;
+ overflow: hidden;
+ padding: 0 28px 4px;
+}
+.aksrt-product-oneline-swiper .swiper-wrapper {
+ display: flex;
+}
+.aksrt-product-oneline-swiper .swiper-slide {
+ width: auto;
+ max-width: 46%;
+ min-width: 150px;
+ height: auto;
+}
+.aksrt-product-oneline-swiper .aksrt-product-card {
+ height: 100%;
+}
+.aksrt-product-oneline-swiper .swiper-button-prev,
+.aksrt-product-oneline-swiper .swiper-button-next {
+ width: 28px;
+ height: 28px;
+ margin-top: 0;
+ top: 42%;
+ border-radius: 50%;
+ background: var(--surface-1, #fff);
+ border: 1px solid var(--color-line, #e5e7eb);
+ color: var(--color-ink, #111);
+ box-shadow: 0 2px 8px rgba(0,0,0,.06);
+}
+.aksrt-product-oneline-swiper .swiper-button-prev:after,
+.aksrt-product-oneline-swiper .swiper-button-next:after {
+ font-size: 12px;
+ font-weight: 700;
+}
+.aksrt-product-oneline-swiper .swiper-button-prev { left: 0; }
+.aksrt-product-oneline-swiper .swiper-button-next { right: 0; }
+.aksrt-product-widget--oneline .aksrt-product-widget__grid {
+ /* legacy flex fallback if swiper unavailable */
+}
diff --git a/src/AKSRT/Widgets/WidgetProductList.php b/src/AKSRT/Widgets/WidgetProductList.php
index 8a76c6c3..e6847945 100644
--- a/src/AKSRT/Widgets/WidgetProductList.php
+++ b/src/AKSRT/Widgets/WidgetProductList.php
@@ -93,6 +93,7 @@ class WidgetProductList extends \WP_Widget {
if (!$products->have_posts()) {
return '';
}
+ $oneline = $style === 'oneline';
ob_start();
while ($products->have_posts()) {
$products->the_post();
@@ -100,30 +101,36 @@ class WidgetProductList extends \WP_Widget {
$card = class_exists('\AKSRT\Shop\Shop')
? \AKSRT\Shop\Shop::get_instance()->build_product_card_view($pid)
: [];
+ if ($oneline) {
+ echo '';
+ }
if (function_exists('get_template_part') && !empty($card['id'])) {
get_template_part('templates/parts/shop/product', 'card', [
'product' => $card,
'show_sales' => true,
]);
- continue;
+ } else {
+ // Fallback mini card.
+ $price = (float) ($card['price'] ?? 0);
+ $image = (string) ($card['image'] ?? '');
+ echo '
';
}
- // Fallback mini card.
- $price = (float) ($card['price'] ?? 0);
- $image = (string) ($card['image'] ?? '');
- echo '
';
}
- echo '
';
}
wp_reset_postdata();
return (string) ob_get_clean();
@@ -231,12 +238,25 @@ class WidgetProductList extends \WP_Widget {
$style_class = 'aksrt-product-widget--' . $style;
echo '';
- // Items sit directly under .ajaxpager so ajax-list inject/append works (zibll product-lists-row).
- echo '
';
+
+ if ($style === 'oneline') {
+ // zibll-style horizontal swiper-scroll (shared slider.js Type A).
+ echo '';
+ } else {
+ // Items sit directly under .ajaxpager so ajax-list inject/append works (zibll product-lists-row).
+ echo '';
+ }
+ echo '';
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
diff --git a/src/AKSRT/Widgets/WidgetStats.php b/src/AKSRT/Widgets/WidgetStats.php
index 5614ee31..9eded52e 100644
--- a/src/AKSRT/Widgets/WidgetStats.php
+++ b/src/AKSRT/Widgets/WidgetStats.php
@@ -67,7 +67,22 @@ class WidgetStats extends \WP_Widget {
if ($s['prefix'] !== '') {
echo '' . esc_html($s['prefix']) . '';
}
- echo '' . esc_html($s['number']) . '';
+ // Wire effects.js count-up when number is numeric.
+ $raw_num = preg_replace('/[^\d.]/', '', (string) $s['number']);
+ $is_numeric = $raw_num !== '' && is_numeric($raw_num);
+ if ($is_numeric) {
+ $dec = 0;
+ if (str_contains((string) $raw_num, '.')) {
+ $dec = strlen(substr(strrchr((string) $raw_num, '.'), 1));
+ }
+ printf(
+ '0',
+ esc_attr((string) $raw_num),
+ (int) $dec
+ );
+ } else {
+ echo '' . esc_html($s['number']) . '';
+ }
if ($s['unit'] !== '') {
echo '' . esc_html($s['unit']) . '';
}