Compare commits

..
73 Commits
Author SHA1 Message Date
Jimmy Cai e937a14367 chore: release 2.0.0 2020-12-23 23:21:50 +01:00
Jimmy Cai 983aaa962f doc: update README 2020-12-23 22:37:21 +01:00
Jimmy CaiandGitHub 2ad65a4c07 feat: add option to disable image processing (#83)
* feat: add option to disable image processing

closes https://github.com/CaiJimmy/hugo-theme-stack/issues/37

* refactor(list): use article/components/header inside default.html

* refactor: use .Fill for cover image
2020-12-23 21:02:35 +01:00
Jimmy Cai 64e06f0beb Bump required Hugo version to 0.78.0 2020-12-23 19:17:17 +01:00
Jimmy CaiandGitHub 358e63e799 feat(darkmode): dark mode toggle (#82)
* feat(darkmode): initial support for dark mode toggle

* fix(darkmode): add svg icons

* feat(darkmode): dispatch onColorSchemeChange event

* add head/darkmode

* feat(darkmode); add colorScheme config

* style: remove empty line

* refactor(darkmode): simplify code

* style: add comment for darkmode config

* i18n support for dark mode toggle

* Some renaming
2020-12-23 19:03:40 +01:00
Jimmy Cai df74fe5f19 chore: change funding link to Ko-fi 2020-12-23 13:20:53 +01:00
Jimmy CaiandGitHub 7d841cf237 feat(list): add subsection support (#81)
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/48
2020-12-22 21:14:50 +01:00
Jimmy CaiandGitHub e606dca404 refactor(article): remove color tag & add custom category color (#75)
closes: https://github.com/CaiJimmy/hugo-theme-stack/issues/71
2020-12-22 19:43:14 +01:00
Jimmy CaiandGitHub 31c42bcfe7 refactor(gallery): rewrite gallery (#79)
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/70
2020-12-22 19:35:37 +01:00
Jimmy CaiandGitHub 54571f036b refactor(ts): import script using new Hugo Module logic (#77)
Allows user to overwrite a specific script.

Note: The required version of Hugo might bump up.
2020-12-21 22:34:54 +01:00
Jimmy CaiandGitHub 33685fb878 fix(list): section title style (#76) 2020-12-21 17:26:58 +01:00
Jimmy CaiandGitHub 680832920f ci: add refactor section for release note 2020-12-20 13:01:56 +01:00
Jimmy CaiandGitHub cea1064927 feat(footer): add copyright since & custom text (#74)
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/55
2020-12-20 12:34:44 +01:00
Jimmy CaiandGitHub ddd6aa2d20 feat(head): add favicon setting in config.yaml (#73)
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/42
2020-12-20 11:38:49 +01:00
Jimmy CaiandGitHub 25a72940b8 refactor(opengraph): add default value for twitter:card (#72) 2020-12-20 11:05:34 +01:00
Jimmy CaiandGitHub d205753323 refactor(i18n): translation key renaming (#68)
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/63
2020-12-19 01:41:26 +01:00
Jimmy CaiandGitHub e9c1abac93 refactor(config): use YAML instead of TOML (#65)
* refactor(config): use YAML instead of TOML

closes https://github.com/CaiJimmy/hugo-theme-stack/issues/62

* refactor(config.yaml): clear up

* style(config): formatting & add i18n comment
2020-12-15 10:59:52 +01:00
Rizki PrastyoandGitHub e1277f4e61 feat(i18n): add Indonesian translation (#64) 2020-12-11 17:18:08 +01:00
f919b8bd90 feat(sidebar): add support for external avatar (#53)
add [params.sidebar.avatar] section in config.toml, with 2 fields: local and src

Migration guide:
 - Copy the old value of `params.sidebar.avatar` to `param.sidebar.avatar.src`
 - Set params.sidebar.avatar.local = true

Co-authored-by: Jimmy Cai <jimmehcai@gmail.com>
2020-12-11 16:33:02 +01:00
Daniel PessoaandGitHub bfeddf3217 feat(i18n): Coverage improvements (#59)
* Replace hardcoded text with i18n

* Add categoriesTitle text

* Add categoriesTitle text

* Add new strings available

Co-authored-by: Daniel Pessoa <cobalto@users.noreply.github.com>
2020-12-11 16:11:47 +01:00
Jimmy CaiandGitHub 69b1524f91 Merge pull request #57 from cobalto/i18n/brazilian-portuguese
Add pt-BR.toml
2020-12-07 14:48:48 +01:00
Jimmy CaiandGitHub c6d752d420 Merge pull request #58 from BasixKOR/master
feat(i18n): add Korean translations
2020-12-07 14:44:13 +01:00
Basix 27c8350f1b Add Korean translations 2020-12-06 22:59:11 +09:00
Daniel PessoaandGitHub 108e017a32 Add pt-BR.toml
Add file for Brazilian Portuguese i18n
2020-12-05 22:48:13 -03:00
Jimmy Cai 5b1bfe566d doc: migrate to GitBook
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/47
2020-12-04 17:38:14 +01:00
Jimmy CaiandGitHub 11cb95fe04 ci: upgrade Hugo version to 0.79.0 (#56) 2020-12-04 16:28:42 +01:00
Jimmy CaiandGitHub 6b5eae9a7e Merge pull request #52 from vlourme/translations
i18n: added french translations & footer translation
2020-11-28 12:35:34 +01:00
Jimmy Cai 81c0b1ba9c i18n: add variables for footer translation 2020-11-28 12:29:43 +01:00
Jimmy CaiandGitHub 50b3ed18e3 Merge branch 'master' into translations 2020-11-28 12:16:38 +01:00
Jimmy CaiandGitHub 6b5e840111 Merge pull request #51 from vlourme/emoji
sidebar: hide emoji placeholder when no emoji is specified
2020-11-23 10:48:38 +01:00
Jimmy CaiandGitHub b36cf5cc4e style: add indent to /partials/sidebar/left.html 2020-11-23 10:47:22 +01:00
Victor Lourme 2b4f529404 i18n: added footer translations 2020-11-23 10:15:47 +01:00
Victor Lourme 819c8ea0b4 i18n: add french translations 2020-11-23 10:15:35 +01:00
Victor Lourme f763adfa60 sidebar: hide emoji placeholder when no emoji is specified 2020-11-23 10:01:39 +01:00
Jimmy Cai cd408279e4 release: 1.1.0 version 2020-11-08 00:28:30 +01:00
Jimmy CaiandGitHub f3107a35a7 Add Release Drafter Template 2020-11-07 14:06:00 +01:00
Jimmy CaiandGitHub 695260cf0d Set up release drafter action 2020-11-07 14:03:57 +01:00
Jimmy Cai 5bc567082b Add release-drafter 2020-11-07 13:53:11 +01:00
Jimmy CaiandGitHub 148f0ff8fe Merge pull request #19 from CaiJimmy/template-search
feat: local search
2020-11-06 12:19:33 +01:00
Jimmy Cai efa7ff14b7 refactor(search): remove .search-result--title style 2020-11-06 12:13:12 +01:00
Jimmy CaiandGitHub a81dac7bb0 Merge branch 'master' into template-search 2020-11-06 12:09:23 +01:00
Jimmy Cai c018f4967a feat(search): i18n support 2020-11-06 11:49:30 +01:00
Jimmy Cai f5d45458fd refactor(search): create Search class 2020-11-06 11:33:51 +01:00
Jimmy Cai b97e86a7a7 style: remove empty line in main.ts 2020-11-06 11:33:33 +01:00
Jimmy Cai 2736fec285 refactor: create createElement.ts 2020-11-06 11:12:48 +01:00
Jimmy CaiandGitHub 3db6df8f46 Merge pull request #46 from crateout/master
fix(article): related contents is showing hidden post
2020-11-06 10:36:23 +01:00
Ahmad YasserandGitHub 37a2090739 fix(article): related contents is showing hidden post 2020-11-06 16:19:26 +08:00
Ahmad YasserandGitHub a3f1d84c51 fix(article): related contents is showing hidden post 2020-11-06 11:10:30 +08:00
Jimmy CaiandGitHub b3df22940d Merge branch 'master' into template-search 2020-10-31 23:37:36 +01:00
Jimmy Cai be416830ad feat: add support for custom SCSS
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/43
2020-10-31 23:36:16 +01:00
Jimmy CaiandGitHub c3b3c2f0ee Merge pull request #44 from CaiJimmy/refactor-rel-permalink
refactor: use relative link for hyperlinks
2020-10-31 23:28:28 +01:00
Jimmy Cai b5645f0180 refactor: use relative link for hyperlinks 2020-10-31 23:24:58 +01:00
Jimmy Cai d6b0f10241 fix(exampleSite): remove Instagram shortcode
ref https://github.com/gohugoio/hugo/issues/7866

closes https://github.com/CaiJimmy/hugo-theme-stack/issues/41
2020-10-26 17:38:16 +01:00
Jimmy CaiandGitHub c666c79800 Merge branch 'master' into template-search 2020-10-17 10:45:17 +02:00
Jimmy CaiandGitHub f7b61b9556 Merge pull request #39 from CaiJimmy/new-archive-widget
feat: new archive widget style
2020-10-17 10:43:55 +02:00
Jimmy Cai 5c55bd2d33 feat(widget): new archive widget style 2020-10-17 10:34:36 +02:00
Jimmy Cai 34cd7852bc refactor: remove partialCached from sidebar/right 2020-10-17 10:34:02 +02:00
Jimmy CaiandGitHub 49e568faac refactor: add .section-title (#36)
For the small title before the card

Remove .widget-title, .archive-date, .taxonomy-type from SCSS, as they all share the same style.
2020-10-12 20:49:56 +02:00
Jimmy Cai 26fedaebd2 refactor(search): include icon using helper/icon 2020-10-12 20:31:40 +02:00
Yasuhiro KobayashiandGitHub 26bdeb3afb feat(i18n): Add Japanese translation (#35) 2020-10-11 10:14:31 +02:00
Jimmy CaiandGitHub c266204f98 Merge branch 'master' into template-search 2020-10-10 10:05:31 +02:00
Jimmy CaiandGitHub 8e8509177d Merge branch 'master' into template-search 2020-10-09 19:12:50 +02:00
Jimmy CaiandGitHub 3bc73a8b48 Merge branch 'master' into template-search 2020-10-05 22:41:47 +02:00
Jimmy Cai 17d77155bf Merge branch 'master' into template-search 2020-10-04 16:27:22 +02:00
Jimmy Cai b027cbf5aa Merge branch 'master' into template-search 2020-10-04 16:05:02 +02:00
Jimmy Cai 84a15e1604 feat(search): return results at typing 2020-10-04 15:53:27 +02:00
Jimmy Cai 8aeb562bb3 feat(search): preload search.json 2020-10-04 15:45:23 +02:00
Jimmy Cai c19780280e feat: add "head" block 2020-09-26 23:00:48 +02:00
Jimmy Cai 6e48765d82 fix(search): HTML escape issue 2020-09-26 22:50:23 +02:00
Jimmy Cai 08102e2f69 feat(exampleSite): add search page and widget 2020-09-26 12:06:56 +02:00
Jimmy Cai 6fa69d7a2b feat: add search icon 2020-09-26 12:05:13 +02:00
Jimmy Cai 21f461ce78 feat(widget): add search widget 2020-09-26 11:40:52 +02:00
Jimmy Cai e5f96c8762 feat: add search template 2020-09-26 11:40:33 +02:00
72 changed files with 1594 additions and 782 deletions
+1 -1
View File
@@ -1 +1 @@
custom: ['https://www.buymeacoffee.com/jimmycai']
ko_fi: jimmycai
+33
View File
@@ -0,0 +1,33 @@
name-template: "v$RESOLVED_VERSION 🌈"
tag-template: "v$RESOLVED_VERSION"
categories:
- title: "🚀 Features"
labels:
- "feature"
- "enhancement"
- title: "🐛 Bug Fixes"
labels:
- "fix"
- "bugfix"
- "bug"
- title: "🧰 Maintenance"
label: "chore"
- title: "♻️ Refactor"
label: refactor
change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
version-resolver:
major:
labels:
- "major"
minor:
labels:
- "minor"
patch:
labels:
- "patch"
default: patch
template: |
## Changes
$CHANGES
+16
View File
@@ -0,0 +1,16 @@
name: Release Drafter
on:
push:
# branches to consider in the event; optional, defaults to all
branches:
- master
jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
# Drafts your next Release notes as Pull Requests are merged into "master"
- uses: release-drafter/release-drafter@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+8 -11
View File
@@ -11,7 +11,7 @@
## Documentation & more information
[Documentation](https://www.notion.so/jimmycai/Hugo-Theme-Stack-511aec5b9ed845ce9b6e3ae0bf7fb6d4) | [中文文档](https://www.notion.so/jimmycai/Hugo-Theme-Stack-511aec5b9ed845ce9b6e3ae0bf7fb6d4)
[Documentation](https://docs.stack.jimmycai.com/) | [中文文档](https://docs.stack.jimmycai.com/v/zh-cn/)
## Introduction
@@ -20,22 +20,19 @@ Stack is a simple card-style Hugo theme designed for bloggers, some of its featu
- Responsive images support
- Lazy load images
- Dark mode
- Local search
- [PhotoSwipe](https://photoswipe.com/) integration
- Archive page template
- Full native JavaScript, no jQuery or any other frameworks are used
- No CSS framework, keep it simple and minimal
- External dependencies (like PhotoSwipe's library) are being loaded selectively, or asynchronously
- Properly cropped thumbnails
The only JavaScript library being used is [node-vibrant](https://github.com/Vibrant-Colors/node-vibrant) for generating colour schemes for articles.
![Page Insight](https://i.imgur.com/0hUWmMh.png)
- Subsection support
## Requirements
This theme uses SCSS and TypeScript. For that reason, it's necessary to use **Hugo ≥ 0.74.0**.
It's necessary to use **Hugo ≥ 0.78.0**.
Use Hugo Extended version if you want to:
Use Hugo Extended version (recommended) if you want to:
* Use the latest feature/fix from `master` branch
* Edit SCSS files
@@ -46,7 +43,7 @@ Use Hugo Extended version if you want to:
Clone / Download this repository to `theme` folder, and edit your site config following `exampleSite/config.toml`.
Check [documentation](https://www.notion.so/jimmycai/Hugo-Theme-Stack-511aec5b9ed845ce9b6e3ae0bf7fb6d4) for more details.
Check [documentation](https://docs.stack.jimmycai.com/) for more details.
## Copyright
@@ -58,9 +55,9 @@ If you want to port this theme to another blogging platform, please let me know
## Sponsoring
If you like this theme, consider supporting its development:
If you like this theme, give it a star, and consider supporting its development:
<a href="https://www.buymeacoffee.com/jimmycai" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-green.png" alt="Buy Me A Coffee" height="60px" width="217px"></a>
[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/C0C530AXX)
Your support is greatly appreciated :)
+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-search" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="10" cy="10" r="7" />
<line x1="21" y1="21" x2="15" y2="15" />
</svg>

After

Width:  |  Height:  |  Size: 355 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="8" cy="12" r="2" />
<rect x="2" y="6" width="20" height="12" rx="6" />
</svg>

After

Width:  |  Height:  |  Size: 369 B

+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<circle cx="16" cy="12" r="2" />
<rect x="2" y="6" width="20" height="12" rx="6" />
</svg>

After

Width:  |  Height:  |  Size: 371 B

+10
View File
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"*": [
"*"
]
},
}
}
+1
View File
@@ -0,0 +1 @@
/* Place your custom SCSS in HUGO_SITE_FOLDER/assets/scss/custom.scss */
+7
View File
@@ -199,6 +199,13 @@
.article-time {
font-size: 1.4rem;
}
.article-preview{
font-size: 1.4rem;
color: var(--card-text-color-tertiary);
margin-top: 10px;
line-height: 1.5;
}
}
}
@@ -3,11 +3,6 @@
* https://xyproto.github.io/splash/docs/monokai.html
*/
:root {
--pre-text-color: #f8f8f2;
--pre-background-color: #272822;
}
/* Background */
.chroma {
color: #f8f8f2;
@@ -2,10 +2,6 @@
* Style: monokailight
* https://xyproto.github.io/splash/docs/monokailight.html
*/
:root {
--pre-text-color: #272822;
--pre-background-color: #fafafa;
}
/* Background */
.chroma {
+1 -39
View File
@@ -1,41 +1,3 @@
.archives-group {
margin-bottom: var(--section-separation);
.archives-date {
text-transform: uppercase;
margin-bottom: 10px;
font-size: 1.6rem;
font-weight: bold;
a {
color: var(--body-text-color);
}
}
}
.template-archives {
.category-list {
margin-bottom: var(--section-separation);
overflow-x: auto;
.article-list--tile {
display: flex;
padding-bottom: 15px;
article {
width: 250px;
height: 150px;
margin-right: 20px;
flex-shrink: 0;
.article-title {
margin: 0;
font-size: 1.8rem;
}
.article-details {
padding: 20px;
}
}
}
}
}
}
@@ -1,12 +1,4 @@
.taxonomy-type {
text-transform: uppercase;
color: var(--body-text-color);
font-weight: bold;
margin-bottom: 5px;
font-size: 1.6rem;
}
.taxonomy-card {
.section-card {
border-radius: var(--card-border-radius);
background-color: var(--card-background);
padding: var(--small-card-padding);
@@ -17,37 +9,37 @@
--separation: 15px;
.taxonomy-term {
.section-term {
font-size: 2.2rem;
margin: 0;
margin-top: calc(var(--separation) / 2);
color: var(--card-text-color-main);
& + .taxonomy-description {
& + .section-description {
margin-top: var(--separation);
}
}
.taxonomy-description {
.section-description {
font-weight: normal;
color: var(--card-text-color-secondary);
font-size: 1.6rem;
margin: 0;
}
.taxonomy-details {
.section-details {
flex-grow: 1;
margin-right: 20px;
}
.taxonomy-image {
.section-image {
img {
width: 60px;
height: 60px;
}
}
.taxonomy-count {
.section-count {
color: var(--card-text-color-tertiary);
font-size: 1.4rem;
margin: 0;
@@ -55,3 +47,29 @@
text-transform: uppercase;
}
}
.subsection-list {
margin-bottom: var(--section-separation);
overflow-x: auto;
.article-list--tile {
display: flex;
padding-bottom: 15px;
article {
width: 250px;
height: 150px;
margin-right: 20px;
flex-shrink: 0;
.article-title {
margin: 0;
font-size: 1.8rem;
}
.article-details {
padding: 20px;
}
}
}
}
+82
View File
@@ -0,0 +1,82 @@
.search-form {
margin-bottom: var(--section-separation);
position: relative;
--button-size: 80px;
&.widget {
--button-size: 60px;
label {
font-size: 1.3rem;
top: 10px;
}
input {
font-size: 1.5rem;
padding: 30px 20px 15px 20px;
}
}
p {
position: relative;
margin: 0;
}
label {
position: absolute;
top: 15px;
left: 20px;
font-size: 1.4rem;
color: var(--card-text-color-tertiary);
}
input {
padding: 40px 20px 20px;
border-radius: var(--card-border-radius);
background-color: var(--card-background);
box-shadow: var(--shadow-l1);
color: var(--card-text-color-main);
width: 100%;
border: 0;
-webkit-appearance: none;
transition: box-shadow 0.3s ease;
font-size: 1.8rem;
&:focus {
outline: 0;
box-shadow: var(--shadow-l2);
}
}
button {
position: absolute;
right: 0;
top: 0;
height: 100%;
width: var(--button-size);
cursor: pointer;
background-color: transparent;
border: 0;
padding: 0 10px;
&:focus {
outline: 0;
svg {
stroke-width: 2;
color: var(--accent-color);
}
}
svg {
color: var(--card-text-color-secondary);
stroke-width: 1.33;
transition: all 0.3s ease;
width: 20px;
height: 20px;
}
}
}
+2 -1
View File
@@ -129,6 +129,8 @@
margin-top: var(--sidebar-element-separation);
margin-bottom: 0;
overflow-y: auto;
flex-grow: 1;
font-size: 1.5rem;
@media (min-width: $on-desktop-large) {
margin-top: 30px;
@@ -192,7 +194,6 @@
display: inline-flex;
align-items: center;
color: var(--body-text-color);
font-size: 1.5rem;
@media (max-width: $on-desktop-large) {
font-size: 1.4rem;
+27
View File
@@ -134,3 +134,30 @@
}
}
}
[data-scheme="dark"] {
#dark-mode-toggle {
color: var(--accent-color);
font-weight: 700;
.icon-tabler-toggle-left {
display: none;
}
.icon-tabler-toggle-right {
display: unset;
}
}
}
#dark-mode-toggle {
margin-top: auto;
color: var(--body-text-color);
display: flex;
align-items: center;
cursor: pointer;
.icon-tabler-toggle-right {
display: none;
}
}
+13 -23
View File
@@ -1,13 +1,4 @@
.widget {
.widget-title {
text-transform: uppercase;
color: var(--body-text-color);
font-weight: bold;
margin: 0;
margin-bottom: 10px;
font-size: 1.6rem;
}
.widget-icon {
svg {
width: 32px;
@@ -45,27 +36,26 @@
/* Archives widget */
.widget.archives {
.widget-archive--list {
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l1);
background-color: var(--card-background);
}
.archives-year {
margin-bottom: 10px;
&:not(:last-of-type) {
border-bottom: 1.5px solid var(--card-separator-color);
}
a {
background-color: var(--card-background);
padding: 15px 25px;
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l1);
font-size: 1.4rem;
padding: 18px 25px;
display: flex;
transition: box-shadow 0.3s ease;
&:hover {
box-shadow: var(--shadow-l2);
}
@media (max-width: $on-desktop-large) {
font-size: 1.4rem;
}
span.year {
flex: 1;
color: var(--card-text-color-main);
font-weight: bold;
}
span.count {
+19 -2
View File
@@ -21,8 +21,11 @@
@import "partials/base.scss";
@import "partials/layout/archives.scss";
@import "partials/layout/article.scss";
@import "partials/layout/taxonomy.scss";
@import "partials/layout/list.scss";
@import "partials/layout/404.scss";
@import "partials/layout/search.scss";
@import "custom.scss";
a {
text-decoration: none;
@@ -40,4 +43,18 @@ a {
box-shadow: 0px -10px 0px rgba(var(--link-background-color), var(--link-background-opacity-hover)) inset;
}
}
}
}
.section-title {
text-transform: uppercase;
margin-top: 0;
margin-bottom: 10px;
display: block;
font-size: 1.6rem;
font-weight: bold;
color: var(--body-text-color);
a {
color: var(--body-text-color);
}
}
+15 -4
View File
@@ -1,6 +1,18 @@
$defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6;
$defaultTagColors: #fff, #fff, #fff, #fff, #fff;
[data-scheme="light"] {
--pre-text-color: #272822;
--pre-background-color: #fafafa;
@import "partials/highlight/light.scss";
}
[data-scheme="dark"] {
--pre-text-color: #f8f8f2;
--pre-background-color: #272822;
@import "partials/highlight/dark.scss";
}
/*
* Global style
*/
@@ -13,7 +25,6 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--main-top-padding: 50px;
}
--body-background: #f5f5fa;
--accent-color: #34495e;
@@ -25,7 +36,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--section-separation: 40px;
@media (prefers-color-scheme: dark) {
[data-scheme="dark"] {
--body-background: #303030;
--accent-color: #ecf0f1;
--accent-color-darker: #bdc3c7;
@@ -72,7 +83,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--small-card-padding: 25px 20px;
}
@media (prefers-color-scheme: dark) {
[data-scheme="dark"] {
--card-background: #424242;
--card-background-selected: rgba(255, 255, 255, 0.16);
--card-text-color-main: rgba(255, 255, 255, 0.9);
@@ -116,7 +127,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--table-border-color: #dadada;
--tr-even-background-color: #efefee;
@media (prefers-color-scheme: dark) {
[data-scheme="dark"] {
--code-background-color: #272822;
--code-text-color: rgba(255, 255, 255, 0.9);
+86
View File
@@ -0,0 +1,86 @@
type colorScheme = 'light' | 'dark' | 'auto';
class StackColorScheme {
private localStorageKey = 'StackColorScheme';
private currentScheme: colorScheme;
private systemPreferScheme: colorScheme;
constructor(toggleEl: HTMLElement) {
this.bindMatchMedia();
this.currentScheme = this.getSavedScheme();
if (toggleEl)
this.bindClick(toggleEl);
if (document.body.style.transition == '')
document.body.style.setProperty('transition', 'background-color .3s ease');
}
private saveScheme() {
localStorage.setItem(this.localStorageKey, this.currentScheme);
}
private bindClick(toggleEl) {
toggleEl.addEventListener('click', (e) => {
if (this.isDark()) {
/// Disable dark mode
this.currentScheme = 'light';
}
else {
this.currentScheme = 'dark';
}
this.setBodyClass();
if (this.currentScheme == this.systemPreferScheme) {
/// Set to auto
this.currentScheme = 'auto';
}
this.saveScheme();
})
}
private isDark() {
return (this.currentScheme == 'dark' || this.currentScheme == 'auto' && this.systemPreferScheme == 'dark');
}
private dispatchEvent(colorScheme: colorScheme) {
const event = new CustomEvent('onColorSchemeChange', {
detail: colorScheme
});
window.dispatchEvent(event);
}
private setBodyClass() {
if (this.isDark()) {
document.body.dataset.scheme = 'dark';
}
else {
document.body.dataset.scheme = 'light';
}
this.dispatchEvent(document.body.dataset.scheme as colorScheme);
}
private getSavedScheme(): colorScheme {
const savedScheme = localStorage.getItem(this.localStorageKey);
if (savedScheme == 'light' || savedScheme == 'dark' || savedScheme == 'auto') return savedScheme;
else return 'auto';
}
private bindMatchMedia() {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
if (e.matches) {
this.systemPreferScheme = 'dark';
}
else {
this.systemPreferScheme = 'light';
}
this.setBodyClass();
});
}
}
export default StackColorScheme;
+34
View File
@@ -0,0 +1,34 @@
/**
* createElement
* Edited from:
* @link https://stackoverflow.com/a/42405694
*/
function createElement(tag, attrs, children) {
var element = document.createElement(tag);
for (let name in attrs) {
if (name && attrs.hasOwnProperty(name)) {
let value = attrs[name];
if (name == "dangerouslySetInnerHTML") {
element.innerHTML = value.__html;
}
else if (value === true) {
element.setAttribute(name, name);
} else if (value !== false && value != null) {
element.setAttribute(name, value.toString());
}
}
}
for (let i = 2; i < arguments.length; i++) {
let child = arguments[i];
if (child) {
element.appendChild(
child.nodeType == null ?
document.createTextNode(child.toString()) : child);
}
}
return element;
}
export default createElement;
+96 -251
View File
@@ -1,287 +1,132 @@
import { loadScript, loadStyle } from './utils';
declare global {
interface Window {
PhotoSwipe: any;
PhotoSwipeUI_Default: any
}
}
/**
* Init PhotoSwipe
* From: https://photoswipe.com/documentation/getting-started.html
* @param gallerySelector
*/
var initPhotoSwipeFromDOM = function (gallerySelector) {
interface PhotoSwipeItem {
w: number;
h: number;
src: string;
msrc: string;
title?: string;
el: HTMLElement;
}
// parse slide data (url, title, size ...) from DOM elements
// (children of gallerySelector)
var parseThumbnailElements = function (el) {
var thumbElements = el.childNodes,
numNodes = thumbElements.length,
items = [],
figureEl,
linkEl,
size,
item;
class StackGallery {
private galleryUID: number;
private items: PhotoSwipeItem[] = [];
for (var i = 0; i < numNodes; i++) {
figureEl = thumbElements[i]; // <figure> element
// include only element nodes
if (figureEl.nodeType !== 1) {
continue;
}
linkEl = figureEl.children[0]; // <a> element
size = linkEl.getAttribute('data-size').split('x');
// create slide object
item = {
src: linkEl.getAttribute('href'),
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
};
if (figureEl.children.length > 1) {
// <figcaption> content
item.title = figureEl.children[1].innerHTML;
}
if (linkEl.children.length > 0) {
// <img> thumbnail element, retrieving thumbnail url
item.msrc = linkEl.children[0].getAttribute('src');
}
item.el = figureEl; // save link to element for getThumbBoundsFn
items.push(item);
}
return items;
};
// find nearest parent element
var closest = function closest(el, fn) {
return el && (fn(el) ? el : closest(el.parentNode, fn));
};
// triggers when user clicks on thumbnail
var onThumbnailsClick = function (e) {
e = e || window.event;
e.preventDefault ? e.preventDefault() : e.returnValue = false;
var eTarget = e.target || e.srcElement;
// find root element of slide
var clickedListItem = closest(eTarget, function (el) {
return (el.tagName && el.tagName.toUpperCase() === 'FIGURE');
});
if (!clickedListItem) {
constructor(container: HTMLElement, galleryUID = 1) {
if (window.PhotoSwipe == undefined || window.PhotoSwipeUI_Default == undefined) {
console.error("PhotoSwipe lib not loaded.");
return;
}
// find index of clicked item by looping through all child nodes
// alternatively, you may define index via data- attribute
var clickedGallery = clickedListItem.parentNode,
childNodes = clickedListItem.parentNode.childNodes,
numChildNodes = childNodes.length,
nodeIndex = 0,
index;
this.galleryUID = galleryUID;
for (var i = 0; i < numChildNodes; i++) {
if (childNodes[i].nodeType !== 1) {
continue;
StackGallery.createGallery(container);
this.loadItems(container);
this.bindClick();
}
private loadItems(container: HTMLElement) {
this.items = [];
const figures = container.querySelectorAll('figure');
for (const el of figures) {
const figcaption = el.querySelector('figcaption'),
img = el.querySelector('img');
let aux: PhotoSwipeItem = {
w: parseInt(img.getAttribute('width')),
h: parseInt(img.getAttribute('height')),
src: img.src,
msrc: img.getAttribute('data-thumb') || img.src,
el: el
}
if (childNodes[i] === clickedListItem) {
index = nodeIndex;
break;
if (figcaption) {
aux.title = figcaption.innerHTML;
}
nodeIndex++;
this.items.push(aux);
}
}
if (index >= 0) {
// open PhotoSwipe if valid index found
openPhotoSwipe(index, clickedGallery);
}
return false;
};
public static createGallery(container: HTMLElement) {
const figuresEl = container.querySelectorAll('figure');
// parse picture index and gallery index from URL (#&pid=1&gid=2)
var photoswipeParseHash = function () {
var hash = window.location.hash.substring(1),
params = {};
let currentGallery = [];
if (hash.length < 5) {
return params;
}
var vars = hash.split('&');
for (var i = 0; i < vars.length; i++) {
if (!vars[i]) {
continue;
for (const figure of figuresEl) {
if (!currentGallery.length) {
/// First iteration
currentGallery = [figure];
}
var pair = vars[i].split('=');
if (pair.length < 2) {
continue;
else if (figure.previousElementSibling === currentGallery[currentGallery.length - 1]) {
/// Adjacent figures
currentGallery.push(figure);
}
else if (currentGallery.length) {
/// End gallery
StackGallery.wrap(currentGallery);
currentGallery = [figure];
}
params[pair[0]] = pair[1];
}
if (params.gid) {
params.gid = parseInt(params.gid, 10);
if (currentGallery.length > 0) {
StackGallery.wrap(currentGallery);
}
}
return params;
};
/**
* Wrap adjacent figure tags with div.gallery
* @param figures
*/
public static wrap(figures: HTMLElement[]) {
const galleryContainer = document.createElement('div');
galleryContainer.className = 'gallery';
var openPhotoSwipe = function (index, galleryElement, disableAnimation, fromURL) {
var pswpElement = document.querySelectorAll('.pswp')[0],
gallery,
options,
items;
const parentNode = figures[0].parentNode,
first = figures[0];
items = parseThumbnailElements(galleryElement);
parentNode.insertBefore(galleryContainer, first)
// define options (if needed)
options = {
for (const figure of figures) {
galleryContainer.appendChild(figure);
}
}
// define gallery index (for URL)
galleryUID: galleryElement.getAttribute('data-pswp-uid'),
getThumbBoundsFn: function (index) {
// See Options -> getThumbBoundsFn section of documentation for more info
var thumbnail = items[index].el.getElementsByTagName('img')[0], // find thumbnail
public open(index: number) {
const pswp = document.querySelector('.pswp') as HTMLDivElement;
const ps = new window.PhotoSwipe(pswp, window.PhotoSwipeUI_Default, this.items, {
index: index,
galleryUID: this.galleryUID,
getThumbBoundsFn: (index) => {
const thumbnail = this.items[index].el.getElementsByTagName('img')[0],
pageYScroll = window.pageYOffset || document.documentElement.scrollTop,
rect = thumbnail.getBoundingClientRect();
return { x: rect.left, y: rect.top + pageYScroll, w: rect.width };
}
});
};
// PhotoSwipe opened from URL
if (fromURL) {
if (options.galleryPIDs) {
// parse real index when custom PIDs are used
// http://photoswipe.com/documentation/faq.html#custom-pid-in-url
for (var j = 0; j < items.length; j++) {
if (items[j].pid == index) {
options.index = j;
break;
}
}
} else {
// in URL indexes start from 1
options.index = parseInt(index, 10) - 1;
}
} else {
options.index = parseInt(index, 10);
}
// exit if index not found
if (isNaN(options.index)) {
return;
}
if (disableAnimation) {
options.showAnimationDuration = 0;
}
// Pass data to PhotoSwipe and initialize it
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
};
// loop through all gallery elements and bind events
var galleryElements = document.querySelectorAll(gallerySelector);
for (var i = 0, l = galleryElements.length; i < l; i++) {
galleryElements[i].setAttribute('data-pswp-uid', i + 1);
galleryElements[i].onclick = onThumbnailsClick;
ps.init();
}
// Parse URL and open gallery if it contains #&pid=3&gid=1
var hashData = photoswipeParseHash();
if (hashData.pid && hashData.gid) {
openPhotoSwipe(hashData.pid, galleryElements[hashData.gid - 1], true, true);
}
};
private bindClick() {
for (const [index, item] of this.items.entries()) {
const a = item.el.querySelector('a');
/**
* Wrap adjacent figure tags with div.gallery, and append style
* Reference: https://github.com/xieranmaya/blog/issues/6
* @param gallery
*/
function wrap(gallery: HTMLElement[]) {
let galleryContainer = document.createElement('div');
galleryContainer.className = 'gallery';
let parentNode = gallery[0].parentNode,
first = gallery[0];
parentNode.insertBefore(galleryContainer, first)
for (let j = 0; j < gallery.length; ++j) {
const width = gallery[j].querySelector('img').width,
height = gallery[j].querySelector('img').height;
gallery[j].style.flexGrow = `${width * 100 / height}`;
gallery[j].style.flexBasis = `${width * 240 / height}px`;
galleryContainer.appendChild(gallery[j]);
a.addEventListener('click', (e) => {
e.preventDefault();
this.open(index);
})
}
}
}
/**
* Load PhotoSwipe library dynamically
*/
function loadPhotoSwipe() {
const tasks = [
loadScript("https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"),
loadScript("https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"),
loadStyle("https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css"),
loadStyle("https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css")
];
return Promise.all(tasks);
}
/**
* Create gallery
* @param selector
*/
function createGallery(selector: string) {
const figures = document.querySelector(selector).querySelectorAll('figure');
if (figures.length) {
let currentGallery = [figures[0]];
for (let i = 1; i < figures.length; ++i) {
if (figures[i].previousElementSibling === currentGallery[currentGallery.length - 1]) {
/* Adjacent */
currentGallery.push(figures[i]);
}
else {
/* End gallery */
wrap(currentGallery);
currentGallery = [figures[i]];
}
}
if (currentGallery.length > 0) {
wrap(currentGallery);
}
/**
* Load PhotoSwipe library, and then initialize
*/
loadPhotoSwipe().then(() => {
const pswp = document.querySelector('.pswp') as HTMLDivElement;
pswp.style.removeProperty('display');
initPhotoSwipeFromDOM(`${selector} .gallery`);
})
}
}
export {
createGallery
}
export default StackGallery;
+19 -21
View File
@@ -5,10 +5,11 @@
* @website: https://jimmycai.com
* @link: https://github.com/CaiJimmy/hugo-theme-stack
*/
import { createGallery } from "./gallery"
import { getColor } from './color';
import menu from './menu';
import StackGallery from "ts/gallery";
import { getColor } from 'ts/color';
import menu from 'ts/menu';
import createElement from 'ts/createElement';
import StackColorScheme from 'ts/colorScheme';
let Stack = {
init: () => {
@@ -17,24 +18,11 @@ let Stack = {
*/
menu();
if (document.querySelector('.article-content')) {
createGallery('.article-content');
const articleContent = document.querySelector('.article-content') as HTMLElement;
if (articleContent) {
new StackGallery(articleContent);
}
/**
* Add color to tags
*/
document.querySelectorAll('.color-tag').forEach(async (tag: HTMLLinkElement) => {
const imageURL = tag.getAttribute('data-image'),
key = tag.getAttribute('data-key'),
hash = tag.getAttribute('data-hash');
const colors = await getColor(key, hash, imageURL);
tag.style.color = colors.Vibrant.bodyTextColor;
tag.style.background = colors.Vibrant.hex;
})
/**
* Add linear gradient background to tile style article
*/
@@ -65,6 +53,8 @@ let Stack = {
observer.observe(articleTile)
}
new StackColorScheme(document.getElementById('dark-mode-toggle'));
}
}
@@ -74,4 +64,12 @@ window.addEventListener('load', () => {
}, 0);
})
window.Stack = Stack;
declare global {
interface Window {
createElement: any;
Stack: any
}
}
window.Stack = Stack;
window.createElement = createElement;
+263
View File
@@ -0,0 +1,263 @@
interface pageData {
title: string,
date: string,
permalink: string,
content: string,
image?: string,
preview: string,
matchCount: number
}
/**
* Escape HTML tags as HTML entities
* Edited from:
* @link https://stackoverflow.com/a/5499821
*/
const tagsToReplace = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'…': '&hellip;'
};
function replaceTag(tag) {
return tagsToReplace[tag] || tag;
}
function replaceHTMLEnt(str) {
return str.replace(/[&<>"]/g, replaceTag);
}
function escapeRegExp(string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
}
class Search {
private data: pageData[];
private form: HTMLFormElement;
private input: HTMLInputElement;
private list: HTMLDivElement;
private resultTitle: HTMLHeadElement;
private resultTitleTemplate: string;
constructor({ form, input, list, resultTitle, resultTitleTemplate }) {
this.form = form;
this.input = input;
this.list = list;
this.resultTitle = resultTitle;
this.resultTitleTemplate = resultTitleTemplate;
this.handleQueryString();
this.bindQueryStringChange();
this.bindSearchForm();
}
private async searchKeywords(keywords: string[]) {
const rawData = await this.getData();
let results: pageData[] = [];
/// Sort keywords by their length
keywords.sort((a, b) => {
return b.length - a.length
});
for (const item of rawData) {
let result = {
...item,
preview: '',
matchCount: 0
}
let matched = false;
for (const keyword of keywords) {
if (keyword === '') continue;
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) {
return '<mark>' + match + '</mark>';
}
private async doSearch(keywords: string[]) {
const startTime = performance.now();
const results = await this.searchKeywords(keywords);
this.clear();
for (const item of results) {
this.list.append(Search.render(item));
}
const endTime = performance.now();
this.resultTitle.innerText = this.generateResultTitle(results.length, ((endTime - startTime) / 1000).toPrecision(1));
}
private generateResultTitle(resultLen, time) {
return this.resultTitleTemplate.replace("#PAGES_COUNT", resultLen).replace("#TIME_SECONDS", time);
}
public async getData() {
if (!this.data) {
/// Not fetched yet
const jsonURL = this.form.dataset.json;
this.data = await fetch(jsonURL).then(res => res.json());
}
return this.data;
}
private bindSearchForm() {
let lastSearch = '';
const eventHandler = (e) => {
e.preventDefault();
const keywords = this.input.value;
Search.updateQueryString(keywords, true);
if (keywords === '') {
return this.clear();
}
if (lastSearch === keywords) return;
lastSearch = keywords;
this.doSearch(keywords.split(' '));
}
this.input.addEventListener('input', eventHandler);
this.input.addEventListener('compositionend', eventHandler);
}
private clear() {
this.list.innerHTML = '';
this.resultTitle.innerText = '';
}
private bindQueryStringChange() {
window.addEventListener('popstate', (e) => {
this.handleQueryString()
})
}
private handleQueryString() {
const pageURL = new URL(window.location.toString());
const keywords = pageURL.searchParams.get('keyword');
this.input.value = keywords;
if (keywords) {
this.doSearch(keywords.split(' '));
}
else {
this.clear()
}
}
private static updateQueryString(keywords: string, replaceState = false) {
const pageURL = new URL(window.location.toString());
if (keywords === '') {
pageURL.searchParams.delete('keyword')
}
else {
pageURL.searchParams.set('keyword', keywords);
}
if (replaceState) {
window.history.replaceState('', '', pageURL.toString());
}
else {
window.history.pushState('', '', pageURL.toString());
}
}
public static render(item: pageData) {
return <article>
<a href={item.permalink}>
<div class="article-details">
<h2 class="article-title" dangerouslySetInnerHTML={{ __html: item.title }}></h2>
<secion class="article-preview" dangerouslySetInnerHTML={{ __html: item.preview }}></secion>
</div>
{item.image &&
<div class="article-image">
<img src={item.image} loading="lazy" />
</div>
}
</a>
</article>;
}
}
declare global {
interface Window {
searchResultTitleTemplate: string;
}
}
window.addEventListener('load', () => {
setTimeout(function () {
const searchForm = document.querySelector('.search-form') as HTMLFormElement,
searchInput = searchForm.querySelector('input') as HTMLInputElement,
searchResultList = document.querySelector('.search-result--list') as HTMLDivElement,
searchResultTitle = document.querySelector('.search-result--title') as HTMLHeadingElement;
new Search({
form: searchForm,
input: searchInput,
list: searchResultList,
resultTitle: searchResultTitle,
resultTitleTemplate: window.searchResultTitleTemplate
});
}, 0);
})
export default Search;
-43
View File
@@ -1,43 +0,0 @@
/**
* Load script asynchronous
* @return {Promise}
* @param url
*/
const loadScript = function (url) {
return new Promise(resolve => {
var scriptTag = document.createElement('script');
scriptTag.src = url;
scriptTag.onload = () => {
resolve();
};
document.head.appendChild(scriptTag);
})
};
/**
* Load style asynchronous
* @return {Promise}
* @param url
*/
const loadStyle = function (url) {
return new Promise(resolve => {
var link = document.createElement('link');
link.href = url;
link.type = "text/css";
link.rel = "stylesheet";
link.onload = () => {
resolve();
};
document.head.appendChild(link);
});
};
export {
loadScript,
loadStyle
}
-97
View File
@@ -1,97 +0,0 @@
baseurl = "https://example.com"
languageCode = "en-us"
theme = "hugo-theme-stack"
paginate = 5
title = "Example Site"
disqusShortname = "hugo-theme-stack" # Change it to your Disqus shortname before using
DefaultContentLanguage = "en" # Theme i18n support
[permalinks]
post = "/p/:slug/"
page = "/:slug/"
[params]
mainSections = ["post"]
featuredImageField = "image"
rssFullContent = true
[params.dateFormat]
published = "Jan 02, 2006"
lastUpdated = "Jan 02, 2006 15:04 MST"
[params.sidebar]
emoji = "🍥"
avatar = "img/avatar.png"
subtitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
[params.article]
math = false
[params.article.license]
enabled = true
default = "Licensed under CC BY-NC-SA 4.0"
[params.comments]
enabled = true
provider = "disqus" # Available: disqus, utterances
[params.comments.utterances]
repo = ""
issueTerm = "pathname"
label = "" # Optional
theme = "preferred-color-scheme"
[params.widgets]
enabled = ['archives', 'tag-cloud']
[params.widgets.archives]
limit = 5
### Archives page relative URL
path = "archives"
[params.widgets.tagCloud]
limit = 10
[params.opengraph]
[params.opengraph.twitter]
site = ""
card = "summary_large_image"
[params.defaultImage]
[params.defaultImage.opengraph]
enabled = false
local = false
src = ""
[menu]
[[menu.main]]
identifier = "home"
name = "Home"
url = "/"
weight = -100
pre = "home"
[[menu.main]]
identifier = "about-cn"
name = "About"
url = "about"
weight = -90
pre = "user"
[[menu.main]]
identifier = "archives"
name = "Archives"
url = "archives"
weight = -70
pre = "archives"
[related]
includeNewer = true
threshold = 60
toLower = false
[[related.indices]]
name = "tags"
weight = 100
[[related.indices]]
name = "categories"
weight = 200
[markup]
[markup.highlight]
noClasses = false
+134
View File
@@ -0,0 +1,134 @@
baseurl: https://example.com
languageCode: en-us
theme: hugo-theme-stack
paginate: 5
title: Example Site
# Change it to your Disqus shortname before using
disqusShortname: hugo-theme-stack
# Theme i18n support
# Available values: en, fr, id, ja, ko, pt-br, zh-cn
DefaultContentLanguage: en
permalinks:
post: /p/:slug/
page: /:slug/
params:
mainSections:
- post
featuredImageField: image
rssFullContent: true
favicon:
footer:
since: 2020
customText:
dateFormat:
published: Jan 02, 2006
lastUpdated: Jan 02, 2006 15:04 MST
sidebar:
emoji: 🍥
subtitle: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
avatar:
local: true
src: img/avatar.png
article:
math: false
license:
enabled: true
default: Licensed under CC BY-NC-SA 4.0
comments:
enabled: true
provider: disqus
utterances:
repo:
issueTerm: pathname
label:
theme: preferred-color-scheme
widgets:
enabled:
- search
- archives
- tag-cloud
archives:
limit: 5
path: archives
tagCloud:
limit: 10
opengraph:
twitter:
# Your Twitter username
site:
# Available values: summary, summary_large_image
card: summary_large_image
defaultImage:
opengraph:
enabled: false
local: false
src:
colorScheme:
# Display toggle
toggle: true
# Available values: auto, light, dark
default: auto
imageProcessing:
cover:
enabled: true
content:
enabled: true
menu:
main:
- identifier: home
name: Home
url: /
weight: -100
pre: home
- identifier: about
name: About
url: about
weight: -90
pre: user
- identifier: archives
name: Archives
url: archives
weight: -70
pre: archives
- identifier: search
name: Search
url: search
weight: -60
pre: search
related:
includeNewer: true
threshold: 60
toLower: false
indices:
- name: tags
weight: 100
- name: categories
weight: 200
markup:
highlight:
noClasses: false
@@ -3,4 +3,7 @@ title: "Test"
description: "This is a example category"
slug: "test"
image: "hutomo-abrianto-l2jk-uxb1BY-unsplash.jpg"
style:
background: "#2a9d8f"
color: "#fff"
---
+8
View File
@@ -0,0 +1,8 @@
---
title: "Search"
slug: "search"
layout: "search"
outputs:
- html
- json
---
@@ -13,14 +13,6 @@ Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-manageme
<!--more-->
---
## Instagram Simple Shortcode
{{< instagram_simple BGvuInzyFAe hidecaption >}}
<br>
---
## YouTube Privacy Enhanced Shortcode
{{< youtube ZJthWmvUzzc >}}
-23
View File
@@ -1,23 +0,0 @@
[toggleMenu]
other = "Toggle Menu"
[relatedContents]
other = "Related contents"
[lastUpdatedOn]
other ="Last updated on"
[widgetArchivesTitle]
other = "Archives"
[widgetArchivesMore]
other = "More"
[widgetTagCloudTitle]
other = "Tags"
[notFoundTitle]
other = "Not Found"
[notFoundSubtitle]
other = "This page does not exist."
+57
View File
@@ -0,0 +1,57 @@
toggleMenu:
other: Toggle Menu
darkMode:
other: Dark Mode
list:
page:
one: "{{ .Count }} page"
other: "{{ .Count }} pages"
section:
other: Section
subsection:
one: Subsection
other: Subsections
archives:
categories:
other: Categories
article:
relatedContents:
other: Related contents
lastUpdatedOn:
other: Last updated on
notFound:
title:
other: Not Found
subtitle:
other: This page does not exist.
widget:
archives:
title:
other: Archives
more:
other: More
tagCloud:
title:
other: Tags
search:
title:
other: Search
placeholder:
other: Type something...
resultTitle:
other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
footer:
builtWith:
other: Built with {{ .Generator }}
designedBy:
other: Theme {{ .Theme }} designed by {{ .DesignedBy }}
+38
View File
@@ -0,0 +1,38 @@
toggleMenu:
other: Afficher le menu
article:
relatedContents:
other: Contenus liés
lastUpdatedOn:
other: Dernière mise à jour le
notFound:
title:
other: Page non trouvée
subtitle:
other: Cette page n'existe pas.
widget:
archives:
title:
other: Archives
more:
other: Autres
tagCloud:
title:
other: Mots clés
search:
title:
other: Rechercher
placeholder:
other: Cherchez un article, une publication, etc.
resultTitle:
other: "#PAGES_COUNT pages (#TIME_SECONDS secondes)"
footer:
builtWith:
other: Généré avec {{ .Generator }}
designedBy:
other: Thème {{ .Theme }} conçu par {{ .DesignedBy }}
+38
View File
@@ -0,0 +1,38 @@
toggleMenu:
other: Tampilkan Menu
article:
relatedContents:
other: Konten terkait
lastUpdatedOn:
other: Terakhir diperbarui pada
notFound:
title:
other: Not Found
subtitle:
other: Halaman ini tidak ada.
widget:
archives:
title:
other: Arsip
more:
other: Lebih
tagCloud:
title:
other: Tag
search:
title:
other: Cari
placeholder:
other: Ketik sesuatu...
resultTitle:
other: "#PAGES_COUNT halaman (#TIME_SECONDS detik)"
footer:
builtWith:
other: Dibangun dengan {{ .Generator }}
designedBy:
other: Tema {{ .Theme }} dirancang oleh {{ .DesignedBy }}
+24
View File
@@ -0,0 +1,24 @@
toggleMenu:
other: メニューを開く・閉じる
article:
relatedContents:
other: 関連するコンテンツ
lastUpdatedOn:
other: 最終更新
notFound:
title:
other: 404 Not Found
subtitle:
other: 指定されたページは存在しません。
widget:
archives:
title:
other: アーカイブ
more:
other: さらに見る
tagCloud:
title:
other: タグ
+38
View File
@@ -0,0 +1,38 @@
toggleMenu:
other: 메뉴 여닫기
article:
relatedContents:
other: 관련 글
lastUpdatedOn:
other: "마지막 수정: "
notFound:
title:
other: 찾을 수 없음
subtitle:
other: 페이지를 찾을 수 없습니다.
widget:
archives:
title:
other: 보관함
more:
other: 더보기
tagCloud:
title:
other: 태그
search:
title:
other: 검색
placeholder:
other: 검색어를 입력하세요...
resultTitle:
other: "#PAGES_COUNT 페이지 (#TIME_SECONDS 초)"
footer:
builtWith:
other: "{{ .Generator }}로 만듦"
designedBy:
other: "{{ .DesignedBy }}의 {{ .Theme }} 테마 사용 중"
+42
View File
@@ -0,0 +1,42 @@
toggleMenu:
other: Alternar Menu
archives:
categories:
other: Categorias
article:
relatedContents:
other: Conteúdos Relacionados
lastUpdatedOn:
other: Última atualização em
notFound:
title:
other: Não Encontrado
subtitle:
other: Esta página não existe.
widget:
archives:
title:
other: Arquivos
more:
other: Mais
tagCloud:
title:
other: Tags
search:
title:
other: Busca
placeholder:
other: Digite algo...
resultTitle:
other: "#PAGES_COUNT páginas (#TIME_SECONDS segundos)"
footer:
builtWith:
other: Criado com {{ .Generator }}
designedBy:
other: Tema {{ .Theme }} desenvolvido por {{ .DesignedBy }}
-23
View File
@@ -1,23 +0,0 @@
[toggleMenu]
other = "切换菜单"
[relatedContents]
other = "相关文章"
[lastUpdatedOn]
other ="最后更新于"
[widgetArchivesTitle]
other = "归档"
[widgetArchivesMore]
other = "更多"
[widgetTagCloudTitle]
other = "标签云"
[notFoundTitle]
other = "404 错误"
[notFoundSubtitle]
other = "页面不存在"
+39
View File
@@ -0,0 +1,39 @@
toggleMenu:
other: 切换菜单
darkMode:
other: 暗色模式
archives:
categories:
other: 分类
article:
relatedContents:
other: 相关文章
lastUpdatedOn:
other: 最后更新于
notFound:
title:
other: 404 错误
subtitle:
other: 页面不存在
widget:
archives:
title:
other: 归档
more:
other: 更多
tagCloud:
title:
other: 标签云
search:
title:
other: 搜索
placeholder:
other: 输入关键词...
resultTitle:
other: "#PAGES_COUNT 个结果 (用时 #TIME_SECONDS 秒)"
+2 -2
View File
@@ -1,7 +1,7 @@
{{ define "main" }}
<div class="not-found-card">
<h1 class="article-title">{{ T "notFoundTitle" }}</h1>
<h2 class="article-subtitle">{{ T "notFoundSubtitle" }}</h2>
<h1 class="article-title">{{ T "notFound.title" }}</h1>
<h2 class="article-subtitle">{{ T "notFound.subtitle" }}</h2>
</div>
{{ partialCached "footer/footer" . }}
{{ end }}
+19 -11
View File
@@ -1,19 +1,27 @@
{{- $image := .Page.Resources.GetMatch (printf "%s" (.Destination | safeURL)) -}}
{{- if $image -}}
{{- $small := $image.Resize "480x" -}}
{{- $big := $image.Resize "1024x" -}}
{{- $alt := .PlainText | safeHTML -}}
{{- $caption := "" -}}
{{- with $alt -}}
{{- $caption = . | safeHTML -}}
{{- end -}}
<figure>
<figure style="flex-grow: {{ div (mul $image.Width 100) $image.Height }}; flex-basis: {{ div (mul $image.Width 240) $image.Height }}px">
<a href="{{ $image.RelPermalink }}" data-size="{{ $image.Width }}x{{ $image.Height }}">
<img srcset="{{ $small.RelPermalink }} 480w, {{ $big.RelPermalink }} 1024w"
src="{{ $image.RelPermalink }}" width="{{ $image.Width }}" height="{{ $image.Height }}" loading="lazy"
alt="{{ if $alt }}{{ $alt }}{{ else if $caption }}{{ $caption | markdownify | plainify }}{{ else }}&nbsp;{{ end }}">
{{- $Permalink := $image.RelPermalink -}}
{{- $Width := $image.Width -}}
{{- $Height := $image.Height -}}
{{- $Srcset := "" -}}
{{- if (default true .Page.Site.Params.imageProcessing.content.enabled) -}}
{{- $small := $image.Resize "480x" -}}
{{- $big := $image.Resize "1024x" -}}
{{- $Srcset = printf "%s 480w, %s 1024w" $small.RelPermalink $big.RelPermalink -}}
{{- end -}}
<img src="{{ $Permalink }}"
{{ with $Srcset }}srcset="{{ . }}"{{ end }}
width="{{ $Width }}"
height="{{ $Height }}"
loading="lazy"
{{ with $alt }}alt="{{ . }}"{{ end }}>
</a>
{{ with $caption }}
{{ with $alt }}
<figcaption>{{ . | markdownify }}</figcaption>
{{ end }}
</figure>
+8 -10
View File
@@ -2,14 +2,12 @@
{{ define "main" }}
{{ $categories := ($.Site.GetPage "taxonomyTerm" "categories").Pages }}
{{ if $categories }}
<div class="widget">
<h1 class="widget-title">Categories</h1>
<div class="category-list">
<div class="article-list--tile">
{{ range $categories }}
{{ partial "article-list/tile" (dict "context" . "size" "250x150" "Type" "taxonomy") }}
{{ end }}
</div>
<h2 class="section-title">{{ T "archives.categories" }}</h2>
<div class="subsection-list">
<div class="article-list--tile">
{{ range $categories }}
{{ partial "article-list/tile" (dict "context" . "size" "250x150" "Type" "taxonomy") }}
{{ end }}
</div>
</div>
{{ end }}
@@ -21,7 +19,7 @@
{{ range $filtered.GroupByDate "2006" }}
{{ $id := lower (replace .Key " " "-") }}
<div class="archives-group" id="{{ $id }}">
<h3 class="archives-date"><a href="{{ $.Permalink }}#{{ $id }}">{{ .Key }}</a></h3>
<h2 class="archives-date section-title"><a href="{{ $.RelPermalink }}#{{ $id }}">{{ .Key }}</a></h2>
<div class="article-list--compact">
{{ range .Pages }}
{{ partial "article-list/compact" . }}
@@ -31,4 +29,4 @@
{{ end }}
{{ partialCached "footer/footer" . }}
{{ end }}
{{ end }}
+6 -2
View File
@@ -1,8 +1,12 @@
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
{{- partial "head/head.html" . -}}
<head>
{{- partial "head/head.html" . -}}
{{- block "head" . -}}{{ end }}
</head>
<body class="{{ block `body-class` . }}{{ end }}">
<div class="container flex on-phone--column align-items--flex-start {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }} {{ block `container-class` . }}{{end}}">
{{- partial "head/colorScheme" . -}}
<div class="container flex on-phone--column {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }} {{ block `container-class` . }}{{end}}">
{{ partial "sidebar/left.html" . }}
<main class="main full-width">
{{- block "main" . }}{{- end }}
+56 -3
View File
@@ -1,10 +1,63 @@
{{ define "main" }}
<div class="widget">
<h3 class="widget-title">{{ .Title }}</h3>
<h3 class="section-title">
{{ if eq .Parent (.GetPage "/") }}
{{ T "list.section" }}
{{ else }}
{{ .Parent.Title }}
{{ end }}
</h3>
<div class="section-card">
<div class="section-details">
<h3 class="section-count">{{ T "list.page" (len .Pages) }}</h3>
<h1 class="section-term">{{ .Title }}</h1>
{{ with .Params.description }}
<h2 class="section-description">{{ . }}</h2>
{{ end }}
</div>
{{- $image := partialCached "helper/image" (dict "Context" . "Type" "section") .RelPermalink "section" -}}
{{ if $image.exists }}
<div class="section-image">
{{ if $image.resource }}
{{- $Permalink := $image.resource.RelPermalink -}}
{{- $Width := $image.resource.Width -}}
{{- $Height := $image.resource.Height -}}
{{- if (default true .Page.Site.Params.imageProcessing.cover.enabled) -}}
{{- $thumbnail := $image.resource.Fill "120x120" -}}
{{- $Permalink = $thumbnail.RelPermalink -}}
{{- $Width = $thumbnail.Width -}}
{{- $Height = $thumbnail.Height -}}
{{- end -}}
<img src="{{ $Permalink }}"
width="{{ $Width }}"
height="{{ $Height }}"
loading="lazy">
{{ else }}
<img src="{{ $image.permalink }}" loading="lazy" />
{{ end }}
</div>
{{ end }}
</div>
{{ $subsections := .Sections }}
{{ with $subsections }}
<h2 class="section-title">{{ T "list.subsection" (len $subsections) }}</h2>
<div class="subsection-list">
<div class="article-list--tile">
{{ range . }}
{{ partial "article-list/tile" (dict "context" . "size" "250x150" "Type" "section") }}
{{ end }}
</div>
</div>
{{ end }}
{{/* List only pages that are not a subsection */}}
{{ $paginator := .Paginate (.Pages | complement $subsections) }}
<section class="article-list--compact">
{{ range .Paginator.Pages }}
{{ range $paginator.Pages }}
{{ partial "article-list/compact" . }}
{{ end }}
</section>
-37
View File
@@ -1,37 +0,0 @@
{{ define "main" }}
<h3 class="taxonomy-type">{{ .Type | singularize | humanize }}</h3>
<div class="taxonomy-card">
<div class="taxonomy-details">
<h3 class="taxonomy-count">{{ len .Pages }} post{{ if gt (len .Pages) 1 }}s{{ end }}</h3>
<h1 class="taxonomy-term">{{ .Title }}</h1>
{{ with .Params.description }}
<h2 class="taxonomy-description">{{ . }}</h2>
{{ end }}
</div>
{{- $image := partialCached "helper/image" (dict "Context" . "Type" "taxonomy") .RelPermalink "taxonomy" -}}
{{ if $image.exists }}
<div class="taxonomy-image">
{{ if $image.resource }}
{{- $thumbnail := $image.resource.Fill "120x120" -}}
<img src="{{ $thumbnail.RelPermalink }}" width="{{ $thumbnail.Width }}"
height="{{ $thumbnail.Height }}" loading="lazy">
{{ else }}
<img src="{{ $image.permalink }}" loading="lazy">
{{ end }}
</div>
{{ end }}
</div>
<section class="article-list--compact">
{{ $v2 := where .Pages "Params.hidden" "!=" true }}
{{ $pag := .Paginate (.Pages) }}
{{ range $pag.Pages }}
{{ partial "article-list/compact" . }}
{{ end }}
</section>
{{- partial "pagination.html" . -}}
{{ partialCached "footer/footer" . }}
{{ end }}
+2 -2
View File
@@ -11,9 +11,9 @@
</section>
{{- partial "pagination.html" . -}}
{{ partialCached "footer/footer" . }}
{{- partial "footer/footer" . -}}
{{ end }}
{{ define "right-sidebar" }}
{{ partialCached "sidebar/right.html" . }}
{{ partial "sidebar/right.html" . }}
{{ end }}
+31
View File
@@ -0,0 +1,31 @@
{{ define "body-class" }}template-search{{ end }}
{{ define "head" }}
{{- with .OutputFormats.Get "json" -}}
<link rel="preload" href="{{ .Permalink }}" as="fetch" crossorigin="anonymous">
{{- end -}}
{{ end }}
{{ define "main" }}
<form action="{{ .Permalink }}" class="search-form"{{ with .OutputFormats.Get "json" -}} data-json="{{ .Permalink }}"{{- end }}>
<p>
<label>{{ T "search.title" }}</label>
<input name="keyword" placeholder="{{ T `search.placeholder` }}" />
</p>
<button title="Search">
{{ partial "helper/icon" "search" }}
</button>
</form>
<h3 class="search-result--title section-title"></h3>
<div class="search-result--list article-list--compact"></div>
<script>
window.searchResultTitleTemplate = "{{ T `search.resultTitle` }}"
</script>
{{- $opts := dict "minify" hugo.IsProduction "JSXFactory" "createElement" -}}
{{- $searchScript := resources.Get "ts/search.tsx" | js.Build $opts -}}
<script type="text/javascript" src="{{ $searchScript.RelPermalink }}" defer></script>
{{ partialCached "footer/footer" . }}
{{ end }}
+24
View File
@@ -0,0 +1,24 @@
{{- $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections -}}
{{- $notHidden := where .Site.RegularPages "Params.hidden" "!=" true -}}
{{- $filtered := ($pages | intersect $notHidden) -}}
{{- $result := slice -}}
{{- range $filtered -}}
{{- $data := dict "title" .Title "date" .Date "permalink" .Permalink "content" (.Plain) -}}
{{- $image := partialCached "helper/image" (dict "Context" . "Type" "articleList") .RelPermalink "articleList" -}}
{{- if and $image.exists $image.resource -}}
{{- if (default true .Page.Site.Params.imageProcessing.cover.enabled) -}}
{{- $thumbnail := $image.resource.Fill "120x120" -}}
{{- $image := dict "image" (absURL $thumbnail.Permalink) -}}
{{- else -}}
{{- $image := dict "image" (absURL $image.resource.Permalink) -}}
{{- end -}}
{{- $data = merge $data $image -}}
{{ end }}
{{- $result = $result | append $data -}}
{{- end -}}
{{ jsonify $result }}
+16 -4
View File
@@ -1,5 +1,5 @@
<article>
<a href="{{ .Permalink }}">
<a href="{{ .RelPermalink }}">
<div class="article-details">
<h2 class="article-title">
{{- .Title -}}
@@ -15,9 +15,21 @@
{{ if $image.exists }}
<div class="article-image">
{{ if $image.resource }}
{{- $thumbnail := $image.resource.Fill "120x120" -}}
<img src="{{ $thumbnail.RelPermalink }}" width="{{ $thumbnail.Width }}"
height="{{ $thumbnail.Height }}" loading="lazy">
{{- $Permalink := $image.resource.RelPermalink -}}
{{- $Width := $image.resource.Width -}}
{{- $Height := $image.resource.Height -}}
{{- if (default true .Page.Site.Params.imageProcessing.cover.enabled) -}}
{{- $thumbnail := $image.resource.Fill "120x120" -}}
{{- $Permalink = $thumbnail.RelPermalink -}}
{{- $Width = $thumbnail.Width -}}
{{- $Height = $thumbnail.Height -}}
{{- end -}}
<img src="{{ $Permalink }}"
width="{{ $Width }}"
height="{{ $Height }}"
loading="lazy">
{{ else }}
<img src="{{ $image.permalink }}" loading="lazy" alt="Featured image of post {{ .Title }}" />
{{ end }}
+1 -19
View File
@@ -1,22 +1,4 @@
{{ $image := partialCached "helper/image" (dict "Context" . "Type" "articleList") .RelPermalink "articleList" }}
<article class="{{ if $image.exists }}has-image{{ end }}">
{{ if $image.exists }}
<div class="article-image">
<a href="{{ .Permalink }}">
{{ if $image.resource }}
{{- $thumbnail := $image.resource.Fill "800x250" -}}
{{- $thumbnailRetina := $image.resource.Fill "1600x500" -}}
<img src="{{ $thumbnail.RelPermalink }}"
srcset="{{ $thumbnail.RelPermalink }} 800w, {{ $thumbnailRetina.RelPermalink }} 1600w"
width="{{ $thumbnail.Width }}" height="{{ $thumbnail.Height }}" loading="lazy"
alt="Featured image of post {{ .Title }}" />
{{ else }}
<img src="{{ $image.permalink }}" loading="lazy" alt="Featured image of post {{ .Title }}" />
{{ end }}
</a>
</div>
{{ end }}
{{ partialCached "article/components/details" . .RelPermalink }}
{{ partial "article/components/header" . }}
</article>
+17 -4
View File
@@ -1,15 +1,28 @@
{{ $image := partialCached "helper/image" (dict "Context" .context "Type" .Type) .context.RelPermalink .Type }}
<article class="{{ if $image.exists }}has-image{{ end }}">
<a href="{{ .context.Permalink }}">
<a href="{{ .context.RelPermalink }}">
{{ if $image.exists }}
<div class="article-image">
{{ if $image.resource }}
{{- $imageRaw := $image.resource | resources.Fingerprint "md5" -}}
{{- $thumbnail := $imageRaw.Fill .size -}}
{{- $Permalink := $imageRaw.RelPermalink -}}
{{- $Width := $imageRaw.Width -}}
{{- $Height := $imageRaw.Height -}}
<img src="{{ $thumbnail.RelPermalink }}" width="{{ $thumbnail.Width }}" height="{{ $thumbnail.Height }}"
loading="lazy" data-key="{{ .context.Slug }}" data-hash="{{ $imageRaw.Data.Integrity }}">
{{- if (default true .Page.Site.Params.imageProcessing.cover.enabled) -}}
{{- $thumbnail := $imageRaw.Fill .size -}}
{{- $Permalink = $thumbnail.RelPermalink -}}
{{- $Width = $thumbnail.Width -}}
{{- $Height = $thumbnail.Height -}}
{{- end -}}
<img src="{{ $Permalink }}"
width="{{ $Width }}"
height="{{ $Height }}"
loading="lazy"
data-key="{{ .context.Slug }}"
data-hash="{{ $imageRaw.Data.Integrity }}">
{{ else }}
<img src="{{ $image.permalink }}" loading="lazy" data-key="{{ .context.Slug }}" data-hash="{{ $image.permalink }}"/>
{{ end }}
@@ -1,28 +1,16 @@
{{ $image := partialCached "helper/image" (dict "Context" . "Type" "article") .RelPermalink "article" }}
{{- $context := . -}}
<div class="article-details">
{{ if .Params.categories }}
<header class="article-category">
{{ range (.GetTerms "categories") }}
{{ if and $image.exists $image.resource }}
{{- $imageRaw := $image.resource | resources.Fingerprint "md5" -}}
{{- $20x := $imageRaw.Fill "20x20 smart" -}}
<a href="{{ .Permalink }}"
class="color-tag"
data-image="{{ $20x.RelPermalink }}"
data-key="{{ $context.Slug }}"
data-hash="{{ $imageRaw.Data.Integrity }}">
{{ .LinkTitle }}
</a>
{{ else }}
<a href="{{ .Permalink }}">{{ .LinkTitle }}</a>
{{ end }}
<a href="{{ .RelPermalink }}" {{ with .Params.style }}style="background-color: {{ .background }}; color: {{ .color }};"{{ end }}>
{{ .LinkTitle }}
</a>
{{ end }}
</header>
{{ end }}
<h2 class="article-title">
<a href="{{ .Permalink }}">
<a href="{{ .RelPermalink }}">
{{- .Title -}}
</a>
</h2>
@@ -12,7 +12,7 @@
<section class="article-time">
{{ partial "helper/icon" "clock" }}
<span class="article-time--modified">
{{ T "lastUpdatedOn" }} {{ .Lastmod.Format ( or .Site.Params.dateFormat.lastUpdated "Jan 02, 2006 15:04 MST" ) }}
{{ T "article.lastUpdatedOn" }} {{ .Lastmod.Format ( or .Site.Params.dateFormat.lastUpdated "Jan 02, 2006 15:04 MST" ) }}
</span>
</section>
{{- end -}}
@@ -2,16 +2,32 @@
{{- $image := partialCached "helper/image" (dict "Context" . "Type" "article") .RelPermalink "article" -}}
{{ if $image.exists }}
<div class="article-image">
{{ if $image.resource }}
{{- $tablet := $image.resource.Resize "1024x" -}}
{{- $desktop := $image.resource.Resize "2000x" -}}
<a href="{{ .RelPermalink }}">
{{ if $image.resource }}
{{- $Permalink := $image.resource.RelPermalink -}}
{{- $Width := $image.resource.Width -}}
{{- $Height := $image.resource.Height -}}
{{- $Srcset := "" -}}
{{- if (default true .Page.Site.Params.imageProcessing.cover.enabled) -}}
{{- $thumbnail := $image.resource.Resize "800x" -}}
{{- $thumbnailRetina := $image.resource.Resize "1600x" -}}
{{- $Srcset = printf "%s 800w, %s 1600w" $thumbnail.RelPermalink $thumbnailRetina.RelPermalink -}}
{{- $Permalink = $thumbnail.RelPermalink -}}
{{- $Width = $thumbnail.Width -}}
{{- $Height = $thumbnail.Height -}}
{{- end -}}
<img srcset="{{ $tablet.RelPermalink }} 1024w, {{ $desktop.RelPermalink }} 2000w"
src="{{ $desktop.RelPermalink }}" width="{{ $image.resource.Width }}" height="{{ $image.resource.Height }}" loading="lazy"
alt="Featured image of post {{ .Title }}" />
{{ else }}
<img src="{{ $image.permalink }}" loading="lazy" alt="Featured image of post {{ .Title }}" />
{{ end }}
<img src="{{ $Permalink }}"
{{ with $Srcset }}srcset="{{ . }}"{{ end }}
width="{{ $Width }}"
height="{{ $Height }}"
loading="lazy"
alt="Featured image of post {{ .Title }}" />
{{ else }}
<img src="{{ $image.permalink }}" loading="lazy" alt="Featured image of post {{ .Title }}" />
{{ end }}
</a>
</div>
{{ end }}
@@ -1,5 +1,5 @@
<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true" style="display:none">
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element as animating opacity is faster than rgba(). -->
@@ -63,4 +63,10 @@
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.css">
@@ -1,7 +1,7 @@
<aside class="widget related-contents--wrapper">
{{ $related := .Site.RegularPages.Related . | first 5 }}
<aside class="related-contents--wrapper">
{{ $related := (where (.Site.RegularPages.Related .) "Params.hidden" "!=" true) | first 5 }}
{{ with $related }}
<h1 class="widget-title">{{ T "relatedContents" }}</h1>
<h2 class="section-title">{{ T "article.relatedContents" }}</h2>
<div class="related-contents">
<div class="flex article-list--tile">
{{ range . }}
@@ -10,4 +10,4 @@
</div>
</div>
{{ end }}
</aside>
</aside>
@@ -1,7 +1,7 @@
{{ if .Params.Tags }}
<section class="article-tags">
{{ range (.GetTerms "tags") }}
<a href="{{ .Permalink }}">{{ .LinkTitle }}</a>
<a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
{{ end }}
</section>
{{ end }}
@@ -1,4 +0,0 @@
{{- $light := resources.Get "css/highlight/light.css" | minify -}}
{{- $dark := resources.Get "css/highlight/dark.css" | minify -}}
<link rel="stylesheet" href="{{ $light.RelPermalink }}" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="{{ $dark.RelPermalink }}" media="(prefers-color-scheme: dark)">
+19 -4
View File
@@ -1,8 +1,23 @@
{{- $ThemeVersion := "2.0.0" -}}
<footer class="site-footer">
<section class="copyright">&copy; {{ now.Format "2006" }} {{ .Site.Title }}</section>
<section class="copyright">
&copy;
{{ if and (.Site.Params.footer.since) (ne .Site.Params.footer.since (int (now.Format "2006"))) }}
{{ .Site.Params.footer.since }} -
{{ end }}
{{ now.Format "2006" }} {{ .Site.Title }}
</section>
<section class="powerby">
Built with <a href="https://gohugo.io/" target="_blank" rel="noopener">Hugo</a> <br />
Theme <b><a href="https://github.com/CaiJimmy/hugo-theme-stack" target="_blank" rel="noopener" data-version="1.0.5">Stack</a></b> designed by
<a href="https://jimmycai.com" target="_blank" rel="noopener">Jimmy</a>
{{ with .Site.Params.footer.customText }}
{{ . | safeHTML }} <br/>
{{ end }}
{{- $Generator := `<a href="https://gohugo.io/" target="_blank" rel="noopener">Hugo</a>` -}}
{{- $Theme := printf `<b><a href="https://github.com/CaiJimmy/hugo-theme-stack" target="_blank" rel="noopener" data-version="%s">Stack</a></b>` $ThemeVersion -}}
{{- $DesignedBy := `<a href="https://jimmycai.com" target="_blank" rel="noopener">Jimmy</a>` -}}
{{ T "footer.builtWith" (dict "Generator" $Generator) | safeHTML }} <br />
{{ T "footer.designedBy" (dict "Theme" $Theme "DesignedBy" $DesignedBy) | safeHTML }}
</section>
</footer>
-1
View File
@@ -1,4 +1,3 @@
{{ partialCached "footer/components/script.html" . }}
{{ partialCached "footer/components/custom-font.html" . }}
{{ partialCached "footer/components/highlight.html" . }}
{{ partial "footer/custom.html" . }}
+39
View File
@@ -0,0 +1,39 @@
{{- $defaultColorScheme := default "auto" .Site.Params.colorScheme.default -}}
{{- if not (default false .Site.Params.colorScheme.toggle) -}}
{{/* If toggle is disabled, force default scheme */}}
<script>
(function() {
const colorSchemeKey = 'StackColorScheme';
localStorage.setItem(colorSchemeKey, "{{ $defaultColorScheme }}");
})();
</script>
{{- else -}}
{{/* Otherwise set to default scheme only if no preference is set by user */}}
<script>
(function() {
const colorSchemeKey = 'StackColorScheme';
if(!localStorage.getItem(colorSchemeKey)){
localStorage.setItem(colorSchemeKey, "{{ $defaultColorScheme }}");
}
})();
</script>
{{- end -}}
<script>
(function() {
const colorSchemeKey = 'StackColorScheme';
const colorSchemeItem = localStorage.getItem(colorSchemeKey);
const supportDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches === true;
if (colorSchemeItem == 'dark' || colorSchemeItem === 'auto' && supportDarkMode) {
/**
* Enable dark mode if:
* 1. If dark mode is set already (in local storage)
* 2. Auto mode & prefere color scheme is dark
*/
document.body.dataset.scheme = 'dark';
} else {
document.body.dataset.scheme = 'light';
}
})();
</script>
+22 -20
View File
@@ -1,22 +1,24 @@
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
{{- $description := partialCached "data/description" . .RelPermalink -}}
<meta name='description' content='{{ $description }}'>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
{{- $title := partialCached "data/title" . .RelPermalink -}}
<title>{{ $title }}</title>
<link rel='canonical' href='{{ .Permalink }}'>
{{- partial "head/style.html" . -}}
{{- partial "head/script.html" . -}}
{{- partial "head/opengraph/include.html" . -}}
{{- $description := partialCached "data/description" . .RelPermalink -}}
<meta name='description' content='{{ $description }}'>
{{- range .AlternativeOutputFormats -}}
<link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .Permalink | safeURL }}">
{{- end -}}
{{- partial "head/custom.html" . -}}
</head>
{{- $title := partialCached "data/title" . .RelPermalink -}}
<title>{{ $title }}</title>
<link rel='canonical' href='{{ .Permalink }}'>
{{- partial "head/style.html" . -}}
{{- partial "head/script.html" . -}}
{{- partial "head/opengraph/include.html" . -}}
{{- range .AlternativeOutputFormats -}}
<link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .Permalink | safeURL }}">
{{- end -}}
{{ with .Site.Params.favicon }}
<link rel="shortcut icon" href="{{ . }}" />
{{ end }}
{{- partial "head/custom.html" . -}}
@@ -6,35 +6,35 @@
<meta property='og:url' content='{{ .Permalink }}'>
<meta property='og:site_name' content='{{ .Site.Title }}'>
<meta property='og:type' content='
{{- if .IsPage -}}
article
{{- else -}}
website
{{- end -}}
{{- if .IsPage -}}
article
{{- else -}}
website
{{- end -}}
'>
{{- with .Params.locale -}}
<meta property='og:locale' content='{{ . }}'>
<meta property='og:locale' content='{{ . }}'>
{{- end -}}
{{- if .IsPage -}}
<meta property='article:section' content='{{ .Section | title }}' />
{{- range .Params.tags -}}
<meta property='article:tag' content='{{ . }}' />
{{- end -}}
<meta property='article:section' content='{{ .Section | title }}' />
{{- range .Params.tags -}}
<meta property='article:tag' content='{{ . }}' />
{{- end -}}
{{- end -}}
{{- if .IsPage -}}
{{- if not .Date.IsZero -}}
<meta property='article:published_time' content='{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}'/>
{{- end -}}
{{- if not .Lastmod.IsZero -}}
<meta property='article:modified_time' content='{{ .Lastmod.Format "2006-01-02T15:04:05-07:00" | safeHTML }}'/>
{{- end -}}
{{- if not .Date.IsZero -}}
<meta property='article:published_time' content='{{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}'/>
{{- end -}}
{{- if not .Lastmod.IsZero -}}
<meta property='article:modified_time' content='{{ .Lastmod.Format "2006-01-02T15:04:05-07:00" | safeHTML }}'/>
{{- end -}}
{{- else -}}
{{- if not .Site.LastChange.IsZero -}}
<meta property='og:updated_time' content='{{ .Site.LastChange.Format " 2006-01-02T15:04:05-07:00 " | safeHTML }}'/>
{{- end -}}
{{- if not .Site.LastChange.IsZero -}}
<meta property='og:updated_time' content='{{ .Site.LastChange.Format " 2006-01-02T15:04:05-07:00 " | safeHTML }}'/>
{{- end -}}
{{- end -}}
{{ $image := partialCached "helper/image" (dict "Context" . "Type" "opengraph") .RelPermalink "opengraph" }}
@@ -10,6 +10,6 @@
{{- $image := partialCached "helper/image" (dict "Context" . "Type" "opengraph") .RelPermalink "opengraph" -}}
{{- if $image.exists -}}
<meta name="twitter:card" content="{{ .Site.Params.opengraph.twitter.card }}">
<meta name="twitter:image" content='{{ absURL $image.permalink }}' />
<meta name="twitter:card" content="{{ default `summary_large_image` .Site.Params.opengraph.twitter.card }}">
<meta name="twitter:image" content='{{ absURL $image.permalink }}' />
{{- end -}}
+24 -10
View File
@@ -8,17 +8,23 @@
<header class="site-info">
{{ with .Site.Params.sidebar.avatar }}
<figure class="site-avatar">
{{ $avatar := resources.Get (.) }}
{{ if $avatar }}
{{ $avatarResized := $avatar.Resize "300x300" }}
<img src="{{ $avatarResized.RelPermalink }}" width="{{ $avatarResized.Width }}"
height="{{ $avatarResized.Height }}" class="site-logo" loading="lazy" alt="Avatar">
{{ if not .local }}
<img src="{{ .src }}" width="300" height="300" class="site-logo" loading="lazy" alt="Avatar">
{{ else }}
{{ errorf "Failed loading avatar from %q" . }}
{{ $avatar := resources.Get (.src) }}
{{ if $avatar }}
{{ $avatarResized := $avatar.Resize "300x" }}
<img src="{{ $avatarResized.RelPermalink }}" width="{{ $avatarResized.Width }}"
height="{{ $avatarResized.Height }}" class="site-logo" loading="lazy" alt="Avatar">
{{ else }}
{{ errorf "Failed loading avatar from %q" . }}
{{ end }}
{{ end }}
<span class="emoji">{{ $.Site.Params.sidebar.emoji }}</span>
{{ with $.Site.Params.sidebar.emoji }}
<span class="emoji">{{ . }}</span>
{{ end }}
</figure>
{{ end }}
<h1 class="site-name"><a href="{{ .Site.BaseURL }}">{{ .Site.Title }}</a></h1>
@@ -31,7 +37,7 @@
{{ $active := or (eq $currentPage.Title .Name) (or ($currentPage.HasMenuCurrent "main" .) ($currentPage.IsMenuCurrent "main" .)) }}
<li {{ if $active }} class='current' {{ end }}>
<a href='{{ .URL | absLangURL }}'>
<a href='{{ .URL | relURL }}'>
{{ if .Pre }}
{{ partial "helper/icon" .Pre }}
{{ end }}
@@ -39,5 +45,13 @@
</a>
</li>
{{ end }}
{{ if (default false .Site.Params.colorScheme.toggle) }}
<li id="dark-mode-toggle">
{{ partial "helper/icon" "toggle-left" }}
{{ partial "helper/icon" "toggle-right" }}
<span>{{ T "darkMode" }}</span>
</li>
{{ end }}
</ol>
</aside>
</aside>
+18 -16
View File
@@ -2,24 +2,26 @@
<div class="widget-icon">
{{ partial "helper/icon" "infinity" }}
</div>
<h1 class="widget-title">{{ T "widgetArchivesTitle" }}</h1>
<h2 class="widget-title section-title">{{ T "widget.archives.title" }}</h2>
{{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
{{ $notHidden := where .Site.RegularPages "Params.hidden" "!=" true }}
{{ $filtered := ($pages | intersect $notHidden) }}
{{ $archives := $filtered.GroupByDate "2006" }}
{{ range $index, $item := first (add .Site.Params.widgets.archives.limit 1) ($archives) }}
{{- $id := lower (replace $item.Key " " "-") -}}
<div class="archives-year">
<a href="{{ $.Site.Params.widgets.archives.path | relLangURL }}#{{ $id }}">
{{ if eq $index $.Site.Params.widgets.archives.limit }}
<span class="year">{{ T "widgetArchivesMore" }}</span>
{{ else }}
<span class="year">{{ .Key }}</span>
<span class="count">{{ len $item.Pages }}</span>
{{ end }}
</a>
</div>
{{ end }}
</section>
<div class="widget-archive--list">
{{ range $index, $item := first (add .Site.Params.widgets.archives.limit 1) ($archives) }}
{{- $id := lower (replace $item.Key " " "-") -}}
<div class="archives-year">
<a href="{{ $.Site.Params.widgets.archives.path | relLangURL }}#{{ $id }}">
{{ if eq $index $.Site.Params.widgets.archives.limit }}
<span class="year">{{ T "widget.archives.more" }}</span>
{{ else }}
<span class="year">{{ .Key }}</span>
<span class="count">{{ len $item.Pages }}</span>
{{ end }}
</a>
</div>
{{ end }}
</div>
</section>
+10
View File
@@ -0,0 +1,10 @@
<form action="/search" class="search-form widget" {{ with .OutputFormats.Get "json" -}}data-json="{{ .Permalink }}" {{- end }}>
<p>
<label>{{ T "search.title" }}</label>
<input name="keyword" required placeholder="{{ T `search.placeholder` }}" />
<button title="Search">
{{ partial "helper/icon" "search" }}
</button>
</p>
</form>
+2 -2
View File
@@ -2,11 +2,11 @@
<div class="widget-icon">
{{ partial "helper/icon" "tag" }}
</div>
<h1 class="widget-title">{{ T "widgetTagCloudTitle" }}</h1>
<h2 class="widget-title section-title">{{ T "widget.tagCloud.title" }}</h2>
<div class="tagCloud-tags">
{{ range first .Site.Params.widgets.tagCloud.limit .Site.Taxonomies.tags.ByCount }}
<a href="{{ .Page.Permalink }}" class="font_size_{{ .Count }}">
<a href="{{ .Page.RelPermalink }}" class="font_size_{{ .Count }}">
{{ .Page.Title }}
</a>
{{ end }}
+1 -1
View File
@@ -2,7 +2,7 @@
publish = "exampleSite/public"
[build.environment]
HUGO_VERSION = "0.74.3"
HUGO_VERSION = "0.79.0"
HUGO_THEME = "repo"
[context.production]
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -20,7 +20,7 @@ features = [
"opengraph",
"widgets"
]
min_version = "0.74.0"
min_version = "0.78.0"
[author]
name = "Jimmy Cai"