From 8c82df90c97ad224e1693d6a790a13dec347a576 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Thu, 7 May 2026 22:03:32 +0700 Subject: [PATCH] feat: Pagefind search and Giscus comments (Phase 7) - search/list.html: Pagefind UI container + module init with full vi i18n strings; loads /pagefind/pagefind-ui.{css,js} from generated dir - search-button.html in header, gated by params.search.enable - comments.html for Giscus, opt-in via params.comments.giscus.enable; reads repo/repoId/category/categoryId/mapping/theme/lang from params - giscus-theme.js: MutationObserver on data-theme, postMessage iframe on toggle so Giscus theme stays in sync with site - search.css: header-actions row, search button, Pagefind UI tokens mapped to tsuki design tokens - comments.css: min-block-size on iframe to prevent layout shift - exampleSite/hugo.yaml: commented giscus params block as docs - i18n/vi.yml: Pagefind UI strings (clear, loadMore, zeroResults...) Both features no-op when disabled. CSS bundle 3.5 KB gz (budget 15). --- themes/tsuki/assets/css/comments.css | 14 ++++++ themes/tsuki/assets/css/search.css | 43 +++++++++++++++++++ themes/tsuki/assets/icons/search.svg | 4 ++ themes/tsuki/assets/js/giscus-theme.js | 16 +++++++ .../exampleSite/content/search/_index.md | 4 ++ themes/tsuki/exampleSite/hugo.yaml | 19 ++++++++ themes/tsuki/i18n/vi.yml | 24 +++++++++++ themes/tsuki/layouts/_partials/comments.html | 24 +++++++++++ themes/tsuki/layouts/_partials/footer.html | 5 +++ themes/tsuki/layouts/_partials/head.html | 2 + themes/tsuki/layouts/_partials/header.html | 11 +++-- .../layouts/_partials/search-button.html | 3 ++ themes/tsuki/layouts/search/.gitkeep | 0 themes/tsuki/layouts/search/list.html | 43 +++++++++++++++++++ themes/tsuki/layouts/single.html | 2 + 15 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 themes/tsuki/assets/css/comments.css create mode 100644 themes/tsuki/assets/css/search.css create mode 100644 themes/tsuki/assets/icons/search.svg create mode 100644 themes/tsuki/assets/js/giscus-theme.js create mode 100644 themes/tsuki/exampleSite/content/search/_index.md create mode 100644 themes/tsuki/layouts/_partials/comments.html create mode 100644 themes/tsuki/layouts/_partials/search-button.html delete mode 100644 themes/tsuki/layouts/search/.gitkeep create mode 100644 themes/tsuki/layouts/search/list.html diff --git a/themes/tsuki/assets/css/comments.css b/themes/tsuki/assets/css/comments.css new file mode 100644 index 0000000..9fb975b --- /dev/null +++ b/themes/tsuki/assets/css/comments.css @@ -0,0 +1,14 @@ +/* tsuki: Giscus comments — reserve space to prevent layout shift on iframe load */ + +.comments { + margin-block-start: var(--tsuki-space-12); + padding-block-start: var(--tsuki-space-8); + border-block-start: 1px solid var(--tsuki-border); +} +.comments-heading { + font-size: var(--tsuki-fs-xl); + margin: 0 0 var(--tsuki-space-4); +} +.giscus, +.giscus-frame { min-block-size: 12rem; } +.giscus-frame { width: 100%; border: none; } diff --git a/themes/tsuki/assets/css/search.css b/themes/tsuki/assets/css/search.css new file mode 100644 index 0000000..1d01939 --- /dev/null +++ b/themes/tsuki/assets/css/search.css @@ -0,0 +1,43 @@ +/* tsuki: search page — Pagefind UI overrides */ + +.site-header-actions { + display: flex; + align-items: center; + gap: var(--tsuki-space-2); + margin-inline-start: var(--tsuki-space-3); +} + +.search-button { + display: inline-grid; + place-items: center; + width: 2rem; + height: 2rem; + border: 1px solid var(--tsuki-border); + border-radius: 999px; + color: var(--tsuki-fg-muted); + transition: var(--tsuki-transition); +} +.search-button:hover { + color: var(--tsuki-fg); + border-color: var(--tsuki-fg-muted); + text-decoration: none; +} +.search-button svg { width: 1rem; height: 1rem; } + +.site-nav { margin-inline-start: auto; } + +.search-container { margin-block: var(--tsuki-space-6); min-block-size: 6rem; } + +/* Pagefind UI tokens — see https://pagefind.app/docs/ui/#styling */ +:root { + --pagefind-ui-primary: var(--tsuki-accent); + --pagefind-ui-text: var(--tsuki-fg); + --pagefind-ui-background: var(--tsuki-bg); + --pagefind-ui-border: var(--tsuki-border); + --pagefind-ui-tag: var(--tsuki-code-bg); + --pagefind-ui-border-width: 1px; + --pagefind-ui-border-radius: var(--tsuki-radius); + --pagefind-ui-font: var(--tsuki-font-sans); +} + +.search-noscript { color: var(--tsuki-fg-muted); } diff --git a/themes/tsuki/assets/icons/search.svg b/themes/tsuki/assets/icons/search.svg new file mode 100644 index 0000000..e1a3017 --- /dev/null +++ b/themes/tsuki/assets/icons/search.svg @@ -0,0 +1,4 @@ + diff --git a/themes/tsuki/assets/js/giscus-theme.js b/themes/tsuki/assets/js/giscus-theme.js new file mode 100644 index 0000000..29271bb --- /dev/null +++ b/themes/tsuki/assets/js/giscus-theme.js @@ -0,0 +1,16 @@ +const giscus = document.querySelector("[data-giscus]"); +if (giscus) { + const root = document.documentElement; + const send = () => { + const frame = document.querySelector("iframe.giscus-frame"); + if (!frame) return; + const explicit = root.getAttribute("data-theme"); + const theme = explicit + || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"); + frame.contentWindow.postMessage( + { giscus: { setConfig: { theme } } }, + "https://giscus.app" + ); + }; + new MutationObserver(send).observe(root, { attributes: true, attributeFilter: ["data-theme"] }); +} diff --git a/themes/tsuki/exampleSite/content/search/_index.md b/themes/tsuki/exampleSite/content/search/_index.md new file mode 100644 index 0000000..28d67d6 --- /dev/null +++ b/themes/tsuki/exampleSite/content/search/_index.md @@ -0,0 +1,4 @@ +--- +title: "Tìm kiếm" +description: "Tìm kiếm trong toàn bộ bài viết và ghi chú." +--- diff --git a/themes/tsuki/exampleSite/hugo.yaml b/themes/tsuki/exampleSite/hugo.yaml index 7b265c0..c72582c 100644 --- a/themes/tsuki/exampleSite/hugo.yaml +++ b/themes/tsuki/exampleSite/hugo.yaml @@ -31,3 +31,22 @@ markup: params: description: Example site for the tsuki Hugo theme. + + search: + enable: true + + # Giscus comments — uncomment and fill in your repo IDs to enable. + # See https://giscus.app to generate values for your repo. + # comments: + # giscus: + # enable: true + # repo: tiennm99/tsuki + # repoId: R_kgDO... + # category: Announcements + # categoryId: DIC_kwDO... + # mapping: pathname # pathname | url | title | og:title + # strict: "0" + # reactionsEnabled: "1" + # inputPosition: bottom # top | bottom + # theme: preferred_color_scheme + # lang: vi diff --git a/themes/tsuki/i18n/vi.yml b/themes/tsuki/i18n/vi.yml index be861dd..f6dac74 100644 --- a/themes/tsuki/i18n/vi.yml +++ b/themes/tsuki/i18n/vi.yml @@ -61,5 +61,29 @@ - id: noResults translation: "Không có kết quả." +- id: searchNoScript + translation: "Tìm kiếm yêu cầu JavaScript." + +- id: clearSearch + translation: "Xoá" + +- id: loadMore + translation: "Xem thêm" + +- id: filters + translation: "Bộ lọc" + +- id: zeroResults + translation: "Không có kết quả cho [SEARCH_TERM]" + +- id: manyResults + translation: "[COUNT] kết quả cho [SEARCH_TERM]" + +- id: oneResult + translation: "[COUNT] kết quả cho [SEARCH_TERM]" + +- id: searching + translation: "Đang tìm [SEARCH_TERM]..." + - id: copyright translation: "© {{ .Year }} {{ .Author }}" diff --git a/themes/tsuki/layouts/_partials/comments.html b/themes/tsuki/layouts/_partials/comments.html new file mode 100644 index 0000000..9e736ec --- /dev/null +++ b/themes/tsuki/layouts/_partials/comments.html @@ -0,0 +1,24 @@ +{{- $g := site.Params.comments.giscus -}} +{{- if and $g $g.enable -}} +{{- if ne .Params.comments false -}} +
+

{{ i18n "comments" | default "Bình luận" }}

+
+ +
+{{- end -}} +{{- end -}} diff --git a/themes/tsuki/layouts/_partials/footer.html b/themes/tsuki/layouts/_partials/footer.html index 4597e00..0194092 100644 --- a/themes/tsuki/layouts/_partials/footer.html +++ b/themes/tsuki/layouts/_partials/footer.html @@ -20,3 +20,8 @@ {{- $tocJs := resources.Get "js/toc-active.js" | js.Build (dict "minify" true) | fingerprint -}} {{- end }} +{{- $giscus := site.Params.comments.giscus -}} +{{- if and (eq .Kind "page") $giscus $giscus.enable (ne .Params.comments false) }} +{{- $giscusJs := resources.Get "js/giscus-theme.js" | js.Build (dict "minify" true) | fingerprint -}} + +{{- end }} diff --git a/themes/tsuki/layouts/_partials/head.html b/themes/tsuki/layouts/_partials/head.html index aea4b0d..e092a7e 100644 --- a/themes/tsuki/layouts/_partials/head.html +++ b/themes/tsuki/layouts/_partials/head.html @@ -73,6 +73,8 @@ (resources.Get "css/home.css") (resources.Get "css/archive.css") (resources.Get "css/toc.css") + (resources.Get "css/search.css") + (resources.Get "css/comments.css") (resources.Get "css/view-transitions.css") -}} {{- $css := $cssFiles | resources.Concat "css/tsuki.bundle.css" | minify | fingerprint -}} diff --git a/themes/tsuki/layouts/_partials/header.html b/themes/tsuki/layouts/_partials/header.html index 2a03529..60d8836 100644 --- a/themes/tsuki/layouts/_partials/header.html +++ b/themes/tsuki/layouts/_partials/header.html @@ -3,7 +3,12 @@ {{- with site.Params.profile.name }}{{ . }}{{ else }}{{ site.Title }}{{ end -}} {{ partial "nav.html" . }} - +
+ {{- if site.Params.search.enable | default true }} + {{ partial "search-button.html" . }} + {{- end }} + +
diff --git a/themes/tsuki/layouts/_partials/search-button.html b/themes/tsuki/layouts/_partials/search-button.html new file mode 100644 index 0000000..e7795d8 --- /dev/null +++ b/themes/tsuki/layouts/_partials/search-button.html @@ -0,0 +1,3 @@ + + {{ partial "icon.html" "search" }} + diff --git a/themes/tsuki/layouts/search/.gitkeep b/themes/tsuki/layouts/search/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/themes/tsuki/layouts/search/list.html b/themes/tsuki/layouts/search/list.html new file mode 100644 index 0000000..1ceca25 --- /dev/null +++ b/themes/tsuki/layouts/search/list.html @@ -0,0 +1,43 @@ +{{ define "body_class" }}search{{ end }} + +{{ define "head_extra" }} + +{{ end }} + +{{ define "main" }} +
+

{{ .Title | default (i18n "search") }}

+ {{- with .Description }}

{{ . }}

{{ end }} +
+ + + + +{{ end }} + +{{ define "scripts" }} + +{{ end }} diff --git a/themes/tsuki/layouts/single.html b/themes/tsuki/layouts/single.html index 2c668d1..985cf49 100644 --- a/themes/tsuki/layouts/single.html +++ b/themes/tsuki/layouts/single.html @@ -28,4 +28,6 @@ {{- if and (gt .WordCount 400) (ne .Params.toc false) }} {{ partial "toc.html" . }} {{- end }} + +{{ partial "comments.html" . }} {{ end }}