Merge branch 'CaiJimmy:master' into master

This commit is contained in:
fariszr 2022-02-23 00:56:44 +03:00 committed by GitHub
commit d119f08631
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 3165 additions and 672 deletions

31
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
# Update the NODE_VERSION arg in docker-compose.yml to pick a Node version: 10, 12, 14
ARG NODE_VERSION=14
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${NODE_VERSION}
# VARIANT can be either 'hugo' for the standard version or 'hugo_extended' for the extended version.
ARG VARIANT=hugo
# VERSION can be either 'latest' or a specific version number
ARG VERSION=latest
# Download Hugo
RUN apt-get update && apt-get install -y ca-certificates openssl git curl && \
rm -rf /var/lib/apt/lists/* && \
case ${VERSION} in \
latest) \
export VERSION=$(curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') ;;\
esac && \
echo ${VERSION} && \
wget -O ${VERSION}.tar.gz https://github.com/gohugoio/hugo/releases/download/v${VERSION}/${VARIANT}_${VERSION}_Linux-64bit.tar.gz && \
tar xf ${VERSION}.tar.gz && \
mv hugo /usr/bin/hugo
# Hugo dev server port
EXPOSE 1313
# [Optional] Uncomment this section to install additional OS packages you may want.
#
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment if you want to install more global node packages
# RUN sudo -u node npm install -g <your-package-list-here>

View File

@ -0,0 +1,45 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.202.3/containers/hugo
{
"name": "Hugo (Community)",
"build": {
"dockerfile": "Dockerfile",
"args": {
// Update VARIANT to pick hugo variant.
// Example variants: hugo, hugo_extended
// Rebuild the container if it already exists to update.
"VARIANT": "hugo_extended",
// Update VERSION to pick a specific hugo version.
// Example versions: latest, 0.73.0, 0,71.1
// Rebuild the container if it already exists to update.
"VERSION": "latest",
// Update NODE_VERSION to pick the Node.js version: 12, 14
"NODE_VERSION": "14",
}
},
// Set *default* container specific settings.json values on container create.
"settings": {
"html.format.templating": true,
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"bungcip.better-toml",
"davidanson.vscode-markdownlint"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
"forwardPorts": [
1313
],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "uname -a",
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node",
"features": {
"golang": "latest"
}
}

View File

@ -7,32 +7,39 @@ assignees: ''
--- ---
**Describe the bug** <!--
Before creating this bug report, make sure you have read the theme documentation: https://docs.stack.jimmycai.com/
-->
## Describe the bug
A clear and concise description of what the bug is. A clear and concise description of what the bug is.
**To Reproduce** ## Expected behavior
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen. A clear and concise description of what you expected to happen.
**Screenshots** ## To Reproduce
If applicable, add screenshots to help explain your problem. Indicate the steps to reproduce this bug, if applicable.
## Screenshots
Add screenshots to help explain your problem.
## Environment
- **Hugo version** [e.g: 0.80.0]:
- **Hugo extended?**: Yes / No
## Additional context
Add any other context about the problem here.
If it's a UI issue, fill the following information:
**Desktop (please complete the following information):**
- OS: [e.g. iOS] - OS: [e.g. iOS]
- Browser [e.g. chrome, safari] - Browser [e.g. chrome, safari]
- Version [e.g. 22] - Version [e.g. 22]
**Smartphone (please complete the following information):** ## Content of config.yaml
- Device: [e.g. iPhone6] ```yaml
- OS: [e.g. iOS8.1] ### Paste the content of the config file here
- Browser [e.g. stock browser, safari] ```
- Version [e.g. 22]
**Additional context** ## Link to the demo site and/or source repository
Add any other context about the problem here.

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
public
resources
assets/jsconfig.json
.hugo_build.lock

View File

@ -5,13 +5,13 @@
## 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)
## Documentation & more information ## Documentation
[Documentation](https://docs.stack.jimmycai.com/) | [中文文档](https://docs.stack.jimmycai.com/v/zh-cn/) [Documentation](https://docs.stack.jimmycai.com/) | [中文文档](https://docs.stack.jimmycai.com/zh/)
## Introduction ## Introduction
@ -27,21 +27,20 @@ Stack is a simple card-style Hugo theme designed for bloggers, some of its featu
- No CSS framework, keep it simple and minimal - No CSS framework, keep it simple and minimal
- Properly cropped thumbnails - Properly cropped thumbnails
- Subsection support - Subsection support
- Table of contents
## Requirements ## Requirements
It's necessary to use **Hugo ≥ 0.78.0**. It's necessary to use **Hugo Extended ≥ 0.87.0**.
Use Hugo Extended version (recommended) if you want to:
* Use the latest feature/fix from `master` branch
* Edit SCSS files
**Compiled CSS are updated once per release.**
## Installation ## Installation
Clone / Download this repository to `theme` folder, and edit your site config following `exampleSite/config.toml`. * 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.*
Check [documentation](https://docs.stack.jimmycai.com/) for more details. Check [documentation](https://docs.stack.jimmycai.com/) for more details.
@ -63,13 +62,22 @@ Your support is greatly appreciated :)
## Thanks to ## Thanks to
- [Vibrant-Colors/node-vibrant](https://github.com/Vibrant-Colors/node-vibrant) | Project | Description | Licence |
- [Normalize.css](https://necolas.github.io/normalize.css/) | ------- | ----------- | ------- |
- [Tabler icons](https://tablericons.com/) | [PhotoSwipe](https://photoswipe.com/) | For the lightbox effect | [MIT](https://github.com/dimsemenov/PhotoSwipe/blob/master/LICENSE) |
- [Pure CSS implementation of Google Photos / 500px image layout](https://github.com/xieranmaya/blog/issues/6) | [Normalize.css](https://github.com/necolas/normalize.css) | - | [MIT](https://github.com/necolas/normalize.css/blob/master/LICENSE.md) |
- [jonsuh/hamburgers](https://github.com/jonsuh/hamburgers) | [Node Vibrant](https://github.com/Vibrant-Colors/node-vibrant) | To extract the color from images | [MIT](https://github.com/Vibrant-Colors/node-vibrant/blob/master/LICENSE.md)
- [PhotoSwipe](https://photoswipe.com/) | [Tabler icons](https://github.com/tabler/tabler-icons) | Default menu icons | [MIT](https://github.com/tabler/tabler-icons/blob/master/LICENSE) |
- [artchen/hexo-theme-element](https://github.com/artchen/hexo-theme-element) | [jonsuh/hamburgers](https://github.com/jonsuh/hamburgers) | Hamburger icon of menu | [MIT](https://github.com/jonsuh/hamburgers/blob/master/LICENSE) |
- [MunifTanjim/minimo](https://github.com/MunifTanjim/minimo) | [lepture/yue.css](https://github.com/lepture/yue.css) | Part of it is used for styling article content | MIT |
- [lepture/yue.css](https://github.com/lepture/yue.css) | [Typlog](https://typlog.com/) | Where the markdown gallery syntax is borrowed from | The author gave me the permission |
- Markdown gallery syntax from [Typlog](https://typlog.com/) | [Pure CSS implementation of Google Photos / 500px image layout](https://github.com/xieranmaya/blog/issues/6) | Used for image gallery | - |
### References
Some references that I took while building this theme:
| Project | Licence|
| ------- | ------|
| [artchen/hexo-theme-element](https://github.com/artchen/hexo-theme-element) | [MIT](https://github.com/artchen/hexo-theme-element/blob/master/LICENSE) |
| [MunifTanjim/minimo](https://github.com/MunifTanjim/minimo) | [MIT](https://github.com/MunifTanjim/minimo/blob/master/LICENSE) |

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-github" 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" fill="none"/>
<path d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" />
</svg>

After

Width:  |  Height:  |  Size: 603 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-twitter" 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" fill="none"/>
<path d="M22 4.01c-1 .49 -1.98 .689 -3 .99c-1.121 -1.265 -2.783 -1.335 -4.38 -.737s-2.643 2.06 -2.62 3.737v1c-3.245 .083 -6.135 -1.395 -8 -4c0 0 -4.182 7.433 4 11c-1.872 1.247 -3.739 2.088 -6 2c3.308 1.803 6.913 2.423 10.034 1.517c3.58 -1.04 6.522 -3.723 7.651 -7.742a13.84 13.84 0 0 0 .497 -3.753c-.002 -.249 1.51 -2.772 1.818 -4.013z" />
</svg>

After

Width:  |  Height:  |  Size: 638 B

9
assets/icons/date.svg Normal file
View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-calendar-time" width="56" height="56" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z"/>
<path d="M11.795 21h-6.795a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v4" />
<circle cx="18" cy="18" r="4" />
<path d="M15 3v4" />
<path d="M7 3v4" />
<path d="M3 11h16" />
<path d="M18 16.496v1.504l1 1" />
</svg>

After

Width:  |  Height:  |  Size: 508 B

View File

@ -6,5 +6,7 @@
"*" "*"
] ]
}, },
"lib": ["es2020", "dom"],
"jsx": "preserve"
} }
} }

View File

@ -1,4 +1,17 @@
$on-phone: 812px; $breakpoints: (
$on-tablet: 1024px; sm: 640px,
$on-desktop: 1519px; md: 768px,
$on-desktop-large: 1920px; lg: 1024px,
xl: 1280px,
2xl: 1536px,
);
@mixin respond($breakpoint) {
@if not map-has-key($breakpoints, $breakpoint) {
@warn "'#{$breakpoint}' is not a valid breakpoint";
} @else {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
}
}

View File

@ -2,67 +2,52 @@
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
.left-sidebar {
max-width: var(--left-sidebar-max-width);
}
.right-sidebar {
max-width: var(--right-sidebar-max-width);
/// Display right sidebar when min-width: lg
@include respond(lg) {
display: block;
}
}
&.extended { &.extended {
@media (min-width: $on-phone) { @include respond(md) {
max-width: 800px; max-width: 1024px;
--left-sidebar-max-width: 25%;
.left-sidebar { --right-sidebar-max-width: 30%;
width: 25%;
}
} }
@media (min-width: $on-tablet) { @include respond(lg) {
max-width: 972px; max-width: 1280px;
--left-sidebar-max-width: 20%;
.right-sidebar { --right-sidebar-max-width: 30%;
width: 25%;
}
} }
@media (min-width: $on-desktop) { @include respond(xl) {
max-width: 1200px;
.left-sidebar {
width: 20%;
}
.right-sidebar {
width: 25%;
}
}
@media (min-width: $on-desktop-large) {
max-width: 1536px; max-width: 1536px;
--left-sidebar-max-width: 15%;
.left-sidebar { --right-sidebar-max-width: 25%;
width: 15%;
}
} }
} }
&.compact { &.compact {
@media (min-width: $on-phone) { @include respond(md) {
max-width: 800px; --left-sidebar-max-width: 25%;
max-width: 768px;
.left-sidebar {
width: 25%;
}
} }
@media (min-width: $on-tablet) { @include respond(lg) {
max-width: 972px; max-width: 1024px;
--left-sidebar-max-width: 20%;
} }
@media (min-width: $on-desktop) { @include respond(xl) {
max-width: 1050px; max-width: 1280px;
.left-sidebar {
width: 20%;
}
}
@media (min-width: $on-desktop-large) {
max-width: 1300px;
} }
} }
} }
@ -76,8 +61,9 @@
} }
&.on-phone--column { &.on-phone--column {
@media (max-width: $on-phone) { flex-direction: column;
flex-direction: column; @include respond(md) {
flex-direction: unset;
} }
} }
@ -106,7 +92,6 @@
main.main { main.main {
min-width: 0; min-width: 0;
padding: 0 15px;
max-width: 100%; max-width: 100%;
flex-grow: 1; flex-grow: 1;
padding-top: var(--main-top-padding); padding-top: var(--main-top-padding);
@ -114,4 +99,11 @@ main.main {
.main-container { .main-container {
min-height: 100vh; min-height: 100vh;
} align-items: flex-start;
padding: 0 15px;
column-gap: var(--section-separation);
@include respond(md) {
padding: 0 20px;
}
}

View File

@ -24,14 +24,14 @@
.article-image { .article-image {
img { img {
width: 100%; width: 100%;
height: 200px; height: 150px;
object-fit: cover; object-fit: cover;
@media (max-width: $on-tablet) { @include respond(md) {
height: 150px; height: 200px;
} }
@media (min-width: $on-desktop-large) { @include respond(xl) {
height: 250px; height: 250px;
} }
} }
@ -62,7 +62,7 @@
color: var(--card-text-color-main); color: var(--card-text-color-main);
font-size: 2.2rem; font-size: 2.2rem;
@media (min-width: $on-desktop-large) { @include respond(xl) {
font-size: 2.4rem; font-size: 2.4rem;
} }
@ -86,7 +86,7 @@
line-height: 1.5; line-height: 1.5;
font-size: 1.75rem; font-size: 1.75rem;
@media (min-width: $on-desktop-large) { @include respond(xl) {
font-size: 2rem; font-size: 2rem;
} }
} }
@ -95,7 +95,9 @@
display: flex; display: flex;
align-items: center; align-items: center;
color: var(--card-text-color-tertiary); color: var(--card-text-color-tertiary);
gap: 15px;
margin-top: 10px; margin-top: 10px;
flex-wrap: wrap;
svg { svg {
vertical-align: middle; vertical-align: middle;
@ -108,6 +110,11 @@
time { time {
font-size: 1.4rem; font-size: 1.4rem;
} }
& > div {
display: inline-flex;
align-items: center;
}
} }
.article-category, .article-category,
@ -135,10 +142,10 @@
border-radius: var(--card-border-radius); border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l1); box-shadow: var(--shadow-l1);
background-color: var(--card-background); background-color: var(--card-background);
--image-size: 60px; --image-size: 50px;
@media (max-width: $on-tablet) { @include respond(md) {
--image-size: 50px; --image-size: 60px;
} }
& + .pagination { & + .pagination {
@ -165,10 +172,10 @@
.article-title { .article-title {
margin: 0; margin: 0;
font-size: 1.8rem; font-size: 1.6rem;
@media (max-width: $on-tablet) { @include respond(md) {
font-size: 1.6rem; font-size: 1.8rem;
} }
} }
@ -242,20 +249,20 @@
flex-direction: column; flex-direction: column;
justify-content: flex-end; justify-content: flex-end;
z-index: 2; z-index: 2;
padding: 20px; padding: 15px;
@media (max-width: $on-phone) { @include respond(sm) {
padding: 15px; padding: 20px;
} }
} }
.article-title { .article-title {
font-size: 2.2rem; font-size: 2rem;
font-weight: 500; font-weight: 500;
color: var(--card-text-color-main); color: var(--card-text-color-main);
@media (max-width: $on-phone) { @include respond(sm) {
font-size: 2rem; font-size: 2.2rem;
} }
} }
} }

View File

@ -15,3 +15,24 @@ body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
/* scrollbar styles for Firefox */
* {
scrollbar-width: auto;
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

@ -0,0 +1,394 @@
.disqus-container {
background-color: var(--card-background);
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l1);
padding: var(--card-padding);
}
#dsqjs * {
margin: 0;
padding: 0
}
#dsqjs a {
text-decoration: none;
color: #076dd0
}
#dsqjs .dsqjs-hide {
display: none!important
}
#dsqjs .dsqjs-disabled {
cursor: not-allowed;
opacity: .5
}
#dsqjs #dsqjs-msg {
text-align: center;
margin-top: 4px;
margin-bottom: 4px;
font-size: 14px
}
#dsqjs #dsqjs-msg .dsqjs-msg-btn {
cursor: pointer
}
#dsqjs .dsqjs-bullet {
line-height: 1.4;
margin: 0 2px
}
#dsqjs .dsqjs-bullet:after {
color: #c2c6cc;
content: "·";
font-weight: 700
}
#dsqjs .dsqjs-clearfix:after,#dsqjs .dsqjs-clearfix:before {
display: table;
content: "";
line-height: 0;
clear: both
}
#dsqjs .dsqjs-nav {
position: relative;
margin: 0 0 20px;
border-bottom: 2px solid #e7e9ee
}
#dsqjs ol,#dsqjs ul {
list-style: none;
list-style-type: none
}
#dsqjs .dsqjs-no-comment {
text-align: center;
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
overflow: hidden;
color: #2a2e2e;
margin-bottom: 6px
}
#dsqjs .dsqjs-nav-tab {
float: left;
text-transform: capitalize;
font-size: 15px;
padding: 12px 8px;
color: #656c7a;
display: block;
margin: 0 15px 0 0;
font-weight: 700;
line-height: 1;
position: relative;
transition: all .2s ease-in-out
}
#dsqjs .dsqjs-nav-tab:last-child {
margin: 0
}
#dsqjs .dsqjs-tab-active {
color: #2a2e2e
}
#dsqjs .dsqjs-tab-active>span:after {
content: " ";
display: block;
height: 2px;
background-color: #076dd0!important;
position: absolute;
bottom: -5px;
left: 0;
right: 0
}
#dsqjs .dsqjs-post-list .dsqjs-post-item {
position: relative;
margin-bottom: 16px
}
#dsqjs .dsqjs-post-list .dsqjs-post-avatar {
float: left;
margin-right: 10px;
position: relative;
background: #dbdfe4;
padding: 0;
display: block;
border-radius: 4px
}
#dsqjs .dsqjs-post-list .dsqjs-post-avatar img {
width: 44px;
height: 44px;
display: block;
border-radius: 4px
}
#dsqjs .dsqjs-post-list .dsqjs-post-header {
line-height: 1;
font-size: 14px;
margin-bottom: 3px
}
#dsqjs .dsqjs-post-list .dsqjs-post-header .dsqjs-post-author {
color: #656c7a;
font-weight: 700
}
#dsqjs .dsqjs-post-list .dsqjs-post-header .dsqjs-admin-badge {
color: #fff;
background: #687a86;
padding: 1px 3px;
margin-left: 4px;
font-size: 12px;
line-height: 1;
font-weight: 700;
border-radius: 3px;
display: inline-block;
position: relative;
top: -1px;
left: 1px
}
#dsqjs .dsqjs-post-list .dsqjs-post-header .dsqjs-meta {
display: inline-block;
font-size: 12px;
color: #656c7a
}
#dsqjs .dsqjs-post-body {
font-size: 15px;
line-height: 1.5;
word-wrap: break-word;
overflow: hidden;
color: #2a2e2e
}
#dsqjs .dsqjs-post-body code {
padding: .2em .4em;
margin: 0;
font-size: 85%;
background: #f5f5f5;
color: inherit;
border-radius: 3px
}
#dsqjs .dsqjs-post-body pre {
padding: .5em;
overflow: auto;
font-size: 85%;
line-height: 1.45;
border-radius: 3px;
background: #f5f5f5;
margin: .5em 0
}
#dsqjs .dsqjs-post-body blockquote {
padding: 0 .8em;
margin: .5em 0;
color: #6a737d;
border-left: .25em solid #dfe2e5
}
#dsqjs .dsqjs-post-body p:last-child {
margin: 0
}
#dsqjs .dsqjs-post-list.dsqjs-children>li {
margin-left: 30px
}
#dsqjs .dsqjs-post-list.dsqjs-children .dsqjs-post-avatar img {
width: 38px;
height: 38px
}
#dsqjs .dsqjs-load-more {
font-size: 14px;
font-weight: 400;
display: block;
text-align: center;
padding: 11px 14px;
margin: 0 0 24px;
background: #687a86;
color: #fff;
cursor: pointer
}
#dsqjs .dsqjs-load-more:hover {
opacity: .8
}
#dsqjs footer {
text-align: right;
line-height: 1.5;
padding-top: 10px;
padding-right: 10px;
border-top: 2px solid #e7e9ee;
margin-top: 12px;
font-weight: 700;
font-size: 16px;
color: #555
}
#dsqjs .dsqjs-disqus-logo {
background-image: url(https://c.disquscdn.com/next/embed/assets/img/sprite.654110a9206fd22f08cca0798e34a65e.png);
background-repeat: no-repeat;
display: inline-block;
background-size: 86px 40.5px;
height: 16.5px;
width: 86px;
}
#dsqjs .dsqjs-order {
display: flex;
float: right;
align-items: center;
margin-top: 10px;
margin-bottom: 12px
}
#dsqjs .dsqjs-order-radio {
display: none
}
#dsqjs .dsqjs-order-radio:checked+.dsqjs-order-label {
color: #fff;
background-color: #888
}
#dsqjs .dsqjs-order-label {
display: block;
height: 20px;
line-height: 20px;
margin-right: 10px;
font-size: 12px;
border-radius: 2px;
padding: 0 5px;
background-color: #dcdcdc;
cursor: pointer
}
#dsqjs p.dsqjs-has-more {
margin-bottom: 24px;
margin-left: 48px;
font-size: 13px;
line-height: 15px
}
#dsqjs p.dsqjs-has-more a.dsqjs-has-more-btn {
color: #656c7a;
text-decoration: underline;
cursor: pointer
}
@media (min-width: 768px) {
#dsqjs .dsqjs-post-list.dsqjs-children>li {
margin-left:48px
}
#dsqjs .dsqjs-post-list .dsqjs-post-avatar {
margin-right: 12px
}
#dsqjs .dsqjs-post-list .dsqjs-post-item {
margin-bottom: 20px
}
}
@media (min-width: 1024px) {
#dsqjs .dsqjs-post-list.dsqjs-children>li {
margin-left:60px
}
}
:root[data-scheme="light"] {
#dsqjs .dsqjs-disqus-logo {
background-position: 0 -7px;
}
}
:root[data-scheme="dark"] {
#dsqjs {
--t-s: rgba(255,255,255,0.9);
--alt: #3e4b5e;
--link-hover: #47a2e0;
--hover-bg: #3e4b5e;
--tag: #3e4b5e;
--border: #435266;
--pre: #3c495b;
--c-bg: #2f3947;
--code: #c3c7cb;
--kbd: #4e5f77;
--hl: #abb2bf;
--hlc: #808895;
--hlk: #c678dd;
--hln: #e06c75;
--hll: #56b6c2;
--hls: #98c379;
--hlt: #e6c07b;
--hlv: #d19a66;
--bg: #181c27;
--main: #252d38;
--t: rgba(255,255,255,0.86);
--t-l: rgba(255,255,255,0.66);
--logo: #fff;
--link: #38a3fd;
--title: rgba(255,255,255,0.92);
--fab: #364151;
--shadow: none;
}
#disqus_thread {
color: var(--body-text-color)
}
#dsqjs #dsqjs-msg {
color: var(--t)
}
#dsqjs a {
color:var(--link)
}
#dsqjs a:focus,#dsqjs a:hover {
color: var(--link-hover)
}
#dsqjs .dsqjs-disqus-logo {
background-position: 0 -24px;
}
#dsqjs .dsqjs-nav,#dsqjs footer {
border-color: var(--hlc)
}
#dsqjs .dsqjs-load-more,#dsqjs .dsqjs-load-more:hover,#dsqjs .dsqjs-nav-tab,#dsqjs .dsqjs-no-comment,#dsqjs .dsqjs-post-content {
color: var(--t)
}
#dsqjs .dsqjs-order-label {
background-color: var(--hlc)
}
#dsqjs .dsqjs-order-radio:checked+.dsqjs-order-label {
background-color: var(--kbd)
}
#dsqjs .dsqjs-tab-active>span:after {
background-color: #2e9fff
}
#dsqjs .dsqjs-footer,#dsqjs .dsqjs-meta {
color: var(--t-l)
}
#dsqjs .dsqjs-post-body blockquote {
border-color: var(--border)
}
}

View File

@ -10,7 +10,8 @@
} }
/* Other */ /* Other */
.chroma .x {} .chroma .x {
}
/* Error */ /* Error */
.chroma .err { .chroma .err {
@ -40,367 +41,369 @@
.chroma .hl { .chroma .hl {
display: block; display: block;
width: 100%; width: 100%;
background-color: #ffffcc background-color: #ffffcc;
} }
/* LineNumbersTable */ /* LineNumbersTable */
.chroma .lnt { .chroma .lnt {
margin-right: 0.4em; margin-right: 0.4em;
padding: 0 0.4em 0 0.4em; padding: 0 0.4em 0 0.4em;
color: #7f7f7f color: #7f7f7f;
} }
/* LineNumbers */ /* LineNumbers */
.chroma .ln { .chroma .ln {
margin-right: 0.4em; margin-right: 0.4em;
padding: 0 0.4em 0 0.4em; padding: 0 0.4em 0 0.4em;
color: #7f7f7f color: #7f7f7f;
} }
/* Keyword */ /* Keyword */
.chroma .k { .chroma .k {
color: #00a8c8 color: #00a8c8;
} }
/* KeywordConstant */ /* KeywordConstant */
.chroma .kc { .chroma .kc {
color: #00a8c8 color: #00a8c8;
} }
/* KeywordDeclaration */ /* KeywordDeclaration */
.chroma .kd { .chroma .kd {
color: #00a8c8 color: #00a8c8;
} }
/* KeywordNamespace */ /* KeywordNamespace */
.chroma .kn { .chroma .kn {
color: #f92672 color: #f92672;
} }
/* KeywordPseudo */ /* KeywordPseudo */
.chroma .kp { .chroma .kp {
color: #00a8c8 color: #00a8c8;
} }
/* KeywordReserved */ /* KeywordReserved */
.chroma .kr { .chroma .kr {
color: #00a8c8 color: #00a8c8;
} }
/* KeywordType */ /* KeywordType */
.chroma .kt { .chroma .kt {
color: #00a8c8 color: #00a8c8;
} }
/* Name */ /* Name */
.chroma .n { .chroma .n {
color: #111111 color: #111111;
} }
/* NameAttribute */ /* NameAttribute */
.chroma .na { .chroma .na {
color: #75af00 color: #75af00;
} }
/* NameBuiltin */ /* NameBuiltin */
.chroma .nb { .chroma .nb {
color: #111111 color: #111111;
} }
/* NameBuiltinPseudo */ /* NameBuiltinPseudo */
.chroma .bp { .chroma .bp {
color: #111111 color: #111111;
} }
/* NameClass */ /* NameClass */
.chroma .nc { .chroma .nc {
color: #75af00 color: #75af00;
} }
/* NameConstant */ /* NameConstant */
.chroma .no { .chroma .no {
color: #00a8c8 color: #00a8c8;
} }
/* NameDecorator */ /* NameDecorator */
.chroma .nd { .chroma .nd {
color: #75af00 color: #75af00;
} }
/* NameEntity */ /* NameEntity */
.chroma .ni { .chroma .ni {
color: #111111 color: #111111;
} }
/* NameException */ /* NameException */
.chroma .ne { .chroma .ne {
color: #75af00 color: #75af00;
} }
/* NameFunction */ /* NameFunction */
.chroma .nf { .chroma .nf {
color: #75af00 color: #75af00;
} }
/* NameFunctionMagic */ /* NameFunctionMagic */
.chroma .fm { .chroma .fm {
color: #111111 color: #111111;
} }
/* NameLabel */ /* NameLabel */
.chroma .nl { .chroma .nl {
color: #111111 color: #111111;
} }
/* NameNamespace */ /* NameNamespace */
.chroma .nn { .chroma .nn {
color: #111111 color: #111111;
} }
/* NameOther */ /* NameOther */
.chroma .nx { .chroma .nx {
color: #75af00 color: #75af00;
} }
/* NameProperty */ /* NameProperty */
.chroma .py { .chroma .py {
color: #111111 color: #111111;
} }
/* NameTag */ /* NameTag */
.chroma .nt { .chroma .nt {
color: #f92672 color: #f92672;
} }
/* NameVariable */ /* NameVariable */
.chroma .nv { .chroma .nv {
color: #111111 color: #111111;
} }
/* NameVariableClass */ /* NameVariableClass */
.chroma .vc { .chroma .vc {
color: #111111 color: #111111;
} }
/* NameVariableGlobal */ /* NameVariableGlobal */
.chroma .vg { .chroma .vg {
color: #111111 color: #111111;
} }
/* NameVariableInstance */ /* NameVariableInstance */
.chroma .vi { .chroma .vi {
color: #111111 color: #111111;
} }
/* NameVariableMagic */ /* NameVariableMagic */
.chroma .vm { .chroma .vm {
color: #111111 color: #111111;
} }
/* Literal */ /* Literal */
.chroma .l { .chroma .l {
color: #ae81ff color: #ae81ff;
} }
/* LiteralDate */ /* LiteralDate */
.chroma .ld { .chroma .ld {
color: #d88200 color: #d88200;
} }
/* LiteralString */ /* LiteralString */
.chroma .s { .chroma .s {
color: #d88200 color: #d88200;
} }
/* LiteralStringAffix */ /* LiteralStringAffix */
.chroma .sa { .chroma .sa {
color: #d88200 color: #d88200;
} }
/* LiteralStringBacktick */ /* LiteralStringBacktick */
.chroma .sb { .chroma .sb {
color: #d88200 color: #d88200;
} }
/* LiteralStringChar */ /* LiteralStringChar */
.chroma .sc { .chroma .sc {
color: #d88200 color: #d88200;
} }
/* LiteralStringDelimiter */ /* LiteralStringDelimiter */
.chroma .dl { .chroma .dl {
color: #d88200 color: #d88200;
} }
/* LiteralStringDoc */ /* LiteralStringDoc */
.chroma .sd { .chroma .sd {
color: #d88200 color: #d88200;
} }
/* LiteralStringDouble */ /* LiteralStringDouble */
.chroma .s2 { .chroma .s2 {
color: #d88200 color: #d88200;
} }
/* LiteralStringEscape */ /* LiteralStringEscape */
.chroma .se { .chroma .se {
color: #8045ff color: #8045ff;
} }
/* LiteralStringHeredoc */ /* LiteralStringHeredoc */
.chroma .sh { .chroma .sh {
color: #d88200 color: #d88200;
} }
/* LiteralStringInterpol */ /* LiteralStringInterpol */
.chroma .si { .chroma .si {
color: #d88200 color: #d88200;
} }
/* LiteralStringOther */ /* LiteralStringOther */
.chroma .sx { .chroma .sx {
color: #d88200 color: #d88200;
} }
/* LiteralStringRegex */ /* LiteralStringRegex */
.chroma .sr { .chroma .sr {
color: #d88200 color: #d88200;
} }
/* LiteralStringSingle */ /* LiteralStringSingle */
.chroma .s1 { .chroma .s1 {
color: #d88200 color: #d88200;
} }
/* LiteralStringSymbol */ /* LiteralStringSymbol */
.chroma .ss { .chroma .ss {
color: #d88200 color: #d88200;
} }
/* LiteralNumber */ /* LiteralNumber */
.chroma .m { .chroma .m {
color: #ae81ff color: #ae81ff;
} }
/* LiteralNumberBin */ /* LiteralNumberBin */
.chroma .mb { .chroma .mb {
color: #ae81ff color: #ae81ff;
} }
/* LiteralNumberFloat */ /* LiteralNumberFloat */
.chroma .mf { .chroma .mf {
color: #ae81ff color: #ae81ff;
} }
/* LiteralNumberHex */ /* LiteralNumberHex */
.chroma .mh { .chroma .mh {
color: #ae81ff color: #ae81ff;
} }
/* LiteralNumberInteger */ /* LiteralNumberInteger */
.chroma .mi { .chroma .mi {
color: #ae81ff color: #ae81ff;
} }
/* LiteralNumberIntegerLong */ /* LiteralNumberIntegerLong */
.chroma .il { .chroma .il {
color: #ae81ff color: #ae81ff;
} }
/* LiteralNumberOct */ /* LiteralNumberOct */
.chroma .mo { .chroma .mo {
color: #ae81ff color: #ae81ff;
} }
/* Operator */ /* Operator */
.chroma .o { .chroma .o {
color: #f92672 color: #f92672;
} }
/* OperatorWord */ /* OperatorWord */
.chroma .ow { .chroma .ow {
color: #f92672 color: #f92672;
} }
/* Punctuation */ /* Punctuation */
.chroma .p { .chroma .p {
color: #111111 color: #111111;
} }
/* Comment */ /* Comment */
.chroma .c { .chroma .c {
color: #75715e color: #75715e;
} }
/* CommentHashbang */ /* CommentHashbang */
.chroma .ch { .chroma .ch {
color: #75715e color: #75715e;
} }
/* CommentMultiline */ /* CommentMultiline */
.chroma .cm { .chroma .cm {
color: #75715e color: #75715e;
} }
/* CommentSingle */ /* CommentSingle */
.chroma .c1 { .chroma .c1 {
color: #75715e color: #75715e;
} }
/* CommentSpecial */ /* CommentSpecial */
.chroma .cs { .chroma .cs {
color: #75715e color: #75715e;
} }
/* CommentPreproc */ /* CommentPreproc */
.chroma .cp { .chroma .cp {
color: #75715e color: #75715e;
} }
/* CommentPreprocFile */ /* CommentPreprocFile */
.chroma .cpf { .chroma .cpf {
color: #75715e color: #75715e;
} }
/* Generic */ /* Generic */
.chroma .g {} .chroma .g {
}
/* GenericDeleted */ /* GenericDeleted */
.chroma .gd {} .chroma .gd {
color: #f92672;
}
/* GenericEmph */ /* GenericEmph */
.chroma .ge { .chroma .ge {
font-style: italic font-style: italic;
} }
/* GenericError */ /* GenericError */
.chroma .gr {} .chroma .gr {
}
/* GenericHeading */ /* GenericHeading */
.chroma .gh {} .chroma .gh {
}
/* GenericInserted */ /* GenericInserted */
.chroma .gi {} .chroma .gi {
color: #7ca727;
}
/* GenericOutput */ /* GenericOutput */
.chroma .go {} .chroma .go {
}
/* GenericPrompt */ /* GenericPrompt */
.chroma .gp {} .chroma .gp {
}
/* GenericStrong */ /* GenericStrong */
.chroma .gs { .chroma .gs {
font-weight: bold font-weight: bold;
} }
/* GenericSubheading */ /* GenericSubheading */
.chroma .gu {} .chroma .gu {
color: #75715e;
}
/* GenericTraceback */ /* GenericTraceback */
.chroma .gt {} .chroma .gt {
}
/* GenericUnderline */ /* GenericUnderline */
.chroma .gl {} .chroma .gl {
}
/* TextWhitespace */ /* TextWhitespace */
.chroma .w {} .chroma .w {
}

View File

@ -1,43 +1,9 @@
.keep-sidebar {
@media (min-width: $on-phone) and (max-width: $on-tablet) {
--main-top-padding: 50px;
}
}
.article-page { .article-page {
&.with-toolbar { &.hide-sidebar-sm .left-sidebar {
@media (max-width: $on-tablet) { display: none;
--main-top-padding: 0;
}
}
&:not(.keep-sidebar) .left-sidebar { @include respond(md) {
@media (max-width: $on-tablet) { display: inherit;
display: none;
}
}
.article-sidebar {
position: sticky;
top: 50px;
flex-shrink: 0;
@media (max-width: $on-tablet) {
display: none;
}
@media (min-width: $on-tablet) {
padding-left: 15px;
margin-left: 1%;
}
@media (min-width: $on-tablet) {
width: 25%;
margin-right: 1%;
}
@media (min-width: $on-desktop) {
width: 30%;
} }
} }
@ -104,6 +70,13 @@
flex-wrap: wrap; flex-wrap: wrap;
text-transform: unset; text-transform: unset;
} }
.article-copyright,
.article-lastmod {
a {
color: var(--body-text-color);
}
}
} }
} }
} }
@ -111,12 +84,9 @@
#article-toolbar { #article-toolbar {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 20px 0;
@media (max-width: $on-tablet) { @include respond(md) {
margin: 20px 0;
}
@media (min-width: $on-tablet) {
display: none; display: none;
} }
@ -147,6 +117,157 @@
span { span {
font-weight: 500; font-weight: 500;
white-space: nowrap;
}
}
}
.article-page.has-toc {
.left-sidebar {
display: none;
}
.right-sidebar {
width: 100%;
padding: 0;
display: none;
@include respond(xl) {
display: block;
top: var(--main-top-padding);
}
}
#article-toolbar {
display: block;
@include respond(md) {
padding: 0;
}
@include respond(xl) {
margin-top: 0;
position: sticky;
top: var(--main-top-padding);
flex-shrink: 1;
a {
background: transparent;
box-shadow: none;
border: 1px solid var(--body-text-color);
width: 100%;
margin-right: 0;
svg {
flex-shrink: 0;
}
}
}
}
.main-container {
align-items: start;
flex-direction: column;
@include respond(xl) {
flex-direction: row;
}
}
.main {
padding-top: 0;
@include respond(xl) {
padding-top: var(--main-top-padding);
}
}
}
.widget--toc {
background-color: var(--card-background);
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l1);
display: flex;
flex-direction: column;
color: var(--card-text-color-main);
overflow: hidden;
::-webkit-scrollbar-thumb {
background-color: var(--card-separator-color);
}
#TableOfContents {
overflow-x: auto;
max-height: 75vh;
ol,
ul {
margin: 0;
padding: 0;
}
ol {
list-style-type: none;
counter-reset: item;
li a::before {
counter-increment: item;
content: counters(item, ".") ". ";
font-weight: bold;
margin-right: 5px;
}
}
& > ul {
padding: 0 1em;
}
li {
margin: 15px 0 15px 20px;
padding: 5px;
& > ol,
& > ul {
margin-top: 10px;
padding-left: 10px;
margin-bottom: -5px;
& > li:last-child {
margin-bottom: 0;
}
}
}
li.active-class > a {
border-left: var(--heading-border-size) solid var(--accent-color);
font-weight: bold;
}
ul li.active-class > a {
display: block;
}
@function repeat($str, $n) {
$result: "";
@for $_ from 0 to $n {
$result: $result + $str;
}
@return $result;
}
// Support up to 6 levels of indentation for lists in ToCs
@for $i from 0 to 5 {
& > ul #{repeat("> li > ul", $i)} > li.active-class > a {
$n: 25 + $i * 35;
margin-left: calc(-#{$n}px - 1em);
padding-left: calc(#{$n}px + 1em - var(--heading-border-size));
}
& > ol #{repeat("> li > ol", $i)} > li.active-class > a {
$n: 9 + $i * 35;
margin-left: calc(-#{$n}px - 1em);
padding-left: calc(#{$n}px + 1em - var(--heading-border-size));
display: block;
}
} }
} }
} }
@ -187,11 +308,11 @@
.article-content { .article-content {
font-family: var(--article-font-family); font-family: var(--article-font-family);
font-size: var(--article-font-size); font-size: var(--article-font-size);
padding: 0 var(--card-padding);
line-height: var(--article-line-height); line-height: var(--article-line-height);
& > p { & > p {
margin: 1.5em 0; margin: 1.5em 0;
padding: 0 var(--card-padding);
} }
h1, h1,
@ -200,12 +321,12 @@
h4, h4,
h5, h5,
h6 { h6 {
padding: 0 calc(var(--card-padding) - var(--heading-border-size)); margin-left: calc((var(--card-padding)) * -1);
padding-left: calc(var(--card-padding) - var(--heading-border-size));
border-left: var(--heading-border-size) solid var(--accent-color); border-left: var(--heading-border-size) solid var(--accent-color);
} }
figure { figure {
margin: 0 auto;
text-align: center; text-align: center;
figcaption { figcaption {
@ -216,17 +337,12 @@
blockquote { blockquote {
position: relative; position: relative;
margin: 10px 0; margin: 1.5em 0;
border-left: var(--blockquote-border-size) solid var(--card-separator-color); border-left: var(--blockquote-border-size) solid var(--card-separator-color);
padding: 15px calc(var(--card-padding) - var(--blockquote-border-size)); padding: 15px calc(var(--card-padding) - var(--blockquote-border-size));
background-color: var(--blockquote-background-color); background-color: var(--blockquote-background-color);
} }
& > ul,
& > ol {
margin: 1em var(--card-padding);
}
hr { hr {
width: 100px; width: 100px;
margin: 40px auto; margin: 40px auto;
@ -249,7 +365,7 @@
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
margin: 20px 0; margin: 1.5em 0;
figure { figure {
margin: 0; margin: 0;
@ -278,10 +394,49 @@
} }
} }
.highlight {
background-color: var(--pre-background-color);
padding: var(--card-padding);
position: relative;
&:hover {
.copyCodeButton {
opacity: 1;
}
}
pre {
margin: initial;
padding: 0;
margin: 0;
width: auto;
}
}
.copyCodeButton {
position: absolute;
top: calc(var(--card-padding));
right: calc(var(--card-padding));
background: var(--card-background);
border: none;
box-shadow: var(--shadow-l2);
border-radius: var(--tag-border-radius);
padding: 8px 16px;
color: var(--card-text-color-main);
cursor: pointer;
font-size: 14px;
opacity: 0;
transition: opacity 0.3s ease;
}
.table-wrapper {
padding: 0 var(--card-padding);
overflow-x: auto;
display: block;
}
table { table {
margin: 0 var(--card-padding);
width: 100%; width: 100%;
max-width: calc(100% - var(--card-padding) * 2);
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
margin-bottom: 1.5em; margin-bottom: 1.5em;
@ -306,4 +461,36 @@
.twitter-tweet { .twitter-tweet {
color: var(--card-text-color-main); color: var(--card-text-color-main);
} }
.video-wrapper {
position: relative;
width: 100%;
height: 0;
padding-bottom: 56.25%;
overflow: hidden;
& > iframe,
& > video {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
border: 0;
}
}
/// Negative margins
blockquote,
figure,
.highlight,
pre,
.gallery,
.video-wrapper,
.table-wrapper,
.s_video_simple {
margin-left: calc((var(--card-padding)) * -1);
margin-right: calc((var(--card-padding)) * -1);
width: calc(100% + var(--card-padding) * 2);
}
} }

View File

@ -106,9 +106,10 @@
z-index: 2; z-index: 2;
cursor: pointer; cursor: pointer;
@media (min-width: $on-phone + 1) { @include respond(md) {
display: none; display: none;
} }
outline: none; outline: none;
&.is-active { &.is-active {
@ -126,67 +127,59 @@
list-style: none; list-style: none;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-top: var(--sidebar-element-separation);
margin-bottom: 0;
overflow-y: auto; overflow-y: auto;
flex-grow: 1; flex-grow: 1;
font-size: 1.5rem; font-size: 1.4rem;
@media (min-width: $on-desktop-large) { background-color: var(--card-background);
margin-top: 30px; padding: 15px 0;
box-shadow: var(--shadow-l1);
display: none;
margin: 0 -15px;
&.show {
display: block;
} }
@media (max-width: $on-phone) { @include respond(md) {
background-color: var(--card-background);
margin-top: 0;
padding: 15px 0;
box-shadow: var(--shadow-l1);
display: none;
&.show {
display: block;
}
}
@media (min-width: $on-phone + 1) {
align-items: flex-end; align-items: flex-end;
display: flex;
background-color: transparent;
padding: 0;
box-shadow: none;
margin: 0;
margin-top: var(--sidebar-element-separation);
}
@include respond(xl) {
margin-top: 30px;
} }
li { li {
position: relative; position: relative;
vertical-align: middle; vertical-align: middle;
padding: 10px 0; padding: 10px 30px;
&:not(:last-of-type) { &:not(:last-of-type) {
margin-bottom: 15px; margin-bottom: 15px;
@media (min-width: $on-desktop-large) { @include respond(xl) {
margin-bottom: 20px; margin-bottom: 20px;
} }
} }
@media (max-width: $on-phone) { @include respond(md) {
padding: 10px 30px; width: 100%;
}
@media (min-width: $on-phone + 1) and (max-width: ($on-desktop - 1)) {
padding: 10px 0; padding: 10px 0;
} }
@media (min-width: $on-phone + 1) {
width: 100%;
}
svg { svg {
width: 25px;
height: 25px;
stroke-width: 1.33; stroke-width: 1.33;
margin-right: 40px; margin-right: 40px;
@media (max-width: $on-desktop-large) { width: 20px;
width: 20px; height: 20px;
height: 20px;
}
} }
a { a {
@ -194,10 +187,6 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
color: var(--body-text-color); color: var(--body-text-color);
@media (max-width: $on-desktop-large) {
font-size: 1.4rem;
}
} }
span { span {
@ -212,3 +201,18 @@
} }
} }
} }
.social-menu {
list-style: none;
padding: 0%;
display: flex;
flex-direction: row;
gap: 10px;
svg {
width: 24px;
height: 24px;
stroke: var(--body-text-color);
stroke-width: 1.33;
}
}

View File

@ -1,7 +1,6 @@
.sidebar { .sidebar {
padding: 0 15px;
&.sticky { &.sticky {
@media (min-width: ($on-phone + 1)) { @include respond(md) {
position: sticky; position: sticky;
} }
} }
@ -11,45 +10,41 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-shrink: 0; flex-shrink: 0;
align-self: stretch;
--sidebar-avatar-size: 150px; width: 100%;
--sidebar-element-separation: 25px; padding: 30px 0 15px 0;
max-width: none;
@media (max-width: $on-desktop-large) { --sidebar-avatar-size: 120px;
--sidebar-avatar-size: 120px; --sidebar-element-separation: 20px;
--sidebar-element-separation: 20px;
@include respond(md) {
width: auto;
padding-top: var(--main-top-padding);
padding-bottom: var(--main-top-padding);
max-height: 100vh;
} }
@media (max-width: $on-phone) { @include respond(2xl) {
width: 100%; --sidebar-avatar-size: 140px;
padding: 30px 0; --sidebar-element-separation: 25px;
max-width: none;
} }
&.sticky { &.sticky {
top: 0; top: 0;
} }
@media (min-width: $on-phone + 1) {
margin-right: 1%;
padding: var(--main-top-padding) 15px;
max-height: 100vh;
}
} }
.right-sidebar { .right-sidebar {
flex-shrink: 0; flex-shrink: 0;
display: none;
&.sticky { &.sticky {
top: 0; top: 0;
} }
@media (max-width: $on-desktop - 1) { @include respond(lg) {
display: none;
}
@media (min-width: $on-tablet) {
margin-left: 1%;
padding-top: var(--main-top-padding); padding-top: var(--main-top-padding);
} }
} }
@ -58,8 +53,10 @@
z-index: 1; z-index: 1;
transition: box-shadow 0.5s ease; transition: box-shadow 0.5s ease;
@media (max-width: $on-phone) { padding: 15px;
padding: 15px 30px;
@include respond(md) {
padding: 0;
} }
.site-avatar { .site-avatar {
@ -79,9 +76,9 @@
.emoji { .emoji {
position: absolute; position: absolute;
width: 50px; width: 40px;
height: 50px; height: 40px;
line-height: 50px; line-height: 40px;
border-radius: 100%; border-radius: 100%;
bottom: 0; bottom: 0;
right: 0; right: 0;
@ -90,20 +87,20 @@
background-color: var(--card-background); background-color: var(--card-background);
box-shadow: var(--shadow-l2); box-shadow: var(--shadow-l2);
@media (max-width: $on-desktop-large) { @include respond(2xl) {
width: 40px; width: 50px;
height: 40px; height: 50px;
line-height: 40px; line-height: 50px;
} }
} }
} }
.site-name { .site-name {
color: var(--accent-color); color: var(--accent-color);
font-size: 2.4rem;
margin: 0; margin: 0;
font-size: 1.8rem;
@media (max-width: $on-desktop-large) { @include respond(2xl) {
font-size: 2rem; font-size: 2rem;
} }
} }
@ -112,10 +109,10 @@
color: var(--body-text-color); color: var(--body-text-color);
font-weight: normal; font-weight: normal;
margin: 10px 0; margin: 10px 0;
font-size: 1.8rem; font-size: 1.6rem;
@media (max-width: $on-desktop-large) { @include respond(2xl) {
font-size: 1.6rem; font-size: 1.8rem;
} }
} }
} }

View File

@ -1,27 +1,15 @@
$defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6; $defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6;
$defaultTagColors: #fff, #fff, #fff, #fff, #fff; $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 * Global style
*/ */
:root { :root {
@media (min-width: $on-phone + 1) { @include respond(md) {
--main-top-padding: 35px; --main-top-padding: 35px;
} }
@media (min-width: $on-desktop-large) { @include respond(xl) {
--main-top-padding: 50px; --main-top-padding: 50px;
} }
@ -36,12 +24,17 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--section-separation: 40px; --section-separation: 40px;
[data-scheme="dark"] { --scrollbar-thumb: hsl(0, 0%, 85%);
--scrollbar-track: var(--body-background);
&[data-scheme="dark"] {
--body-background: #303030; --body-background: #303030;
--accent-color: #ecf0f1; --accent-color: #ecf0f1;
--accent-color-darker: #bdc3c7; --accent-color-darker: #bdc3c7;
--accent-color-text: #000; --accent-color-text: #000;
--body-text-color: rgba(255, 255, 255, 0.7); --body-text-color: rgba(255, 255, 255, 0.7);
--scrollbar-thumb: hsl(0, 0%, 40%);
--scrollbar-track: var(--body-background);
} }
} }
@ -70,20 +63,23 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--card-border-radius: 10px; --card-border-radius: 10px;
--card-padding: 30px; --card-padding: 20px;
@media (max-width: $on-desktop-large) {
@include respond(md) {
--card-padding: 25px; --card-padding: 25px;
} }
@media (max-width: $on-tablet) {
--card-padding: 20px; @include respond(xl) {
--card-padding: 30px;
} }
--small-card-padding: 25px; --small-card-padding: 25px 20px;
@media (max-width: $on-tablet) {
--small-card-padding: 25px 20px; @include respond(md) {
--small-card-padding: 25px;
} }
[data-scheme="dark"] { &[data-scheme="dark"] {
--card-background: #424242; --card-background: #424242;
--card-background-selected: rgba(255, 255, 255, 0.16); --card-background-selected: rgba(255, 255, 255, 0.16);
--card-text-color-main: rgba(255, 255, 255, 0.9); --card-text-color-main: rgba(255, 255, 255, 0.9);
@ -98,10 +94,12 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
*/ */
:root { :root {
--article-font-family: var(--base-font-family); --article-font-family: var(--base-font-family);
--article-font-size: 1.7rem; --article-font-size: 1.6rem;
@media (max-width: $on-tablet) {
--article-font-size: 1.6rem; @include respond(md) {
--article-font-size: 1.7rem;
} }
--article-line-height: 1.85; --article-line-height: 1.85;
} }
@ -127,7 +125,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--table-border-color: #dadada; --table-border-color: #dadada;
--tr-even-background-color: #efefee; --tr-even-background-color: #efefee;
[data-scheme="dark"] { &[data-scheme="dark"] {
--code-background-color: #272822; --code-background-color: #272822;
--code-text-color: rgba(255, 255, 255, 0.9); --code-text-color: rgba(255, 255, 255, 0.9);
@ -149,3 +147,15 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--shadow-l4: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), --shadow-l4: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04),
0px 0px 1px rgba(0, 0, 0, 0.04); 0px 0px 1px rgba(0, 0, 0, 0.04);
} }
[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";
}

View File

@ -9,7 +9,7 @@ class StackColorScheme {
this.bindMatchMedia(); this.bindMatchMedia();
this.currentScheme = this.getSavedScheme(); this.currentScheme = this.getSavedScheme();
this.dispatchEvent(document.body.dataset.scheme as colorScheme); this.dispatchEvent(document.documentElement.dataset.scheme as colorScheme);
if (toggleEl) if (toggleEl)
this.bindClick(toggleEl); this.bindClick(toggleEl);
@ -22,7 +22,7 @@ class StackColorScheme {
localStorage.setItem(this.localStorageKey, this.currentScheme); localStorage.setItem(this.localStorageKey, this.currentScheme);
} }
private bindClick(toggleEl) { private bindClick(toggleEl: HTMLElement) {
toggleEl.addEventListener('click', (e) => { toggleEl.addEventListener('click', (e) => {
if (this.isDark()) { if (this.isDark()) {
/// Disable dark mode /// Disable dark mode
@ -56,13 +56,13 @@ class StackColorScheme {
private setBodyClass() { private setBodyClass() {
if (this.isDark()) { if (this.isDark()) {
document.body.dataset.scheme = 'dark'; document.documentElement.dataset.scheme = 'dark';
} }
else { else {
document.body.dataset.scheme = 'light'; document.documentElement.dataset.scheme = 'light';
} }
this.dispatchEvent(document.body.dataset.scheme as colorScheme); this.dispatchEvent(document.documentElement.dataset.scheme as colorScheme);
} }
private getSavedScheme(): colorScheme { private getSavedScheme(): colorScheme {
@ -85,4 +85,4 @@ class StackColorScheme {
} }
} }
export default StackColorScheme; export default StackColorScheme;

View File

@ -34,7 +34,7 @@ class StackGallery {
private loadItems(container: HTMLElement) { private loadItems(container: HTMLElement) {
this.items = []; this.items = [];
const figures = container.querySelectorAll('figure'); const figures = container.querySelectorAll('figure.gallery-image');
for (const el of figures) { for (const el of figures) {
const figcaption = el.querySelector('figcaption'), const figcaption = el.querySelector('figcaption'),
@ -57,7 +57,61 @@ class StackGallery {
} }
public static createGallery(container: HTMLElement) { public static createGallery(container: HTMLElement) {
const figuresEl = container.querySelectorAll('figure'); /// The process of wrapping image with figure tag is done using JavaScript instead of only Hugo markdown render hook
/// because it can not detect whether image is being wrapped by a link or not
/// and it lead to a invalid HTML construction (<a><figure><img></figure></a>)
const images = container.querySelectorAll('img');
for (const img of Array.from(images)) {
/// Images are wrapped with figure tag if the paragraph has only images without texts
/// This is done to allow inline images within paragraphs
const paragraph = img.closest('p');
if (!paragraph || !container.contains(paragraph)) continue;
if (paragraph.textContent.trim() == '') {
/// Once we insert figcaption, this check no longer works
/// So we add a class to paragraph to mark it
paragraph.classList.add('no-text');
}
let isNewLineImage = paragraph.classList.contains('no-text');
if (!isNewLineImage) continue;
const hasLink = img.parentElement.tagName == 'A';
let el: HTMLElement = img;
/// Wrap image with figure tag, with flex-grow and flex-basis values extracted from img's data attributes
const figure = document.createElement('figure');
figure.style.setProperty('flex-grow', img.getAttribute('data-flex-grow') || '1');
figure.style.setProperty('flex-basis', img.getAttribute('data-flex-basis') || '0');
if (hasLink) {
/// Wrap <a> if it exists
el = img.parentElement;
}
el.parentElement.insertBefore(figure, el);
figure.appendChild(el);
/// Add figcaption if it exists
if (img.hasAttribute('alt')) {
const figcaption = document.createElement('figcaption');
figcaption.innerText = img.getAttribute('alt');
figure.appendChild(figcaption);
}
/// Wrap img tag with <a> tag if image was not wrapped by <a> tag
if (!hasLink) {
figure.className = 'gallery-image';
const a = document.createElement('a');
a.href = img.src;
a.setAttribute('target', '_blank');
img.parentNode.insertBefore(a, img);
a.appendChild(img);
}
}
const figuresEl = container.querySelectorAll('figure.gallery-image');
let currentGallery = []; let currentGallery = [];

View File

@ -10,6 +10,8 @@ import { getColor } from 'ts/color';
import menu from 'ts/menu'; import menu from 'ts/menu';
import createElement from 'ts/createElement'; import createElement from 'ts/createElement';
import StackColorScheme from 'ts/colorScheme'; import StackColorScheme from 'ts/colorScheme';
import { setupScrollspy } from 'ts/scrollspy';
import { setupSmoothAnchors } from "ts/smoothAnchors";
let Stack = { let Stack = {
init: () => { init: () => {
@ -21,6 +23,8 @@ let Stack = {
const articleContent = document.querySelector('.article-content') as HTMLElement; const articleContent = document.querySelector('.article-content') as HTMLElement;
if (articleContent) { if (articleContent) {
new StackGallery(articleContent); new StackGallery(articleContent);
setupSmoothAnchors();
setupScrollspy();
} }
/** /**
@ -54,6 +58,39 @@ let Stack = {
observer.observe(articleTile) observer.observe(articleTile)
} }
/**
* Add copy button to code block
*/
const highlights = document.querySelectorAll('.article-content div.highlight');
const copyText = `Copy`,
copiedText = `Copied!`;
highlights.forEach(highlight => {
const copyButton = document.createElement('button');
copyButton.innerHTML = copyText;
copyButton.classList.add('copyCodeButton');
highlight.appendChild(copyButton);
const codeBlock = highlight.querySelector('code[data-lang]');
if (!codeBlock) return;
copyButton.addEventListener('click', () => {
navigator.clipboard.writeText(codeBlock.textContent)
.then(() => {
copyButton.textContent = copiedText;
setTimeout(() => {
copyButton.textContent = copyText;
}, 1000);
})
.catch(err => {
alert(err)
console.log('Something went wrong', err);
});
});
});
new StackColorScheme(document.getElementById('dark-mode-toggle')); new StackColorScheme(document.getElementById('dark-mode-toggle'));
} }
} }

131
assets/ts/scrollspy.ts Normal file
View File

@ -0,0 +1,131 @@
// Implements a scroll spy system for the ToC, displaying the current section with an indicator and scrolling to it when needed.
// Inspired from https://gomakethings.com/debouncing-your-javascript-events/
function debounced(func: Function) {
let timeout;
return () => {
if (timeout) {
window.cancelAnimationFrame(timeout);
}
timeout = window.requestAnimationFrame(() => func());
}
}
const headersQuery = ".article-content h1[id], .article-content h2[id], .article-content h3[id], .article-content h4[id], .article-content h5[id], .article-content h6[id]";
const tocQuery = "#TableOfContents";
const navigationQuery = "#TableOfContents li";
const activeClass = "active-class";
function scrollToTocElement(tocElement: HTMLElement, scrollableNavigation: HTMLElement) {
let textHeight = tocElement.querySelector("a").offsetHeight;
let scrollTop = tocElement.offsetTop - scrollableNavigation.offsetHeight / 2 + textHeight / 2 - scrollableNavigation.offsetTop;
if (scrollTop < 0) {
scrollTop = 0;
}
scrollableNavigation.scrollTo({ top: scrollTop, behavior: "smooth" });
}
type IdToElementMap = { [key: string]: HTMLElement };
function buildIdToNavigationElementMap(navigation: NodeListOf<Element>): IdToElementMap {
const sectionLinkRef: IdToElementMap = {};
navigation.forEach((navigationElement: HTMLElement) => {
const link = navigationElement.querySelector("a");
const href = link.getAttribute("href");
if (href.startsWith("#")) {
sectionLinkRef[href.slice(1)] = navigationElement;
}
});
return sectionLinkRef;
}
function computeOffsets(headers: NodeListOf<Element>) {
let sectionsOffsets = [];
headers.forEach((header: HTMLElement) => { sectionsOffsets.push({ id: header.id, offset: header.offsetTop }) });
sectionsOffsets.sort((a, b) => a.offset - b.offset);
return sectionsOffsets;
}
function setupScrollspy() {
let headers = document.querySelectorAll(headersQuery);
if (!headers) {
console.warn("No header matched query", headers);
return;
}
let scrollableNavigation = document.querySelector(tocQuery) as HTMLElement | undefined;
if (!scrollableNavigation) {
console.warn("No toc matched query", tocQuery);
return;
}
let navigation = document.querySelectorAll(navigationQuery);
if (!navigation) {
console.warn("No navigation matched query", navigationQuery);
return;
}
let sectionsOffsets = computeOffsets(headers);
// We need to avoid scrolling when the user is actively interacting with the ToC. Otherwise, if the user clicks on a link in the ToC,
// we would scroll their view, which is not optimal usability-wise.
let tocHovered: boolean = false;
scrollableNavigation.addEventListener("mouseenter", debounced(() => tocHovered = true));
scrollableNavigation.addEventListener("mouseleave", debounced(() => tocHovered = false));
let activeSectionLink: Element;
let idToNavigationElement: IdToElementMap = buildIdToNavigationElementMap(navigation);
function scrollHandler() {
let scrollPosition = document.documentElement.scrollTop || document.body.scrollTop;
let newActiveSection: HTMLElement | undefined;
// Find the section that is currently active.
// It is possible for no section to be active, so newActiveSection may be undefined.
sectionsOffsets.forEach((section) => {
if (scrollPosition >= section.offset - 20) {
newActiveSection = document.getElementById(section.id);
}
});
// Find the link for the active section. Once again, there are a few edge cases:
// - No active section = no link => undefined
// - No active section but the link does not exist in toc (e.g. because it is outside of the applicable ToC levels) => undefined
let newActiveSectionLink: HTMLElement | undefined
if (newActiveSection) {
newActiveSectionLink = idToNavigationElement[newActiveSection.id];
}
if (newActiveSection && !newActiveSectionLink) {
// The active section does not have a link in the ToC, so we can't scroll to it.
console.debug("No link found for section", newActiveSection);
} else if (newActiveSectionLink !== activeSectionLink) {
if (activeSectionLink)
activeSectionLink.classList.remove(activeClass);
if (newActiveSectionLink) {
newActiveSectionLink.classList.add(activeClass);
if (!tocHovered) {
// Scroll so that newActiveSectionLink is in the middle of scrollableNavigation, except when it's from a manual click (hence the tocHovered check)
scrollToTocElement(newActiveSectionLink, scrollableNavigation);
}
}
activeSectionLink = newActiveSectionLink;
}
}
window.addEventListener("scroll", debounced(scrollHandler));
// Resizing may cause the offset values to change: recompute them.
function resizeHandler() {
sectionsOffsets = computeOffsets(headers);
scrollHandler();
}
window.addEventListener("resize", debounced(resizeHandler));
}
export { setupScrollspy };

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,79 +58,131 @@ 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) { const titleMatchAll = item.title.matchAll(regex);
result.preview += '[...]'; for (const match of Array.from(titleMatchAll)) {
results.push(result); 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 */ /// Result with more matches appears first
return results.sort((a, b) => { return results.sort((a, b) => {
return b.matchCount - a.matchCount; return b.matchCount - a.matchCount;
}); });
} }
public static marker(match) {
return '<mark>' + match + '</mark>';
}
private async doSearch(keywords: string[]) { private async doSearch(keywords: string[]) {
const startTime = performance.now(); const startTime = performance.now();
@ -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

@ -0,0 +1,34 @@
// Implements smooth scrolling when clicking on an anchor link.
// This is required instead of using modern CSS because Chromium does not currently support scrolling
// one element with scrollTo while another element is scrolled because of a click on a link. This would
// thus not work with the ToC scrollspy and e.g. footnotes.
// Here are additional links about this issue:
// - https://stackoverflow.com/questions/49318497/google-chrome-simultaneously-smooth-scrollintoview-with-more-elements-doesn
// - https://stackoverflow.com/questions/57214373/scrollintoview-using-smooth-function-on-multiple-elements-in-chrome
// - https://bugs.chromium.org/p/chromium/issues/detail?id=833617
// - https://bugs.chromium.org/p/chromium/issues/detail?id=1043933
// - https://bugs.chromium.org/p/chromium/issues/detail?id=1121151
const anchorLinksQuery = "a[href]";
function setupSmoothAnchors() {
document.querySelectorAll(anchorLinksQuery).forEach(aElement => {
let href = aElement.getAttribute("href");
if (!href.startsWith("#")) {
return;
}
aElement.addEventListener("click", clickEvent => {
clickEvent.preventDefault();
let targetId = aElement.getAttribute("href").substring(1);
// The replace done on ':' is here for footnotes, as this character would otherwise interfere when used as a CSS selector.
let target = document.querySelector(`#${targetId.replace(":", "\\:")}`) as HTMLElement;
window.history.pushState({}, "", aElement.getAttribute("href"));
scrollTo({ top: target.offsetTop, behavior: "smooth" });
});
});
}
export { setupSmoothAnchors };

147
config.yaml Normal file
View File

@ -0,0 +1,147 @@
module:
hugoVersion:
extended: true
min: "0.87.0"
params:
mainSections:
- post
featuredImageField: image
rssFullContent: true
favicon:
footer:
since:
customText:
dateFormat:
published: Jan 02, 2006
lastUpdated: Jan 02, 2006 15:04 MST
sidebar:
emoji:
subtitle:
avatar:
enabled: true
local: true
src: img/avatar.png
article:
math: false
toc: true
readingTime: true
license:
enabled: false
default: Licensed under CC BY-NC-SA 4.0
comments:
enabled: false
provider: disqus
disqusjs:
shortname:
apiUrl:
apiKey:
admin:
adminLabel:
utterances:
repo:
issueTerm: pathname
label:
remark42:
host:
site:
locale:
vssue:
platform:
owner:
repo:
clientId:
clientSecret:
autoCreateIssue: false
# Waline client configuration see: https://waline.js.org/en/reference/client.html
waline:
serverURL:
lang:
visitor:
avatar:
emoji:
- https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo
requiredMeta:
- name
- email
- url
placeholder:
locale:
admin: Admin
twikoo:
envId:
region:
path:
lang:
giscus:
repo:
repoID:
category:
categoryID:
mapping:
lightTheme:
darkTheme:
reactionsEnabled: 1
emitMetadata: 0
gitalk:
owner:
admin:
repo:
clientID:
clientSecret:
cusdis:
host:
id:
widgets:
enabled:
- search
- archives
- tag-cloud
archives:
limit: 5
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

View File

@ -23,16 +23,24 @@ PhotoSwipe:
type: style type: style
KaTeX: KaTeX:
- src: https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css - src: https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css
integrity: sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X integrity: sha384-RZU/ijkSsFbcmivfdRBQDtwuwVqK7GMOw6IMvKyeWL2K5UAlyp6WonmB8m7Jd0Hn
type: style type: style
- src: https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js - src: https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.js
integrity: sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4 integrity: sha384-pK1WpvzWVBQiP0/GjnvRxV4mOb0oxFuyRxJlk6vVw146n3egcN5C925NCP7a7BY8
type: script type: script
defer: true defer: true
- src: https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js - src: https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/contrib/auto-render.min.js
integrity: sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa integrity: sha384-vZTG03m+2yp6N6BNi5iM4rW4oIwk5DfcNdFfxkk9ZWpDriOkXX8voJBFrAO7MpVl
type: script type: script
defer: true defer: true
Cactus:
- src: https://latest.cactus.chat/cactus.js
integrity:
type: script
- src: https://latest.cactus.chat/style.css
integrity:
type: style

1
debug.sh Executable file
View File

@ -0,0 +1 @@
cd exampleSite && hugo server --gc --themesDir=../..

View File

@ -22,6 +22,9 @@ _testmain.go
*.exe *.exe
*.test *.test
/public public
/themes themes
resources
assets/jsconfig.json
.DS_Store .DS_Store

View File

@ -11,9 +11,13 @@ disqusShortname: hugo-theme-stack
googleAnalytics: googleAnalytics:
# Theme i18n support # Theme i18n support
# Available values: en, fr, id, ja, ko, pt-br, zh-cn # Available values: en, fr, id, ja, ko, pt-br, zh-cn, zh-tw, es, de, nl, it, th, el, uk
DefaultContentLanguage: en DefaultContentLanguage: en
# Set hasCJKLanguage to true if DefaultContentLanguage is in [zh-cn ja ko]
# This will make .Summary and .WordCount behave correctly for CJK languages.
hasCJKLanguage: false
permalinks: permalinks:
post: /p/:slug/ post: /p/:slug/
page: /:slug/ page: /:slug/
@ -37,11 +41,14 @@ params:
emoji: 🍥 emoji: 🍥
subtitle: Lorem ipsum dolor sit amet, consectetur adipiscing elit. subtitle: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
avatar: avatar:
enabled: true
local: true local: true
src: img/avatar.png src: img/avatar.png
article: article:
math: false math: false
toc: true
readingTime: true
license: license:
enabled: true enabled: true
default: Licensed under CC BY-NC-SA 4.0 default: Licensed under CC BY-NC-SA 4.0
@ -50,6 +57,13 @@ params:
enabled: true enabled: true
provider: disqus provider: disqus
disqusjs:
shortname:
apiUrl:
apiKey:
admin:
adminLabel:
utterances: utterances:
repo: repo:
issueTerm: pathname issueTerm: pathname
@ -60,6 +74,63 @@ params:
site: site:
locale: locale:
vssue:
platform:
owner:
repo:
clientId:
clientSecret:
autoCreateIssue: false
# Waline client configuration see: https://waline.js.org/en/reference/client.html
waline:
serverURL:
lang:
visitor:
avatar:
emoji:
- https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo
requiredMeta:
- name
- email
- url
placeholder:
locale:
admin: Admin
twikoo:
envId:
region:
path:
lang:
# See https://cactus.chat/docs/reference/web-client/#configuration for description of the various options
cactus:
defaultHomeserverUrl: "https://matrix.cactus.chat:8448"
serverName: "cactus.chat"
siteName: "" # You must insert a unique identifier here matching the one you registered (See https://cactus.chat/docs/getting-started/quick-start/#register-your-site)
giscus:
repo:
repoID:
category:
categoryID:
mapping:
lightTheme:
darkTheme:
reactionsEnabled: 1
emitMetadata: 0
gitalk:
owner:
admin:
repo:
clientID:
clientSecret:
cusdis:
host:
id:
widgets: widgets:
enabled: enabled:
- search - search
@ -99,31 +170,32 @@ params:
content: content:
enabled: true enabled: true
### Custom menu
### See https://docs.stack.jimmycai.com/configuration/custom-menu.html
### To remove about, archive and search page menu item, remove `menu` field from their FrontMatter
menu: menu:
main: main:
- identifier: home - identifier: home
name: Home name: Home
url: / url: /
weight: -100 weight: -100
pre: home params:
### For demonstration purpose, the home link will be open in a new tab
newTab: true
icon: home
- identifier: about social:
name: About - identifier: github
url: about name: GitHub
weight: -90 url: https://github.com/CaiJimmy/hugo-theme-stack
pre: user params:
icon: brand-github
- identifier: archives - identifier: twitter
name: Archives name: Twitter
url: archives url: https://twitter.com
weight: -70 params:
pre: archives icon: brand-twitter
- identifier: search
name: Search
url: search
weight: -60
pre: search
related: related:
includeNewer: true includeNewer: true
@ -137,5 +209,19 @@ related:
weight: 200 weight: 200
markup: markup:
goldmark:
renderer:
## Set to true if you have HTML content inside Markdown
unsafe: false
tableOfContents:
endLevel: 4
ordered: true
startLevel: 2
highlight: highlight:
noClasses: false noClasses: false
codeFences: true
guessSyntax: true
lineNoStart: 1
lineNos: true
lineNumbersInTable: true
tabWidth: 4

View File

@ -1,9 +1,9 @@
--- ---
title: "Test" title: "Test"
description: "This is a example category" description: "This is an example category"
slug: "test" slug: "test"
image: "hutomo-abrianto-l2jk-uxb1BY-unsplash.jpg" image: "hutomo-abrianto-l2jk-uxb1BY-unsplash.jpg"
style: style:
background: "#2a9d8f" background: "#2a9d8f"
color: "#fff" color: "#fff"
--- ---

View File

@ -1,12 +1,19 @@
+++ ---
title = "About" title: About
description = "Hugo, the world's fastest framework for building websites" description: Hugo, the world's fastest framework for building websites
date = "2019-02-28" date: '2019-02-28'
aliases = ["about-us", "about-hugo", "contact"] aliases:
author = "Hugo Authors" - about-us
license = "CC BY-NC-ND" - about-hugo
lastmod = "2020-10-09" - contact
+++ license: CC BY-NC-ND
lastmod: '2020-10-09'
menu:
main:
weight: -90
params:
icon: user
---
Written in Go, Hugo is an open source static site generator available under the [Apache Licence 2.0.](https://github.com/gohugoio/hugo/blob/master/LICENSE) Hugo supports TOML, YAML and JSON data file types, Markdown and HTML content files and uses shortcodes to add rich content. Other notable features are taxonomies, multilingual mode, image processing, custom output formats, HTML/CSS/JS minification and support for Sass SCSS workflows. Written in Go, Hugo is an open source static site generator available under the [Apache Licence 2.0.](https://github.com/gohugoio/hugo/blob/master/LICENSE) Hugo supports TOML, YAML and JSON data file types, Markdown and HTML content files and uses shortcodes to add rich content. Other notable features are taxonomies, multilingual mode, image processing, custom output formats, HTML/CSS/JS minification and support for Sass SCSS workflows.

View File

@ -1,6 +0,0 @@
---
title: "Archives"
date: 2019-05-28
layout: "archives"
slug: "archives"
---

View File

@ -0,0 +1,11 @@
---
title: "Archives"
date: 2019-05-28
layout: "archives"
slug: "archives"
menu:
main:
weight: -70
params:
icon: archives
---

View File

@ -0,0 +1,37 @@
---
title: Links
links:
- title: GitHub
description: GitHub is the world's largest software development platform.
website: https://github.com
image: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
- title: TypeScript
description: TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
website: https://www.typescriptlang.org
image: ts-logo-128.jpg
menu:
main:
weight: -50
params:
icon: link
comments: false
---
To use this feature, add `links` section to frontmatter.
This page's frontmatter:
```yaml
links:
- title: GitHub
description: GitHub is the world's largest software development platform.
website: https://github.com
image: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
- title: TypeScript
description: TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
website: https://www.typescriptlang.org
image: ts-logo-128.jpg
```
`image` field accepts both local and external images.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -5,4 +5,9 @@ layout: "search"
outputs: outputs:
- html - html
- json - json
menu:
main:
weight: -60
params:
icon: search
--- ---

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
@ -113,6 +117,16 @@ Tables aren't part of the core Markdown spec, but Hugo supports supports them ou
</html> </html>
{{< /highlight >}} {{< /highlight >}}
#### Diff code block
```diff
[dependencies.bevy]
git = "https://github.com/bevyengine/bevy"
rev = "11f52b8c72fc3a568e8bb4a4cd1f3eb025ac2e13"
- features = ["dynamic"]
+ features = ["jpeg", "dynamic"]
```
## List Types ## List Types
#### Ordered List #### Ordered List
@ -148,3 +162,7 @@ X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session. Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session.
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures. Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
## Hyperlinked image
[![Google](https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png)](https://google.com)

View File

@ -32,3 +32,11 @@ Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-manageme
## Vimeo Simple Shortcode ## Vimeo Simple Shortcode
{{< vimeo_simple 48912912 >}} {{< vimeo_simple 48912912 >}}
## bilibilibi Shortcode
{{< bilibili av498363026 >}}
## Gist Shortcode
{{< gist spf13 7896402 >}}

View File

@ -1,27 +0,0 @@
> Original Repo: https://github.com/cdeleeuwe/netlify-plugin-hugo-cache-resources
>
# Netlify Build Plugin: Persist Hugo resources Between Builds
Persist [Hugo](https://gohugo.io/) resources folder between Netlify builds for huge build speed improvements! ⚡️
This plugin caches the `resources` folder after build. If you are processing many images, this would improve build duration significantly.
Note: Restoring cache only comes from the production branch. So once cache is saved on the production branch, the next preview build would use the cache. For example, when pushing to the same preview build, the latest preview build will not get the cache from the previous preview build, but will get it from master.
## Usage
To install, add the following lines to your `netlify.toml` file:
```toml
[build]
publish = "public"
[[plugins]]
package = "netlify-plugin-hugo-cache-resources"
[plugins.inputs]
# If it should show more verbose logs (optional, default = true)
debug = true
```
Note: The `[[plugins]]` line is required for each plugin, even if you have other plugins in your `netlify.toml` file already.

View File

@ -1,39 +0,0 @@
const getResourcesDir = () => {
return 'exampleSite/resources';
}
const printList = (items) => {
console.log('---');
items.forEach((item, index) => {
console.log(`${index + 1}. ${item}`);
});
}
module.exports = {
async onPreBuild({ utils, inputs }) {
const path = getResourcesDir();
const success = await utils.cache.restore(path);
if (success) {
const cachedFiles = await utils.cache.list(path);
console.log(`Restored cached resources folder. Total files: ${cachedFiles.length}`);
if (inputs.debug) printList(cachedFiles);
} else {
console.log(`No cache found for resources folder`);
}
},
async onPostBuild({ utils, inputs }) {
const path = getResourcesDir();
const success = await utils.cache.save(path);
if (success) {
const cachedFiles = await utils.cache.list(path);
console.log(`Saved resources folder to cache. Total files: ${cachedFiles.length}`);
if (inputs.debug) printList(cachedFiles);
} else {
console.log(`No resources folder cached`);
}
}
};

View File

@ -1,5 +0,0 @@
name: netlify-plugin-hugo-cache
inputs:
- name: debug
description: Show more verbose logs
default: true

3
go.mod Normal file
View File

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

70
i18n/de.yaml Normal file
View File

@ -0,0 +1,70 @@
toggleMenu:
other: Menü umschalten
darkMode:
other: Dunkler Modus
list:
page:
one: "{{ .Count }} Seite"
other: "{{ .Count }} Seiten"
section:
other: Abschnitt
subsection:
one: Unterabschnitt
other: Unterabschnitte
article:
back:
other: Zurück
tableOfContents:
other: Inhaltsverzeichnis
relatedContents:
other: Verwandte Inhalte
lastUpdatedOn:
other: Zuletzt aktualisiert am
readingTime:
one: "{{ .Count }} Minute Lesezeit"
other: "{{ .Count }} Minuten Lesezeit"
notFound:
title:
other: Seite nicht gefunden
subtitle:
other: Diese Seite existiert nicht
widget:
archives:
title:
other: Archiv
more:
other: Weitere
tagCloud:
title:
other: Schlagwörter
search:
title:
other: Suche
placeholder:
other: Etwas tippen...
resultTitle:
other: "#PAGES_COUNT Seiten (#TIME_SECONDS Sekunden)"
footer:
builtWith:
other: Erstellt mit {{ .Generator }}
designedBy:
other: Theme {{ .Theme }} gestaltet von {{ .DesignedBy }}

70
i18n/el.yaml Normal file
View File

@ -0,0 +1,70 @@
toggleMenu:
other: Εναλλαγή Μενού
darkMode:
other: Σκοτεινό θέμα
list:
page:
one: "{{ .Count }} σελιδα"
other: "{{ .Count }} σελιδες"
section:
other: Ενότητα
subsection:
one: Υποενότητα
other: Υποενότητες
article:
back:
other: Πισω
tableOfContents:
other: Πινακας περιεχομενων
relatedContents:
other: Σχετικο περιεχομενο
lastUpdatedOn:
other: Τελευταια τροποποιηση στις
readingTime:
one: "{{ .Count }} λεπτό ανάγνωσης"
### Seems that there's no need to add 's' even if it's plural in English
other: "{{ .Count }} λεπτά ανάγνωσης"
notFound:
title:
other: Δε βρέθηκε
subtitle:
other: Η σελίδα δε βρέθηκε.
widget:
archives:
title:
other: Αρχειο
more:
other: Περισσότερα
tagCloud:
title:
other: Tags
search:
title:
other: Αναζήτηση
placeholder:
other: Πληκτρολογήστε κάτι...
resultTitle:
other: "#PAGES_COUNT σελιδες (#TIME_SECONDS δευτερολεπτα)"
footer:
builtWith:
other: Δημιουργήθηκε με τη χρήση {{ .Generator }}
designedBy:
other: Το θέμα {{ .Theme }} σχεδιάστηκε από το {{ .DesignedBy }}

View File

@ -17,23 +17,37 @@ list:
other: Subsections other: Subsections
article: article:
back:
other: Back
tableOfContents:
other: Table of contents
relatedContents: relatedContents:
other: Related contents other: Related contents
lastUpdatedOn: lastUpdatedOn:
other: Last updated on other: Last updated on
readingTime:
one: "{{ .Count }} minute read"
other: "{{ .Count }} minute read"
notFound: notFound:
title: title:
other: Not Found other: Not Found
subtitle: subtitle:
other: This page does not exist. other: This page does not exist
widget: widget:
archives: archives:
title: title:
other: Archives other: Archives
more: more:
other: More other: More
tagCloud: tagCloud:
title: title:
other: Tags other: Tags
@ -41,13 +55,16 @@ widget:
search: search:
title: title:
other: Search other: Search
placeholder: placeholder:
other: Type something... other: Type something...
resultTitle: resultTitle:
other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)" other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
footer: footer:
builtWith: builtWith:
other: Built with {{ .Generator }} other: Built with {{ .Generator }}
designedBy: designedBy:
other: Theme {{ .Theme }} designed by {{ .DesignedBy }} other: Theme {{ .Theme }} designed by {{ .DesignedBy }}

70
i18n/es.yaml Normal file
View File

@ -0,0 +1,70 @@
toggleMenu:
other: Toggle Menu
darkMode:
other: Modo oscuro
list:
page:
one: "{{ .Count }} página"
other: "{{ .Count }} páginas"
section:
other: Sección
subsection:
one: Subsección
other: Subsecciones
article:
back:
other: Volver
tableOfContents:
other: Tabla de contenido
relatedContents:
other: Contenidos relacionados
lastUpdatedOn:
other: Última vez actualizado
readingTime:
one: "{{ .Count }} minuto a leer"
other: "{{ .Count }} minutos a leer"
notFound:
title:
other: No Encontrado
subtitle:
other: Esta página no existe
widget:
archives:
title:
other: Archivo
more:
other: Más
tagCloud:
title:
other: Etiquetas
search:
title:
other: Búsqueda
placeholder:
other: Teclea algo...
resultTitle:
other: "#PAGES_COUNT páginas en (#TIME_SECONDS segundos)"
footer:
builtWith:
other: Creado con {{ .Generator }}
designedBy:
other: Tema {{ .Theme }} diseñado por {{ .DesignedBy }}

View File

@ -1,12 +1,38 @@
toggleMenu: toggleMenu:
other: Afficher le menu other: Afficher le menu
darkMode:
other: Mode sombre
list:
page:
one: "{{ .Count }} page"
other: "{{ .Count }} pages"
section:
other: Section
subsection:
one: Sous-section
other: Sous-sections
article: article:
back:
other: Retour
tableOfContents:
other: Table des matières
relatedContents: relatedContents:
other: Contenus liés other: Contenus liés
lastUpdatedOn: lastUpdatedOn:
other: Dernière mise à jour le other: Dernière mise à jour le
readingTime:
one: "{{ .Count }} minute de lecture"
other: "{{ .Count }} minutes de lecture"
notFound: notFound:
title: title:
other: Page non trouvée other: Page non trouvée
@ -17,8 +43,10 @@ widget:
archives: archives:
title: title:
other: Archives other: Archives
more: more:
other: Autres other: Autres
tagCloud: tagCloud:
title: title:
other: Mots clés other: Mots clés
@ -26,13 +54,16 @@ widget:
search: search:
title: title:
other: Rechercher other: Rechercher
placeholder: placeholder:
other: Cherchez un article, une publication, etc. other: Cherchez un article, une publication, etc.
resultTitle: resultTitle:
other: "#PAGES_COUNT pages (#TIME_SECONDS secondes)" other: "#PAGES_COUNT pages (#TIME_SECONDS secondes)"
footer: footer:
builtWith: builtWith:
other: Généré avec {{ .Generator }} other: Généré avec {{ .Generator }}
designedBy: designedBy:
other: Thème {{ .Theme }} conçu par {{ .DesignedBy }} other: Thème {{ .Theme }} conçu par {{ .DesignedBy }}

View File

@ -1,24 +1,52 @@
toggleMenu: toggleMenu:
other: Tampilkan Menu other: Tampilkan Menu
darkMode:
other: Mode Gelap
list:
page:
one: "{{ .Count }} halaman"
other: "{{ .Count }} halaman"
section:
other: Bagian
subsection:
one: Subbagian
other: Subbagian
article: article:
back:
other: Kembali
tableOfContents:
other: Daftar Isi
relatedContents: relatedContents:
other: Konten terkait other: Konten terkait
lastUpdatedOn: lastUpdatedOn:
other: Terakhir diperbarui pada other: Terakhir diperbarui pada
readingTime:
one: "Waktu Membaca: {{ .Count }} menit"
other: "Waktu Membaca: {{ .Count }} menit"
notFound: notFound:
title: title:
other: Not Found other: Tidak ditemukan
subtitle: subtitle:
other: Halaman ini tidak ada. other: Halaman yang Anda akses tidak ditemukan.
widget: widget:
archives: archives:
title: title:
other: Arsip other: Arsip
more: more:
other: Lebih other: Lebih
tagCloud: tagCloud:
title: title:
other: Tag other: Tag
@ -26,13 +54,16 @@ widget:
search: search:
title: title:
other: Cari other: Cari
placeholder: placeholder:
other: Ketik sesuatu... other: Ketik sesuatu...
resultTitle: resultTitle:
other: "#PAGES_COUNT halaman (#TIME_SECONDS detik)" other: "#PAGES_COUNT halaman (#TIME_SECONDS detik)"
footer: footer:
builtWith: builtWith:
other: Dibangun dengan {{ .Generator }} other: Dibangun dengan {{ .Generator }}
designedBy: designedBy:
other: Tema {{ .Theme }} dirancang oleh {{ .DesignedBy }} other: Tema {{ .Theme }} dirancang oleh {{ .DesignedBy }}

69
i18n/it.yaml Normal file
View File

@ -0,0 +1,69 @@
toggleMenu:
other: Toggle Menu
darkMode:
other: Dark Mode
list:
page:
one: "{{ .Count }} pagina"
other: "{{ .Count }} pagine"
section:
other: Sezione
subsection:
one: Sottosezione
other: Sottosezioni
article:
back:
other: Indietro
tableOfContents:
other: Indice
relatedContents:
other: Contenuti correlati
lastUpdatedOn:
other: Aggiornato il
readingTime:
one: "{{ .Count }} min per leggere"
other: "{{ .Count }} min per leggere"
notFound:
title:
other: Non trovato
subtitle:
other: Questa pagina non esiste.
widget:
archives:
title:
other: Archivi
more:
other: Di più
tagCloud:
title:
other: Tags
search:
title:
other: Cerca
placeholder:
other: Scrivi qualcosa...
resultTitle:
other: "#PAGES_COUNT pagine (#TIME_SECONDS secondi)"
footer:
builtWith:
other: Realizzato con {{ .Generator }}
designedBy:
other: Tema {{ .Theme }} realizzato da {{ .DesignedBy }}

View File

@ -5,11 +5,21 @@ darkMode:
other: ダークモード other: ダークモード
article: article:
back:
other: 前のページ
tableOfContents:
other: 目次
relatedContents: relatedContents:
other: 関連するコンテンツ other: 関連するコンテンツ
lastUpdatedOn: lastUpdatedOn:
other: 最終更新 other: 最終更新
readingTime:
other: "読了時間: {{ .Count }}分"
notFound: notFound:
title: title:
other: 404 Not Found other: 404 Not Found
@ -20,16 +30,20 @@ widget:
archives: archives:
title: title:
other: アーカイブ other: アーカイブ
more: more:
other: さらに見る other: さらに見る
tagCloud: tagCloud:
title: title:
other: タグ other: タグ
search: search:
title: title:
other: サーチ other: 検索
placeholder: placeholder:
other: 入力... other: 入力...
resultTitle: resultTitle:
other: "#PAGES_COUNT 件 #TIME_SECONDS 秒)" other: "#PAGES_COUNT 件 #TIME_SECONDS 秒)"

View File

@ -1,15 +1,42 @@
toggleMenu: toggleMenu:
other: 메뉴 여닫기 other: 메뉴 여닫기
darkMode:
other: 다크 모드
list:
page:
one: "{{ .Count }} 페이지"
other: "{{ .Count }} 페이지"
section:
other: 섹션
subsection:
one: 서브섹션
other: 서브섹션
article: article:
back:
other: 뒤로가기
tableOfContents:
other: 목차
relatedContents: relatedContents:
other: 관련 글 other: 관련 글
lastUpdatedOn: lastUpdatedOn:
other: "마지막 수정: " other: "마지막 수정: "
readingTime:
one: "{{ .Count }} 분 정도"
other: "{{ .Count }} 분 정도"
notFound: notFound:
title: title:
other: 찾을 수 없음 other: 찾을 수 없음
subtitle: subtitle:
other: 페이지를 찾을 수 없습니다. other: 페이지를 찾을 수 없습니다.
@ -19,6 +46,7 @@ widget:
other: 보관함 other: 보관함
more: more:
other: 더보기 other: 더보기
tagCloud: tagCloud:
title: title:
other: 태그 other: 태그
@ -26,8 +54,10 @@ widget:
search: search:
title: title:
other: 검색 other: 검색
placeholder: placeholder:
other: 검색어를 입력하세요... other: 검색어를 입력하세요...
resultTitle: resultTitle:
other: "#PAGES_COUNT 페이지 (#TIME_SECONDS 초)" other: "#PAGES_COUNT 페이지 (#TIME_SECONDS 초)"

53
i18n/nl.yaml Normal file
View File

@ -0,0 +1,53 @@
toggleMenu:
other: Open Menu
darkMode:
other: Donkere modus
list:
page:
one: "{{ .Count }} pagina"
other: "{{ .Count }} pagina's"
section:
other: Sectie
subsection:
one: Subsectie
other: Subsecties
article:
relatedContents:
other: Gerelateerde inhoud
lastUpdatedOn:
other: Laatst bijgewerkt op
notFound:
title:
other: Niet gevonden
subtitle:
other: Deze pagina bestaat niet.
widget:
archives:
title:
other: Archief
more:
other: Meer
tagCloud:
title:
other: Tags
search:
title:
other: Zoeken
placeholder:
other: Typ iets
resultTitle:
other: "#PAGES_COUNT pagina's (#TIME_SECONDS seconden)"
footer:
builtWith:
other: Gemaakt met {{ .Generator }}
designedBy:
other: Theme {{ .Theme }} ontworpen door {{ .DesignedBy }}

69
i18n/pl.yaml Normal file
View File

@ -0,0 +1,69 @@
toggleMenu:
other: Przełącz Menu
darkMode:
other: Tryb ciemny
list:
page:
one: "{{ .Count }} strona"
other: "{{ .Count }} stron"
section:
other: Sekcja
subsection:
one: Podsekcja
other: Podsekcje
article:
back:
other: Wróć
tableOfContents:
other: Spis treści
relatedContents:
other: Powiązane artykuły
lastUpdatedOn:
other: Ostatnio zaktualizowany
readingTime:
one: "Przeczytasz w {{ .Count }} minutę"
other: "Przeczytasz w {{ .Count }} minut"
notFound:
title:
other: Nie znaleziono
subtitle:
other: Ta strona nie istnieje
widget:
archives:
title:
other: Archiwum
more:
other: Więcej
tagCloud:
title:
other: Tagi
search:
title:
other: Szukaj
placeholder:
other: Wpisz coś...
resultTitle:
other: "#PAGES_COUNT stron (#TIME_SECONDS sekund)"
footer:
builtWith:
other: Zbudowano z {{ .Generator }}
designedBy:
other: Motyw {{ .Theme }} zaprojektowany przez {{ .DesignedBy }}

View File

@ -17,11 +17,22 @@ list:
other: Subseções other: Subseções
article: article:
back:
other: Voltar
tableOfContents:
other: Índice
relatedContents: relatedContents:
other: Conteúdos Relacionados other: Conteúdo relacionado
lastUpdatedOn: lastUpdatedOn:
other: Última atualização em other: Última atualização em
readingTime:
one: "{{ .Count }} minuto de leitura"
other: "{{ .Count }} minutos de leitura"
notFound: notFound:
title: title:
other: Não Encontrado other: Não Encontrado

View File

@ -21,10 +21,16 @@ list:
other: Подразделы other: Подразделы
article: article:
back:
other: Назад
relatedContents: relatedContents:
other: Также рекомендуем other: Также рекомендуем
lastUpdatedOn: lastUpdatedOn:
other: Обновлено other: Обновлено
tableOfContents:
other: Содержание
readingTime:
other: "Время чтения: {{ .Count }} мин."
notFound: notFound:
title: title:

70
i18n/th.yaml Normal file
View File

@ -0,0 +1,70 @@
toggleMenu:
other: สลับเมนู
darkMode:
other: ธีมมืด
list:
page:
one: "{{ .Count }} หน้า"
other: "{{ .Count }} หน้า"
section:
other: หมวดหมู่
subsection:
one: หมวดหมู่ย่อย
other: หมวดหมู่ย่อยอื่นๆ
article:
back:
other: กลับไป
tableOfContents:
other: สารบัญ
relatedContents:
other: เนื้อหาคล้ายคลึงกัน
lastUpdatedOn:
other: อัปเดตล่าสุดเมื่อ
readingTime:
one: "น่าจะใช้เวลา {{ .Count }} นาทีในการอ่าน"
other: "น่าจะใช้เวลา {{ .Count }} นาทีในการอ่าน"
notFound:
title:
other: ไม่พบหัวข้อ
subtitle:
other: ไม่พบหน้านี้ในระบบ
widget:
archives:
title:
other: เนื้อหาที่เก็บถาวรแล้ว
more:
other: อื่นๆ นอกจากนี้
tagCloud:
title:
other: แท็ก
search:
title:
other: ค้นหา
placeholder:
other: พิมพ์เพื่อค้นหา ...
resultTitle:
other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
footer:
builtWith:
other: ถูกสร้างด้วย {{ .Generator }}
designedBy:
other: ธีม {{ .Theme }} ออกแบบโดย {{ .DesignedBy }}

71
i18n/uk.yaml Normal file
View File

@ -0,0 +1,71 @@
toggleMenu:
other: Показати меню
darkMode:
other: Темна тема
list:
page:
one: "{{ .Count }} сторінка"
few: "{{ .Count }} сторінки"
other: "{{ .Count }} сторінок"
section:
other: Секція
subsection:
one: Підсекція
other: Підсекції
article:
back:
other: Назад
tableOfContents:
other: Зміст
relatedContents:
other: Схожі матеріали
lastUpdatedOn:
other: Востаннє оновлено
readingTime:
one: "Час читання: {{ .Count }} хв"
other: "Час читання: {{ .Count }} хв"
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: Тема {{ .Theme }}, дизайн {{ .DesignedBy }}

View File

@ -5,11 +5,21 @@ darkMode:
other: 暗色模式 other: 暗色模式
article: article:
back:
other: 返回
tableOfContents:
other: 目录
relatedContents: relatedContents:
other: 相关文章 other: 相关文章
lastUpdatedOn: lastUpdatedOn:
other: 最后更新于 other: 最后更新于
readingTime:
other: "阅读时长: {{ .Count }} 分钟"
notFound: notFound:
title: title:
other: 404 错误 other: 404 错误
@ -20,8 +30,10 @@ widget:
archives: archives:
title: title:
other: 归档 other: 归档
more: more:
other: 更多 other: 更多
tagCloud: tagCloud:
title: title:
other: 标签云 other: 标签云
@ -29,7 +41,9 @@ widget:
search: search:
title: title:
other: 搜索 other: 搜索
placeholder: placeholder:
other: 输入关键词... other: 输入关键词...
resultTitle: resultTitle:
other: "#PAGES_COUNT 个结果 (用时 #TIME_SECONDS 秒)" other: "#PAGES_COUNT 个结果 (用时 #TIME_SECONDS 秒)"

49
i18n/zh-TW.yaml Normal file
View File

@ -0,0 +1,49 @@
toggleMenu:
other: 切換選單
darkMode:
other: 夜晚模式
article:
back:
other: 返回
tableOfContents:
other: 目錄
relatedContents:
other: 相關文章
lastUpdatedOn:
other: 最後更新
readingTime:
other: "閱讀時間: {{ .Count }} 分鐘"
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 秒)"

View File

@ -1,30 +1,41 @@
{{- $image := .Page.Resources.GetMatch (printf "%s" (.Destination | safeURL)) -}} {{- $image := .Page.Resources.GetMatch (printf "%s" (.Destination | safeURL)) -}}
{{- if and $image (ne (path.Ext .Destination) ".svg") -}} {{- $Permalink := .Destination | relURL | safeURL -}}
{{- $alt := .PlainText | safeHTML -}} {{- $alt := .PlainText | safeHTML -}}
<figure style="flex-grow: {{ div (mul $image.Width 100) $image.Height }}; flex-basis: {{ div (mul $image.Width 240) $image.Height }}px"> {{- $Width := 0 -}}
<a href="{{ $image.RelPermalink }}" data-size="{{ $image.Width }}x{{ $image.Height }}"> {{- $Height := 0 -}}
{{- $Permalink := $image.RelPermalink -}} {{- $Srcset := "" -}}
{{- $Width := $image.Width -}}
{{- $Height := $image.Height -}}
{{- $Srcset := "" -}}
{{- if (default true .Page.Site.Params.imageProcessing.content.enabled) -}} {{/* SVG and external images won't work with gallery layout, because their width and height attributes are unknown */}}
{{- $small := $image.Resize "480x" -}} {{- $galleryImage := false -}}
{{- $big := $image.Resize "1024x" -}}
{{- $Srcset = printf "%s 480w, %s 1024w" $small.RelPermalink $big.RelPermalink -}} {{- if $image -}}
{{- end -}} {{- $notSVG := ne (path.Ext .Destination) ".svg" -}}
{{- $Permalink = $image.RelPermalink -}}
<img src="{{ $Permalink }}"
{{ with $Srcset }}srcset="{{ . }}"{{ end }} {{- if $notSVG -}}
width="{{ $Width }}" {{- $Width = $image.Width -}}
height="{{ $Height }}" {{- $Height = $image.Height -}}
loading="lazy" {{- $galleryImage = true -}}
{{ with $alt }}alt="{{ . }}"{{ end }}>
</a> {{- if (default true .Page.Site.Params.imageProcessing.content.enabled) -}}
{{ with $alt }} {{- $small := $image.Resize `480x` -}}
<figcaption>{{ . | markdownify }}</figcaption> {{- $big := $image.Resize `1024x` -}}
{{ end }} {{- $Srcset = printf `%s 480w, %s 1024w` $small.RelPermalink $big.RelPermalink -}}
</figure> {{- end -}}
{{- else -}} {{- end -}}
<img src="{{ .Destination | safeURL }}" alt="{{ .Text }}" {{ with .Title }} title="{{ . }}"{{ end }} /> {{- end -}}
{{- end -}}
<img src="{{ $Permalink }}"
{{ with $Width }}width="{{ . }}"{{ end }}
{{ with $Height }}height="{{ . }}"{{ end }}
{{ with $Srcset }}srcset="{{ . }}"{{ end }}
loading="lazy"
{{ with $alt }}
alt="{{ . }}"
{{ end }}
{{ if $galleryImage }}
class="gallery-image"
data-flex-grow="{{ div (mul $image.Width 100) $image.Height }}"
data-flex-basis="{{ div (mul $image.Width 240) $image.Height }}px"
{{ end }}
>

View File

@ -6,8 +6,10 @@
</head> </head>
<body class="{{ block `body-class` . }}{{ end }}"> <body class="{{ block `body-class` . }}{{ end }}">
{{- partial "head/colorScheme" . -}} {{- partial "head/colorScheme" . -}}
<div class="container main-container flex on-phone--column {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }} {{ block `container-class` . }}{{end}}"> <div class="container main-container flex {{ block `container-class` . }}on-phone--column {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }}{{ end }}">
{{ partial "sidebar/left.html" . }} {{- block "left-sidebar" . -}}
{{ partial "sidebar/left.html" . }}
{{- end -}}
<main class="main full-width"> <main class="main full-width">
{{- block "main" . }}{{- end }} {{- block "main" . }}{{- end }}
</main> </main>

View File

@ -1,12 +1,61 @@
{{ define "body-class" }}article-page keep-sidebar{{ end }} {{ define "body-class" }}
{{ $TOCEnabled := default (default false .Site.Params.article.toc) .Params.toc }}
{{- .Scratch.Set "hasTOC" (and (ge (len .TableOfContents) 100) $TOCEnabled) -}}
article-page {{ if (.Scratch.Get "hasTOC") }}has-toc{{ end }}
{{ end }}
{{ define "container-class" }}
{{ if (.Scratch.Get "hasTOC") }}
extended
{{ else }}
on-phone--column {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }}
{{ end }}
{{ end }}
{{ define "main" }} {{ define "main" }}
{{ partial "article/article.html" . }} {{ partial "article/article.html" . }}
{{ if or (not (isset .Params "comments")) (eq .Params.comments "true")}} {{ if .Params.links }}
{{ partial "article/components/links" . }}
{{ end }}
{{ partial "article/components/related-contents" . }}
{{ if not (eq .Params.comments false) }}
{{ partial "comments/include" . }} {{ partial "comments/include" . }}
{{ end }} {{ end }}
{{ partialCached "footer/footer" . }} {{ partialCached "footer/footer" . }}
{{ partialCached "article/components/photoswipe" . }} {{ partialCached "article/components/photoswipe" . }}
{{ end }} {{ end }}
{{ define "left-sidebar" }}
{{ if (.Scratch.Get "hasTOC") }}
<div id="article-toolbar">
<a href="{{ .Site.BaseURL | relLangURL }}" class="back-home">
{{ (resources.Get "icons/back.svg").Content | safeHTML }}
<span>{{ T "article.back" }}</span>
</a>
</div>
{{ else }}
{{ partial "sidebar/left.html" . }}
{{ end }}
{{ end }}
{{ define "right-sidebar" }}
{{ if (.Scratch.Get "hasTOC") }}
<aside class="sidebar right-sidebar sticky">
<section class="widget archives">
<div class="widget-icon">
{{ partial "helper/icon" "hash" }}
</div>
<h2 class="widget-title section-title">{{ T "article.tableOfContents" }}</h2>
<div class="widget--toc">
{{ .TableOfContents }}
</div>
</section>
</aside>
{{ end }}
{{ end }}

View File

@ -29,6 +29,7 @@
<img src="{{ $Permalink }}" <img src="{{ $Permalink }}"
width="{{ $Width }}" width="{{ $Width }}"
height="{{ $Height }}" height="{{ $Height }}"
alt="{{ .Title }}"
loading="lazy"> loading="lazy">
{{ else }} {{ else }}
<img src="{{ $image.permalink }}" loading="lazy" alt="Featured image of post {{ .Title }}" /> <img src="{{ $image.permalink }}" loading="lazy" alt="Featured image of post {{ .Title }}" />

View File

@ -20,8 +20,9 @@
<img src="{{ $Permalink }}" <img src="{{ $Permalink }}"
width="{{ $Width }}" width="{{ $Width }}"
height="{{ $Height }}" height="{{ $Height }}"
loading="lazy" loading="lazy"
data-key="{{ .context.Slug }}" alt="Featured image of post {{ .context.Title }}"
{{ with .context.Slug }}data-key="{{ . }}" {{ end }}
data-hash="{{ $imageRaw.Data.Integrity }}"> data-hash="{{ $imageRaw.Data.Integrity }}">
{{ else }} {{ else }}
<img src="{{ $image.permalink }}" loading="lazy" data-key="{{ .context.Slug }}" data-hash="{{ $image.permalink }}"/> <img src="{{ $image.permalink }}" loading="lazy" data-key="{{ .context.Slug }}" data-hash="{{ $image.permalink }}"/>

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

@ -21,12 +21,25 @@
</h3> </h3>
{{ end }} {{ end }}
{{- if not .Date.IsZero -}} {{ if or (not .Date.IsZero) (.Site.Params.article.readingTime) }}
<footer class="article-time"> <footer class="article-time">
{{ partial "helper/icon" "clock" }} {{ if not .Date.IsZero }}
<time class="article-time--published"> <div>
{{- .Date.Format (or .Site.Params.dateFormat.published "Jan 02, 2006") -}} {{ partial "helper/icon" "date" }}
</time> <time class="article-time--published">
{{- .Date.Format (or .Site.Params.dateFormat.published "Jan 02, 2006") -}}
</time>
</div>
{{ end }}
{{ if .Site.Params.article.readingTime }}
<div>
{{ partial "helper/icon" "clock" }}
<time class="article-time--reading">
{{ T "article.readingTime" .ReadingTime }}
</time>
</div>
{{ end }}
</footer> </footer>
{{- end -}} {{ end }}
</div> </div>

View File

@ -4,16 +4,16 @@
{{ if and (.Site.Params.article.license.enabled) (not (eq .Params.license false)) }} {{ if and (.Site.Params.article.license.enabled) (not (eq .Params.license false)) }}
<section class="article-copyright"> <section class="article-copyright">
{{ partial "helper/icon" "copyright" }} {{ partial "helper/icon" "copyright" }}
<span>{{ default .Site.Params.article.license.default .Params.license }}</span> <span>{{ default .Site.Params.article.license.default .Params.license | markdownify }}</span>
</section> </section>
{{ end }} {{ end }}
{{- if ne .Lastmod .Date -}} {{- if ne .Lastmod .Date -}}
<section class="article-time"> <section class="article-lastmod">
{{ partial "helper/icon" "clock" }} {{ partial "helper/icon" "clock" }}
<span class="article-time--modified"> <span>
{{ T "article.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> </span>
</section> </section>
{{- end -}} {{- end -}}
</footer> </footer>

View File

@ -0,0 +1,26 @@
<div class="article-list--compact links">
{{ range $i, $link := .Params.links }}
<article>
<a href="{{ $link.website }}" target="_blank" rel="noopener">
<div class="article-details">
<h2 class="article-title">
{{- $link.title -}}
</h2>
<footer class="article-time">
{{ with $link.description }}
{{ . }}
{{ else }}
{{ $link.website }}
{{ end }}
</footer>
</div>
{{ with $link.image }}
<div class="article-image">
<img src="{{ . }}" loading="lazy">
</div>
{{ end }}
</a>
</article>
{{ end }}
</div>

View File

@ -0,0 +1,29 @@
{{- with .Site.Params.comments.cactus -}}
{{- partial "helper/external" (dict "Context" $ "Namespace" "Cactus") -}}
<style>
.cactus-editor-textarea {
color: var(--body-text-color);
}
.cactus-comment-header {
color: var(--card-text-color-main);
}
.cactus-message-text > p {
color: var(--body-text-color);
}
</style>
<div id="comment-section"></div>
<script>
initComments({
node: document.getElementById("comment-section"),
defaultHomeserverUrl: "{{ .defaultHomeserverUrl | safeJS }}",
serverName: "{{ .serverName }}",
siteName: "{{ .siteName }}",
commentSectionId: "{{ $.File.UniqueID }}"
})
</script>
{{- end -}}

View File

@ -0,0 +1,21 @@
{{- $host := default "https://cusdis.com" .Site.Params.comments.cusdis.host -}}
<div id="cusdis_thread"
data-host="{{ $host }}"
data-app-id="{{ .Site.Params.comments.cusdis.id }}"
data-page-id="{{ .File.UniqueID }}"
data-page-url="{{ .Permalink }}"
data-page-title="{{ .Title }}"></div>
<script async defer src="{{ $host }}/js/cusdis.es.js"></script>
<script>
function setCusdisTheme(theme) {
let cusdis = document.querySelector('#cusdis_thread iframe');
if (cusdis) {
window.CUSDIS.setTheme(theme)
}
}
window.addEventListener('onColorSchemeChange', (e) => {
setCusdisTheme(e.detail)
})
</script>

View File

@ -0,0 +1,61 @@
{{- $pc := .Site.Config.Privacy.Disqus -}}
{{- $disqusjs := .Site.Params.Comments.disqusjs -}}
{{- if and (not $pc.Disable) (and $disqusjs.Shortname $disqusjs.ApiKey) -}}
{{- $style := resources.Get "scss/partials/comments/disqusjs.scss" | resources.ToCSS | minify -}}
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
<div class="disqus-container">
<div id="disqus_thread"></div>
<script type="application/javascript">
let disqusjs;
function loadDisqusJS() {
disqusjs = new DisqusJS({
shortname: {{ $disqusjs.Shortname }},
siteName: {{ .Site.Title }},
apikey: {{ $disqusjs.ApiKey }},
{{ with $disqusjs.ApiUrl }}api: {{ . }},{{ end }}
{{ with $disqusjs.Admin }}admin: {{ . }},{{ end }}
{{ with $disqusjs.AdminLabel }}adminLabel: {{ . }}{{ end }}
});
}
function lazyLoadDisqusJS() {
if (["localhost", "127.0.0.1"].indexOf(window.location.hostname) != -1) {
document.getElementById('disqus_thread').innerHTML = 'Disqus comments not available by default when the website is previewed locally.';
return;
}
let d = document.createElement('script');
d.src = 'https://cdn.jsdelivr.net/npm/disqusjs@1.3/dist/disqus.js';
d.async = false;
document.body.appendChild(d);
d.onload = () => {
loadDisqusJS();
window.addEventListener('onColorSchemeChange', (e) => {
if (disqusjs) {
loadDisqusJS();
}
})
}
}
let runningOnBrowser = typeof window !== "undefined";
let isBot = runningOnBrowser && !("onscroll" in window) || typeof navigator !== "undefined" && /(gle|ing|ro|msn)bot|crawl|spider|yand|duckgo/i.test(navigator.userAgent);
let supportsIntersectionObserver = runningOnBrowser && "IntersectionObserver" in window;
if (!isBot && supportsIntersectionObserver) {
let disqus_observer = new IntersectionObserver(function(entries) {
if (entries[0].isIntersecting) {
lazyLoadDisqusJS();
disqus_observer.disconnect();
}
});
disqus_observer.observe(document.getElementById('disqus_thread'));
} else {
lazyLoadDisqusJS();
}
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div>
{{- end -}}

View File

@ -0,0 +1,49 @@
{{- with .Site.Params.comments.giscus -}}
<script
src="https://giscus.app/client.js"
data-repo="{{- .repo -}}"
data-repo-id="{{- .repoID -}}"
data-category="{{- .category -}}"
data-category-id="{{- .categoryID -}}"
data-mapping="{{- default `title` .mapping -}}"
data-reactions-enabled="{{- default 1 .reactionsEnabled -}}"
data-emit-metadata="{{- default 0 .emitMetadata -}}"
data-theme="{{- default `light` .lightTheme -}}"
crossorigin="anonymous"
async
></script>
<script>
function setGiscusTheme(theme) {
let giscus = document.querySelector('iframe.giscus-frame');
if (giscus) {
giscus.contentWindow.postMessage(
{
giscus: {
setConfig: {
theme: theme
}
}
},
"https://giscus.app"
);
};
};
(function(){
addEventListener('message', (e) => {
if (event.origin !== 'https://giscus.app') return;
handler()
});
window.addEventListener('onColorSchemeChange', handler);
function handler() {
if (document.documentElement.dataset.scheme === "light") {
setGiscusTheme('{{- default "light" .lightTheme -}}');
} else {
setGiscusTheme('{{- default "dark_dimmed" .darkTheme -}}');
};
};
}());
</script>
{{- end -}}

View File

@ -0,0 +1,30 @@
{{- with .Site.Params.comments.gitalk -}}
<div id="gitalk-container"></div>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.css"
/>
<script src="https://cdn.jsdelivr.net/npm/gitalk@1.7.2/dist/gitalk.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/blueimp-md5@2.18.0/js/md5.min.js"></script>
<script>
const gitalk = new Gitalk({
clientID: "{{- .clientID -}}",
clientSecret: "{{- .clientSecret -}}",
repo: "{{- .repo -}}",
owner: "{{- .owner -}}",
admin: ["{{- .admin -}}"],
distractionFreeMode: false, // Facebook-like distraction free mode
id: md5(location.pathname), // Max Location.pathname Legth:75 https://github.com/gitalk/gitalk/issues/102
});
(function () {
if (
["localhost", "127.0.0.1"].indexOf(window.location.hostname) != -1
) {
document.getElementById("gitalk-container").innerHTML =
"Gitalk comments not available by default when the website is previewed locally.";
return;
}
gitalk.render("gitalk-container");
})();
</script>
{{ end }}

View File

@ -7,7 +7,7 @@
components: ['embed'], components: ['embed'],
url: "{{ $.Permalink }}", url: "{{ $.Permalink }}",
max_shown_comments: {{ default 15 .max_shown_comments }}, max_shown_comments: {{ default 15 .max_shown_comments }},
theme: document.body.dataset.scheme, theme: document.documentElement.dataset.scheme,
page_title: '{{ $.Title }}', page_title: '{{ $.Title }}',
locale: '{{ default "en" .locale }}', locale: '{{ default "en" .locale }}',
show_email_subscription: {{ default true .show_email_subscription }} show_email_subscription: {{ default true .show_email_subscription }}
@ -26,4 +26,4 @@
window.REMARK42.changeTheme(e.detail); window.REMARK42.changeTheme(e.detail);
}) })
</script> </script>
{{- end -}} {{- end -}}

View File

@ -0,0 +1,49 @@
<script src="//cdn.jsdelivr.net/npm/twikoo@1.4.15/dist/twikoo.all.min.js"></script>
<div id="tcomment"></div>
<style>
.twikoo {
background-color: var(--card-background);
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l1);
padding: var(--card-padding);
}
:root[data-scheme="dark"] {
--twikoo-body-text-color-main: rgba(255, 255, 255, 0.9);
--twikoo-body-text-color: rgba(255, 255, 255, 0.7);
}
.twikoo .el-input-group__prepend,
.twikoo .tk-action-icon,
.twikoo .tk-time,
.twikoo .tk-comments-count {
color: var(--twikoo-body-text-color);
}
.twikoo .el-input__inner,
.twikoo .el-textarea__inner,
.twikoo .tk-preview-container,
.twikoo .tk-content,
.twikoo .tk-nick,
.twikoo .tk-send {
color: var(--twikoo-body-text-color-main);
}
.twikoo .el-button{
color: var(--twikoo-body-text-color)!important;
}
</style>
{{- with .Site.Params.comments.twikoo -}}
<script>
twikoo.init({
envId: '{{- .envId -}}',
el: '#tcomment',
{{- with .region -}}
region: '{{- . -}}',
{{- end -}}
{{- with .path -}}
path: '{{- . -}}',
{{- end -}}
{{- with .lang -}}
lang: '{{- . -}}',
{{- end -}}
})
</script>
{{- end -}}

View File

@ -31,10 +31,10 @@
addEventListener('message', event => { addEventListener('message', event => {
if (event.origin !== 'https://utteranc.es') return; if (event.origin !== 'https://utteranc.es') return;
setUtterancesTheme(document.body.dataset.scheme) setUtterancesTheme(document.documentElement.dataset.scheme)
}); });
window.addEventListener('onColorSchemeChange', (e) => { window.addEventListener('onColorSchemeChange', (e) => {
setUtterancesTheme(e.detail) setUtterancesTheme(e.detail)
}) })
</script> </script>

View File

@ -0,0 +1,27 @@
{{- with .Site.Params.comments.vssue -}}
<link rel="stylesheet" href="https://unpkg.com/vssue/dist/vssue.min.css" />
<div id="vssue"></div>
<script src="https://unpkg.com/vue/dist/vue.runtime.min.js"></script>
<script src="https://unpkg.com/vssue/dist/vssue.{{ .platform }}.min.js"></script>
<script>
new Vue({
el: "#vssue",
render: (h) =>
h("Vssue", {
props: {
title: "{{ $.Title }}",
options: {
autoCreateIssue: {{ default false .autoCreateIssue }},
owner: "{{ .owner }}",
repo: "{{ .repo }}",
clientId: "{{ .clientId }}",
clientSecret: "{{ .clientSecret }}",
},
},
}),
});
</script>
{{- end -}}

View File

@ -0,0 +1,32 @@
<script src='//cdn.jsdelivr.net/npm/@waline/client/dist/Waline.min.js'></script>
<div id="waline" class="waline-container"></div>
<style>
.waline-container {
background-color: var(--card-background);
border-radius: var(--card-border-radius);
box-shadow: var(--shadow-l1);
padding: var(--card-padding);
}
.waline-container .vcount {
color: var(--card-text-color-main);
}
</style>
{{- with .Site.Params.comments.waline -}}
{{- $config := dict "el" "#waline" "dark" `html[data-scheme="dark"]` -}}
{{- $replaceKeys := dict "serverurl" "serverURL" "requiredmeta" "requiredMeta" "wordlimit" "wordLimit" "pagesize" "pageSize" "avatarcdn" "avatarCDN" "avatarforce" "avatarForce" -}}
{{- range $key, $val := . -}}
{{- if $val -}}
{{- $replaceKey := index $replaceKeys $key -}}
{{- $k := default $key $replaceKey -}}
{{- $config = merge $config (dict $k $val) -}}
{{- end -}}
{{- end -}}
<script>
/// Waline client configuration see: https://waline.js.org/en/reference/client.html
new Waline({{ $config | jsonify | safeJS }});
</script>
{{- end -}}

View File

@ -3,4 +3,10 @@
{{- $opts := dict "minify" hugo.IsProduction -}} {{- $opts := dict "minify" hugo.IsProduction -}}
{{- $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 := "2.2.0" -}} {{- $ThemeVersion := "3.8.0" -}}
<footer class="site-footer"> <footer class="site-footer">
<section class="copyright"> <section class="copyright">
&copy; &copy;
@ -20,4 +20,4 @@
{{ T "footer.builtWith" (dict "Generator" $Generator) | safeHTML }} <br /> {{ T "footer.builtWith" (dict "Generator" $Generator) | safeHTML }} <br />
{{ T "footer.designedBy" (dict "Theme" $Theme "DesignedBy" $DesignedBy) | safeHTML }} {{ T "footer.designedBy" (dict "Theme" $Theme "DesignedBy" $DesignedBy) | safeHTML }}
</section> </section>
</footer> </footer>

View File

@ -31,9 +31,9 @@
* 1. If dark mode is set already (in local storage) * 1. If dark mode is set already (in local storage)
* 2. Auto mode & prefere color scheme is dark * 2. Auto mode & prefere color scheme is dark
*/ */
document.body.dataset.scheme = 'dark'; document.documentElement.dataset.scheme = 'dark';
} else { } else {
document.body.dataset.scheme = 'light'; document.documentElement.dataset.scheme = 'light';
} }
})(); })();
</script> </script>

View File

@ -21,5 +21,5 @@
<link rel="shortcut icon" href="{{ . }}" /> <link rel="shortcut icon" href="{{ . }}" />
{{ end }} {{ end }}
{{- template "_internal/google_analytics_async.html" . -}} {{- template "_internal/google_analytics.html" . -}}
{{- partial "head/custom.html" . -}} {{- partial "head/custom.html" . -}}

View File

@ -1,5 +1,6 @@
{{- with .Site.Params.opengraph.twitter.site -}} {{- with .Site.Params.opengraph.twitter.site -}}
<meta name="twitter:site" content="{{ . }}"> <meta name="twitter:site" content="@{{ . }}">
<meta name="twitter:creator" content="@{{ . }}">
{{- end -}} {{- end -}}
{{- $title := partialCached "data/title" . .RelPermalink -}} {{- $title := partialCached "data/title" . .RelPermalink -}}

View File

@ -1,3 +1,3 @@
{{ $sass := resources.Get "scss/style.scss" }} {{ $sass := resources.Get "scss/style.scss" }}
{{ $style := $sass | resources.ToCSS | minify }} {{ $style := $sass | resources.ToCSS | minify | resources.Fingerprint "sha256" }}
<link rel="stylesheet" href="{{ $style.RelPermalink }}"> <link rel="stylesheet" href="{{ $style.RelPermalink }}">

View File

@ -8,7 +8,7 @@
integrity="{{ . }}" integrity="{{ . }}"
{{- end -}} {{- end -}}
crossorigin="anonymous" crossorigin="anonymous"
defer="{{ default false .defer }}" {{ if .defer }}defer{{ end }}
> >
</script> </script>
{{- else if eq .type "style" -}} {{- else if eq .type "style" -}}

View File

@ -8,7 +8,7 @@
{{ $url := urls.Parse $imageValue }} {{ $url := urls.Parse $imageValue }}
{{ if or (eq $url.Scheme "http") (eq $url.Scheme "https") }} {{ if or (eq $url.Scheme "http") (eq $url.Scheme "https") }}
<!-- Is a external image --> <!-- Is an external image -->
{{ $result = merge $result (dict "permalink" $imageValue) }} {{ $result = merge $result (dict "permalink" $imageValue) }}
{{ else }} {{ else }}
{{ $pageResourceImage := .Context.Resources.GetMatch (printf "%s" ($imageValue | safeURL)) }} {{ $pageResourceImage := .Context.Resources.GetMatch (printf "%s" ($imageValue | safeURL)) }}
@ -23,7 +23,7 @@
{{ end }} {{ end }}
{{ else }} {{ else }}
<!-- Can not find the image under page bundle. Could be a relative linked image --> <!-- Can not find the image under page bundle. Could be a relative linked image -->
{{ $result = merge $result (dict "permalink" $imageValue) }} {{ $result = merge $result (dict "permalink" (relURL $imageValue)) }}
{{ end }} {{ end }}
{{ end }} {{ end }}
@ -51,11 +51,11 @@
{{ else }} {{ else }}
<!-- External image --> <!-- External image -->
{{ $result = merge $result (dict "permalink" $defaultImageSetting.src) }} {{ $result = merge $result (dict "permalink" (relURL $defaultImageSetting.src)) }}
{{ end }} {{ end }}
{{ end }} {{ end }}
{{ end }} {{ end }}
{{ return $result }} {{ return $result }}

View File

@ -7,7 +7,9 @@
<header class="site-info"> <header class="site-info">
{{ with .Site.Params.sidebar.avatar }} {{ with .Site.Params.sidebar.avatar }}
{{ 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 }}
@ -21,14 +23,35 @@
{{ 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 }}
</figure> </figure>
{{ end }}
{{ end }} {{ end }}
<h1 class="site-name"><a href="{{ .Site.BaseURL }}">{{ .Site.Title }}</a></h1>
<h1 class="site-name"><a href="{{ .Site.BaseURL | relLangURL }}">{{ .Site.Title }}</a></h1>
<h2 class="site-description">{{ .Site.Params.sidebar.subtitle }}</h2> <h2 class="site-description">{{ .Site.Params.sidebar.subtitle }}</h2>
{{- with .Site.Menus.social -}}
<ol class="social-menu">
{{ range . }}
<li>
<a
href='{{ .URL }}'
{{ if eq (default true .Params.newTab) true }}target="_blank"{{ end }}
{{ with .Name }}title="{{ . }}"{{ end }}
>
{{ $icon := default "link" .Params.Icon }}
{{ with $icon }}
{{ partial "helper/icon" . }}
{{ end }}
</a>
</li>
{{ end }}
</ol>
{{- end -}}
</header> </header>
<ol class="menu" id="main-menu"> <ol class="menu" id="main-menu">
@ -37,9 +60,13 @@
{{ $active := or (eq $currentPage.Title .Name) (or ($currentPage.HasMenuCurrent "main" .) ($currentPage.IsMenuCurrent "main" .)) }} {{ $active := or (eq $currentPage.Title .Name) (or ($currentPage.HasMenuCurrent "main" .) ($currentPage.IsMenuCurrent "main" .)) }}
<li {{ if $active }} class='current' {{ end }}> <li {{ if $active }} class='current' {{ end }}>
<a href='{{ .URL | relURL }}'> <a href='{{ .URL | relLangURL }}' {{ if eq .Params.newTab true }}target="_blank"{{ end }}>
{{ $icon := default .Pre .Params.Icon }}
{{ if .Pre }} {{ if .Pre }}
{{ partial "helper/icon" .Pre }} {{ warnf "Menu item [%s] is using [pre] field to set icon, please use [params.icon] instead.\nMore information: https://docs.stack.jimmycai.com/configuration/custom-menu.html" .URL }}
{{ end }}
{{ with $icon }}
{{ partial "helper/icon" . }}
{{ end }} {{ end }}
<span>{{- .Name -}}</span> <span>{{- .Name -}}</span>
</a> </a>

View File

@ -1,21 +0,0 @@
{{ define "container-class" }}article-page with-toolbar{{ end }}
{{ define "main" }}
<div id="article-toolbar">
<a href="{{ .Site.BaseURL }}" class="back-home">
{{ (resources.Get "icons/back.svg").Content | safeHTML }}
<span>Back</span>
</a>
</div>
{{ partial "article/article.html" . }}
{{ partial "article/components/related-contents" . }}
{{ if or (not (isset .Params "comments")) (eq .Params.comments "true")}}
{{ partial "comments/include" . }}
{{ end }}
{{ partialCached "footer/footer" . }}
{{- partialCached "article/components/photoswipe.html" . -}}
{{ end }}

View File

@ -12,13 +12,12 @@
<p>当前视频av或BV号{{ $vid }}视频分P{{ $videopage }}</p> <p>当前视频av或BV号{{ $vid }}视频分P{{ $videopage }}</p>
{{ end }} {{ end }}
<div style="position: relative; width: 100%; height: 0; padding-bottom: 56.25%;"> <div class="video-wrapper">
<iframe src="//player.bilibili.com/player.html?{{ $basicQuery | safeURL }}&{{ $videoQuery | safeURL }}" <iframe src="https://player.bilibili.com/player.html?{{ $basicQuery | safeURL }}&{{ $videoQuery | safeURL }}"
scrolling="no" scrolling="no"
frameborder="no" frameborder="no"
framespacing="0" framespacing="0"
allowfullscreen="true" allowfullscreen="true"
style="position: absolute; width: 100%; height: 100%; left: 0; top: 0;"
> >
</iframe> </iframe>
</div> </div>

View File

@ -1,10 +1,10 @@
{{ $vid := .Get 0 }} {{ $vid := .Get 0 }}
<div style="position: relative; width: 100%; height: 0; padding-bottom: 56.25%;"> <div class="video-wrapper">
<iframe src="http://v.qq.com/txp/iframe/player.html?vid={{ $vid }}&auto=0" <iframe src="https://v.qq.com/txp/iframe/player.html?vid={{ $vid }}&auto=0"
scrolling="no" scrolling="no"
frameborder="no" frameborder="no"
framespacing="0" framespacing="0"
allowfullscreen="true" allowfullscreen="true"
style="position: absolute; width: 100%; height: 100%;"> >
</iframe> </iframe>
</div> </div>

View File

@ -0,0 +1,14 @@
{{- $src := .Get "src" | default (.Get 0) -}}
<div class="video-wrapper">
<video
controls
src="{{- $src -}}"
{{ with .Get "poster" }}poster="{{- . -}}"{{ end }}
{{ with .Get "autoplay" }}autoplay{{ end }}
>
<p>
Your browser doesn't support HTML5 video. Here is a
<a href="{{- $src -}}">link to the video</a> instead.
</p>
</video>
</div>

View File

@ -2,8 +2,12 @@
{{- if not $pc.Disable -}} {{- if not $pc.Disable -}}
{{- $ytHost := cond $pc.PrivacyEnhanced "www.youtube-nocookie.com" "www.youtube.com" -}} {{- $ytHost := cond $pc.PrivacyEnhanced "www.youtube-nocookie.com" "www.youtube.com" -}}
{{- $id := .Get "id" | default (.Get 0) -}} {{- $id := .Get "id" | default (.Get 0) -}}
{{- $class := .Get "class" | default (.Get 1) }} <div class="video-wrapper">
<div {{ with $class }}class="{{ . }}"{{ else }}style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"{{ end }}> <iframe loading="lazy"
<iframe loading="lazy" src="https://{{ $ytHost }}/embed/{{ $id }}{{ with .Get "autoplay" }}{{ if eq . "true" }}?autoplay=1{{ end }}{{ end }}" {{ if not $class }}style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" {{ end }}allowfullscreen title="YouTube Video"></iframe> src="https://{{ $ytHost }}/embed/{{ $id }}{{ with .Get "autoplay" }}{{ if eq . "true" }}?autoplay=1{{ end }}{{ end }}"
allowfullscreen
title="YouTube Video"
>
</iframe>
</div> </div>
{{ end -}} {{ end -}}

View File

@ -2,7 +2,7 @@
publish = "exampleSite/public" publish = "exampleSite/public"
[build.environment] [build.environment]
HUGO_VERSION = "0.79.0" HUGO_VERSION = "0.87.0"
HUGO_THEME = "repo" HUGO_THEME = "repo"
[context.production] [context.production]
@ -17,4 +17,10 @@
command = "cd exampleSite && hugo --gc --themesDir ../.. -b ${DEPLOY_PRIME_URL}" command = "cd exampleSite && hugo --gc --themesDir ../.. -b ${DEPLOY_PRIME_URL}"
[[plugins]] [[plugins]]
package = "/exampleSite/plugins/netlify-plugin-hugo-cache-resources" package = "netlify-plugin-hugo-cache-resources"
[plugins.inputs]
# If it should show more verbose logs (optional, default = true)
debug = true
# Relative path to source directory in case you use Hugo's "--s" option
srcdir = "exampleSite"

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"Target":"scss/style.min.css","MediaType":"text/css","Data":{}}

View File

@ -5,23 +5,23 @@ name = "Stack"
license = "GPL-3.0-only" license = "GPL-3.0-only"
licenselink = "https://github.com/CaiJimmy/hugo-theme-stack/blob/master/LICENSE" licenselink = "https://github.com/CaiJimmy/hugo-theme-stack/blob/master/LICENSE"
description = "Card-style Hugo theme designed for bloggers" description = "Card-style Hugo theme designed for bloggers"
homepage = "https://theme-stack.jimmycai.com" homepage = "https://github.com/CaiJimmy/hugo-theme-stack"
tags = [ demosite = "https://demo.stack.jimmycai.com"
"blog",
"responsive", tags = ["blog", "responsive", "clean", "light", "dark", "personal"]
"clean",
"light",
"dark",
"personal"
]
features = [ features = [
"disqus", "disqus",
"photoswipe", "photoswipe",
"opengraph", "opengraph",
"widgets" "widgets",
"darkmode",
"table of contents",
"search",
] ]
min_version = "0.78.0"
min_version = "0.87.0"
[author] [author]
name = "Jimmy Cai" name = "Jimmy Cai"
homepage = "https://jimmycai.com" homepage = "https://jimmycai.com"