From ba6fdbf01d16551286364fc788bc92eedffc703f Mon Sep 17 00:00:00 2001 From: dark Date: Tue, 22 Sep 2026 22:51:05 +0800 Subject: [PATCH 1/5] feat: upgrade OINK and streamline theme updates - adopt OINK 1.1 sidebar and search extension APIs - reconcile print overrides and inherit upstream translations - add reviewed upgrade tooling and compatibility checks - refresh maintenance docs and regression coverage --- .github/workflows/hugo.yml | 6 +- AGENTS.md | 148 +++-------- NOTICE | 2 +- README.md | 12 +- assets/js/hugegraph-shell.js | 282 ++++----------------- assets/js/kapa-adapter.js | 175 +++++-------- assets/scss/_styles_project.scss | 60 ----- contribution.md | 19 +- go.mod | 3 +- go.sum | 5 +- i18n/en.yaml | 2 - i18n/zh-CN.yaml | 238 +---------------- layouts/_partials/hooks/body-end.html | 2 - layouts/_partials/navbar-item.html | 2 +- layouts/_partials/navbar.html | 7 - layouts/_partials/print/page-content.html | 26 +- layouts/_partials/shell/config.html | 2 +- layouts/_partials/shell/sidebar-panel.html | 2 +- layouts/_partials/shell/toc.html | 2 +- scripts/oink-overrides.json | 97 +++++++ scripts/oink-upgrade.md | 106 ++++++++ scripts/oink_module.py | 63 +++++ scripts/test_download_data.py | 6 +- scripts/test_oink_upgrade.py | 132 ++++++++++ scripts/update-oink.sh | 9 + scripts/update_oink.py | 141 +++++++++++ scripts/versioning.py | 28 +- tests/e2e/ai.spec.js | 69 ++++- tests/e2e/package.json | 2 +- tests/e2e/platform.spec.js | 22 +- tests/e2e/search-ranking.spec.js | 10 +- tests/e2e/theme-upgrade.spec.js | 38 +++ tests/e2e/visual.spec.js | 4 +- tests/ui-ai/kapa-adapter.test.cjs | 180 ++++++------- tests/ui-ai/sidebar-persistence.test.cjs | 95 +++++++ tests/ui-ai/ui-contract.test.cjs | 4 +- 36 files changed, 1050 insertions(+), 951 deletions(-) create mode 100644 scripts/oink-overrides.json create mode 100644 scripts/oink-upgrade.md create mode 100644 scripts/oink_module.py create mode 100644 scripts/test_oink_upgrade.py create mode 100755 scripts/update-oink.sh create mode 100644 scripts/update_oink.py create mode 100644 tests/e2e/theme-upgrade.spec.js create mode 100644 tests/ui-ai/sidebar-persistence.test.cjs diff --git a/.github/workflows/hugo.yml b/.github/workflows/hugo.yml index f9c751bcc6..bd0946e576 100644 --- a/.github/workflows/hugo.yml +++ b/.github/workflows/hugo.yml @@ -231,11 +231,7 @@ jobs: restore-keys: ${{ runner.os }}-hugo-${{ env.HUGO_VERSION }}-${{ matrix.version.id }}- - name: Verify pinned OINK module run: | - go mod verify - test "$(go list -m -f '{{ .Path }}')" = "github.com/apache/hugegraph-doc" - test "$(go list -m all | wc -l)" -eq 2 - test "$(go list -m -f '{{ .Path }}@{{ .Version }}' github.com/pgsty/oink)" = "github.com/pgsty/oink@v1.0.0" - test -z "$(go list -m -f '{{ with .Replace }}{{ .Path }}@{{ .Version }}{{ end }}' github.com/pgsty/oink)" + python3 scripts/update_oink.py --check-baseline - name: Build isolated version artifact env: OINK_PYTHON: python3 diff --git a/AGENTS.md b/AGENTS.md index c4a0545150..6e89817082 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,110 +1,38 @@ -# AGENTS.md - -This file provides guidance to AI coding assistants (Claude Code, Cursor, GitHub Copilot, etc.) when working with code in this repository. - -## Project Overview - -Apache HugeGraph documentation website built with Hugo static site generator and the Docsy theme. The site is bilingual (Chinese/English) and covers the complete HugeGraph graph database ecosystem. - -## Development Commands - -```bash -# Install dependencies -npm install - -# Start development server (auto-reload enabled) -hugo server - -# Build production site (output to ./public) -hugo --minify - -# Clean build -rm -rf public/ - -# Production build with garbage collection -HUGO_ENV="production" hugo --gc - -# Custom server configuration -hugo server -b http://127.0.0.1 -p 80 --bind=0.0.0.0 -``` - -## Prerequisites - -- **Hugo Extended** v0.95.0 recommended (v0.102.3 in CI) - must be the "extended" version for SASS/SCSS support -- **Node.js** v16+ and npm -- Download Hugo from: https://github.com/gohugoio/hugo/releases - -## Architecture - -``` -content/ -├── cn/ # Chinese documentation (default language) -│ ├── docs/ # Main documentation -│ ├── blog/ # Blog posts -│ ├── community/ -│ └── about/ -└── en/ # English documentation (parallel structure) - -themes/docsy/ # Docsy theme (submodule) -layouts/ # Custom template overrides -assets/ # Processed assets (SCSS, images) -static/ # Static files served directly -config.toml # Main site configuration -``` - -### Content Structure - -Documentation sections in `content/{cn,en}/docs/`: -- `quickstart/` - Getting started guides for HugeGraph components -- `config/` - Configuration documentation -- `clients/` - Client API documentation (Gremlin, RESTful) -- `guides/` - User guides and tutorials -- `performance/` - Benchmarks and optimization -- `language/` - Query language docs -- `contribution-guidelines/` - Contributing guides -- `changelog/` - Release notes -- `download/` - Download instructions - -## Key Configuration Files - -- `config.toml` - Site-wide settings, language config, menu structure, version (currently 0.13) -- `package.json` - Node dependencies for CSS processing (postcss, autoprefixer, mermaid) -- `.editorconfig` - UTF-8, LF line endings, spaces for indentation - -## Working with Content - -When editing documentation: -1. Maintain parallel structure between `content/cn/` and `content/en/` -2. Use Markdown with Hugo front matter (title, weight, description) -3. For bilingual changes, update both Chinese and English versions -4. Include mermaid diagrams where appropriate (mermaid.js is available) - -## Deployment - -- **CI/CD**: GitHub Actions (`.github/workflows/hugo.yml`) -- **Trigger**: Push to `master` branch or pull requests -- **Build**: `npm i && hugo --minify` with Node v16 and Hugo v0.102.3 extended -- **Deploy**: Publishes to `asf-site` branch (GitHub Pages) -- **PR Requirements**: Include screenshots showing before/after changes - -## HugeGraph Ecosystem Context - -This documentation covers: -- **HugeGraph-Server** - Core graph database with REST API -- **HugeGraph-Store** - Distributed storage engine -- **HugeGraph-PD** - Placement Driver for metadata -- **Toolchain** - Client, Loader, Hubble (web UI), Tools -- **HugeGraph-Computer** - Distributed OLAP graph processing -- **HugeGraph-AI** - GNN, LLM/RAG components - -## Troubleshooting - -**"TOCSS: failed to transform scss/main.scss"** -- Install Hugo Extended (not standard Hugo) - -**Theme/module not found** -- Run: `git submodule update --init --recursive` - -**CI build fails but works locally** -- Match Hugo version (v0.102.3) and Node.js (v16) -- Verify npm dependencies are installed +# HugeGraph documentation + +Bilingual Hugo site using OINK as a pinned Go module. English routes live under +`/docs/`, Chinese routes under `/cn/docs/`; historical releases share the current +site shell. + +## Task entry points + +- Preview or build: `scripts/hugo.sh server` / `scripts/hugo.sh build`. +- Content and contribution checks: [contribution.md](contribution.md). +- OINK upgrades, customization boundaries and recovery: + [scripts/oink-upgrade.md](scripts/oink-upgrade.md). +- Version assembly and publishing: `scripts/versioning.py` and + `.github/workflows/hugo.yml`; release selection comes from `versions.json`. +- Browser regression: `tests/e2e/package.json` and its Playwright configuration. + Node dependencies are for tests, not the Hugo site build. + +Read the entry relevant to the task; small content edits do not require the full +upgrade or deployment workflow. Use `go.mod` / `go.sum` and CI for pinned versions. + +## Project constraints + +- Keep English and Chinese documentation aligned when a change applies to both. +- Preserve public routes, historical-version navigation and language switching. +- Keep HugeGraph branding and behavior in site configuration, data, hooks and + public OINK APIs. Do not edit the module cache or vendor a theme fork. +- Record necessary upstream template overrides in the upgrade inventory; when + upstream changes them, reconcile the customization with the new implementation. +- AI resources load only after explicit user consent; disabled AI must make no + third-party AI requests. +- Validate behavior affected by the change. Theme/runtime changes require strict + builds and browser checks; visual/navigation changes also need before/after + screenshots. A rendered site alone does not prove interaction compatibility. +- Independent work may run in parallel. Review the final combined diff independently + for changes affecting runtime behavior or multiple components; local content + edits can use self-review. +- Local builds and disposable tests may run without repeated confirmation. Remote + writes and publishing follow the user's explicit task scope. diff --git a/NOTICE b/NOTICE index 91cb6e33a5..c7bce531b6 100644 --- a/NOTICE +++ b/NOTICE @@ -6,5 +6,5 @@ The Apache Software Foundation (http://www.apache.org/). The initial codebase was donated to the ASF by HugeGraph Authors, copyright 2017-2021. -This product includes OINK v1.0.0 (https://github.com/pgsty/oink), +This product includes OINK (https://github.com/pgsty/oink), Copyright 2026 PGSTY contributors, licensed under the Apache License 2.0. diff --git a/README.md b/README.md index 6b2b71db2c..1e618d088d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ scripts/hugo.sh server Open http://localhost:1313 to preview. -The module graph must resolve `github.com/pgsty/oink@v1.0.0`. For a production-equivalent check, run the strict build command shown below. +The module graph must resolve the OINK version pinned in `go.mod`; verify it with `python3 scripts/oink_module.py`. For a production-equivalent check, run the strict build command shown below. ## Repository Structure @@ -51,7 +51,7 @@ hugegraph-doc/ │ └── en/ # 🇺🇸 English documentation (mirrors cn/ structure) │ ├── data/ # 🧭 Landing-page and footer data -├── i18n/zh-CN.yaml # 🌐 OINK interface strings for the /cn/ locale +├── i18n/zh-CN.yaml # 🌐 HugeGraph labels for the /cn/ locale ├── assets/ # 🖼️ Project brand assets ├── layouts/ # 📐 Hugo template overrides ├── static/ # 📁 Static files @@ -79,6 +79,7 @@ hugegraph-doc/ ### Detailed Guide See [contribution.md](./contribution.md) for the pinned toolchain, strict build, OINK customization, and translation rules. +For theme updates, compatibility review and rollback, use the [OINK upgrade SOP](./scripts/oink-upgrade.md). ## Commands @@ -87,6 +88,7 @@ See [contribution.md](./contribution.md) for the pinned toolchain, strict build, | `scripts/hugo.sh server` | Start the manifest-aware dev server (hot reload) | | `scripts/hugo.sh build` | Strict, production-equivalent build to `./public/` | | `scripts/hugo.sh server -p 8080` | Start the dev server on a custom port | +| `scripts/update-oink.sh v1.1.0` | Update the theme and run compatibility checks (see the SOP) | --- @@ -116,7 +118,7 @@ scripts/hugo.sh server 打开 http://localhost:1313 预览网站。 -模块图必须解析为 `github.com/pgsty/oink@v1.0.0`。需要执行与生产一致的检查时,请运行下方的严格构建命令。 +模块图必须解析为 `go.mod` 固定的 OINK 版本,可运行 `python3 scripts/oink_module.py` 校验。需要执行与生产一致的检查时,请运行下方的严格构建命令。 ### 仓库结构 @@ -135,7 +137,7 @@ hugegraph-doc/ │ └── en/ # 🇺🇸 英文文档(与 cn/ 结构一致) │ ├── data/ # 🧭 首页与页尾数据 -├── i18n/zh-CN.yaml # 🌐 /cn/ 语言的 OINK 界面文案 +├── i18n/zh-CN.yaml # 🌐 /cn/ 语言的 HugeGraph 专属文案 ├── assets/ # 🖼️ 项目品牌资源 ├── layouts/ # 📐 Hugo 模板覆盖 ├── static/ # 📁 静态文件 @@ -163,6 +165,7 @@ hugegraph-doc/ #### 详细指南 查看 [contribution.md](./contribution.md) 了解固定工具链、严格构建、OINK 定制和翻译要求。 +主题升级、兼容适配和回退请按 [OINK 升级 SOP](./scripts/oink-upgrade.md) 操作。 ### 常用命令 @@ -171,6 +174,7 @@ hugegraph-doc/ | `scripts/hugo.sh server` | 启动读取版本清单的开发服务器(支持热重载) | | `scripts/hugo.sh build` | 严格构建与生产等价的站点到 `./public/` | | `scripts/hugo.sh server -p 8080` | 在指定端口启动开发服务器 | +| `scripts/update-oink.sh v1.1.0` | 更新主题并执行兼容检查(详见升级 SOP) | --- diff --git a/assets/js/hugegraph-shell.js b/assets/js/hugegraph-shell.js index 09240eb546..f01ba349b2 100644 --- a/assets/js/hugegraph-shell.js +++ b/assets/js/hugegraph-shell.js @@ -2,8 +2,7 @@ * HugeGraph additions around OINK's shell. * * This file deliberately does not replace OINK's command palette. It only - * persists authored tree disclosures, makes a collapsed/dismissed sidebar - * inert, and adds an explicit retry control to the existing search error. + * persists authored tree disclosures through OINK's public API and adds an explicit retry control to the existing search error. */ (function (global) { 'use strict'; @@ -32,245 +31,59 @@ } } - function setTreeExpanded(button, expanded, documentObject) { - var target = documentObject.getElementById( - button.getAttribute('aria-controls'), - ); - if (!target) return; - button.setAttribute('aria-expanded', expanded ? 'true' : 'false'); - target.classList.toggle('td-is-open', expanded); - var label = expanded - ? button.dataset.tdLabelCollapse - : button.dataset.tdLabelExpand; - if (label) button.setAttribute('aria-label', label); - } - function initTreePersistence(windowObject, documentObject, config) { - var buttons = Array.prototype.slice.call( - documentObject.querySelectorAll('[data-td-shell-tree-toggle][aria-controls]'), - ); - if (!buttons.length) return; - var storage = safeStorage(windowObject); - var key = - 'oink.sidebar.v2.' + - String(config.version || 'latest') + - '.' + - String(config.locale || 'en'); - var valid = new Set( - buttons.map(function (button) { + var sidebar = windowObject.OinkSidebar; + if (!sidebar) return; + return sidebar.ready.then(function () { + var buttons = Array.prototype.slice.call(documentObject.querySelectorAll( + '#td-shell-sidebar [data-td-shell-tree-toggle][aria-controls]', + )); + if (!buttons.length) return; + var storage = safeStorage(windowObject); + var key = 'oink.sidebar.v2.' + String(config.version || 'latest') + + '.' + String(config.locale || 'en'); + var valid = new Set(buttons.map(function (button) { return button.getAttribute('aria-controls'); - }), - ); - var saved = []; - var hasSavedState = false; - if (storage) { - try { - var stored = storage.getItem(key); - var parsed = JSON.parse(stored || '[]'); - if (Array.isArray(parsed)) { - hasSavedState = stored !== null; - saved = parsed.filter(function (id) { - return typeof id === 'string' && valid.has(id); - }); - } - } catch (_) { - saved = []; - } - } - var remembered = new Set(saved); - var docsRoot = /(?:^|\/)(?:cn\/)?docs\/?$/.test(windowObject.location.pathname); - - buttons.forEach(function (button) { - var item = button.closest('li'); - var activePath = item && item.classList.contains('td-active-path'); - var control = button.getAttribute('aria-controls') || ''; - var defaultOpen = - !hasSavedState && - docsRoot && - /_nav(?:start|components)-children$/.test(control); - setTreeExpanded( - button, - Boolean(activePath || remembered.has(control) || defaultOpen), - documentObject, - ); - button.addEventListener('click', function () { - global.setTimeout(function () { - if (!storage) return; - var expanded = buttons - .filter(function (candidate) { - var candidateItem = candidate.closest('li'); - return ( - candidate.getAttribute('aria-expanded') === 'true' && - !(candidateItem && - candidateItem.classList.contains('td-active-path')) - ); - }) - .map(function (candidate) { - return candidate.getAttribute('aria-controls'); - }); - try { - storage.setItem(key, JSON.stringify(expanded)); - } catch (_) { - /* Active-path expansion remains the storage-free fallback. */ + })); + var remembered = new Set(); + var hasSavedState = false; + if (storage) { + try { + var stored = storage.getItem(key); + var parsed = JSON.parse(stored || '[]'); + if (Array.isArray(parsed)) { + hasSavedState = stored !== null; + remembered = new Set(parsed.filter(function (id) { + return typeof id === 'string' && valid.has(id); + })); } - }, 0); - }); - }); - - // Seed the new persistence schema once so the docs-home defaults survive - // reloads; later clicks replace this set with the user's choices. - if (storage && !hasSavedState) { - try { - var initial = buttons - .filter(function (button) { - var item = button.closest('li'); - return ( - button.getAttribute('aria-expanded') === 'true' && - !(item && item.classList.contains('td-active-path')) - ); - }) - .map(function (button) { - return button.getAttribute('aria-controls'); - }); - storage.setItem(key, JSON.stringify(initial)); - } catch (_) { - /* Ignore storage becoming unavailable after the probe. */ + } catch (_) { /* Keep active-path and docs-home defaults. */ } } - } - } - - function initSidebarIsolation(windowObject, documentObject) { - var html = documentObject.documentElement; - var sidebar = documentObject.getElementById('td-shell-sidebar'); - if (!sidebar) return; - var restore = documentObject.querySelector('.hg-sidebar-restore'); - var desktop = windowObject.matchMedia('(min-width: 768px)'); - - var panel = sidebar.querySelector('.td-shell-sidebar__panel'); - // The native 16px panel edge cannot receive pointers while the sidebar is - // inert. Keep an equivalent pointer-only strip outside the inert subtree; - // the labelled navbar button remains the keyboard/touch equivalent. - var edge = documentObject.createElement('div'); - edge.className = 'hg-sidebar-edge d-print-none'; - edge.setAttribute('aria-hidden', 'true'); - documentObject.body.appendChild(edge); - var closeTimer; - var pointerLockUntil = 0; - function dynamic() { - return desktop.matches && - html.getAttribute('data-td-shell-sidebar') === 'collapsed'; - } - function preview() { - if (!dynamic()) return; - windowObject.clearTimeout(closeTimer); - sidebar.classList.add('td-shell-sidebar--overlay'); - sync(); - } - function closePreview() { - windowObject.clearTimeout(closeTimer); - closeTimer = windowObject.setTimeout(function () { - if (!sidebar.contains(documentObject.activeElement)) { - sidebar.classList.remove('td-shell-sidebar--overlay'); - sync(); - } - }, 350); - } - edge.addEventListener('pointerenter', function (event) { - if (event.pointerType !== 'touch' && Date.now() >= pointerLockUntil) preview(); - }); - edge.addEventListener('pointerleave', closePreview); - if (restore) { - restore.addEventListener('pointerenter', function (event) { - if (event.pointerType !== 'touch' && Date.now() >= pointerLockUntil) preview(); - }); - restore.addEventListener('pointerleave', closePreview); - restore.addEventListener('keydown', function (event) { - if (event.key !== 'ArrowRight' || !dynamic()) return; - event.preventDefault(); - preview(); - var first = sidebar.querySelector('a[href], button'); - if (first) first.focus(); - }); - } - if (panel) { - panel.addEventListener('pointerenter', function () { - windowObject.clearTimeout(closeTimer); + var docsRoot = /(?:^|\/)(?:cn\/)?docs\/?$/.test(windowObject.location.pathname); + buttons.forEach(function (button) { + var id = button.getAttribute('aria-controls'); + var defaultOpen = !hasSavedState && docsRoot && + /_nav(?:start|components)-children$/.test(id); + // OINK preserves the active path and owns DOM, inert and ARIA state. + sidebar.setExpanded(id, remembered.has(id) || defaultOpen, { source: 'api' }); }); - panel.addEventListener('pointerleave', closePreview); - panel.addEventListener('focusout', closePreview); - panel.addEventListener('keydown', function (event) { - if (event.key !== 'Escape' || !dynamic()) return; - event.preventDefault(); - if (restore) restore.focus(); - sidebar.classList.remove('td-shell-sidebar--overlay'); - sync(); - }); - } - function sync() { - var collapsed = - html.getAttribute('data-td-shell-sidebar') === 'collapsed'; - var drawerOpen = - html.getAttribute('data-td-shell-drawer') === 'open'; - // OINK owns the persistent collapsed mode and pointer overlay. Keep - // focus inside an open preview safe when its native pointerleave fires. - if (desktop.matches && collapsed && - !sidebar.classList.contains('td-shell-sidebar--overlay') && - sidebar.contains(documentObject.activeElement)) { - sidebar.classList.add('td-shell-sidebar--overlay'); - } - var overlay = sidebar.classList.contains('td-shell-sidebar--overlay'); - var isolated = desktop.matches ? collapsed && !overlay : !drawerOpen; - edge.hidden = !desktop.matches || !collapsed; - if (restore) restore.setAttribute('aria-expanded', String(!isolated)); - if (restore) restore.hidden = !desktop.matches || !collapsed; - sidebar.inert = isolated; - if (isolated) sidebar.setAttribute('aria-hidden', 'true'); - else sidebar.removeAttribute('aria-hidden'); - if ( - isolated && - sidebar.contains(documentObject.activeElement) && - restore && - restore.offsetParent !== null - ) { - restore.focus(); + function persist() { + if (!storage) return; + var expanded = buttons.filter(function (button) { + var item = button.closest('li'); + var state = sidebar.getState(button.getAttribute('aria-controls')); + return state && state.expanded && + !(item && item.classList.contains('td-active-path')); + }).map(function (button) { return button.getAttribute('aria-controls'); }); + try { storage.setItem(key, JSON.stringify(expanded)); } + catch (_) { /* Storage can become unavailable after initialization. */ } } - } - - new MutationObserver(sync).observe(html, { - attributes: true, - attributeFilter: ['data-td-shell-sidebar', 'data-td-shell-drawer'], - }); - new MutationObserver(sync).observe(sidebar, { attributes: true, attributeFilter: ['class'] }); - desktop.addEventListener('change', sync); - documentObject - .querySelectorAll('[data-td-shell-sidebar-toggle], [data-td-shell-drawer-close]') - .forEach(function (button) { - button.addEventListener('click', function () { - var hadFocus = documentObject.activeElement === button; - // Match OINK's cooldown so a newly visible trigger under the pointer - // does not immediately undo an explicit keyboard collapse. - pointerLockUntil = Date.now() + 150; - // Run after OINK's native click listener regardless of chunk order. - global.setTimeout(function () { - if (dynamic() && sidebar.contains(button) && restore) { - restore.hidden = false; - restore.focus(); - sidebar.classList.remove('td-shell-sidebar--overlay'); - } - sync(); - // Both external restore controls disappear when pinned. Transfer - // their focus after making the sidebar operable; hover and clicks - // that did not focus a trigger must not steal unrelated focus. - if (hadFocus && desktop.matches && !dynamic() && - !sidebar.contains(button) && button.offsetParent === null) { - var collapse = sidebar.querySelector('.td-shell-sidebar__collapse'); - if (collapse) collapse.focus(); - } - }, 0); - }, true); + documentObject.addEventListener('oink:sidebar-disclosure', function (event) { + if (event.detail && event.detail.source === 'user' && valid.has(event.detail.id)) persist(); }); - sync(); + if (!hasSavedState) persist(); + }); } function initScrollableTables(documentObject) { @@ -455,7 +268,6 @@ consumeVersionFallback(windowObject, documentObject, config); initVersionSwitching(windowObject, documentObject); initTreePersistence(windowObject, documentObject, config); - initSidebarIsolation(windowObject, documentObject); initScrollableTables(documentObject); initSearchRetry(windowObject, documentObject); } @@ -464,7 +276,7 @@ init: init, readConfig: readConfig, safeStorage: safeStorage, - setTreeExpanded: setTreeExpanded, + initTreePersistence: initTreePersistence, versionTarget: versionTarget, initVersionSwitching: initVersionSwitching, consumeVersionFallback: consumeVersionFallback, diff --git a/assets/js/kapa-adapter.js b/assets/js/kapa-adapter.js index b5d1913437..552ac9dfd4 100644 --- a/assets/js/kapa-adapter.js +++ b/assets/js/kapa-adapter.js @@ -115,6 +115,7 @@ var lastTrigger = null; var activeScript = null; var activeQueue = null; + var operation = null; var status = documentObject.querySelector('[data-hg-ai-status]'); function renderState(next, message) { @@ -133,6 +134,15 @@ } } + function settle(error) { + if (!operation) return; + var current = operation; + operation = null; + current.signal.removeEventListener('abort', current.abort); + if (error) current.reject(error); + else current.resolve(); + } + function openWidget(query, submit) { invokeKapa(windowObject, 'setSourceGroupIDs', [config.sourceGroupId]); invokeKapa(windowObject, 'open', { @@ -140,6 +150,7 @@ query: query, submit: submit, }); + settle(); } function discardAttempt(serial) { @@ -172,6 +183,7 @@ windowObject.clearTimeout(timer); discardAttempt(serial); renderState('error', config.labels.error); + settle(new Error(config.labels.error)); } function ready(serial, query, submit) { @@ -230,7 +242,7 @@ openWidget(query, Boolean(submit && query)); return; } - var retrying = state === 'error'; + var retrying = attempt > 0; var serial = ++attempt; renderState('loading', ''); timer = windowObject.setTimeout(function () { @@ -244,23 +256,49 @@ renderState('idle', ''); if (consent && consent.open) consent.close(); restoreFocus(); + settle(); } - function activate(query, submit, trigger) { + function activate(query, submit, trigger, context) { if (state === 'loading' || state === 'consent') return; + if (context && context.signal.aborted) return Promise.resolve(); lastTrigger = trigger || documentObject.activeElement; + var completion; + if (context) { + completion = new Promise(function (resolve, reject) { + operation = { signal: context.signal, resolve: resolve, reject: reject }; + operation.abort = function () { + windowObject.clearTimeout(timer); + if (state === 'loading') discardAttempt(attempt); + attempt += 1; + pending = null; + if (consent && consent.open) consent.close(); + renderState('idle', ''); + settle(); + }; + context.signal.addEventListener('abort', operation.abort, { once: true }); + }); + // Transfer focus before opening a dialog; OINK keeps cancellation alive + // until this promise settles, including when search is opened again. + if (!context.handoff()) { + settle(); + return completion; + } + } if (consented) { load(query, submit); - return; + return completion; } // Fail closed if the local consent panel is unavailable. if (!consent || typeof consent.showModal !== 'function') { renderState('error', config.labels.error); - return; + settle(new Error(config.labels.error)); + return completion; } pending = { query: trimmedQuery(query), submit: submit }; renderState('consent', ''); consent.showModal(); + return completion; } if (consent) { @@ -306,119 +344,32 @@ var config = readConfig(documentObject); if (!config) return null; var controller = createController(windowObject, documentObject, config); - var root = documentObject.getElementById('td-shell-search'); - var input = root && root.querySelector('.td-shell-search__input'); - var list = root && root.querySelector('.td-shell-search__list'); - var syncing = false; - - function bind(button) { + documentObject.querySelectorAll('[data-hg-ask-ai]').forEach(function (button) { if (button.dataset.hgAiBound !== undefined) return; button.dataset.hgAiBound = ''; button.addEventListener('click', function () { - controller.activate( - button.dataset.hgAiQuery || '', - button.dataset.hgAiSubmit === 'true', - button, - ); + controller.activate('', false, button); }); - } - documentObject.querySelectorAll('[data-hg-ask-ai]').forEach(bind); - - if (!input || !list) return controller; + }); - // Keep the OINK palette untouched: only intercept Enter when local search - // is empty and the site-owned Ask AI tail is the available follow-up. - input.addEventListener('keydown', function (event) { - if (event.isComposing || event.keyCode === 229 || event.key !== 'Enter') return; - var empty = list.querySelector('.td-shell-search__empty'); - var localRow = list.querySelector('.td-shell-search__item:not(.hg-ai-search-tail__button)'); - var tailButton = list.querySelector('[data-hg-ai-search-tail] [data-hg-ask-ai]'); - if (!empty || localRow || !tailButton) return; - event.preventDefault(); - event.stopImmediatePropagation(); - controller.activate(input.value, true, tailButton); - }, true); - - function syncTail() { - syncing = false; - if (!root || !input || !list || root.hidden) return; - var old = list.querySelector('[data-hg-ai-search-tail]'); - var query = trimmedQuery(input.value); - if (!query || query.charAt(0) === '>') { - if (old) old.remove(); - return; - } - var choiceLabel = root.dataset.tdTChoice || ''; - if ( - choiceLabel && - Array.prototype.some.call( - list.querySelectorAll('.td-shell-search__group-label'), - function (label) { return label.textContent.trim() === choiceLabel; }, - ) - ) { - if (old) old.remove(); - return; - } - var loading = root.dataset.tdTLoading || ''; - if ( - loading && - Array.prototype.some.call( - list.querySelectorAll('.td-shell-search__empty'), - function (node) { return node.textContent.trim() === loading; }, - ) - ) { - if (old) old.remove(); - return; - } - var empty = list.querySelector('.td-shell-search__empty'); - if (empty && config.labels.noResults) empty.textContent = config.labels.noResults; - var oldButton = old && old.querySelector('[data-hg-ask-ai]'); - if (oldButton && oldButton.dataset.hgAiQuery === query) return; - if (old) old.remove(); - - var group = documentObject.createElement('div'); - group.className = 'td-shell-search__group hg-ai-search-tail'; - group.dataset.hgAiSearchTail = ''; - group.setAttribute('role', 'group'); - var label = documentObject.createElement('div'); - label.className = 'td-shell-search__group-label'; - label.textContent = config.labels.ask; - var row = documentObject.createElement('button'); - row.type = 'button'; - row.className = 'td-shell-search__item hg-ai-search-tail__button'; - row.dataset.hgAskAi = ''; - row.dataset.hgAiQuery = query; - row.dataset.hgAiSubmit = 'true'; - var icon = documentObject.createElement('i'); - icon.className = - 'fa-solid fa-wand-magic-sparkles td-shell-search__item-icon'; - icon.setAttribute('aria-hidden', 'true'); - var meta = documentObject.createElement('span'); - meta.className = 'td-shell-search__item-meta'; - var title = documentObject.createElement('span'); - title.className = 'td-shell-search__item-title'; - title.textContent = config.labels.ask + ': “' + query + '”'; - var detail = documentObject.createElement('span'); - detail.className = 'td-shell-search__item-ref'; - detail.textContent = config.historical ? config.labels.latest + '.' : ''; - meta.appendChild(title); - meta.appendChild(detail); - row.appendChild(icon); - row.appendChild(meta); - group.appendChild(label); - group.appendChild(row); - list.appendChild(group); - bind(row); - } - - if (list) { - new MutationObserver(function () { - if (syncing) return; - syncing = true; - windowObject.requestAnimationFrame(syncTail); - }).observe(list, { childList: true, subtree: true }); - input.addEventListener('input', syncTail); - syncTail(); + var palette = windowObject.OinkCommandPalette; + if (palette && typeof palette.registerSearchTail === 'function') { + palette.registerSearchTail({ + id: 'hugegraph-ai', + rows: function (context) { + return [{ + id: 'ask', + title: config.labels.ask + ': “' + context.query + '”', + description: config.historical ? config.labels.latest + '.' : '', + icon: 'fa-solid fa-wand-magic-sparkles', + }]; + }, + activate: function (_, context) { + // Search options are ephemeral and become hidden on handoff. + var trigger = documentObject.querySelector('.hg-ask-ai-launcher'); + return controller.activate(context.query, true, trigger, context); + }, + }); } return controller; } diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index 9ebca8f7c0..29fbad69f6 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -507,30 +507,6 @@ display: none; } -.td-nav-util.hg-sidebar-restore { - display: none; -} - -.hg-sidebar-edge { - position: fixed; - inset-inline-start: 0; - top: 64px; - bottom: 0; - width: 16px; - z-index: 121; - border-inline-start: 3px solid var(--bs-border-color); - - &:hover { border-inline-start-color: var(--bs-primary); } -} - -@media (min-width: 768px) { - [data-td-shell-sidebar='collapsed'] { - .td-nav-util.hg-sidebar-restore:not([hidden]) { - display: inline-flex; - } - } -} - .hg-version-overflow { color: var(--bs-body-color); summary { @@ -556,42 +532,6 @@ font-size: 0.84rem; } -.hg-ai-search-tail { - position: sticky; - bottom: 0; - z-index: 1; - margin-inline: -6px; - padding-inline: 6px; - background: var(--td-shell-popover); - box-shadow: 0 -8px 16px color-mix(in srgb, var(--td-shell-popover) 88%, transparent); - border-block-start: 1px solid var(--bs-border-color); - - &__button { - width: 100%; - border: 0; - background: transparent; - color: inherit; - text-align: start; - - &:hover, - &:focus-visible { - background: var(--hg-theme-color-soft); - color: inherit; - } - } -} - -/* Keep the optional Ask AI tail discoverable without changing the OINK theme. */ -.td-shell-search__panel { - max-height: unquote('min(84vh, 720px)'); -} - -@media (max-width: 767.98px) { - .td-shell-search__panel { - max-height: unquote('min(86vh, 680px)'); - } -} - .hg-ask-ai-launcher { position: fixed; z-index: 1040; diff --git a/contribution.md b/contribution.md index cd95f7ce47..84dd069834 100644 --- a/contribution.md +++ b/contribution.md @@ -14,23 +14,20 @@ For the short workflow, start with [README.md](./README.md). This file records t The site uses the Hugo Module recorded in `go.mod` and `go.sum`: -```text -Go: 1.27.0 or newer -Hugo Extended: 0.165.0 -OINK: v1.0.0 -``` - -Node.js, npm, PostCSS, and a vendored Docsy checkout are not part of the build. +Toolchain versions are maintained in `go.mod` and `.github/workflows/hugo.yml`; +`go.mod` / `go.sum` pin the theme. Node.js is needed only for the browser tests, +with its version declared by `tests/e2e/package.json` and CI. -Verify the resolved theme before editing: +Verify the installed toolchain and locked module: ```bash hugo version go version -hugo mod graph +python3 scripts/oink_module.py ``` -The module graph must contain exactly the pinned `github.com/pgsty/oink@v1.0.0` dependency for this site. +For theme updates and breaking-change recovery, follow +[scripts/oink-upgrade.md](scripts/oink-upgrade.md). ## Local preview @@ -71,7 +68,7 @@ A successful command proves that Hugo rendered the configured outputs. It does n - `hugo.yaml` owns routing, languages, outputs, search, navigation, and OINK parameters. - `data/home/.yaml` owns the bilingual homepage. - `data/footer/.yaml` owns the bilingual footer. -- `i18n/zh-CN.yaml` carries the Simplified Chinese OINK interface catalogue for the preserved `cn` URL language key; the file is named after the `zh-CN` locale because Hugo resolves translations by locale, not by the URL key. +- `i18n/zh-CN.yaml` carries only HugeGraph-specific Simplified Chinese labels; generic interface translations come from OINK for the preserved `cn` URL language key; the file is named after the `zh-CN` locale because Hugo resolves translations by locale, not by the URL key. - `assets/` and `static/` contain site-owned brand and compatibility assets. OINK is a module dependency. Do not copy or edit generated module-cache files. Site-specific overrides belong in the corresponding root `layouts/`, `assets/`, or data path and require focused regression evidence. diff --git a/go.mod b/go.mod index 685c7ce27f..d4cb4ccab0 100644 --- a/go.mod +++ b/go.mod @@ -2,5 +2,4 @@ module github.com/apache/hugegraph-doc go 1.27.0 -require github.com/pgsty/oink v1.0.0 - +require github.com/pgsty/oink v1.1.0 diff --git a/go.sum b/go.sum index 8bd5c21c7b..946ebedd7e 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,2 @@ -github.com/pgsty/oink v1.0.0 h1:E+WHFP9zSRT+5RKoIkWNp+ASRGS1BKG+rDEi9by/BjE= -github.com/pgsty/oink v1.0.0/go.mod h1:pHvbUhJCfseB41n5RGwsF7abT3i32VSTpofLQoq4b7Y= - +github.com/pgsty/oink v1.1.0 h1:121L5g57ChRCPyidzEBBcln2Co+0zYRQ+XDDXjymd0Q= +github.com/pgsty/oink v1.1.0/go.mod h1:pHvbUhJCfseB41n5RGwsF7abT3i32VSTpofLQoq4b7Y= diff --git a/i18n/en.yaml b/i18n/en.yaml index 2544995466..ad2a00080f 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -2,7 +2,6 @@ ui_version_fallback: This page is not available in the selected version. You hav ui_ask_ai: Ask AI ui_ask_ai_description: Powered by Kapa; only your question is sent. ui_ask_ai_latest: Answers use the latest documentation -ui_ask_ai_no_results: No results found. Try AI mode. ui_retry: Retry ui_ai_error: AI is temporarily unavailable. Local search is unaffected. ui_more_versions: More versions @@ -20,7 +19,6 @@ download_component_ai: AI download_component_computer: Computer download_component_commons: Common download_asf_note: Source archives are the official Apache Software Foundation releases; binary packages are convenience builds made from them. All files are served from ASF mirrors, with signatures and checksums hosted on downloads.apache.org. Source archives generated automatically by GitHub are not ASF releases. -ui_assets_download: Download asset ui_ai_consent_description: "Continuing loads Kapa, a third-party service. Your question will be sent to Kapa to generate an answer." ui_ai_consent_continue: "Continue to Ask AI" diff --git a/i18n/zh-CN.yaml b/i18n/zh-CN.yaml index f373dc0d91..a75023877f 100644 --- a/i18n/zh-CN.yaml +++ b/i18n/zh-CN.yaml @@ -1,244 +1,8 @@ -# Alert labels -callout_caution: 注意 -callout_important: 重要 -callout_note: 说明 -callout_tip: 提示 -callout_warning: 警告 -callout_success: 成功 -callout_danger: 危险 -callout_question: 问题 -callout_example: 示例 -callout_quote: 引用 -callout_details: 详情 - -# UI strings. Buttons and similar. -ui_pager_prev: 上一页 -ui_pager_next: 下一页 -ui_search: 搜索… -ui_search_empty: 未找到结果 -ui_search_loading: 正在加载搜索索引…… -ui_search_results: '找到 {count} 条结果' -ui_search_nav: 选择 -ui_search_open: 打开 -ui_search_close: 关闭 -ui_palette_actions: 操作 -ui_palette_page_actions: 页面操作 -ui_palette_preferences: 偏好设置 -ui_palette_commands: 命令 -ui_palette_quick_links: 快速链接 -ui_palette_no_commands: 未找到匹配命令 -ui_palette_choose: 请选择一项 -ui_palette_action_failed: 无法完成此操作 -ui_palette_pages: 页面 -ui_palette_index_unavailable: 页面索引暂不可用,操作仍可使用 -ui_sidebar_nav: 章节导航 -ui_heading_self_link: 标题锚点 -ui_field_self_link: 链接到此字段 -ui_preview_source: Markdown -ui_preview_rendered: 渲染结果 -ui_main_nav: 主导航 -ui_home: 首页 -ui_sidebar_expand: 展开侧栏 -ui_sidebar_collapse: 收起侧栏 -ui_drawer_open: 打开导航 -ui_drawer_close: 关闭导航 -ui_root_menu_label: 选择栏目 -ui_tags_title: 标签 -ui_tag_title: 标签 -ui_categories_title: 分类 -ui_category_title: 分类 -ui_modules_title: 模块 -ui_module_title: 模块 -ui_authors_title: 作者 -ui_author_title: 作者 -ui_theme_toggle: 切换配色 -ui_language: 语言切换 -ui_theme: 主题颜色 -ui_theme_auto: 跟随系统 -ui_theme_light: 亮色 -ui_theme_dark: 暗色 -ui_toc_hide: 隐藏目录 -ui_toc_show: 显示目录 -ui_language_select: 选择语言 -ui_language_switch: 切换语言 -ui_skip_to_content: 跳转到主要内容 -ui_page_actions: 操作 -ui_open_in_chatgpt: 在 ChatGPT 中打开 -ui_open_in_claude: 在 Claude 中打开 -ui_open_in_prompt: 请阅读 %s 的内容,以便我就此向你提问。 -ui_copy_markdown: 复制 Markdown 文本 -ui_copy_success: 已复制 Markdown -ui_copy_error: 无法复制 Markdown -ui_share: 分享 -ui_share_email: 邮件 -ui_copy_link: 复制链接 -ui_copy_link_success: 已复制链接 -ui_copy_link_error: 无法复制链接 -ui_code_copy: 复制 -ui_code_copy_label: 复制代码 -ui_code_copied: 已复制 -ui_code_copy_error: 复制失败 -ui_code_show_all: 显示全部 {{ .Count }} 行 -ui_code_collapse: 收起代码 -ui_tabs_label: 选项卡 -ui_pricing_featured: 推荐 -ui_pricing_included: 包含 -ui_pricing_excluded: 不包含 -ui_marquee_pause: 暂停滚动 -ui_kbd_with: 加 -ui_keyboard_shortcuts: 键盘快捷键 -ui_shortcut_tree_move: 在侧栏中移动 -ui_shortcut_tree_toggle: 收起或展开章节 -ui_shortcut_tree_open: 打开当前页面 -ui_shortcut_heading_move: 上一个或下一个标题 -ui_shortcut_page_move: 上一页或下一页 -ui_shortcut_search: 搜索 -ui_shortcut_commands: 命令面板 -ui_shortcut_reading_mode: 专注阅读模式 -ui_shortcut_language: 切换语言 -ui_shortcut_theme: 切换主题 -ui_shortcut_route: 切换顶层页面 -ui_page_annotation: 页面信息 -ui_backlinks: 反链 -ui_backlinks_more: "再显示 {{ . }} 条" -ui_field_required: 必填 -ui_action_unavailable: 暂不可用 -ui_image_zoom_dialog: 图片预览 -ui_image_zoom_open: 打开图片预览 -ui_image_zoom_close: 关闭图片预览 -ui_print_page: 打印此页面 -ui_sidebar_expand_section: 展开章节 -ui_sidebar_collapse_section: 收起章节 -ui_asciinema_timer: 播放时间 -ui_openapi_spec: OpenAPI 规格文件 -ui_release_view: 查看发布 -ui_release_source: 源码 -ui_release_compare: 比较变更 -ui_release_released: 发布于 -ui_release_checksums: 校验和 -ui_assets_file: 文件 -ui_assets_checksum: 校验和 -ui_assets_copy: 复制校验和 -ui_assets_copied: 已复制 -ui_assets_copy_all: 复制全部校验和 -ui_assets_download: 下载资产 -ui_download_channels: 下载渠道 -ui_download_unpublished: 待发布 - -# Used in sentences such as "All Tags" -ui_all: 全部 - -# Footer text -footer_all_rights_reserved: 保留所有权利 - -ui_footer_collapse: 收起页脚链接 -ui_footer_expand: 展开页脚链接 - -# Post (blog, article, etc.) -post_last_mod: 最后修改 -post_upstream: "{{ .work }},{{ .copyright }},依 {{ .license }} 授权使用,详见{{ .notice }}。" -post_upstream_adapted: 本页改编自 {{ .work }},{{ .copyright }},依 {{ .license }} 授权,详见{{ .notice }}与{{ .history }}。 -post_upstream_adapted_plain: 本页改编自 {{ .work }},{{ .copyright }},依 {{ .license }} 授权,详见{{ .notice }}。 -post_upstream_notice: 归属声明 -post_upstream_history: 修改历史 -post_translated: 本页为译文,如有出入以{{ .original }}为准。 -post_translated_original: 原文 -post_edit_this: 编辑当前页面 -post_view_markdown: 查阅 Markdown 源码 -post_create_child_page: 添加子页面 -post_create_issue: 提交文档议题 -post_create_project_issue: 提交项目议题 -post_reading_time: 分钟阅读 -post_less_than_a_minute_read: 1分钟内可阅读完 -post_word_count: '{{ .Count }} 字' -post_reading_minutes: '{{ .Minutes }} 分钟' -post_read_original: 阅读原文 - -# Print support -print_printable_section: 这是本节的多页打印视图。 -print_click_to_print: 点击此处打印 -print_show_regular: 返回本页常规视图 -print_entire_section: 打印完整章节 - -# Feedback -feedback_question: 这篇文档解决了你的问题吗? -feedback_positive: 是 -feedback_negative: 否 -feedback_thanks: 感谢反馈,我们会据此改进这篇文档。 -feedback_reason_prompt: 哪里没有解决?(可选) -feedback_reason_missing: 缺少必要信息 -feedback_reason_outdated: 内容错误或已过时 -feedback_reason_failed: 操作步骤没有生效 -feedback_reason_unclear: 内容难以理解 -feedback_details: 在评论区补充详情 -feedback_change: 修改反馈 - -# Table of contents -toc_on_this_page: 目录 - -# Error pages -error_404_title: 页面不存在 -error_404_body: >- - 抱歉,该页面不存在。可以从首页重新开始。 -error_404_home: 返回首页 - -# Version banner -version_banner_archived: >- - 文档 {{ .Version }} 版本已不再积极维护,你当前浏览的是一份归档快照。 -version_banner_latest: 最新文档请参阅 {{ .Link }}。 -version_banner_latest_link: 最新版本 - -# Comments -comments_noscript: 加载评论需要启用 JavaScript。 -comments_noscript_link: 在 GitHub 上查看讨论。 - -# LLM 页面操作 -ui_open_in_prompt_label: 就本页提问 -ui_view_history: 查阅编辑历史 -ui_table_scroll: "可横向滚动的表格" -ui_filetree_divider: "拖动以调整目录树备注列宽度" -book_figure: 图 -book_table: 表 -book_equation: 公式 -book_example: 示例 -book_toc: 全书目录 -book_draft: 草稿 -book_draft_notice: 本章仍在修订中。 -contributors_count: 位贡献者 - -# Article series -ui_series_title: 系列 -ui_series_part: 第 {{ .Part }} / 共 {{ .Total }} 篇 - -# Replace these values with reviewed translations when available. -ui_list_separator: 、 - -# Footer text -post_meta_by_in: "{{ .Authors }} 发布于 {{ .Section }}" -post_meta_in: "发布于 {{ .Section }}" - -# Replace these values with reviewed translations when available. -ui_blog_index_toggle: 切换布局 - -# Markdown output -markdown_llms_index: "LLMS 索引:" -markdown_backlinks: "反链:" -markdown_section_pages: "本节页面:" - -# Explicit English fallbacks for untranslated OINK UI strings. -# Replace these values with reviewed translations when available. -ui_diagram_expand: 放大图表 -ui_diagram_zoom_dialog: 图表预览 -ui_diagram_zoom_close: 关闭图表预览 -ui_diagram_zoom_in: 放大 -ui_diagram_zoom_out: 缩小 -ui_diagram_zoom_reset: 复位视图 -ui_diagram_error: 图表无法渲染 +# HugeGraph-specific labels. Generic UI translations come from OINK. ui_version_fallback: 目标版本没有此页面,已转到该版本的文档首页。 ui_ask_ai: 询问 AI ui_ask_ai_description: 由 Kapa 提供;仅发送你的问题。 ui_ask_ai_latest: 回答基于 latest 文档 -ui_ask_ai_no_results: 未找到结果,可以试试 Ask AI 模式。 ui_retry: 重试 ui_ai_error: AI 暂时不可用,本地搜索不受影响。 ui_more_versions: 更多版本 diff --git a/layouts/_partials/hooks/body-end.html b/layouts/_partials/hooks/body-end.html index fff6e6dd25..06d57502bb 100644 --- a/layouts/_partials/hooks/body-end.html +++ b/layouts/_partials/hooks/body-end.html @@ -24,8 +24,6 @@ "ask" (T "ui_ask_ai") "description" (T "ui_ask_ai_description") "latest" (T "ui_ask_ai_latest") - "noResults" (T "ui_ask_ai_no_results") - "retry" (T "ui_retry") "error" (T "ui_ai_error") -}} {{- $clientConfig := dict diff --git a/layouts/_partials/navbar-item.html b/layouts/_partials/navbar-item.html index d07dd32d2e..74c9b0ca30 100644 --- a/layouts/_partials/navbar-item.html +++ b/layouts/_partials/navbar-item.html @@ -1,4 +1,4 @@ -{{- /* OINK v1.0.0 override: Documentation gains manifest-driven versions. +{{- /* OINK override (reviewed against v1.1.0): Documentation gains manifest-driven versions. One top-level Hugo Menu item. Leaves keep the historical link path; parents add a separate disclosure and one dropdown/accordion panel. */ -}} {{- $page := .page -}} diff --git a/layouts/_partials/navbar.html b/layouts/_partials/navbar.html index 9eeb35de01..74dfc846c1 100644 --- a/layouts/_partials/navbar.html +++ b/layouts/_partials/navbar.html @@ -53,13 +53,6 @@ {{- /* Right zone: the search box leads as the elastic boundary before the fixed controls — version, language, theme, GitHub. */ -}}