update local changes

This commit is contained in:
happylittle7 2022-03-07 12:30:05 +08:00
parent d165621568
commit 90959c505b
16 changed files with 191 additions and 93 deletions

View File

@ -5,7 +5,7 @@
## Demo ## Demo
[Example Site](https://theme-stack.jimmycai.com/) [Example Site](https://demo.stack.jimmycai.com/)
[![Netlify Status](https://api.netlify.com/api/v1/badges/a2d2807a-a905-4bcb-97da-8da8d847da3d/deploy-status)](https://app.netlify.com/sites/hugo-theme-stack/deploys) [![Netlify Status](https://api.netlify.com/api/v1/badges/a2d2807a-a905-4bcb-97da-8da8d847da3d/deploy-status)](https://app.netlify.com/sites/hugo-theme-stack/deploys)
@ -35,7 +35,10 @@ It's necessary to use **Hugo Extended ≥ 0.87.0**.
## Installation ## Installation
Clone / Download this repository to `theme` folder, and edit your site config following `exampleSite/config.yaml`. * Route 1: Clone / Download this repository to `theme` folder
* Route 2: Turn your site into a hugo module and add this theme as a module dependency
Edit your site config following `exampleSite/config.yaml`.
*Note: Remove `config.toml` if there is one in the site folder.* *Note: Remove `config.toml` if there is one in the site folder.*

View File

@ -5,6 +5,6 @@
"*": [ "*": [
"*" "*"
] ]
}, }
} }
} }

View File

@ -23,17 +23,3 @@ body {
scrollbar-color: var(--scrollbar-thumb) transparent; scrollbar-color: var(--scrollbar-thumb) transparent;
} }
/**/ /**/
/* scrollbar styles for Chromium */
::-webkit-scrollbar {
height: auto;
}
::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb);
}
::-webkit-scrollbar-track {
background-color: transparent;
}
/**/

View File

@ -71,7 +71,8 @@
text-transform: unset; text-transform: unset;
} }
.article-copyright, .article-lastmod { .article-copyright,
.article-lastmod {
a { a {
color: var(--body-text-color); color: var(--body-text-color);
} }
@ -359,6 +360,12 @@
} }
} }
.table-wrapper {
padding: 0 var(--card-padding);
overflow-x: auto;
display: block;
}
table { table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
@ -407,9 +414,10 @@
/// Negative margins /// Negative margins
blockquote, blockquote,
figure, figure,
.gallery,
pre, pre,
.gallery,
.video-wrapper, .video-wrapper,
.table-wrapper,
.s_video_simple { .s_video_simple {
margin-left: calc((var(--card-padding)) * -1); margin-left: calc((var(--card-padding)) * -1);
margin-right: calc((var(--card-padding)) * -1); margin-right: calc((var(--card-padding)) * -1);

View File

@ -8,6 +8,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
} }
[data-scheme="dark"] { [data-scheme="dark"] {
color-scheme: dark;
--pre-text-color: #f8f8f2; --pre-text-color: #f8f8f2;
--pre-background-color: #272822; --pre-background-color: #272822;
@import "partials/highlight/dark.scss"; @import "partials/highlight/dark.scss";

View File

@ -58,7 +58,7 @@ let Stack = {
/** /**
* Add copy button to code block * Add copy button to code block
*/ */
const codeBlocks = document.querySelectorAll('.article-content .highlight'); const codeBlocks = document.querySelectorAll('.article-content > div.highlight');
const copyText = `Copy`, const copyText = `Copy`,
copiedText = `Copied!`; copiedText = `Copied!`;
codeBlocks.forEach(codeBlock => { codeBlocks.forEach(codeBlock => {

View File

@ -8,6 +8,11 @@ interface pageData {
matchCount: number matchCount: number
} }
interface match {
start: number,
end: number
}
/** /**
* Escape HTML tags as HTML entities * Escape HTML tags as HTML entities
* Edited from: * Edited from:
@ -53,77 +58,129 @@ class Search {
this.bindSearchForm(); this.bindSearchForm();
} }
private async searchKeywords(keywords: string[]) { /**
const rawData = await this.getData(); * Processes search matches
let results: pageData[] = []; * @param str original text
* @param matches array of matches
/// Sort keywords by their length * @param ellipsis whether to add ellipsis to the end of each match
keywords.sort((a, b) => { * @param charLimit max length of preview string
return b.length - a.length * @param offset how many characters before and after the match to include in preview
* @returns preview string
*/
private static processMatches(str: string, matches: match[], ellipsis: boolean = true, charLimit = 140, offset = 20): string {
matches.sort((a, b) => {
return a.start - b.start;
}); });
let i = 0,
lastIndex = 0,
charCount = 0;
const resultArray: string[] = [];
while (i < matches.length) {
const item = matches[i];
/// item.start >= lastIndex (equal only for the first iteration)
/// because of the while loop that comes after, iterating over variable j
if (ellipsis && item.start - offset > lastIndex) {
resultArray.push(`${replaceHTMLEnt(str.substring(lastIndex, lastIndex + offset))} [...] `);
resultArray.push(`${replaceHTMLEnt(str.substring(item.start - offset, item.start))}`);
charCount += offset * 2;
}
else {
/// If the match is too close to the end of last match, don't add ellipsis
resultArray.push(replaceHTMLEnt(str.substring(lastIndex, item.start)));
charCount += item.start - lastIndex;
}
let j = i + 1,
end = item.end;
/// Include as many matches as possible
/// [item.start, end] is the range of the match
while (j < matches.length && matches[j].start <= end) {
end = Math.max(matches[j].end, end);
++j;
}
resultArray.push(`<mark>${replaceHTMLEnt(str.substring(item.start, end))}</mark>`);
charCount += end - item.start;
i = j;
lastIndex = end;
if (ellipsis && charCount > charLimit) break;
}
/// Add the rest of the string
if (lastIndex < str.length) {
let end = str.length;
if (ellipsis) end = Math.min(end, lastIndex + offset);
resultArray.push(`${replaceHTMLEnt(str.substring(lastIndex, end))}`);
if (ellipsis && end != str.length) {
resultArray.push(` [...]`);
}
}
return resultArray.join('');
}
private async searchKeywords(keywords: string[]) {
const rawData = await this.getData();
const results: pageData[] = [];
const regex = new RegExp(keywords.filter((v, index, arr) => {
arr[index] = escapeRegExp(v);
return v.trim() !== '';
}).join('|'), 'gi');
for (const item of rawData) { for (const item of rawData) {
const titleMatches: match[] = [],
contentMatches: match[] = [];
let result = { let result = {
...item, ...item,
preview: '', preview: '',
matchCount: 0 matchCount: 0
} }
let matched = false; const contentMatchAll = item.content.matchAll(regex);
for (const match of Array.from(contentMatchAll)) {
for (const keyword of keywords) { contentMatches.push({
if (keyword === '') continue; start: match.index,
end: match.index + match[0].length
const regex = new RegExp(escapeRegExp(replaceHTMLEnt(keyword)), 'gi');
const contentMatch = regex.exec(result.content);
regex.lastIndex = 0; /// Reset regex
const titleMatch = regex.exec(result.title);
regex.lastIndex = 0; /// Reset regex
if (titleMatch) {
result.title = result.title.replace(regex, Search.marker);
}
if (titleMatch || contentMatch) {
matched = true;
++result.matchCount;
let start = 0,
end = 100;
if (contentMatch) {
start = contentMatch.index - 20;
end = contentMatch.index + 80
if (start < 0) start = 0;
}
if (result.preview.indexOf(keyword) !== -1) {
result.preview = result.preview.replace(regex, Search.marker);
}
else {
if (start !== 0) result.preview += `[...] `;
result.preview += `${result.content.slice(start, end).replace(regex, Search.marker)} `;
}
}
}
if (matched) {
result.preview += '[...]';
results.push(result);
}
}
/** Result with more matches appears first */
return results.sort((a, b) => {
return b.matchCount - a.matchCount;
}); });
} }
public static marker(match) { const titleMatchAll = item.title.matchAll(regex);
return '<mark>' + match + '</mark>'; for (const match of Array.from(titleMatchAll)) {
titleMatches.push({
start: match.index,
end: match.index + match[0].length
});
}
if (titleMatches.length > 0) result.title = Search.processMatches(result.title, titleMatches, false);
if (contentMatches.length > 0) {
result.preview = Search.processMatches(result.content, contentMatches);
}
else {
/// If there are no matches in the content, use the first 140 characters as preview
result.preview = replaceHTMLEnt(result.content.substring(0, 140));
}
result.matchCount = titleMatches.length + contentMatches.length;
if (result.matchCount > 0) results.push(result);
}
/// Result with more matches appears first
return results.sort((a, b) => {
return b.matchCount - a.matchCount;
});
} }
private async doSearch(keywords: string[]) { private async doSearch(keywords: string[]) {
@ -150,6 +207,11 @@ class Search {
/// Not fetched yet /// Not fetched yet
const jsonURL = this.form.dataset.json; const jsonURL = this.form.dataset.json;
this.data = await fetch(jsonURL).then(res => res.json()); this.data = await fetch(jsonURL).then(res => res.json());
const parser = new DOMParser();
for (const item of this.data) {
item.content = parser.parseFromString(item.content, 'text/html').body.innerText;
}
} }
return this.data; return this.data;
@ -160,7 +222,7 @@ class Search {
const eventHandler = (e) => { const eventHandler = (e) => {
e.preventDefault(); e.preventDefault();
const keywords = this.input.value; const keywords = this.input.value.trim();
Search.updateQueryString(keywords, true); Search.updateQueryString(keywords, true);
@ -225,7 +287,7 @@ class Search {
<a href={item.permalink}> <a href={item.permalink}>
<div class="article-details"> <div class="article-details">
<h2 class="article-title" dangerouslySetInnerHTML={{ __html: item.title }}></h2> <h2 class="article-title" dangerouslySetInnerHTML={{ __html: item.title }}></h2>
<secion class="article-preview" dangerouslySetInnerHTML={{ __html: item.preview }}></secion> <section class="article-preview" dangerouslySetInnerHTML={{ __html: item.preview }}></section>
</div> </div>
{item.image && {item.image &&
<div class="article-image"> <div class="article-image">

View File

@ -69,6 +69,10 @@ Tables aren't part of the core Markdown spec, but Hugo supports supports them ou
| -------- | -------- | ------ | | -------- | -------- | ------ |
| *italics* | **bold** | `code` | | *italics* | **bold** | `code` |
| A | B | C | D | E | F |
|----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------|------------------------------------------------------------|----------------------------------------------------------------------|
| Lorem ipsum dolor sit amet, consectetur adipiscing elit. | Phasellus ultricies, sapien non euismod aliquam, dui ligula tincidunt odio, at accumsan nulla sapien eget ex. | Proin eleifend dictum ipsum, non euismod ipsum pulvinar et. Vivamus sollicitudin, quam in pulvinar aliquam, metus elit pretium purus | Proin sit amet velit nec enim imperdiet vehicula. | Ut bibendum vestibulum quam, eu egestas turpis gravida nec | Sed scelerisque nec turpis vel viverra. Vivamus vitae pretium sapien |
## Code Blocks ## Code Blocks
#### Code block with backticks #### Code block with backticks

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/CaiJimmy/hugo-theme-stack
go 1.12

View File

@ -4,6 +4,18 @@ toggleMenu:
darkMode: darkMode:
other: 夜晚模式 other: 夜晚模式
list:
page:
one: "{{ .Count }} 個頁面"
other: "{{ .Count }} 個頁面"
section:
other: Section
subsection:
one: Subsection
other: Subsections
article: article:
back: back:
other: 返回 other: 返回
@ -18,13 +30,15 @@ article:
other: 最後更新 other: 最後更新
readingTime: readingTime:
one: "閱讀時間: {{ .Count }} 分鐘"
other: "閱讀時間: {{ .Count }} 分鐘" other: "閱讀時間: {{ .Count }} 分鐘"
notFound: notFound:
title: title:
other: 404 錯誤 other: 404 錯誤:(
subtitle: subtitle:
other: 頁面不存在 other: 頁面不存在:(
widget: widget:
archives: archives:
@ -43,7 +57,14 @@ search:
other: 搜尋 other: 搜尋
placeholder: placeholder:
other: 輸入關鍵字... other: 輸入關鍵字...
resultTitle: resultTitle:
other: "#PAGES_COUNT 個結果 (用時 #TIME_SECONDS 秒)" other: "#PAGES_COUNT 個結果 (花了 #TIME_SECONDS 秒)"
footer:
builtWith:
other: 使用 {{ .Generator }} 建造
designedBy:
other: 自豪的使用由 {{ .DesignedBy }} 設計的 {{ .Theme }}

View File

@ -1,3 +1,5 @@
<section class="article-content"> <section class="article-content">
{{ .Content }} <!-- Refer to https://discourse.gohugo.io/t/responsive-tables-in-markdown/10639/5 -->
{{ $wrappedTable := printf "<div class=\"table-wrapper\">${1}</div>" }}
{{ .Content | replaceRE "(<table>(?:.|\n)+?</table>)" $wrappedTable | safeHTML }}
</section> </section>

View File

@ -9,6 +9,7 @@
data-reactions-enabled="{{- default 1 .reactionsEnabled -}}" data-reactions-enabled="{{- default 1 .reactionsEnabled -}}"
data-emit-metadata="{{- default 0 .emitMetadata -}}" data-emit-metadata="{{- default 0 .emitMetadata -}}"
data-theme="{{- default `light` .lightTheme -}}" data-theme="{{- default `light` .lightTheme -}}"
data-lang="zh-TW"
crossorigin="anonymous" crossorigin="anonymous"
async async
></script> ></script>

View File

@ -1,4 +1,4 @@
<script src="//cdn.jsdelivr.net/npm/twikoo@1.4.3/dist/twikoo.all.min.js"></script> <script src="//cdn.jsdelivr.net/npm/twikoo@1.4.15/dist/twikoo.all.min.js"></script>
<div id="tcomment"></div> <div id="tcomment"></div>
<style> <style>
.twikoo { .twikoo {

View File

@ -4,3 +4,9 @@
{{- $script := resources.Get "ts/main.ts" | js.Build $opts -}} {{- $script := resources.Get "ts/main.ts" | js.Build $opts -}}
<script type="text/javascript" src="{{ $script.RelPermalink }}" defer></script> <script type="text/javascript" src="{{ $script.RelPermalink }}" defer></script>
{{- with resources.Get "ts/custom.ts" -}}
{{/* Place your custom script in HUGO_SITE_FOLDER/assets/ts/custom.ts */}}
{{- $customScript := . | js.Build $opts -}}
<script type="text/javascript" src="{{ $customScript.RelPermalink }}" defer></script>
{{- end -}}

View File

@ -1,4 +1,4 @@
{{- $ThemeVersion := "3.6.0" -}} {{- $ThemeVersion := "3.7.0" -}}
<footer class="site-footer"> <footer class="site-footer">
<section class="copyright"> <section class="copyright">
&copy; &copy;

View File

@ -9,6 +9,7 @@
{{ with .Site.Params.sidebar.avatar }} {{ with .Site.Params.sidebar.avatar }}
{{ if (default true .enabled) }} {{ if (default true .enabled) }}
<figure class="site-avatar"> <figure class="site-avatar">
<a href="{{ .Site.BaseURL | relLangURL }}">
{{ if not .local }} {{ if not .local }}
<img src="{{ .src }}" width="300" height="300" class="site-logo" loading="lazy" alt="Avatar"> <img src="{{ .src }}" width="300" height="300" class="site-logo" loading="lazy" alt="Avatar">
{{ else }} {{ else }}
@ -22,7 +23,7 @@
{{ errorf "Failed loading avatar from %q" . }} {{ errorf "Failed loading avatar from %q" . }}
{{ end }} {{ end }}
{{ end }} {{ end }}
</a>
{{ with $.Site.Params.sidebar.emoji }} {{ with $.Site.Params.sidebar.emoji }}
<span class="emoji">{{ . }}</span> <span class="emoji">{{ . }}</span>
{{ end }} {{ end }}