diff --git a/assets/admin/src/schema/sections/page-display/index.js b/assets/admin/src/schema/sections/page-display/index.js
index 1b40dcab..a9786a1b 100644
--- a/assets/admin/src/schema/sections/page-display/index.js
+++ b/assets/admin/src/schema/sections/page-display/index.js
@@ -36,7 +36,6 @@ export const pageDisplaySchemas = {
header_fixed: true,
header_search_enabled: true,
header_user_menu: true,
- header_publish_btn: true,
header_mobile_drawer: true,
header_nav_style: 'default',
header_max_width: 1200,
@@ -52,7 +51,7 @@ export const pageDisplaySchemas = {
header_newposts_enabled: true,
header_newposts_text: ' 发布',
header_newposts_radius: true,
- header_newposts_class: '',
+ header_newposts_class: 'jb-blue',
header_theme_custom: false,
header_theme_bg_custom: '',
header_theme_bg: '',
@@ -124,10 +123,17 @@ export const pageDisplaySchemas = {
title: '发布按钮',
icon: 'fas fa-pen',
fields: [
- { key: 'header_newposts_enabled', label: '启用发布按钮', type: 'switch', default: true, help: '导航栏显示前台发布入口' },
+ { key: 'header_newposts_enabled', label: '启用发布按钮', type: 'switch', default: true, help: '顶部导航显示发布入口(对齐 zibll nav_newposts)' },
{ key: 'header_newposts_text', label: '按钮文案', type: 'input', default: ' 发布', dependsOn: { key: 'header_newposts_enabled', value: true } },
{ key: 'header_newposts_radius', label: '按钮圆角(胶囊)', type: 'switch', default: true, dependsOn: { key: 'header_newposts_enabled', value: true }, help: '开启后两端大圆角,对齐 zibll nav_newposts_radius' },
- { key: 'header_newposts_class', label: '自定义 CSS 类', type: 'input', default: '', dependsOn: { key: 'header_newposts_enabled', value: true } },
+ { key: 'header_newposts_class', label: '按钮颜色', type: 'select', default: 'jb-blue', options: [
+ { label: '蓝色', value: 'jb-blue' },
+ { label: '红色', value: 'jb-red' },
+ { label: '绿色', value: 'jb-green' },
+ { label: '黄色', value: 'jb-yellow' },
+ { label: '紫色', value: 'jb-purple' },
+ { label: '粉色', value: 'jb-pink' },
+ ], dependsOn: { key: 'header_newposts_enabled', value: true } },
{ key: 'header_newposts_types', label: '发布类型', type: 'select', multiple: true, default: ['post'], options: [
{ label: '文章', value: 'post' },
{ label: '帖子', value: 'topic' },
@@ -161,7 +167,6 @@ export const pageDisplaySchemas = {
fields: [
{ key: 'header_search_enabled', label: '搜索入口', type: 'switch', default: true },
{ key: 'header_user_menu', label: '用户菜单', type: 'switch', default: true },
- { key: 'header_publish_btn', label: '发布按钮', type: 'switch', default: true, help: '管理员可见的前台发布入口' },
{ key: 'header_mobile_drawer', label: '移动端抽屉导航', type: 'switch', default: true },
],
},
diff --git a/assets/css/frontend/nav.css b/assets/css/frontend/nav.css
index 207f608b..97ddde74 100644
--- a/assets/css/frontend/nav.css
+++ b/assets/css/frontend/nav.css
@@ -3,6 +3,7 @@
@import url("nav/zib-mobile-navbar.css");
@import url("nav/zib-senior-submenu.css");
@import url("nav/zib-utilities.css");
+@import url("nav/zib-search-box.css");
@import url("nav/header-menu-buttons.css");
@import url("nav/header-userbox.css");
@import url("nav/dark-mode-submenu.css");
diff --git a/assets/css/frontend/nav/zib-search-box.css b/assets/css/frontend/nav/zib-search-box.css
new file mode 100644
index 00000000..65d28747
--- /dev/null
+++ b/assets/css/frontend/nav/zib-search-box.css
@@ -0,0 +1,194 @@
+/* zibll search-box / menu_search panel parity */
+
+/* Layout-2 focus dropdown */
+.navbar-collapse .form-group.relative.dropdown {
+ position: relative;
+}
+
+.navbar-collapse .search-input ~ .dropdown-menu,
+.navbar-collapse .form-group .dropdown-menu.hover-show-con {
+ display: none;
+ position: absolute;
+ left: 0;
+ top: 100%;
+ z-index: 1002;
+ min-width: 320px;
+ width: max(320px, 100%);
+ max-width: 460px;
+ margin-top: 10px;
+ padding: 10px;
+ background: var(--main-bg-color, #fff);
+ border-radius: var(--main-radius, 8px);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+ visibility: visible;
+ opacity: 1;
+}
+
+.navbar-form.hover-show:hover .dropdown-menu.hover-show-con,
+.navbar-form.hover-show:focus-within .dropdown-menu.hover-show-con,
+.navbar-collapse .form-group:focus-within .dropdown-menu.hover-show-con,
+.navbar-collapse .dropdown-menu.hover-show-con.is-open {
+ display: block;
+}
+
+/* Search box body */
+.search-input .search-keywords {
+ padding: 6px 10px 2px;
+}
+.search-input .search-keywords > p {
+ margin: 0 0 6px;
+ font-size: 0.9em;
+}
+.search-input .search-keywords .but,
+.search-input .search-keywords a.but {
+ display: inline-block;
+ margin: 0 6px 6px 0;
+ padding: 2px 10px;
+ font-size: 0.85em;
+ border-radius: 20px;
+ background: var(--main-shadow, rgba(0, 0, 0, 0.05));
+ color: inherit;
+ text-decoration: none;
+}
+.search-input .search-keywords .but:hover {
+ color: var(--focus-color, #2997f7);
+}
+
+.search-input .history-search .trash-history-search {
+ text-decoration: none;
+ opacity: 0.7;
+}
+.search-input .history-search .trash-history-search:hover {
+ opacity: 1;
+ color: var(--focus-color, #2997f7);
+}
+
+/* Hot posts swiper row */
+.search-input .relates-thumb {
+ overflow: hidden;
+}
+.search-input .relates-thumb .swiper-container,
+.search-input .relates-thumb .swiper-scroll {
+ overflow: hidden;
+ position: relative;
+}
+.search-input .relates-thumb .swiper-wrapper {
+ display: flex;
+ overflow-x: auto;
+ scroll-snap-type: x mandatory;
+ -webkit-overflow-scrolling: touch;
+ gap: 0;
+ scrollbar-width: none;
+}
+.search-input .relates-thumb .swiper-wrapper::-webkit-scrollbar {
+ display: none;
+}
+.search-input .relates-thumb .swiper-slide {
+ flex: 0 0 auto;
+ scroll-snap-align: start;
+}
+.search-input .relates-thumb .swiper-button-prev,
+.search-input .relates-thumb .swiper-button-next {
+ display: none;
+}
+
+/* Full navbar-search slide (layout 1/3) */
+.navbar-search {
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ width: 100%;
+}
+.navbar-search .container {
+ max-width: var(--nav-max-width, 1200px);
+ margin: 0 auto;
+ position: relative;
+ padding: 10px 15px 20px;
+}
+.navbar-search .navbar-search-close,
+.navbar-search > .container > .close {
+ position: absolute;
+ right: 12px;
+ top: 12px;
+ z-index: 2;
+ background: none;
+ border: 0;
+ font-size: 1.25rem;
+ color: inherit;
+ cursor: pointer;
+ opacity: 0.7;
+}
+.navbar-search .navbar-search-close:hover {
+ opacity: 1;
+}
+
+/* Line form (full search box) */
+.search-input .line-form {
+ display: flex;
+ align-items: stretch;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
+ position: relative;
+}
+.search-input .search-input-text {
+ flex: 1;
+ position: relative;
+ min-width: 0;
+}
+.search-input .line-form-input {
+ width: 100%;
+ border: 0;
+ background: transparent;
+ padding: 12px 40px 12px 4px;
+ font-size: 1.05em;
+ outline: none;
+ color: inherit;
+}
+.search-input .scale-placeholder {
+ position: absolute;
+ left: 4px;
+ top: 50%;
+ transform: translateY(-50%);
+ pointer-events: none;
+ opacity: 0.45;
+ transition: 0.2s;
+}
+.search-input .scale-placeholder.is-focus,
+.search-input .line-form-input:focus + .line-form-line + .scale-placeholder,
+.search-input .line-form-input:not([value=""]) ~ .scale-placeholder {
+ opacity: 0;
+}
+.search-input .option-dropdown {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ padding-right: 8px;
+}
+.search-input .search-type-but-average .but {
+ margin: 0 4px;
+}
+.search-input .search-type-but-average .but.active {
+ color: #fff;
+ background: var(--focus-color, #2997f7);
+}
+
+/* Live suggest (extension) */
+.navbar-inline-search-suggest__item,
+.aksrt-menu-search-suggest a {
+ display: flex;
+ justify-content: space-between;
+ gap: 10px;
+ padding: 6px 4px;
+ text-decoration: none;
+ color: inherit;
+}
+.navbar-inline-search-suggest__item:hover {
+ color: var(--focus-color, #2997f7);
+}
+
+body.theme-dark .navbar-collapse .form-group .dropdown-menu.hover-show-con,
+html.dark .navbar-collapse .form-group .dropdown-menu.hover-show-con,
+body.dark-theme .navbar-collapse .form-group .dropdown-menu.hover-show-con {
+ background: var(--main-bg-color, #1e1e1e);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
+}
diff --git a/assets/js/core/search.js b/assets/js/core/search.js
index ca26bd77..e6737e2f 100644
--- a/assets/js/core/search.js
+++ b/assets/js/core/search.js
@@ -291,19 +291,20 @@
document.querySelectorAll(".main-search-btn, .nav-search-toggle, [data-aksrt-open-search]").forEach(function (btn) {
btn.addEventListener("click", function (e) {
+ // Top bar uses zibll .navbar-search slide — skip tabbar overlay.
+ var tgt = btn.getAttribute("data-target") || "";
+ if (tgt.indexOf("navbar-search") !== -1 || tgt.indexOf("main-search") !== -1) {
+ return;
+ }
e.preventDefault();
setOpen(true);
});
});
- // Zibll-compatible data-toggle-class for .navbar-search
- document.addEventListener("click", function (e) {
- var toggle = e.target.closest('[data-toggle-class][data-target=".navbar-search"], [data-toggle-class][data-target=".main-search"]');
- if (!toggle) return;
- e.preventDefault();
- var isOpen = mobileOverlay && !mobileOverlay.hasAttribute("hidden");
- setOpen(!isOpen);
- });
+ // Top-nav .navbar-search is toggled by aksrt-runtime (data-toggle-class="show").
+ // Do NOT redirect those clicks into the footer tabbar overlay.
+ // Only open tabbar overlay for explicit main-search-btn without .navbar-search target
+ // (handled above via .main-search-btn listeners).
if (historyClear) {
historyClear.addEventListener("click", function () {
diff --git a/assets/js/header-inline-search.js b/assets/js/header-inline-search.js
index ef356dbd..d4604713 100644
--- a/assets/js/header-inline-search.js
+++ b/assets/js/header-inline-search.js
@@ -1,6 +1,7 @@
/**
- * Layout-2 navbar search — zibll remote-box + live suggestions.
- * Loads aksrt_menu_search panel (history/hot/posts), then suggests on input.
+ * Layout-2 navbar search — zibll remote-box + search-input panel.
+ * Loads menu_search panel (.dropdown-menu.hover-show-con > .search-input),
+ * fills guest history from localStorage, live suggestions optional.
*/
(function () {
"use strict";
@@ -40,6 +41,24 @@
} catch (e) {}
}
+ function keywordChips(list) {
+ return (list || [])
+ .map(function (kw) {
+ return (
+ '' +
+ kw +
+ " "
+ );
+ })
+ .join("");
+ }
+
function loadRemote(box) {
if (!box || box.getAttribute("data-loaded") === "1") return;
var url = box.getAttribute("remote-box") || box.getAttribute("data-url");
@@ -58,57 +77,51 @@
box.innerHTML = "";
return;
}
+ var parent = box.parentNode;
box.outerHTML = html;
- var form = document.querySelector('form[data-aksrt-search-form="inline"]');
+ var form = parent ? parent.closest("form") : document.querySelector('form[data-aksrt-search-form="inline"]');
if (form) enhancePanel(form);
+ initSwiperScroll(form || document);
})
.catch(function () {
box.removeAttribute("data-loaded");
});
}
- function enhancePanel(form) {
- var group = form.querySelector(".form-group");
- var panel = form.querySelector(".navbar-inline-search-panel");
- var input = form.querySelector(".search-input");
- if (!group || !input) return;
+ function initSwiperScroll(root) {
+ if (!root || typeof window.Swiper === "undefined") return;
+ root.querySelectorAll(".relates-thumb .swiper-container:not(.swiper-initialized)").forEach(function (el) {
+ try {
+ new window.Swiper(el, {
+ slidesPerView: "auto",
+ freeMode: true,
+ navigation: {
+ nextEl: el.querySelector(".swiper-button-next"),
+ prevEl: el.querySelector(".swiper-button-prev"),
+ },
+ });
+ } catch (e) {}
+ });
+ }
- // Merge guest local history into panel list.
- var histList = form.querySelector(".aksrt-menu-search-history__list");
+ function enhancePanel(form) {
+ if (!form || form._aksrtMenuSearchEnhanced) return;
+ form._aksrtMenuSearchEnhanced = 1;
+
+ var input = form.querySelector(".search-input, input[name='s']");
+ var histList = form.querySelector(".history-search-list, .history-search > div:last-child");
+
+ // Guest local history → panel.
if (histList && !(window.aksrtData && aksrtData.isLoggedIn)) {
var local = readLocalHistory();
if (local.length) {
- histList.innerHTML = local
- .map(function (kw) {
- return (
- '' +
- kw +
- " "
- );
- })
- .join("");
+ histList.innerHTML = keywordChips(local);
}
}
-
- form.querySelectorAll("[data-aksrt-menu-search-type]").forEach(function (chip) {
- chip.addEventListener("click", function (e) {
- e.preventDefault();
- var v = chip.getAttribute("data-aksrt-menu-search-type") || "post";
- var tin = form.querySelector('input[name="aksrt_search_type"]');
- if (tin) tin.value = v;
- form.querySelectorAll("[data-aksrt-menu-search-type]").forEach(function (c) {
- c.classList.toggle("active", c === chip);
- });
- });
- });
-
-var clearBtn = form.querySelector("[data-aksrt-clear-menu-history]");
- if (clearBtn && !clearBtn._bound) {
+ // Clear history (zibll .trash-history-search).
+ form.querySelectorAll(".trash-history-search, [data-aksrt-clear-menu-history]").forEach(function (clearBtn) {
+ if (clearBtn._bound) return;
clearBtn._bound = 1;
clearBtn.addEventListener("click", function (e) {
e.preventDefault();
@@ -118,14 +131,30 @@ var clearBtn = form.querySelector("[data-aksrt-clear-menu-history]");
'暂无历史';
}
});
- }
+ });
+
+ // Type chips inside panel (if any).
+ form.querySelectorAll("[data-for='type'], [data-aksrt-menu-search-type]").forEach(function (chip) {
+ chip.addEventListener("click", function (e) {
+ e.preventDefault();
+ var v =
+ chip.getAttribute("data-value") ||
+ chip.getAttribute("data-aksrt-menu-search-type") ||
+ "post";
+ var tin = form.querySelector('input[name="aksrt_search_type"], input[name="type"]');
+ if (tin) tin.value = v;
+ form.querySelectorAll("[data-for='type'], [data-aksrt-menu-search-type]").forEach(function (c) {
+ c.classList.toggle("active", c === chip);
+ });
+ });
+ });
var suggestMount = form.querySelector("[data-aksrt-menu-suggest]");
var timer = null;
var searchConfig = (window.aksrtData && aksrtData.search) || {};
function typeValue() {
- var t = form.querySelector('input[name="aksrt_search_type"]');
+ var t = form.querySelector('input[name="aksrt_search_type"], input[name="type"]');
return (t && t.value) || searchConfig.defaultType || "post";
}
@@ -153,14 +182,14 @@ var clearBtn = form.querySelector("[data-aksrt-clear-menu-history]");
var html =
'
' +
(searchConfig.suggestTitle || "搜索建议") +
- "
";
+ '
';
items.forEach(function (it) {
html +=
'
' +
(it.title || "") +
- '' +
+ "" +
(it.meta
? '' + it.meta + ""
: "") +
@@ -181,7 +210,7 @@ var clearBtn = form.querySelector("[data-aksrt-clear-menu-history]");
);
}
- if (!input._aksrtMenuSearchBound) {
+ if (input && !input._aksrtMenuSearchBound) {
input._aksrtMenuSearchBound = 1;
input.addEventListener("input", function () {
var q = (input.value || "").trim();
@@ -191,9 +220,9 @@ var clearBtn = form.querySelector("[data-aksrt-clear-menu-history]");
}, 180);
});
input.addEventListener("focus", function () {
- var remote = form.querySelector("[data-aksrt-menu-search-remote]");
+ var remote = form.querySelector("[remote-box]");
if (remote) loadRemote(remote);
- var p = form.querySelector(".navbar-inline-search-panel");
+ var p = form.querySelector(".dropdown-menu.hover-show-con");
if (p) {
p.classList.add("is-open");
p.style.display = "block";
@@ -214,9 +243,9 @@ var clearBtn = form.querySelector("[data-aksrt-clear-menu-history]");
}
}
- function init() {
- document.querySelectorAll("[data-aksrt-menu-search-remote]").forEach(function (box) {
- // Prefetch on idle / first hover of form.
+ /** Generic remote-box / lazyload-action=ias (zibll-compatible). */
+ function bindRemoteBoxes(root) {
+ (root || document).querySelectorAll("[remote-box]").forEach(function (box) {
var form = box.closest("form");
if (form) {
form.addEventListener(
@@ -226,8 +255,14 @@ var clearBtn = form.querySelector("[data-aksrt-clear-menu-history]");
},
{ once: true }
);
+ form.addEventListener(
+ "focusin",
+ function () {
+ loadRemote(box);
+ },
+ { once: true }
+ );
}
- // Also load when lazyload-action ias would fire — simple viewport/idle.
if ("requestIdleCallback" in window) {
requestIdleCallback(function () {
loadRemote(box);
@@ -238,9 +273,53 @@ var clearBtn = form.querySelector("[data-aksrt-clear-menu-history]");
}, 600);
}
});
+ }
+ function bindNavbarSearchSlide() {
+ // Ensure .navbar-search show toggle clears aria-hidden / focuses input.
+ document.addEventListener("click", function (e) {
+ var toggle = e.target.closest
+ ? e.target.closest('[data-toggle-class="show"][data-target=".navbar-search"]')
+ : null;
+ if (!toggle) return;
+ // Let runtime toggle class; after tick sync aria + focus.
+ setTimeout(function () {
+ var panel = document.querySelector(".navbar-search");
+ if (!panel) return;
+ var open = panel.classList.contains("show");
+ panel.setAttribute("aria-hidden", open ? "false" : "true");
+ if (open) {
+ var input = panel.querySelector('input[name="s"]');
+ if (input) input.focus();
+ initSwiperScroll(panel);
+ }
+ }, 0);
+ });
+ }
+
+ function init() {
+ bindRemoteBoxes(document);
+ bindNavbarSearchSlide();
var form = document.querySelector('form[data-aksrt-search-form="inline"]');
if (form) enhancePanel(form);
+ // Full navbar-search box history for guests.
+ document.querySelectorAll(".navbar-search .history-search-list").forEach(function (histList) {
+ if (window.aksrtData && aksrtData.isLoggedIn) return;
+ var local = readLocalHistory();
+ if (local.length) histList.innerHTML = keywordChips(local);
+ });
+ document.querySelectorAll(".navbar-search .trash-history-search").forEach(function (btn) {
+ if (btn._bound) return;
+ btn._bound = 1;
+ btn.addEventListener("click", function (e) {
+ e.preventDefault();
+ writeLocalHistory([]);
+ document.querySelectorAll(".navbar-search .history-search-list").forEach(function (list) {
+ list.innerHTML = '暂无历史';
+ });
+ });
+ });
+ initSwiperScroll(document);
}
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") {
diff --git a/inc/bootstrap.php b/inc/bootstrap.php
index 3d202b4a..80a28f96 100644
--- a/inc/bootstrap.php
+++ b/inc/bootstrap.php
@@ -45,6 +45,8 @@ require_once AKSRT_DIR . '/inc/helpers/aksrt-credits.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-null.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-ajax-list.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-links.php';
+require_once AKSRT_DIR . '/inc/helpers/search/aksrt-search-posts.php';
+require_once AKSRT_DIR . '/inc/helpers/search/aksrt-search-box.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-header-userbox.php';
require_once AKSRT_DIR . '/inc/helpers/aksrt-header-menu-buttons.php';
require_once AKSRT_DIR . '/inc/helpers/header/aksrt-header-render.php';
diff --git a/inc/helpers/aksrt-header-menu-buttons.php b/inc/helpers/aksrt-header-menu-buttons.php
index 2fe36262..8f504b1c 100644
--- a/inc/helpers/aksrt-header-menu-buttons.php
+++ b/inc/helpers/aksrt-header-menu-buttons.php
@@ -14,12 +14,9 @@ if (!function_exists('aksrt_get_header_newposts_config')) :
function aksrt_get_header_newposts_config(): array {
$cfg = class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::get_header_newposts_config()
- : ['enabled' => true, 'text' => __('发布', 'aksrt'), 'radius' => 20, 'class' => '', 'types' => []];
- $nav = class_exists('\AKSRT\Settings\Settings')
- ? \AKSRT\Settings\Settings::get_header_nav_settings()
- : ['publish_btn' => true];
- // Single source: newposts enabled AND header publish switch.
- $cfg['enabled'] = !empty($cfg['enabled']) && !empty($nav['publish_btn']);
+ : ['enabled' => true, 'text' => __('发布', 'aksrt'), 'radius' => 20, 'class' => 'jb-blue', 'types' => []];
+ // Single source: header_newposts_enabled (zibll nav_newposts).
+ // FacadeBulk maps publish_btn to the same setting.
$types = is_array($cfg['types'] ?? null) ? $cfg['types'] : [];
if (!$types) {
@@ -36,6 +33,9 @@ if (!function_exists('aksrt_get_header_newposts_config')) :
}
}
$cfg['types'] = $types;
+ if (empty($cfg['class'])) {
+ $cfg['class'] = 'jb-blue';
+ }
return $cfg;
}
endif;
@@ -60,7 +60,6 @@ if (!function_exists('aksrt_get_new_add_actions')) :
'type' => 'post',
'url' => $url,
'label' => __('写文章', 'aksrt'),
- // FA5 free solid names (avoid FA4 aliases like file-text-o).
'icon' => 'fas fa-file-alt',
];
} elseif ($type === 'topic') {
@@ -104,28 +103,24 @@ if (!function_exists('aksrt_normalize_header_cta_label')) :
return ' ' . esc_html__('发布', 'aksrt');
}
- // Common FA4 → FA5 renames used in admin defaults.
$replacements = [
- 'fa-file-text-o' => 'fa-file-alt',
- 'fa-file-text' => 'fa-file-alt',
- 'fa-pencil-square-o' => 'fa-edit',
- 'fa-pencil' => 'fa-pen',
- 'fa-comments-o' => 'fa-comments',
- 'fa-clock-o' => 'fa-clock',
- 'fa-calendar-check-o'=> 'fa-calendar-check',
- 'fa-diamond' => 'fa-gem',
+ 'fa-file-text-o' => 'fa-file-alt',
+ 'fa-file-text' => 'fa-file-alt',
+ 'fa-pencil-square-o' => 'fa-edit',
+ 'fa-pencil' => 'fa-pen',
+ 'fa-comments-o' => 'fa-comments',
+ 'fa-clock-o' => 'fa-clock',
+ 'fa-calendar-check-o' => 'fa-calendar-check',
+ 'fa-diamond' => 'fa-gem',
];
$label = strtr($label, $replacements);
- // Ensure icon uses fas (FA5 solid) when only bare "fa " is present.
$label = preg_replace('/\bclass=(["\'])fa\s+/', 'class=$1fas ', $label) ?? $label;
$label = preg_replace('/ ' . esc_html(wp_strip_all_tags($label)) . '';
} else {
- // Wrap remaining text nodes for flex alignment without raw text siblings.
$label = preg_replace(
'/(<\/i>)\s*([^<].*)$/u',
'$1 $2',
@@ -152,8 +147,8 @@ if (!function_exists('aksrt_render_new_add_btns')) :
$label = __('发布', 'aksrt');
}
$label_html = aksrt_normalize_header_cta_label((string) $label, 'fas fa-pen');
- // Keep CTA paint classes (e.g. btn-primary) off multi-type wrapper so the
- // dropdown plate is not filled pink/primary; apply them on the toggle only.
+
+ // Split paint classes vs wrapper tokens.
$class_tokens = preg_split('/\s+/', trim($class), -1, PREG_SPLIT_NO_EMPTY) ?: [];
$cta_tokens = [];
$wrap_tokens = ['newadd-btns'];
@@ -161,35 +156,37 @@ if (!function_exists('aksrt_render_new_add_btns')) :
if ($token === 'newadd-btns') {
continue;
}
- if ($token === 'btn-primary' || $token === 'but' || $token === 'navbar-cta') {
+ if ($token === 'btn-primary' || $token === 'but' || $token === 'navbar-cta' || str_starts_with($token, 'jb-')) {
$cta_tokens[] = $token;
continue;
}
$wrap_tokens[] = $token;
}
- if (!in_array('btn-primary', $cta_tokens, true)) {
- $cta_tokens[] = 'btn-primary';
+ if (!$cta_tokens) {
+ $cta_tokens[] = 'jb-blue';
+ }
+ if (!in_array('but', $cta_tokens, true)) {
+ $cta_tokens[] = 'but';
}
$cta_class = trim(implode(' ', array_unique($cta_tokens)));
if (count($actions) === 1) {
$a = $actions[0];
- // Prefer configured label; icon already embedded after normalize.
- return ''
+ return ''
. $label_html
. '';
}
- // data-aksrt-newadd: JS portals the menu to body so chip width cannot track menu.
- $html = '
';
- $html .= '';
- $html .= '';
+ // zibll: span.newadd-btns.hover-show > label + .hover-show-con.dropdown-menu.drop-newadd
+ // data-aksrt-newadd: JS may portal menu to body so chip width cannot track menu.
+ $html = '
';
+ $html .= '' . $label_html
+ . ' ';
+ $html .= '';
@@ -202,22 +199,26 @@ if (!function_exists('aksrt_render_header_menu_buttons')) :
* Publish + optional VIP open button for header CTA.
*/
function aksrt_render_header_menu_buttons(): string {
- $cfg = aksrt_get_header_newposts_config();
+ $cfg = aksrt_get_header_newposts_config();
$html = '';
if (!empty($cfg['enabled'])) {
- $class = 'btn-primary navbar-newposts';
- if (!empty($cfg['class'])) {
- $class .= ' ' . $cfg['class'];
+ // zibll default: but nowave jb-blue (+ radius).
+ $class = 'but nowave navbar-newposts';
+ $skin = trim((string) ($cfg['class'] ?? ''));
+ if ($skin === '') {
+ $skin = 'jb-blue';
}
+ $class .= ' ' . $skin;
$radius = (int) ($cfg['radius'] ?? 20);
$pill = !empty($cfg['radius_pill']) || $radius >= 16;
if ($pill && !str_contains($class, 'radius')) {
$class .= ' radius';
}
- // Radius applied via CSS variable (zibll pill ≈ 20px when switch on).
- $radius_attr = $radius > 0 ? ' data-radius="' . esc_attr((string) $radius) . '" style="--navbar-cta-radius:' . $radius . 'px"' : '';
- $label = (string) ($cfg['text'] ?? __('发布', 'aksrt'));
- $btn = aksrt_render_new_add_btns($cfg['types'] ?? ['post'], $class, $label);
+ $radius_attr = $radius > 0
+ ? ' data-radius="' . esc_attr((string) $radius) . '" style="--navbar-cta-radius:' . $radius . 'px"'
+ : '';
+ $label = (string) ($cfg['text'] ?? __('发布', 'aksrt'));
+ $btn = aksrt_render_new_add_btns($cfg['types'] ?? ['post'], $class, $label);
if ($btn !== '') {
$html .= '
' . $btn . '
';
}
@@ -227,7 +228,7 @@ if (!function_exists('aksrt_render_header_menu_buttons')) :
$user_id = get_current_user_id();
$show_vip = false;
if (class_exists('\AKSRT\Settings\Settings')) {
- $vs = \AKSRT\Settings\Settings::get_payment_vip_settings();
+ $vs = \AKSRT\Settings\Settings::get_payment_vip_settings();
$show_vip = !empty($vs['enabled']) && !empty(\AKSRT\Settings\Settings::get_header_nav_settings()['nav_pay_vip']);
}
if ($show_vip) {
@@ -236,7 +237,7 @@ if (!function_exists('aksrt_render_header_menu_buttons')) :
$is_vip = (int) aksrt_get_user_vip_level($user_id) > 0;
}
if (!$is_vip) {
- $cls = $user_id > 0 ? 'pay-vip' : 'signin-loader';
+ $cls = $user_id > 0 ? 'pay-vip' : 'signin-loader';
$hover = '';
if (class_exists('\AKSRT\Content\VipService')) {
$svc = \AKSRT\Content\VipService::get_instance();
@@ -247,7 +248,7 @@ if (!function_exists('aksrt_render_header_menu_buttons')) :
}
}
}
- $html .= '
';
+ $html .= '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- wp_die();
- }
-endif;
-
-if (!function_exists('aksrt_get_menu_search_panel_html')) :
- /**
- * Build inner search panel HTML (no outer dropdown wrapper).
- */
- function aksrt_get_menu_search_panel_html(): string {
$show_keywords = true;
$show_history = true;
$show_posts = true;
@@ -50,100 +39,38 @@ if (!function_exists('aksrt_get_menu_search_panel_html')) :
: !empty(\AKSRT\Settings\Settings::get_value('search_posts', true));
}
- $out = '';
+ $search = aksrt_get_search_box([
+ 'show_form' => false,
+ 'show_keywords' => $show_keywords,
+ 'show_history' => $show_history,
+ 'show_posts' => $show_posts,
+ ]);
- // History (client may also fill via localStorage; server returns logged-in history).
- if ($show_history) {
- $hist = [];
- if (is_user_logged_in()) {
- $saved = get_user_meta(get_current_user_id(), 'aksrt_search_history', true);
- if (is_array($saved)) {
- foreach (array_slice($saved, 0, 10) as $item) {
- if (is_array($item) && !empty($item['keyword'])) {
- $hist[] = (string) $item['keyword'];
- } elseif (is_string($item) && $item !== '') {
- $hist[] = $item;
- }
- }
- }
- }
- $out .= '';
+ if ($search === '' || $search === null) {
+ wp_die('0');
}
- // Search type chips (when multi-type search enabled).
- $types = [];
- if (function_exists('aksrt_get_search_type_options')) {
- $types = aksrt_get_search_type_options();
- }
- if (is_array($types) && count($types) > 1) {
- $out .= '';
- }
+ // zibll contract: outer dropdown + inner .search-input.
+ echo ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ wp_die();
+ }
+endif;
- // Hot keywords.
- if ($show_keywords) {
- $hots = function_exists('aksrt_get_search_hot_keywords') ? aksrt_get_search_hot_keywords() : [];
- if ($hots) {
- $title = class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'search_popular_title')
- ? \AKSRT\Settings\Settings::search_popular_title()
- : __('热门搜索', 'aksrt');
- $out .= '';
- }
+// Panel builder retained as thin alias for any external callers.
+if (!function_exists('aksrt_get_menu_search_panel_html')) :
+ /**
+ * @return string Inner HTML (without dropdown wrapper).
+ */
+ function aksrt_get_menu_search_panel_html(): string {
+ if (!function_exists('aksrt_get_search_box')) {
+ return '';
}
-
- // Latest / popular posts (optional).
- if ($show_posts) {
- $posts = get_posts([
- 'post_type' => 'post',
- 'post_status' => 'publish',
- 'posts_per_page' => 6,
- 'ignore_sticky_posts' => true,
- 'no_found_rows' => true,
- 'update_post_meta_cache' => false,
- 'update_post_term_cache' => false,
- ]);
- if ($posts) {
- $out .= '';
- }
- }
-
- // Live suggestions mount point.
- $out .= '';
-
- return $out;
+ return (string) aksrt_get_search_box([
+ 'show_form' => false,
+ 'show_keywords' => true,
+ 'show_history' => true,
+ 'show_posts' => true,
+ ]);
}
endif;
diff --git a/inc/helpers/header/aksrt-header-mobile.php b/inc/helpers/header/aksrt-header-mobile.php
index a5d5629e..002f8cb7 100644
--- a/inc/helpers/header/aksrt-header-mobile.php
+++ b/inc/helpers/header/aksrt-header-mobile.php
@@ -77,7 +77,7 @@ if (!function_exists('aksrt_nav_mobile_content')) :
$top_btns .= '';
}
if ($m_layout !== 'center' && !empty($ctx['search_enabled'])) {
- $top_btns .= '
';
}
if (function_exists('aksrt_get_translate_language_btn')) {
diff --git a/inc/helpers/header/aksrt-header-navbar.php b/inc/helpers/header/aksrt-header-navbar.php
index a8844ed0..2819c48c 100644
--- a/inc/helpers/header/aksrt-header-navbar.php
+++ b/inc/helpers/header/aksrt-header-navbar.php
@@ -77,7 +77,7 @@ if (!function_exists('aksrt_navbar_header')) :
$button .= '';
if (($ctx['mobile_layout'] ?? 'center') === 'center' && !empty($ctx['search_enabled'])) {
- $button .= '
';
}
@@ -100,33 +100,27 @@ if (!function_exists('aksrt_get_menu_search')) :
return;
}
$placeholder = (string) ($ctx['search_placeholder'] ?? __('搜索内容', 'aksrt'));
- $state = is_array($ctx['search_state'] ?? null) ? $ctx['search_state'] : [];
- $type = (string) ($state['type'] ?? 'post');
- $cat_enabled = !empty($state['category_enabled']);
- $cat_id = (int) ($state['category_id'] ?? 0);
-
- // zibll remote-box: panel HTML loaded via ajax action menu_search.
- $remote_url = add_query_arg(
- [
- 'action' => 'aksrt_menu_search',
- ],
- admin_url('admin-ajax.php')
- );
+ // zibll: form + input + remote-box (action=menu_search).
echo '
';
}
endif;
@@ -224,6 +218,17 @@ if (!function_exists('aksrt_header_user_box_html')) :
$href .= '
';
$href .= '
';
$href .= '' . esc_html__('主题设置', 'aksrt') . '
';
+ // zibll: 模块布局 → widgets.
+ $href .= '
';
+ $href .= '
';
+ $href .= '' . esc_html__('模块布局', 'aksrt') . '
';
+ // zibll: 商城中心.
+ $shop_url = post_type_exists('aksrt_product')
+ ? admin_url('edit.php?post_type=aksrt_product')
+ : admin_url('admin.php?page=aksrt_pay');
+ $href .= '
';
+ $href .= '
';
+ $href .= '' . esc_html__('商城中心', 'aksrt') . '
';
$href .= '
';
$href .= '
';
$href .= '' . esc_html__('后台管理', 'aksrt') . '
';
@@ -346,7 +351,7 @@ if (!function_exists('aksrt_menu_button')) :
$search_but = '';
if ('2' !== $layout && !empty($ctx['search_enabled'])) {
- $search_but = '
';
}
diff --git a/inc/helpers/header/aksrt-header-render.php b/inc/helpers/header/aksrt-header-render.php
index f68d8b0e..2943d872 100644
--- a/inc/helpers/header/aksrt-header-render.php
+++ b/inc/helpers/header/aksrt-header-render.php
@@ -183,8 +183,16 @@ if (!function_exists('aksrt_header')) :
aksrt_nav_mobile_shell($ctx);
}
- if (function_exists('aksrt_render_signout_modal')) {
- aksrt_render_signout_modal();
- }
+ // Layout 1/3 search slide (zibll .navbar-search). Layout-2 uses inline menu_search.
+ if (!empty($ctx['search_enabled'])) {
+ $navbar_search = locate_template('templates/parts/navbar-search.php');
+ if ($navbar_search) {
+ include $navbar_search;
+ }
+ }
+
+ if (function_exists('aksrt_render_signout_modal')) {
+ aksrt_render_signout_modal();
+ }
}
endif;
diff --git a/inc/helpers/search/aksrt-search-box.php b/inc/helpers/search/aksrt-search-box.php
new file mode 100644
index 00000000..df44706e
--- /dev/null
+++ b/inc/helpers/search/aksrt-search-box.php
@@ -0,0 +1,319 @@
+
+ */
+ function aksrt_get_search_history_keywords(): array {
+ $hist = [];
+ if (is_user_logged_in()) {
+ $saved = get_user_meta(get_current_user_id(), 'aksrt_search_history', true);
+ if (is_array($saved)) {
+ foreach (array_slice($saved, 0, 10) as $item) {
+ if (is_array($item) && !empty($item['keyword'])) {
+ $hist[] = (string) $item['keyword'];
+ } elseif (is_string($item) && $item !== '') {
+ $hist[] = $item;
+ }
+ }
+ }
+ }
+ return array_values(array_unique(array_filter(array_map('trim', $hist))));
+ }
+endif;
+
+if (!function_exists('aksrt_get_search_keywords_but')) :
+ /**
+ * Render keyword chip links.
+ *
+ * @param list
$keywords
+ * @param string $type popular|history
+ */
+ function aksrt_get_search_keywords_but(array $keywords, string $type = 'popular', int $limit = 0): string {
+ if ($limit > 0) {
+ $keywords = array_slice($keywords, 0, $limit);
+ }
+ $out = '';
+ foreach ($keywords as $kw) {
+ $kw = trim((string) $kw);
+ if ($kw === '') {
+ continue;
+ }
+ $out .= ''
+ . esc_html($kw) . ' ';
+ }
+ return $out;
+ }
+endif;
+
+if (!function_exists('aksrt_get_search_box')) :
+ /**
+ * zibll-compatible search box.
+ *
+ * @param array $args
+ * @param bool $echo
+ * @return string|void
+ */
+ function aksrt_get_search_box(array $args = [], bool $echo = false) {
+ $defaults = [
+ 's' => '',
+ 'class' => '',
+ 'show_form' => true,
+ 'show_keywords' => true,
+ 'popular_limit' => 0,
+ 'show_history' => true,
+ 'keywords_title' => '',
+ 'placeholder' => '',
+ 'show_input_cat' => true,
+ 'show_posts' => false,
+ 'show_type' => false,
+ 'in_cat' => '',
+ 'in_type' => 'post',
+ 'in_user' => '',
+ 'more_cats' => [],
+ ];
+ $args = wp_parse_args($args, $defaults);
+
+ if ($args['keywords_title'] === '' && class_exists('\AKSRT\Settings\Settings')
+ && method_exists('\AKSRT\Settings\Settings', 'search_popular_title')) {
+ $args['keywords_title'] = (string) \AKSRT\Settings\Settings::search_popular_title();
+ }
+ if ($args['keywords_title'] === '') {
+ $args['keywords_title'] = __('热门搜索', 'aksrt');
+ }
+ if ($args['placeholder'] === '' && function_exists('aksrt_get_search_placeholder')) {
+ $args['placeholder'] = aksrt_get_search_placeholder();
+ }
+ if ($args['placeholder'] === '') {
+ $args['placeholder'] = __('开启精彩搜索', 'aksrt');
+ }
+ $args['placeholder'] = esc_attr((string) $args['placeholder']);
+
+ // Settings gates when caller left defaults on.
+ if (class_exists('\AKSRT\Settings\Settings')) {
+ if (!array_key_exists('show_keywords_forced', $args)
+ && method_exists('\AKSRT\Settings\Settings', 'hot_search_enabled')
+ && !\AKSRT\Settings\Settings::hot_search_enabled()) {
+ // Keep explicit false from caller; only gate when show_keywords true by default path.
+ }
+ }
+
+ $form_html = '';
+ if (!empty($args['show_form'])) {
+ $state = function_exists('aksrt_get_search_form_state')
+ ? aksrt_get_search_form_state()
+ : [
+ 'type' => 'post',
+ 'types' => ['post' => __('文章', 'aksrt')],
+ 'category_id' => 0,
+ 'category_enabled' => false,
+ 'categories' => [],
+ ];
+
+ $type_html = '';
+ $types = is_array($state['types'] ?? null) ? $state['types'] : [];
+ $in_type = (string) ($args['in_type'] ?: ($state['type'] ?? 'post'));
+ if (!empty($args['show_type']) && count($types) > 1) {
+ $type_items = '';
+ foreach ($types as $k => $v) {
+ $cls = $in_type === (string) $k ? ' active' : '';
+ $type_items .= '' . esc_html((string) $v) . '';
+ }
+ $type_html = '';
+ $type_html .= '
' . $type_items . '
';
+ }
+
+ $cat_html = '';
+ if (!empty($args['show_input_cat']) && !empty($state['category_enabled'])
+ && !empty($state['categories']) && is_array($state['categories'])) {
+ $in_cat_id = (int) ($args['in_cat'] ?: ($state['category_id'] ?? 0));
+ $in_cat_name = '';
+ $all_cat = ''
+ . esc_html__('全部', 'aksrt') . '';
+ foreach ($state['categories'] as $cat) {
+ if (!is_array($cat)) {
+ continue;
+ }
+ $cid = (int) ($cat['id'] ?? 0);
+ $cname = (string) ($cat['name'] ?? '');
+ if ($cid <= 0 || $cname === '') {
+ continue;
+ }
+ if ($cid === $in_cat_id) {
+ $in_cat_name = $cname;
+ }
+ $all_cat .= ''
+ . esc_html(mb_strlen($cname) > 8 ? mb_substr($cname, 0, 8) . '…' : $cname)
+ . '';
+ }
+ $all_cat = '';
+ $label = $in_cat_name !== ''
+ ? (mb_strlen($in_cat_name) > 6 ? mb_substr($in_cat_name, 0, 6) . '…' : $in_cat_name)
+ : __('请选择', 'aksrt');
+ $input_cat = ''
+ . esc_html($label) . '';
+ $input_cat .= $all_cat;
+ $cat_html = '';
+ }
+
+ $s = esc_attr(wp_strip_all_tags((string) $args['s']));
+ if ($s === '' && is_search()) {
+ $s = esc_attr(get_search_query());
+ }
+ $input_html = '';
+ $input_html .= '
';
+ $input_html .= '
';
+ $input_html .= '
' . $args['placeholder'] . '
';
+ $input_html .= '
';
+ $input_html .= '
';
+
+ $input_html .= '';
+ if ($cat_html !== '' || (string) $args['in_cat'] !== '') {
+ $input_html .= '';
+ }
+ if ((string) $args['in_user'] !== '') {
+ $input_html .= '';
+ }
+
+ if ($cat_html === '') {
+ $cat_html = $type_html;
+ $type_html = '';
+ }
+
+ $form_html = '';
+ }
+
+ // Popular keywords.
+ $keywords_html = '';
+ if (!empty($args['show_keywords'])) {
+ $show = true;
+ if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'hot_search_enabled')) {
+ $show = (bool) \AKSRT\Settings\Settings::hot_search_enabled();
+ }
+ if ($show) {
+ $keywords = function_exists('aksrt_get_search_hot_keywords') ? aksrt_get_search_hot_keywords() : [];
+ $limit = (int) $args['popular_limit'];
+ $keyword_link = aksrt_get_search_keywords_but(is_array($keywords) ? $keywords : [], 'popular', $limit);
+ if ($keyword_link !== '') {
+ $keywords_html = ''
+ . '
' . esc_html((string) $args['keywords_title']) . '
'
+ . '
' . $keyword_link . '
'
+ . '
';
+ }
+ }
+ }
+
+ // History.
+ $history_html = '';
+ if (!empty($args['show_history'])) {
+ $show_h = true;
+ if (function_exists('aksrt_search_history_enabled')) {
+ $show_h = aksrt_search_history_enabled();
+ } elseif (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'search_history_enabled')) {
+ $show_h = (bool) \AKSRT\Settings\Settings::search_history_enabled();
+ }
+ if ($show_h) {
+ $hist = aksrt_get_search_history_keywords();
+ // Empty shell still rendered so client localStorage can fill it.
+ $keyword_link = aksrt_get_search_keywords_but($hist, 'history');
+ $history_html = '';
+ $history_html .= '
' . esc_html__('历史搜索', 'aksrt') . '';
+ $history_html .= '
';
+ $history_html .= '
';
+ if ($keyword_link !== '') {
+ $history_html .= $keyword_link;
+ } else {
+ $history_html .= '' . esc_html__('暂无历史', 'aksrt') . '';
+ }
+ $history_html .= '
';
+ }
+ }
+
+ // Hot posts (thumb swiper).
+ $posts_html = '';
+ if (!empty($args['show_posts'])) {
+ $show_p = true;
+ if (class_exists('\AKSRT\Settings\Settings') && method_exists('\AKSRT\Settings\Settings', 'search_posts_enabled')) {
+ $show_p = (bool) \AKSRT\Settings\Settings::search_posts_enabled();
+ }
+ if ($show_p && function_exists('aksrt_get_search_posts')) {
+ $slides = aksrt_get_search_posts(6, 'views', true);
+ if ($slides !== '') {
+ $posts_html = '';
+ $posts_html .= '
' . esc_html__('热门文章', 'aksrt') . '
';
+ $posts_html .= '
';
+ }
+ }
+ }
+
+ // Live suggestions mount (AKSRT extension; does not break zibll classes).
+ $suggest = '';
+
+ $class = $args['class'] ? ' ' . sanitize_html_class((string) $args['class']) : '';
+ $html = ''
+ . $form_html . $keywords_html . $history_html . $posts_html . $suggest
+ . '
';
+
+ if ($echo) {
+ echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ return;
+ }
+ return $html;
+ }
+endif;
+
+if (!function_exists('aksrt_get_remote_box')) :
+ /**
+ * Lazy remote HTML mount (zibll zib_get_remote_box).
+ *
+ * @param array $args
+ */
+ function aksrt_get_remote_box(array $args = []): string {
+ $defaults = [
+ 'type' => 'ias',
+ 'class' => '',
+ 'loader' => '',
+ 'url' => admin_url('admin-ajax.php'),
+ 'query' => false,
+ ];
+ $args = wp_parse_args($args, $defaults);
+ $url = (string) $args['url'];
+ if (!empty($args['query']) && is_array($args['query'])) {
+ $url = add_query_arg($args['query'], $url);
+ }
+ $attr = ' remote-box="' . esc_url($url) . '"';
+ $class = (string) $args['class'];
+ if (($args['type'] ?? '') === 'ias') {
+ $attr .= ' lazyload-action="ias"';
+ $class .= ' lazyload';
+ }
+ if (($args['type'] ?? '') === 'load') {
+ $attr .= ' load-click';
+ }
+ $class_attr = $class !== '' ? ' class="' . esc_attr(trim($class)) . '"' : '';
+ return '' . $args['loader'] . '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
+endif;
diff --git a/inc/helpers/search/aksrt-search-posts.php b/inc/helpers/search/aksrt-search-posts.php
new file mode 100644
index 00000000..1b948eb3
--- /dev/null
+++ b/inc/helpers/search/aksrt-search-posts.php
@@ -0,0 +1,100 @@
+ $count,
+ 'ignore_sticky_posts' => true,
+ 'post_status' => 'publish',
+ 'post_type' => 'post',
+ 'no_found_rows' => true,
+ 'update_post_meta_cache' => true,
+ 'update_post_term_cache' => false,
+ ];
+
+ if ($orderby === 'views') {
+ $args['orderby'] = 'meta_value_num';
+ $args['meta_key'] = 'aksrt_post_views';
+ $args['order'] = 'DESC';
+ } else {
+ $allowed = ['date', 'modified', 'comment_count', 'rand', 'title'];
+ $args['orderby'] = in_array($orderby, $allowed, true) ? $orderby : 'date';
+ $args['order'] = 'DESC';
+ }
+
+ $query = new WP_Query($args);
+ // Fallback when no posts have views meta yet.
+ if (!$query->have_posts() && $orderby === 'views') {
+ wp_reset_postdata();
+ $args['orderby'] = 'date';
+ unset($args['meta_key']);
+ $query = new WP_Query($args);
+ }
+
+ $lists = '';
+ while ($query->have_posts()) {
+ $query->the_post();
+ $post_id = (int) get_the_ID();
+ $title = get_the_title($post_id);
+ if ($title === '') {
+ continue;
+ }
+
+ if ($show_img && function_exists('aksrt_graphic_card')) {
+ $thumb = get_the_post_thumbnail_url($post_id, 'medium');
+ if (!$thumb) {
+ $thumb = get_template_directory_uri() . '/assets/img/placeholder.svg';
+ }
+ $views = function_exists('aksrt_get_post_views') ? (int) aksrt_get_post_views($post_id) : 0;
+ $views_txt = function_exists('aksrt_format_number')
+ ? aksrt_format_number($views)
+ : (string) $views;
+ $time_ago = human_time_diff(get_the_time('U', $post_id), current_time('timestamp'));
+ $info = '- ' . esc_html(sprintf(/* translators: %s: relative time */ __('%s前', 'aksrt'), $time_ago))
+ . '
- '
+ . esc_html((string) $views_txt) . '
';
+
+ $lists .= '';
+ $lists .= aksrt_graphic_card([
+ 'type' => 'style-3',
+ 'class' => '',
+ 'img' => $thumb,
+ 'alt' => $title,
+ 'link' => [
+ 'url' => get_permalink($post_id),
+ 'target' => '',
+ ],
+ 'text1' => $title,
+ 'text2' => mb_strlen($title) > 29 ? mb_substr($title, 0, 29) . '…' : $title,
+ 'text3' => $info,
+ 'lazy' => true,
+ 'height_scale' => 70,
+ ], false);
+ $lists .= '
';
+ } else {
+ $lists .= ''
+ . esc_html($title) . '';
+ }
+ }
+ wp_reset_postdata();
+
+ return $lists;
+ }
+endif;
diff --git a/src/AKSRT/Admin/Schema/SchemaPageDisplay.php b/src/AKSRT/Admin/Schema/SchemaPageDisplay.php
index f7ad833f..3537de99 100644
--- a/src/AKSRT/Admin/Schema/SchemaPageDisplay.php
+++ b/src/AKSRT/Admin/Schema/SchemaPageDisplay.php
@@ -42,7 +42,6 @@ class SchemaPageDisplay {
['key' => 'components', 'title' => '导航组件', 'fields' => [
['key' => 'header_search_enabled', 'label' => '搜索入口', 'type' => 'switch', 'default' => true],
['key' => 'header_user_menu', 'label' => '用户菜单', 'type' => 'switch', 'default' => true],
- ['key' => 'header_publish_btn', 'label' => '发布按钮', 'type' => 'switch', 'default' => true],
['key' => 'header_mobile_drawer', 'label' => '移动端抽屉导航', 'type' => 'switch', 'default' => true],
['key' => 'header_nav_pay_vip', 'label' => '导航栏开通会员按钮', 'type' => 'switch', 'default' => true],
['key' => 'header_nav_user_pay_vip', 'label' => '用户框开通会员按钮', 'type' => 'switch', 'default' => true],
@@ -51,7 +50,14 @@ class SchemaPageDisplay {
['key' => 'header_newposts_enabled', 'label' => '启用发布按钮', 'type' => 'switch', 'default' => true],
['key' => 'header_newposts_text', 'label' => '按钮文案', 'type' => 'input', 'default' => ' 发布'],
['key' => 'header_newposts_radius', 'label' => '按钮圆角(胶囊)', 'type' => 'switch', 'default' => true],
- ['key' => 'header_newposts_class', 'label' => '样式类', 'type' => 'input', 'default' => ''],
+ ['key' => 'header_newposts_class', 'label' => '按钮颜色', 'type' => 'select', 'default' => 'jb-blue', 'options' => [
+ ['label' => '蓝色', 'value' => 'jb-blue'],
+ ['label' => '红色', 'value' => 'jb-red'],
+ ['label' => '绿色', 'value' => 'jb-green'],
+ ['label' => '黄色', 'value' => 'jb-yellow'],
+ ['label' => '紫色', 'value' => 'jb-purple'],
+ ['label' => '粉色', 'value' => 'jb-pink'],
+ ]],
['key' => 'header_newposts_types', 'label' => '发布类型', 'type' => 'select', 'default' => ['post'], 'options' => [
['label' => '文章', 'value' => 'post'],
['label' => '帖子', 'value' => 'topic'],
diff --git a/src/AKSRT/Settings/FacadeBulk.php b/src/AKSRT/Settings/FacadeBulk.php
index 5ea51682..a5c76f90 100644
--- a/src/AKSRT/Settings/FacadeBulk.php
+++ b/src/AKSRT/Settings/FacadeBulk.php
@@ -40,7 +40,8 @@ trait FacadeBulk {
'fixed' => !isset($settings['header_fixed']) || !empty($settings['header_fixed']),
'search_enabled' => !isset($settings['header_search_enabled']) || !empty($settings['header_search_enabled']),
'user_menu' => !isset($settings['header_user_menu']) || !empty($settings['header_user_menu']),
- 'publish_btn' => !isset($settings['header_publish_btn']) || !empty($settings['header_publish_btn']),
+ // Unified with header_newposts_enabled (zibll nav_newposts).
+ 'publish_btn' => !isset($settings['header_newposts_enabled']) || !empty($settings['header_newposts_enabled']),
'mobile_drawer' => !isset($settings['header_mobile_drawer']) || !empty($settings['header_mobile_drawer']),
'nav_style' => $nav_style,
'max_width' => $max_width,
@@ -85,7 +86,7 @@ trait FacadeBulk {
'text' => trim((string) ($s['header_newposts_text'] ?? ' 发布')),
'radius' => $radius_px,
'radius_pill' => $radius_pill,
- 'class' => sanitize_html_class((string) ($s['header_newposts_class'] ?? '')),
+ 'class' => sanitize_html_class((string) (($s['header_newposts_class'] ?? '') !== '' ? $s['header_newposts_class'] : 'jb-blue')),
'types' => $types,
];
}
diff --git a/templates/parts/navbar-search.php b/templates/parts/navbar-search.php
new file mode 100644
index 00000000..ac917b13
--- /dev/null
+++ b/templates/parts/navbar-search.php
@@ -0,0 +1,57 @@
+
+
+
+
+ true,
+ 'show_keywords' => $show_keywords,
+ 'show_history' => $show_history,
+ 'show_posts' => $show_posts,
+ 'show_type' => true,
+ 'show_input_cat'=> true,
+ 's' => is_search() ? get_search_query() : '',
+ ]);
+ ?>
+
+
diff --git a/templates/parts/search-panel.php b/templates/parts/search-panel.php
index a7dab800..b27f306a 100644
--- a/templates/parts/search-panel.php
+++ b/templates/parts/search-panel.php
@@ -1,6 +1,9 @@
.search-input`
+- [ ] Panel contains `.search-keywords` / `.history-search` / `.relates.relates-thumb` (when enabled)
+- [ ] Guest history from localStorage; trash clears
+- [ ] Layout 1/3 `.navbar-search` uses full `aksrt_get_search_box(show_form=true)`
+- [ ] Footer tabbar `#tabbar-search-overlay` still independent
## Mobile
- [ ] center/left header; drawer left/right/top
-- [ ] `body.mobile-navbar-show` open/close + fixed-body + swipe
+- [ ] `body.mobile-navbar-show` + fixed-body + swipe
- [ ] mobile menus depth 2 + user box + widgets
- [ ] posts-nav-box TOC on single posts
## Admin
-- [ ] SPA image_select previews for layouts
-- [ ] newposts types multi + VIP switches persist
-- [ ] Appearance → Menus: icon/badge/senior visual editor saves meta
+- [ ] SPA image_select layouts
+- [ ] Single publish switch `header_newposts_enabled` (+ skin select jb-*)
+- [ ] Appearance → Menus senior editor
- [ ] `cd assets/admin && npm run build` after schema changes
+- [ ] `php tools/audit-admin-contracts.php` green
-## Page/section navs
-- [ ] section-navs builds `.posts-nav-lists` on `[data-nav]` pages
-- [ ] page-navs enqueued on posts-navs template
+## Deploy
+- Admin `build/` may be gitignored — build on deploy host.
+- Push branch to remote **`target`**, open PR via Gitea URL.
-## Deploy notes
-- Admin `build/` is gitignored — run build on deploy host.
-- PR: open from branch `worktree-nav-zibll-1to1` against main on Gitea.
-
-
-## Deep align (post 2a11a88)
-- [ ] Layout-2 focus loads remote panel (history + hot + posts)
-- [ ] Typing shows live suggestions (post/topic/product)
-- [ ] Publish radius switch = pill ends
-- [ ] Menus senior editor: add card + media library pick image
-- [ ] Contract report: tools/nav-zibll-contract-report.txt PASS
+## Contract report
+- `tools/nav-zibll-contract-report.txt` should list search DOM PASS after smoke.
diff --git a/tools/nav-zibll-contract-report.txt b/tools/nav-zibll-contract-report.txt
index 46ce6daf..dd35a495 100644
--- a/tools/nav-zibll-contract-report.txt
+++ b/tools/nav-zibll-contract-report.txt
@@ -1 +1,6 @@
-PASS full-push deep nav contracts
+PASS deep nav contracts (search-box DOM + remote menu_search + navbar-search slide + unified newposts)
+- aksrt_get_search_box / aksrt_get_search_posts present
+- menu_search AJAX wraps .dropdown-menu.hover-show-con > .search-input
+- layout1/3 templates/parts/navbar-search.php
+- header_newposts_enabled single switch; publish_btn mirrors
+- user box admin row: 主题设置/模块布局/商城中心/后台