Refine frontend navigation, loading UI, and site copy
Some checks failed
docker-images / resolve-build-targets (push) Successful in 6s
ui-regression / playwright-regression (push) Failing after 13m3s
docker-images / build-and-push (admin) (push) Successful in 4s
docker-images / submit-indexnow (push) Has been cancelled
docker-images / build-and-push (frontend) (push) Has been cancelled
docker-images / build-and-push (backend) (push) Has been cancelled
Some checks failed
docker-images / resolve-build-targets (push) Successful in 6s
ui-regression / playwright-regression (push) Failing after 13m3s
docker-images / build-and-push (admin) (push) Successful in 4s
docker-images / submit-indexnow (push) Has been cancelled
docker-images / build-and-push (frontend) (push) Has been cancelled
docker-images / build-and-push (backend) (push) Has been cancelled
This commit is contained in:
@@ -35,6 +35,14 @@ const navItems = [
|
||||
{ icon: 'fa-user-secret', text: t('nav.about'), href: '/about' },
|
||||
...(aiEnabled ? [{ icon: 'fa-robot', text: t('nav.ask'), href: '/ask' }] : []),
|
||||
];
|
||||
const mobileDockItems = [
|
||||
{ icon: 'fa-house', text: t('common.home'), href: '/' },
|
||||
{ icon: 'fa-file-code', text: t('nav.articles'), href: '/articles' },
|
||||
...(aiEnabled
|
||||
? [{ icon: 'fa-robot', text: t('nav.ask'), href: '/ask' }]
|
||||
: [{ icon: 'fa-folder', text: t('nav.categories'), href: '/categories' }]),
|
||||
{ icon: 'fa-user-secret', text: t('nav.about'), href: '/about' },
|
||||
];
|
||||
const localeLinks = SUPPORTED_LOCALES.map((item) => ({
|
||||
locale: item,
|
||||
href: buildLocaleUrl(item),
|
||||
@@ -149,7 +157,7 @@ const currentNavLabel =
|
||||
{aiEnabled && (
|
||||
<a
|
||||
href="/ask"
|
||||
class="inline-flex shrink-0 items-center gap-2 rounded-xl border border-[var(--primary)]/18 bg-[var(--primary)]/8 px-2.5 py-1.5 text-sm font-medium text-[var(--primary)] transition hover:border-[var(--primary)]/32 hover:text-[var(--title-color)]"
|
||||
class="inline-flex shrink-0 items-center gap-2 rounded-xl border border-[color:color-mix(in_oklab,var(--primary)_28%,var(--border-color))] bg-[linear-gradient(180deg,color-mix(in_oklab,var(--primary)_14%,var(--terminal-bg)),color-mix(in_oklab,var(--primary)_8%,var(--terminal-bg)))] px-2.5 py-1.5 text-sm font-medium text-[var(--primary)] shadow-[0_10px_24px_rgba(var(--primary-rgb),0.10)] transition hover:border-[color:color-mix(in_oklab,var(--primary)_40%,var(--border-color))] hover:text-[var(--title-color)]"
|
||||
>
|
||||
<i class="fas fa-robot text-sm"></i>
|
||||
<span class="hidden xl:inline">{t('nav.ask')}</span>
|
||||
@@ -357,6 +365,40 @@ const currentNavLabel =
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="fixed inset-x-0 bottom-0 z-40 px-3 pb-[calc(0.8rem+env(safe-area-inset-bottom))] lg:hidden">
|
||||
<div class="mx-auto max-w-md">
|
||||
<div class="grid grid-cols-5 gap-1 rounded-[24px] border border-[color:color-mix(in_oklab,var(--primary)_12%,var(--border-color))] bg-[linear-gradient(180deg,color-mix(in_oklab,var(--terminal-bg)_96%,transparent),color-mix(in_oklab,var(--header-bg)_92%,transparent))] p-1.5 shadow-[0_18px_36px_rgba(15,23,42,0.22)] backdrop-blur-xl">
|
||||
{mobileDockItems.map((item) => {
|
||||
const isActive = currentPath === item.href || (item.href !== '/' && currentPath.startsWith(item.href));
|
||||
return (
|
||||
<a
|
||||
href={item.href}
|
||||
class:list={[
|
||||
'flex min-w-0 flex-col items-center gap-1 rounded-[18px] px-2 py-2 text-[11px] font-medium transition',
|
||||
isActive
|
||||
? 'border border-[color:color-mix(in_oklab,var(--primary)_30%,var(--border-color))] bg-[linear-gradient(180deg,color-mix(in_oklab,var(--primary)_14%,var(--terminal-bg)),color-mix(in_oklab,var(--primary)_8%,var(--terminal-bg)))] text-[var(--primary)]'
|
||||
: 'border border-transparent text-[var(--text-secondary)] hover:bg-[color-mix(in_oklab,var(--primary)_6%,var(--terminal-bg))] hover:text-[var(--title-color)]',
|
||||
]}
|
||||
aria-current={isActive ? 'page' : undefined}
|
||||
>
|
||||
<i class={`fas ${item.icon} text-[13px]`}></i>
|
||||
<span class="truncate">{item.text}</span>
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
<button
|
||||
type="button"
|
||||
class="flex min-w-0 flex-col items-center gap-1 rounded-[18px] border border-transparent px-2 py-2 text-[11px] font-medium text-[var(--text-secondary)] transition hover:bg-[color-mix(in_oklab,var(--primary)_6%,var(--terminal-bg))] hover:text-[var(--title-color)]"
|
||||
data-mobile-dock-menu
|
||||
aria-label={t('header.toggleMenu')}
|
||||
>
|
||||
<i class="fas fa-bars text-[13px]"></i>
|
||||
<span class="truncate">{t('header.navigation')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script is:inline define:vars={{ apiBase: publicApiBaseUrl, musicPlaylistPayload }}>
|
||||
const t = window.__termiTranslate;
|
||||
|
||||
@@ -365,12 +407,16 @@ const currentNavLabel =
|
||||
const mobileMenu = document.getElementById('mobile-menu');
|
||||
const mobileSearchInput = document.getElementById('mobile-search-input');
|
||||
const mobileSearchBtn = document.getElementById('mobile-search-btn');
|
||||
const mobileDockMenuBtn = document.querySelector('[data-mobile-dock-menu]');
|
||||
|
||||
mobileMenuBtn?.addEventListener('click', () => {
|
||||
function toggleMobileMenu() {
|
||||
const nextExpanded = mobileMenu?.classList.contains('hidden');
|
||||
mobileMenu?.classList.toggle('hidden');
|
||||
mobileMenuBtn.setAttribute('aria-expanded', String(nextExpanded));
|
||||
});
|
||||
mobileMenuBtn?.setAttribute('aria-expanded', String(nextExpanded));
|
||||
}
|
||||
|
||||
mobileMenuBtn?.addEventListener('click', toggleMobileMenu);
|
||||
mobileDockMenuBtn?.addEventListener('click', toggleMobileMenu);
|
||||
|
||||
document.querySelectorAll('#mobile-menu a[href]').forEach((link) => {
|
||||
link.addEventListener('click', () => {
|
||||
@@ -715,7 +761,7 @@ const currentNavLabel =
|
||||
if (state === 'loading') {
|
||||
searchResults.innerHTML = `
|
||||
<div class="px-4 py-4 text-sm text-[var(--text-secondary)]">
|
||||
${escapeHtml(t('header.searching', { query }))}
|
||||
${escapeHtml(t('header.searching', { query }))}
|
||||
</div>
|
||||
`;
|
||||
searchResults.classList.remove('hidden');
|
||||
|
||||
Reference in New Issue
Block a user