diff options
| author | yingyu5658 <i@yingyu5658.me> | 2025-12-13 08:33:08 +0800 |
|---|---|---|
| committer | yingyu5658 <i@yingyu5658.me> | 2025-12-13 08:33:08 +0800 |
| commit | 1e5f8eb33bc41cb59faf059e83701152785cabea (patch) | |
| tree | 45867273ac2178285be840764f7962d2b55556c6 /layouts/_default | |
| download | blog-1e5f8eb33bc41cb59faf059e83701152785cabea.tar.gz blog-1e5f8eb33bc41cb59faf059e83701152785cabea.zip | |
Initial commit
Diffstat (limited to 'layouts/_default')
| -rw-r--r-- | layouts/_default/_markup/render-image.html | 4 | ||||
| -rw-r--r-- | layouts/_default/baseof.html | 43 | ||||
| -rw-r--r-- | layouts/_default/list.html | 70 | ||||
| -rw-r--r-- | layouts/_default/rss.xml | 85 | ||||
| -rw-r--r-- | layouts/_default/single.html | 297 | ||||
| -rw-r--r-- | layouts/_default/term.html | 36 |
6 files changed, 535 insertions, 0 deletions
diff --git a/layouts/_default/_markup/render-image.html b/layouts/_default/_markup/render-image.html new file mode 100644 index 0000000..bf19bf9 --- /dev/null +++ b/layouts/_default/_markup/render-image.html @@ -0,0 +1,4 @@ +<figure class="image-caption"> + <img src="{{ .Destination | safeURL }}" alt="{{ .Text }}"> + <figcaption>{{ .Text }}</figcaption> +</figure>
\ No newline at end of file diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html new file mode 100644 index 0000000..8729df0 --- /dev/null +++ b/layouts/_default/baseof.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html lang="{{ with .Site.LanguageCode }}{{ . }}{{ else }}en-US{{ end }}"> + +<head> + <meta http-equiv="X-Clacks-Overhead" content="GNU Terry Pratchett" /> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + {{- partial "favicon.html" . -}} + <title>{{ .Title }}</title> + + {{- partial "seo_tags.html" . -}} + <meta name="referrer" content="no-referrer-when-downgrade" /> + + {{ with .OutputFormats.Get "rss" -}} + {{ printf `<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .Permalink $.Site.Title | safeHTML }} + {{ end -}} + + {{- partial "style.html" . -}} + + <!-- A partial to be overwritten by the user. + Simply place a custom_head.html into + your local /layouts/partials-directory --> + {{- partial "custom_head.html" . -}} +</head> + +<body> + <header> + {{- partial "header.html" . -}} + </header> + <main> + {{- block "main" . }}{{- end }} + </main> + <footer> + {{- partial "footer.html" . -}} + </footer> + + <!-- A partial to be overwritten by the user. + Simply place a custom_body.html into + your local /layouts/partials-directory --> + {{- partial "custom_body.html" . -}} +</body> + +</html> diff --git a/layouts/_default/list.html b/layouts/_default/list.html new file mode 100644 index 0000000..8c1db86 --- /dev/null +++ b/layouts/_default/list.html @@ -0,0 +1,70 @@ +{{ define "main" }} +<content> + {{ if .Site.Params.postSearch }} + <input + id="search-input" + type="text" + placeholder="Search..." + style="margin-top: 16px" + /> + <script> + // 等待 DOM 完全加载后执行 + document.addEventListener('DOMContentLoaded', function () { + // 缓存 DOM 元素 + const searchInput = document.getElementById('search-input'); + const posts = document.querySelectorAll('.blog-posts li'); + const years = document.querySelectorAll('.blog-posts h3'); + + // 更新搜索结果 + function updateSearchResults(searchTerm) { + let visiblePosts = 0; + const displayedYears = new Set(); + posts.forEach(function (post) { + const title = post.querySelector('a').textContent.toLowerCase(); + const year = post.querySelector('time').getAttribute('datetime').split('-')[0]; + if (title.includes(searchTerm)) { + post.style.display = ''; + visiblePosts++; + displayedYears.add(year); + } else { + post.style.display = 'none'; + } + }); + + {{ if .Site.Params.groupByYear }} + years.forEach(function (y) { + const year = y.textContent; + y.style.display = displayedYears.has(year) ? '' : 'none'; + }); + {{ end }} + } + + searchInput.addEventListener('input', function () { + updateSearchResults(this.value.toLowerCase().trim()); + }); + }); + </script> + {{ end }} + <ul class="blog-posts"> + {{ $currentYear := 0 }} {{ range .Pages }} {{ if and (not .Params.hidden) + (not (in .Params.categories "往昔")) }} + + <li> + <span + class="{{ if .Site.Params.groupByYear }} grouped {{ else }} ungrouped {{ end }}" + > + <i> + <time datetime='{{ .Date.Format "2006-01-02" }}' pubdate> + {{ .Date.Format (default "2006-01-02" .Site.Params.dateFormat) }} + </time> + </i> + </span> + <a href="{{ .Permalink }}">{{ .Title }}</a> + </li> + + {{ end }} {{ else }} + <li>No posts yet</li> + {{ end }} + </ul> +</content> +{{ end }} diff --git a/layouts/_default/rss.xml b/layouts/_default/rss.xml new file mode 100644 index 0000000..37ab6ea --- /dev/null +++ b/layouts/_default/rss.xml @@ -0,0 +1,85 @@ +{{- /* Deprecate site.Author.email in favor of site.Params.author.email */}} +{{- $authorEmail := "" }} +{{- with site.Params.author }} + {{- if reflect.IsMap . }} + {{- with .email }} + {{- $authorEmail = . }} + {{- end }} + {{- end }} +{{- else }} + {{- with site.Author.email }} + {{- $authorEmail = . }} + {{- warnf "The author key in site configuration is deprecated. Use params.author.email instead." }} + {{- end }} +{{- end }} + +{{- /* Deprecate site.Author.name in favor of site.Params.author.name */}} +{{- $authorName := "" }} +{{- with site.Params.author }} + {{- if reflect.IsMap . }} + {{- with .name }} + {{- $authorName = . }} + {{- end }} + {{- else }} + {{- $authorName = . }} + {{- end }} +{{- else }} + {{- with site.Author.name }} + {{- $authorName = . }} + {{- warnf "The author key in site configuration is deprecated. Use params.author.name instead." }} + {{- end }} +{{- end }} + +{{- $pctx := . }} +{{- if .IsHome }}{{ $pctx = .Site }}{{ end }} +{{- $pages := slice }} +{{- if or $.IsHome $.IsSection }} +{{- $pages = $pctx.RegularPages }} +{{- else }} +{{- $pages = $pctx.Pages }} +{{- end }} +{{- $limit := .Site.Params.RSS.pageLimit }} +{{- if ge $limit 1 }} +{{- $pages = $pages | first $limit }} +{{- end }} +{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }} +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{ . }} on {{ end }}{{ .Site.Title }}{{ end }}</title> + <link>{{ .Permalink }}</link> + <description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{ . }} {{ end }}{{ end }}on {{ .Site.Title }}</description> + <generator>Hugo</generator> + <language>{{ site.LanguageCode }}</language> + {{ with $authorEmail }} + <managingEditor>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</managingEditor> + {{ end }} + {{ with $authorEmail }} + <webMaster>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</webMaster> + {{ end }} + {{ with .Site.Params.copyright }} + <copyright>{{ . }}</copyright> + {{ end }} + {{ if not .Date.IsZero }} + <lastBuildDate>{{ (index $pages.ByLastmod.Reverse 0).Lastmod.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate> + {{ end }} + {{ if and .Site.Params.RSS.followFeedId .Site.Params.RSS.followUserId }} + <follow_challenge> + <feedId>{{ .Site.Params.RSS.followFeedId }}</feedId> + <userId>{{ .Site.Params.RSS.followUserId }}</userId> + </follow_challenge> + {{ end }} + {{- with .OutputFormats.Get "RSS" }} + {{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }} + {{- end }} + {{- range $pages }} + <item> + <title>{{ .Title }}</title> + <link>{{ .Permalink }}</link> + <pubDate>{{ .PublishDate.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate> + {{- with $authorEmail }}<author>{{ . }}{{ with $authorName }} ({{ . }}){{ end }}</author>{{ end }} + <guid>{{ .Permalink }}</guid> + <description>{{ .Content | transform.XMLEscape | safeHTML }}</description> + </item> + {{- end }} + </channel> +</rss>
\ No newline at end of file diff --git a/layouts/_default/single.html b/layouts/_default/single.html new file mode 100644 index 0000000..78814e5 --- /dev/null +++ b/layouts/_default/single.html @@ -0,0 +1,297 @@ +{{ define "main" }} {{ if eq .Type "blog" }} {{ if not .Params.menu }} +<h1>{{ .Title }}</h1> +{{ end }} {{ end }} + +<article class="h-entry"> + <h1 class="post-title p-name"><a href="{{ .RelPermalink }}" style="color:#222222;">{{ .Title }}</a></h1> + +<div class="post-info"> + <time class="post-date dt-published" datetime="{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}"> + {{ .Date.Format "2006年1月2日" }} + </time> + {{ range .Params.categories }} + {{ $url := printf "/categories/%s/" (. | urlize) }} + <a href="{{ $url }}" class="category-link">{{ . }}</a> + {{ end }} +</div> +<div class="e-content"> + {{ .Content }} +</div> +</article> +<a class="u-url" href="{{ .Permalink }}" style="display:none;">Permalink</a> + +<p> + {{ range (.GetTerms "tags") }} + <a href="{{ .Permalink }}">#{{ .LinkTitle }}</a> + {{ end }} +</p> + +{{ $upvoteEnabled := default .Site.Params.upvote .Params.upvote }} +{{ if $upvoteEnabled }} +<div class="upvote-container"> +<small class="upvote"> + <button class="upvote-btn" id="upvote-btn"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1"> + <polyline points="17 11 12 6 7 11"></polyline> + <polyline points="17 18 12 13 7 18"></polyline> + </svg> + <span class="upvote-count" id="upvote-count">0</span> + </button> +</small> +</div> + +<script> + let hasUpvoted = false; + let upvoteBtn; + let upvoteCount; + + // 页面加载时获取点赞数量 + document.addEventListener('DOMContentLoaded', function() { + const slug = '{{ .Slug }}'; + upvoteBtn = document.getElementById('upvote-btn'); + upvoteCount = document.getElementById('upvote-count'); + getCount(slug); + + // 处理点赞按钮的点击事件 + upvoteBtn.addEventListener('click', handleUpvote); + }); + + // 点赞方法 + async function handleUpvote() { + if (hasUpvoted) { + console.log('You have already upvoted this post!'); + return; + } + const slug = '{{ .Slug }}'; + + // 禁用按钮以防止重复点击 + upvoteBtn.disabled = true; + // 给按钮添加 upvoted 类以赋以点击过的样式 + upvoteBtn.classList.add('upvoted'); + // 更新 upvote-count 的值 +1 + upvoteCount.innerText = parseInt(upvoteCount.innerText) + 1; + + try { + const response = await fetch('{{ .Site.Params.upvoteURL }}upvote', { + method: 'POST', + mode: 'cors', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ postId: slug, diff: 1 }), + }); + + if (response.ok) { + console.log('Upvote successful!'); + hasUpvoted = true; + await getCount(slug, 3); + } else { + console.log('Upvote failed!'); + } + } catch (error) { + console.error('Error: ', error); + } finally { + upvoteBtn.disabled = false; + } + } + + // 获取 Upvote 数量的方法,支持设置重试次数,默认不重试 + async function getCount(slug, retryCount = 0) { + try { + const response = await fetch('{{ .Site.Params.upvoteURL }}count?post=' + slug, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + + const data = await response.json(); + + if (data.code === 0) { + const count = data.data.count; + upvoteCount.innerText = count; + hasUpvoted = data.data.hasUpvoted; + if (hasUpvoted) { + upvoteBtn.classList.add('upvoted'); + } else { + upvoteBtn.classList.remove('upvoted'); + } + } else { + console.error('Failed to get upvote count: ', data.msg); + } + } catch (error) { + console.error('Error: ', error); + if (retryCount > 0) { + setTimeout(() => { + getCount(slug, retryCount - 1); + }, 1000); + } + } + } +</script> +{{ end }} + +<!-- Place the TOC at the end to ensure the article content loads first. --> +{{ $tocEnabled := default .Site.Params.toc .Params.toc }} {{ if $tocEnabled }} +<div class="toc">{{ partial "toc.html" . }}</div> +{{ end }} +<hr /> +<details> + <summary> + <p style="display: inline">评论 与 Webmentions</p> + </summary> + <div id="giscus-container"></div> + + <div class="wm"> +<div id="webmentions"></div> +<script src="/js/webmention.min.js" + data-id="webmentions" + data-page-url="https://www.glowisle.me{{ .RelPermalink }}" + data-max-webmentions="50" + data-wordcount="30" + data-sort-by="published" + data-sort-dir="up" + async> +</script> + +<details> + <summary>如何参与 Webmentions 互动?</summary> + <div class="wm-guide-content"> +<div class="webmention-form"> + <p>如果你想回应这篇文章,请在你的博客或社交媒体中链接本页面,然后在下面的表单中提交你的页面链接。</p> + <form id="webmention-submit-form" action="https://webmention.io/www.glowisle.me/webmention" method="post"> + <!-- 自动填充的目标文章链接 --> + <input type="hidden" name="target" value="https://www.glowisle.me{{ .RelPermalink }}"> + + <div> + <label for="webmention-source">你的文章链接:</label> + <input + type="url" + id="webmention-source" + name="source" + required + pattern="https?://.+" + > + <button type="submit" id="webmention-submit-btn">提交</button> + </div> + <div id="webmention-form-feedback"></div> + </form> + <br> +<a href="https://indieweb.org/webmention">关于 Webmention 的更多信息</a> 以及 <a href="https://www.glowisle.me/posts/tear-hypocrisy-apart/">为什么要这么做?</a> +</details> + + + + <script> + /* +src="https://giscus.app/client.js" + data-repo="yingyu5658/yingyu5658.github.io" + data-repo-id="R_kgDOOBetsA" + data-category="Announcements" + data-category-id="DIC_kwDOOBetsM4CoF_Z" + data-mapping="title" + data-strict="0" + data-reactions-enabled="1" + data-emit-metadata="0" + data-input-position="bottom" + data-theme="dark" + data-lang="zh-CN" + crossorigin="anonymous" + async> + + +*/ + function getInitialTheme() { + // 1. 优先检查 localStorage 中的用户偏好 + if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) { + return localStorage.getItem('theme'); + } + // 2. 检查系统偏好 + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + return 'dark'; + } + // 3. 默认值 + return 'light'; +} + +const currentTheme = getInitialTheme(); +const giscusTheme = currentTheme === 'dark' ? 'dark' : 'light'; + +const giscusScript = document.createElement('script'); +giscusScript.src = 'https://giscus.app/client.js'; +giscusScript.setAttribute('data-repo', 'yingyu5658/yingyu5658.github.io'); +giscusScript.setAttribute('data-repo-id', 'R_kgDOOBetsA'); +giscusScript.setAttribute('data-category', 'Announcements'); +giscusScript.setAttribute('data-category-id', 'DIC_kwDOOBetsM4CoF_Z'); +giscusScript.setAttribute('data-mapping', 'title'); +giscusScript.setAttribute('data-strict', '0'); +giscusScript.setAttribute('data-reactions-enabled', '1'); +giscusScript.setAttribute('data-emit-metadata', '0'); +giscusScript.setAttribute('data-input-position', 'bottom'); +giscusScript.setAttribute('data-theme', giscusTheme); +giscusScript.setAttribute('data-lang', 'zh-CN'); +giscusScript.crossOrigin = 'anonymous'; +giscusScript.async = true; + +document.getElementById('giscus-container').appendChild(giscusScript); + +document.addEventListener('DOMContentLoaded', function() { + const form = document.getElementById('webmention-submit-form'); + const submitBtn = document.getElementById('webmention-submit-btn'); + const feedbackEl = document.getElementById('webmention-form-feedback'); + + if (!form) return; + + form.addEventListener('submit', async function(e) { + e.preventDefault(); + + // 禁用提交按钮防止重复提交 + const originalBtnText = submitBtn.textContent; + submitBtn.disabled = true; + submitBtn.textContent = '发送中...'; + + // 清除之前的反馈信息 + feedbackEl.textContent = ''; + + try { + // 使用 FormData 收集表单数据 + const formData = new FormData(form); + + // 发送 POST 请求 + const response = await fetch(form.action, { + method: 'POST', + headers: { + 'Accept': 'application/json', + }, + body: new URLSearchParams(formData) + }); + + const result = await response.json(); + + if (response.ok) { + // 成功响应 + feedbackEl.textContent = '✅ 提交成功!Webmention 正在处理中。'; + form.reset(); + } else { + // 错误响应 + let errorMsg = '提交失败:'; + if (result.error || result.summary) { + errorMsg += result.error || result.summary; + } + feedbackEl.textContent = errorMsg; + } + } catch (error) { + // 网络错误 + feedbackEl.textContent = '❌ 提交过程中出现网络错误,请稍后重试。'; + } finally { + // 恢复提交按钮状态 + submitBtn.disabled = false; + submitBtn.textContent = originalBtnText; + } + }); +}); + + +</script> + +{{ end }} diff --git a/layouts/_default/term.html b/layouts/_default/term.html new file mode 100644 index 0000000..0d55fc1 --- /dev/null +++ b/layouts/_default/term.html @@ -0,0 +1,36 @@ +{{ define "main" }} +<div class="taxonomy-term"> + <h2>{{ .Title }}</h2> + + <div class="posts-list"> + {{ range .Pages }} + + <li style="list-style-type: none; margin-bottom: 12px"> + <span + class="post-date" + {{ + if + .Site.Params.groupByYear + }} + grouped + {{ + else + }} + ungrouped + {{ + end + }} + > + <time datetime='{{ .Date.Format "2006-01-02" }}' pubdate> + {{ .Date.Format "2006-01-02" }} + </time> + </span> + <a href="{{ .Permalink }}">{{ .Title }}</a> + </li> + + {{ else }} + <p>该分类下还没有文章。</p> + {{ end }} + </div> +</div> +{{ end }} |
