mirror of
https://github.com/tiennm99/blog.git
synced 2026-10-11 03:13:10 +00:00
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).
This commit is contained in:
1 parent
52d9eab2b1
commit
8c82df90c9
15 files changed
+211
-3
No files matched your search
@@ -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; }
|
||||
@@ -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); }
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<circle cx="11" cy="11" r="7"/>
|
||||
<path d="m20 20-3.5-3.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 271 B |
@@ -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"] });
|
||||
}
|
||||
@@ -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ú."
|
||||
---
|
||||
@@ -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
|
||||
@@ -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 }}"
|
||||
@@ -0,0 +1,24 @@
|
||||
{{- $g := site.Params.comments.giscus -}}
|
||||
{{- if and $g $g.enable -}}
|
||||
{{- if ne .Params.comments false -}}
|
||||
<section class="comments" aria-labelledby="comments-heading">
|
||||
<h2 id="comments-heading" class="comments-heading">{{ i18n "comments" | default "Bình luận" }}</h2>
|
||||
<div class="giscus" data-giscus></div>
|
||||
<script src="https://giscus.app/client.js"
|
||||
data-repo="{{ $g.repo }}"
|
||||
data-repo-id="{{ $g.repoId }}"
|
||||
data-category="{{ $g.category | default "Announcements" }}"
|
||||
data-category-id="{{ $g.categoryId }}"
|
||||
data-mapping="{{ $g.mapping | default "pathname" }}"
|
||||
data-strict="{{ $g.strict | default "0" }}"
|
||||
data-reactions-enabled="{{ $g.reactionsEnabled | default "1" }}"
|
||||
data-emit-metadata="0"
|
||||
data-input-position="{{ $g.inputPosition | default "bottom" }}"
|
||||
data-theme="{{ $g.theme | default "preferred_color_scheme" }}"
|
||||
data-lang="{{ $g.lang | default "vi" }}"
|
||||
data-loading="lazy"
|
||||
crossorigin="anonymous"
|
||||
async></script>
|
||||
</section>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -20,3 +20,8 @@
|
||||
{{- $tocJs := resources.Get "js/toc-active.js" | js.Build (dict "minify" true) | fingerprint -}}
|
||||
<script type="module" src="{{ $tocJs.RelPermalink }}" integrity="{{ $tocJs.Data.Integrity }}" crossorigin="anonymous"></script>
|
||||
{{- 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 -}}
|
||||
<script type="module" src="{{ $giscusJs.RelPermalink }}" integrity="{{ $giscusJs.Data.Integrity }}" crossorigin="anonymous"></script>
|
||||
{{- end }}
|
||||
@@ -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 -}}
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
{{- with site.Params.profile.name }}{{ . }}{{ else }}{{ site.Title }}{{ end -}}
|
||||
</a>
|
||||
{{ partial "nav.html" . }}
|
||||
<button type="button" class="theme-toggle" aria-label="{{ i18n "toggleTheme" | default "Toggle theme" }}" data-theme-toggle hidden>
|
||||
<span aria-hidden="true">◐</span>
|
||||
</button>
|
||||
<div class="site-header-actions">
|
||||
{{- if site.Params.search.enable | default true }}
|
||||
{{ partial "search-button.html" . }}
|
||||
{{- end }}
|
||||
<button type="button" class="theme-toggle" aria-label="{{ i18n "toggleTheme" | default "Toggle theme" }}" data-theme-toggle hidden>
|
||||
<span aria-hidden="true">◐</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
@@ -0,0 +1,3 @@
|
||||
<a class="search-button" href="{{ "/search/" | relURL }}" aria-label="{{ i18n "search" | default "Tìm kiếm" }}">
|
||||
{{ partial "icon.html" "search" }}
|
||||
</a>
|
||||
Whitespace-only changes.
@@ -0,0 +1,43 @@
|
||||
{{ define "body_class" }}search{{ end }}
|
||||
|
||||
{{ define "head_extra" }}
|
||||
<link rel="stylesheet" href="{{ "/pagefind/pagefind-ui.css" | relURL }}">
|
||||
{{ end }}
|
||||
|
||||
{{ define "main" }}
|
||||
<header class="list-header">
|
||||
<h1 class="list-title">{{ .Title | default (i18n "search") }}</h1>
|
||||
{{- with .Description }}<p class="list-description">{{ . }}</p>{{ end }}
|
||||
</header>
|
||||
|
||||
<div id="search" class="search-container" data-pagefind-ui></div>
|
||||
|
||||
<noscript>
|
||||
<p class="search-noscript">{{ i18n "searchNoScript" | default "Tìm kiếm yêu cầu JavaScript." }}</p>
|
||||
</noscript>
|
||||
{{ end }}
|
||||
|
||||
{{ define "scripts" }}
|
||||
<script type="module">
|
||||
import { PagefindUI } from "{{ "/pagefind/pagefind-ui.js" | relURL }}";
|
||||
new PagefindUI({
|
||||
element: "#search",
|
||||
showSubResults: true,
|
||||
showImages: false,
|
||||
resetStyles: false,
|
||||
translations: {
|
||||
placeholder: {{ i18n "searchPlaceholder" | default "Tìm bài viết..." | jsonify }},
|
||||
clear_search: {{ i18n "clearSearch" | default "Xoá" | jsonify }},
|
||||
load_more: {{ i18n "loadMore" | default "Xem thêm" | jsonify }},
|
||||
search_label: {{ i18n "search" | default "Tìm kiếm" | jsonify }},
|
||||
filters_label: {{ i18n "filters" | default "Bộ lọc" | jsonify }},
|
||||
zero_results: {{ i18n "zeroResults" | default "Không có kết quả cho [SEARCH_TERM]" | jsonify }},
|
||||
many_results: {{ i18n "manyResults" | default "[COUNT] kết quả cho [SEARCH_TERM]" | jsonify }},
|
||||
one_result: {{ i18n "oneResult" | default "[COUNT] kết quả cho [SEARCH_TERM]" | jsonify }},
|
||||
alt_search: {{ i18n "altSearch" | default "Không có kết quả cho [SEARCH_TERM]. Đang hiển thị kết quả cho [DIFFERENT_TERM]." | jsonify }},
|
||||
search_suggestion: {{ i18n "searchSuggestion" | default "Không có kết quả cho [SEARCH_TERM]. Thử các từ khoá: [DIFFERENT_TERMS]." | jsonify }},
|
||||
searching: {{ i18n "searching" | default "Đang tìm [SEARCH_TERM]..." | jsonify }}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{ end }}
|
||||
@@ -28,4 +28,6 @@
|
||||
{{- if and (gt .WordCount 400) (ne .Params.toc false) }}
|
||||
{{ partial "toc.html" . }}
|
||||
{{- end }}
|
||||
|
||||
{{ partial "comments.html" . }}
|
||||
{{ end }}
|
||||
Reference in new issue
Block a user