mirror of
https://github.com/CaiJimmy/hugo-theme-stack.git
synced 2025-04-29 20:13:31 +08:00
Merge branch 'master' of https://github.com/CaiJimmy/hugo-theme-stack into upstream-3.11
This commit is contained in:
commit
4f9788a9a0
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1 +1,2 @@
|
|||||||
ko_fi: jimmycai
|
ko_fi: jimmycai
|
||||||
|
github: CaiJimmy
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
public
|
public
|
||||||
resources
|
resources
|
||||||
assets/jsconfig.json
|
assets/jsconfig.json
|
||||||
|
.hugo_build.lock
|
||||||
|
12
README.md
12
README.md
@ -3,9 +3,13 @@
|
|||||||
|
|
||||||
> Card-style Hugo theme designed for bloggers.
|
> Card-style Hugo theme designed for bloggers.
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
Use this template: [CaiJimmy/hugo-theme-stack-starter](https://github.com/CaiJimmy/hugo-theme-stack-starter)
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
[Example Site](https://theme-stack.jimmycai.com/)
|
[Example Site](https://demo.stack.jimmycai.com/)
|
||||||
|
|
||||||
[](https://app.netlify.com/sites/hugo-theme-stack/deploys)
|
[](https://app.netlify.com/sites/hugo-theme-stack/deploys)
|
||||||
|
|
||||||
@ -28,6 +32,7 @@ Stack is a simple card-style Hugo theme designed for bloggers, some of its featu
|
|||||||
- Properly cropped thumbnails
|
- Properly cropped thumbnails
|
||||||
- Subsection support
|
- Subsection support
|
||||||
- Table of contents
|
- Table of contents
|
||||||
|
- Multilingual mode and RTL support
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@ -35,7 +40,10 @@ It's necessary to use **Hugo Extended ≥ 0.87.0**.
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Clone / Download this repository to `theme` folder, and edit your site config following `exampleSite/config.yaml`.
|
* Route 1: Clone / Download this repository to `themes` folder
|
||||||
|
* Route 2: Turn your site into a hugo module and add this theme as a module dependency
|
||||||
|
|
||||||
|
Edit your site config following `exampleSite/config.yaml`.
|
||||||
|
|
||||||
*Note: Remove `config.toml` if there is one in the site folder.*
|
*Note: Remove `config.toml` if there is one in the site folder.*
|
||||||
|
|
||||||
|
9
assets/icons/categories.svg
Normal file
9
assets/icons/categories.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-hash" 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"/>
|
||||||
|
<line x1="5" y1="9" x2="19" y2="9" />
|
||||||
|
<line x1="5" y1="15" x2="19" y2="15" />
|
||||||
|
<line x1="11" y1="4" x2="7" y2="20" />
|
||||||
|
<line x1="17" y1="4" x2="13" y2="20" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 440 B |
10
assets/icons/language.svg
Normal file
10
assets/icons/language.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-language" 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="M4 5h7" />
|
||||||
|
<path d="M9 3v2c0 4.418 -2.239 8 -5 8" />
|
||||||
|
<path d="M5 9c-.003 2.144 2.952 3.908 6.7 4" />
|
||||||
|
<path d="M12 20l4 -9l4 9" />
|
||||||
|
<path d="M19.1 18h-6.2" />
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 467 B |
@ -6,5 +6,7 @@
|
|||||||
"*"
|
"*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"lib": ["es2020", "dom"],
|
||||||
|
"jsx": "preserve"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
/// Display right sidebar when min-width: lg
|
/// Display right sidebar when min-width: lg
|
||||||
@include respond(lg) {
|
@include respond(lg) {
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,24 +67,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.align-items--flex-start {
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grow {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.do-not-shrink {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.do-not-overflow {
|
|
||||||
min-width: 0;
|
|
||||||
flex-shrink: 1;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-width {
|
.full-width {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@ -94,14 +76,21 @@ main.main {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--section-separation);
|
||||||
|
|
||||||
|
@include respond(md) {
|
||||||
padding-top: var(--main-top-padding);
|
padding-top: var(--main-top-padding);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.main-container {
|
.main-container {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
column-gap: var(--section-separation);
|
gap: var(--section-separation);
|
||||||
|
padding-top: var(--main-top-padding);
|
||||||
|
|
||||||
@include respond(md) {
|
@include respond(md) {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
.article-list {
|
.article-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: var(--section-separation);
|
||||||
|
|
||||||
article {
|
article {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -17,10 +18,6 @@
|
|||||||
box-shadow: var(--shadow-l2);
|
box-shadow: var(--shadow-l2);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:last-of-type) {
|
|
||||||
margin-bottom: var(--section-separation);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-image {
|
.article-image {
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -52,13 +49,13 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
padding: var(--card-padding);
|
padding: var(--card-padding);
|
||||||
|
gap: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-title {
|
.article-title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin: 10px 0;
|
margin: 0;
|
||||||
color: var(--card-text-color-main);
|
color: var(--card-text-color-main);
|
||||||
font-size: 2.2rem;
|
font-size: 2.2rem;
|
||||||
|
|
||||||
@ -73,52 +70,58 @@
|
|||||||
color: var(--card-text-color-main);
|
color: var(--card-text-color-main);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
& + .article-subtitle {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-subtitle {
|
.article-subtitle {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
color: var(--card-text-color-secondary);
|
color: var(--card-text-color-secondary);
|
||||||
margin: 5px 0;
|
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
font-size: 1.75rem;
|
font-size: 1.75rem;
|
||||||
@include respond(xl) {
|
@include respond(xl) {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-time {
|
.article-title-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-time,
|
||||||
|
.article-translations {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--card-text-color-tertiary);
|
color: var(--card-text-color-tertiary);
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
margin-top: 10px;
|
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-right: 15px;
|
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
stroke-width: 1.33;
|
stroke-width: 1.33;
|
||||||
}
|
}
|
||||||
|
|
||||||
time {
|
time,
|
||||||
|
a {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
|
color: var(--card-text-color-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-category,
|
.article-category,
|
||||||
.article-tags {
|
.article-tags {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--accent-color-text);
|
color: var(--accent-color-text);
|
||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
@ -126,8 +129,6 @@
|
|||||||
border-radius: var(--tag-border-radius);
|
border-radius: var(--tag-border-radius);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
margin-right: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
transition: background-color 0.5s ease;
|
transition: background-color 0.5s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -148,15 +149,12 @@
|
|||||||
--image-size: 60px;
|
--image-size: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& + .pagination {
|
|
||||||
margin-top: var(--section-separation);
|
|
||||||
}
|
|
||||||
|
|
||||||
article {
|
article {
|
||||||
& > a {
|
& > a {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: var(--small-card-padding);
|
padding: var(--small-card-padding);
|
||||||
|
gap: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(:last-of-type) {
|
&:not(:last-of-type) {
|
||||||
@ -166,8 +164,8 @@
|
|||||||
.article-details {
|
.article-details {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
padding-right: 15px;
|
|
||||||
min-height: var(--image-size);
|
min-height: var(--image-size);
|
||||||
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-title {
|
.article-title {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
html {
|
html {
|
||||||
font-size: 62.5%;
|
font-size: 62.5%;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
|
@ -2,7 +2,6 @@ footer.site-footer {
|
|||||||
padding: 20px 0 var(--section-separation) 0;
|
padding: 20px 0 var(--section-separation) 0;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
line-height: 1.75;
|
line-height: 1.75;
|
||||||
margin-top: var(--section-separation);
|
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
content: "";
|
content: "";
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
.archives-group {
|
|
||||||
margin-bottom: var(--section-separation);
|
|
||||||
}
|
|
@ -13,10 +13,6 @@
|
|||||||
box-shadow: var(--shadow-l1);
|
box-shadow: var(--shadow-l1);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&.main-article {
|
|
||||||
margin-bottom: var(--section-separation);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-header {
|
.article-header {
|
||||||
.article-image {
|
.article-image {
|
||||||
img {
|
img {
|
||||||
@ -57,11 +53,11 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
|
gap: 15px;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
margin-right: 15px;
|
|
||||||
stroke-width: 1.33;
|
stroke-width: 1.33;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +67,8 @@
|
|||||||
text-transform: unset;
|
text-transform: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-copyright, .article-lastmod {
|
.article-copyright,
|
||||||
|
.article-lastmod {
|
||||||
a {
|
a {
|
||||||
color: var(--body-text-color);
|
color: var(--body-text-color);
|
||||||
}
|
}
|
||||||
@ -80,110 +77,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#article-toolbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 20px 0;
|
|
||||||
|
|
||||||
@include respond(md) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-home {
|
|
||||||
background: var(--card-background);
|
|
||||||
border-radius: var(--tag-border-radius);
|
|
||||||
color: var(--card-text-color-tertiary);
|
|
||||||
margin-right: 30px;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 1.4rem;
|
|
||||||
text-transform: uppercase;
|
|
||||||
padding: 10px 20px 10px 15px;
|
|
||||||
|
|
||||||
transition: box-shadow 0.3s ease;
|
|
||||||
|
|
||||||
box-shadow: var(--shadow-l1);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: var(--shadow-l2);
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
margin-right: 5px;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-weight: 500;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-page.has-toc {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
|
|
||||||
.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 {
|
.widget--toc {
|
||||||
background-color: var(--card-background);
|
background-color: var(--card-background);
|
||||||
border-radius: var(--card-border-radius);
|
border-radius: var(--card-border-radius);
|
||||||
@ -193,6 +86,10 @@
|
|||||||
color: var(--card-text-color-main);
|
color: var(--card-text-color-main);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--card-separator-color);
|
||||||
|
}
|
||||||
|
|
||||||
#TableOfContents {
|
#TableOfContents {
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
max-height: 75vh;
|
max-height: 75vh;
|
||||||
@ -207,7 +104,7 @@
|
|||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
counter-reset: item;
|
counter-reset: item;
|
||||||
|
|
||||||
li:before {
|
li a::before {
|
||||||
counter-increment: item;
|
counter-increment: item;
|
||||||
content: counters(item, ".") ". ";
|
content: counters(item, ".") ". ";
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -220,7 +117,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin: 15px 20px;
|
margin: 15px 0 15px 20px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
|
||||||
& > ol,
|
& > ol,
|
||||||
@ -234,11 +131,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
li.active-class > a {
|
||||||
|
border-left: var(--heading-border-size) solid var(--accent-color);
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.related-contents--wrapper {
|
ul li.active-class > a {
|
||||||
margin-bottom: var(--section-separation);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.related-contents {
|
.related-contents {
|
||||||
@ -286,9 +211,9 @@
|
|||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
margin-left: calc((var(--card-padding)) * -1);
|
margin-inline-start: calc((var(--card-padding)) * -1);
|
||||||
padding-left: calc(var(--card-padding) - var(--heading-border-size));
|
padding-inline-start: calc(var(--card-padding) - var(--heading-border-size));
|
||||||
border-left: var(--heading-border-size) solid var(--accent-color);
|
border-inline-start: var(--heading-border-size) solid var(--accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
figure {
|
figure {
|
||||||
@ -303,7 +228,7 @@
|
|||||||
blockquote {
|
blockquote {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 1.5em 0;
|
margin: 1.5em 0;
|
||||||
border-left: var(--blockquote-border-size) solid var(--card-separator-color);
|
border-inline-start: 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);
|
||||||
}
|
}
|
||||||
@ -331,13 +256,10 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin: 1.5em 0;
|
margin: 1.5em 0;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
figure {
|
figure {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
& + figure {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +272,10 @@
|
|||||||
line-height: 1.428571429;
|
line-height: 1.428571429;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
padding: var(--card-padding);
|
padding: var(--card-padding);
|
||||||
|
// keep Codeblocks LTR
|
||||||
|
[dir="rtl"] & {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
code {
|
code {
|
||||||
color: unset;
|
color: unset;
|
||||||
border: none;
|
border: none;
|
||||||
@ -359,6 +284,50 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
background-color: var(--pre-background-color);
|
||||||
|
padding: var(--card-padding);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.copyCodeButton {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// keep Codeblocks LTR
|
||||||
|
[dir="rtl"] & {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
@ -407,38 +376,14 @@
|
|||||||
/// Negative margins
|
/// Negative margins
|
||||||
blockquote,
|
blockquote,
|
||||||
figure,
|
figure,
|
||||||
.gallery,
|
.highlight,
|
||||||
pre,
|
pre,
|
||||||
|
.gallery,
|
||||||
.video-wrapper,
|
.video-wrapper,
|
||||||
|
.table-wrapper,
|
||||||
.s_video_simple {
|
.s_video_simple {
|
||||||
margin-left: calc((var(--card-padding)) * -1);
|
margin-left: calc((var(--card-padding)) * -1);
|
||||||
margin-right: calc((var(--card-padding)) * -1);
|
margin-right: calc((var(--card-padding)) * -1);
|
||||||
width: calc(100% + var(--card-padding) * 2);
|
width: calc(100% + var(--card-padding) * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlight {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.copyCodeButton {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.copyCodeButton {
|
|
||||||
position: absolute;
|
|
||||||
top: calc(var(--card-padding));
|
|
||||||
right: 0;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,16 @@
|
|||||||
background-color: var(--card-background);
|
background-color: var(--card-background);
|
||||||
padding: var(--small-card-padding);
|
padding: var(--small-card-padding);
|
||||||
box-shadow: var(--shadow-l1);
|
box-shadow: var(--shadow-l1);
|
||||||
margin-bottom: var(--section-separation);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
--separation: 15px;
|
--separation: 15px;
|
||||||
|
|
||||||
.section-term {
|
.section-term {
|
||||||
font-size: 2.2rem;
|
font-size: 2.2rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-top: calc(var(--separation) / 2);
|
|
||||||
color: var(--card-text-color-main);
|
color: var(--card-text-color-main);
|
||||||
|
|
||||||
& + .section-description {
|
|
||||||
margin-top: var(--separation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-description {
|
.section-description {
|
||||||
@ -29,7 +24,9 @@
|
|||||||
|
|
||||||
.section-details {
|
.section-details {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
margin-right: 20px;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-image {
|
.section-image {
|
||||||
@ -49,7 +46,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.subsection-list {
|
.subsection-list {
|
||||||
margin-bottom: var(--section-separation);
|
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
|
||||||
.article-list--tile {
|
.article-list--tile {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
.search-form {
|
.search-form {
|
||||||
margin-bottom: var(--section-separation);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
--button-size: 80px;
|
--button-size: 80px;
|
||||||
|
|
||||||
@ -25,7 +24,7 @@
|
|||||||
label {
|
label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 15px;
|
top: 15px;
|
||||||
left: 20px;
|
inset-inline-start: 20px;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
color: var(--card-text-color-tertiary);
|
color: var(--card-text-color-tertiary);
|
||||||
}
|
}
|
||||||
@ -52,7 +51,7 @@
|
|||||||
|
|
||||||
button {
|
button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
inset-inline-end: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: var(--button-size);
|
width: var(--button-size);
|
||||||
@ -79,4 +78,5 @@
|
|||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -101,11 +101,16 @@
|
|||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 30px;
|
right: 0;
|
||||||
top: 30px;
|
top: 0;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
[dir="rtl"] & {
|
||||||
|
left: 0;
|
||||||
|
right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
@include respond(md) {
|
@include respond(md) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -125,21 +130,31 @@
|
|||||||
.menu {
|
.menu {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
|
|
||||||
background-color: var(--card-background);
|
background-color: var(--card-background);
|
||||||
padding: 15px 0;
|
|
||||||
box-shadow: var(--shadow-l1);
|
box-shadow: var(--shadow-l1);
|
||||||
display: none;
|
display: none;
|
||||||
|
margin: 0 calc(var(--container-padding) * -1);
|
||||||
|
|
||||||
margin: 0 -15px;
|
padding: 30px 30px;
|
||||||
|
@include respond(xl) {
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&,
|
||||||
|
.menu-bottom-section {
|
||||||
|
gap: 30px;
|
||||||
|
@include respond(xl) {
|
||||||
|
gap: 25px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.show {
|
&.show {
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@include respond(md) {
|
@include respond(md) {
|
||||||
@ -149,34 +164,19 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
margin: 0;
|
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 30px;
|
padding: 0;
|
||||||
|
|
||||||
&:not(:last-of-type) {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
|
|
||||||
@include respond(xl) {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include respond(md) {
|
@include respond(md) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
stroke-width: 1.33;
|
stroke-width: 1.33;
|
||||||
margin-right: 40px;
|
|
||||||
|
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -187,6 +187,7 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--body-text-color);
|
color: var(--body-text-color);
|
||||||
|
gap: var(--menu-icon-separation);
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
@ -200,11 +201,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-bottom-section {
|
||||||
|
margin-top: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.social-menu {
|
.social-menu {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0%;
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
border-radius: var(--card-border-radius);
|
border-radius: var(--card-border-radius);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
margin: var(--section-separation) 0;
|
|
||||||
|
|
||||||
.page-link {
|
.page-link {
|
||||||
padding: 16px 32px;
|
padding: 16px 32px;
|
||||||
|
@ -11,13 +11,15 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
|
gap: var(--sidebar-element-separation);
|
||||||
width: 100%;
|
|
||||||
padding: 30px 0 15px 0;
|
|
||||||
max-width: none;
|
max-width: none;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
--sidebar-avatar-size: 120px;
|
--sidebar-avatar-size: 100px;
|
||||||
--sidebar-element-separation: 20px;
|
--sidebar-element-separation: 20px;
|
||||||
|
--emoji-size: 40px;
|
||||||
|
--emoji-font-size: 20px;
|
||||||
|
|
||||||
@include respond(md) {
|
@include respond(md) {
|
||||||
width: auto;
|
width: auto;
|
||||||
@ -27,18 +29,49 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include respond(2xl) {
|
@include respond(2xl) {
|
||||||
--sidebar-avatar-size: 140px;
|
--sidebar-avatar-size: 120px;
|
||||||
--sidebar-element-separation: 25px;
|
--sidebar-element-separation: 25px;
|
||||||
|
--emoji-size: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.sticky {
|
&.sticky {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.compact {
|
||||||
|
--sidebar-avatar-size: 80px;
|
||||||
|
--emoji-size: 30px;
|
||||||
|
--emoji-font-size: 15px;
|
||||||
|
|
||||||
|
header {
|
||||||
|
@include respond(lg) {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-meta {
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-name {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
|
||||||
|
@include respond(2xl) {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-description {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-sidebar {
|
.right-sidebar {
|
||||||
flex-shrink: 0;
|
width: 100%;
|
||||||
display: none;
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--widget-separation);
|
||||||
|
|
||||||
&.sticky {
|
&.sticky {
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -49,11 +82,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-info {
|
.sidebar header {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
transition: box-shadow 0.5s ease;
|
transition: box-shadow 0.5s ease;
|
||||||
|
display: flex;
|
||||||
padding: 15px;
|
flex-direction: column;
|
||||||
|
gap: var(--sidebar-element-separation);
|
||||||
|
|
||||||
@include respond(md) {
|
@include respond(md) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -64,8 +98,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
width: var(--sidebar-avatar-size);
|
width: var(--sidebar-avatar-size);
|
||||||
height: var(--sidebar-avatar-size);
|
height: var(--sidebar-avatar-size);
|
||||||
|
flex-shrink: 0;
|
||||||
margin-bottom: var(--sidebar-element-separation);
|
|
||||||
|
|
||||||
.site-logo {
|
.site-logo {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -76,58 +109,44 @@
|
|||||||
|
|
||||||
.emoji {
|
.emoji {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 40px;
|
width: var(--emoji-size);
|
||||||
height: 40px;
|
height: var(--emoji-size);
|
||||||
line-height: 40px;
|
line-height: var(--emoji-size);
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 20px;
|
font-size: var(--emoji-font-size);
|
||||||
background-color: var(--card-background);
|
background-color: var(--card-background);
|
||||||
box-shadow: var(--shadow-l2);
|
box-shadow: var(--shadow-l2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include respond(2xl) {
|
.site-meta {
|
||||||
width: 50px;
|
display: flex;
|
||||||
height: 50px;
|
flex-direction: column;
|
||||||
line-height: 50px;
|
gap: 10px;
|
||||||
}
|
justify-content: center;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-name {
|
.site-name {
|
||||||
color: var(--accent-color);
|
color: var(--accent-color);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.8rem;
|
|
||||||
|
|
||||||
@include respond(2xl) {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.site-description {
|
|
||||||
color: var(--body-text-color);
|
|
||||||
font-weight: normal;
|
|
||||||
margin: 10px 0;
|
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
|
|
||||||
@include respond(2xl) {
|
@include respond(2xl) {
|
||||||
font-size: 1.8rem;
|
font-size: 1.8rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
.site-description {
|
||||||
.widget {
|
color: var(--body-text-color);
|
||||||
margin-bottom: var(--section-separation);
|
font-weight: normal;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
|
||||||
&:not(:last-of-type):after {
|
@include respond(2xl) {
|
||||||
content: "";
|
font-size: 1.6rem;
|
||||||
width: 100px;
|
|
||||||
height: 2px;
|
|
||||||
background-color: var(--body-text-color);
|
|
||||||
display: block;
|
|
||||||
margin-top: var(--section-separation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,8 +172,27 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
gap: var(--menu-icon-separation);
|
||||||
|
|
||||||
.icon-tabler-toggle-right {
|
.icon-tabler-toggle-right {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#i18n-switch {
|
||||||
|
color: var(--body-text-color);
|
||||||
|
display: inline-flex;
|
||||||
|
align-content: center;
|
||||||
|
gap: var(--menu-icon-separation);
|
||||||
|
|
||||||
|
select {
|
||||||
|
border: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--body-text-color);
|
||||||
|
|
||||||
|
option {
|
||||||
|
color: var(--card-text-color-main);
|
||||||
|
background-color: var(--card-background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
.widget {
|
.widget {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
.widget-icon {
|
.widget-icon {
|
||||||
svg {
|
svg {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
@ -14,16 +17,14 @@
|
|||||||
.tagCloud-tags {
|
.tagCloud-tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
background: var(--card-background);
|
background: var(--card-background);
|
||||||
box-shadow: var(--shadow-l1);
|
box-shadow: var(--shadow-l1);
|
||||||
border-radius: var(--tag-border-radius);
|
border-radius: var(--tag-border-radius);
|
||||||
padding: 8px 20px;
|
padding: 8px 20px;
|
||||||
|
|
||||||
color: var(--card-text-color-main);
|
color: var(--card-text-color-main);
|
||||||
margin-bottom: 10px;
|
|
||||||
margin-right: 5px;
|
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
transition: box-shadow 0.3s ease;
|
transition: box-shadow 0.3s ease;
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
@import "partials/pagination.scss";
|
@import "partials/pagination.scss";
|
||||||
@import "partials/sidebar.scss";
|
@import "partials/sidebar.scss";
|
||||||
@import "partials/base.scss";
|
@import "partials/base.scss";
|
||||||
@import "partials/layout/archives.scss";
|
|
||||||
@import "partials/layout/article.scss";
|
@import "partials/layout/article.scss";
|
||||||
@import "partials/layout/list.scss";
|
@import "partials/layout/list.scss";
|
||||||
@import "partials/layout/404.scss";
|
@import "partials/layout/404.scss";
|
||||||
|
@ -1,25 +1,11 @@
|
|||||||
$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 {
|
||||||
@include respond(md) {
|
|
||||||
--main-top-padding: 35px;
|
--main-top-padding: 35px;
|
||||||
}
|
|
||||||
|
|
||||||
@include respond(xl) {
|
@include respond(xl) {
|
||||||
--main-top-padding: 50px;
|
--main-top-padding: 50px;
|
||||||
@ -45,7 +31,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
|
|||||||
--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: #424242;
|
--scrollbar-thumb: hsl(0, 0%, 40%);
|
||||||
--scrollbar-track: var(--body-background);
|
--scrollbar-track: var(--body-background);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,3 +145,21 @@ $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";
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--menu-icon-separation: 40px;
|
||||||
|
--container-padding: 15px;
|
||||||
|
--widget-separation: var(--section-separation);
|
||||||
|
}
|
||||||
|
@ -57,6 +57,60 @@ class StackGallery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static createGallery(container: HTMLElement) {
|
public static createGallery(container: HTMLElement) {
|
||||||
|
/// 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.gallery-image');
|
||||||
|
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');
|
const figuresEl = container.querySelectorAll('figure.gallery-image');
|
||||||
|
|
||||||
let currentGallery = [];
|
let currentGallery = [];
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,20 +62,21 @@ let Stack = {
|
|||||||
/**
|
/**
|
||||||
* Add copy button to code block
|
* Add copy button to code block
|
||||||
*/
|
*/
|
||||||
const codeBlocks = document.querySelectorAll('.article-content .highlight');
|
const highlights = document.querySelectorAll('.article-content div.highlight');
|
||||||
const copyText = `Copy`,
|
const copyText = `Copy`,
|
||||||
copiedText = `Copied!`;
|
copiedText = `Copied!`;
|
||||||
codeBlocks.forEach(codeBlock => {
|
|
||||||
|
highlights.forEach(highlight => {
|
||||||
const copyButton = document.createElement('button');
|
const copyButton = document.createElement('button');
|
||||||
copyButton.innerHTML = copyText;
|
copyButton.innerHTML = copyText;
|
||||||
copyButton.classList.add('copyCodeButton');
|
copyButton.classList.add('copyCodeButton');
|
||||||
codeBlock.appendChild(copyButton);
|
highlight.appendChild(copyButton);
|
||||||
|
|
||||||
const pre = codeBlock.getElementsByTagName('pre');
|
const codeBlock = highlight.querySelector('code[data-lang]');
|
||||||
const code = pre[0].textContent;
|
if (!codeBlock) return;
|
||||||
|
|
||||||
copyButton.addEventListener('click', () => {
|
copyButton.addEventListener('click', () => {
|
||||||
navigator.clipboard.writeText(code)
|
navigator.clipboard.writeText(codeBlock.textContent)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
copyButton.textContent = copiedText;
|
copyButton.textContent = copiedText;
|
||||||
|
|
||||||
|
131
assets/ts/scrollspy.ts
Normal file
131
assets/ts/scrollspy.ts
Normal 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 };
|
@ -8,6 +8,11 @@ interface pageData {
|
|||||||
matchCount: number
|
matchCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface match {
|
||||||
|
start: number,
|
||||||
|
end: number
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escape HTML tags as HTML entities
|
* Escape HTML tags as HTML entities
|
||||||
* Edited from:
|
* Edited from:
|
||||||
@ -53,77 +58,129 @@ class Search {
|
|||||||
this.bindSearchForm();
|
this.bindSearchForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async searchKeywords(keywords: string[]) {
|
/**
|
||||||
const rawData = await this.getData();
|
* Processes search matches
|
||||||
let results: pageData[] = [];
|
* @param str original text
|
||||||
|
* @param matches array of matches
|
||||||
/// Sort keywords by their length
|
* @param ellipsis whether to add ellipsis to the end of each match
|
||||||
keywords.sort((a, b) => {
|
* @param charLimit max length of preview string
|
||||||
return b.length - a.length
|
* @param offset how many characters before and after the match to include in preview
|
||||||
|
* @returns preview string
|
||||||
|
*/
|
||||||
|
private static processMatches(str: string, matches: match[], ellipsis: boolean = true, charLimit = 140, offset = 20): string {
|
||||||
|
matches.sort((a, b) => {
|
||||||
|
return a.start - b.start;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let i = 0,
|
||||||
|
lastIndex = 0,
|
||||||
|
charCount = 0;
|
||||||
|
|
||||||
|
const resultArray: string[] = [];
|
||||||
|
|
||||||
|
while (i < matches.length) {
|
||||||
|
const item = matches[i];
|
||||||
|
|
||||||
|
/// item.start >= lastIndex (equal only for the first iteration)
|
||||||
|
/// because of the while loop that comes after, iterating over variable j
|
||||||
|
|
||||||
|
if (ellipsis && item.start - offset > lastIndex) {
|
||||||
|
resultArray.push(`${replaceHTMLEnt(str.substring(lastIndex, lastIndex + offset))} [...] `);
|
||||||
|
resultArray.push(`${replaceHTMLEnt(str.substring(item.start - offset, item.start))}`);
|
||||||
|
charCount += offset * 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/// If the match is too close to the end of last match, don't add ellipsis
|
||||||
|
resultArray.push(replaceHTMLEnt(str.substring(lastIndex, item.start)));
|
||||||
|
charCount += item.start - lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
let j = i + 1,
|
||||||
|
end = item.end;
|
||||||
|
|
||||||
|
/// Include as many matches as possible
|
||||||
|
/// [item.start, end] is the range of the match
|
||||||
|
while (j < matches.length && matches[j].start <= end) {
|
||||||
|
end = Math.max(matches[j].end, end);
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultArray.push(`<mark>${replaceHTMLEnt(str.substring(item.start, end))}</mark>`);
|
||||||
|
charCount += end - item.start;
|
||||||
|
|
||||||
|
i = j;
|
||||||
|
lastIndex = end;
|
||||||
|
|
||||||
|
if (ellipsis && charCount > charLimit) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the rest of the string
|
||||||
|
if (lastIndex < str.length) {
|
||||||
|
let end = str.length;
|
||||||
|
if (ellipsis) end = Math.min(end, lastIndex + offset);
|
||||||
|
|
||||||
|
resultArray.push(`${replaceHTMLEnt(str.substring(lastIndex, end))}`);
|
||||||
|
|
||||||
|
if (ellipsis && end != str.length) {
|
||||||
|
resultArray.push(` [...]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultArray.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async searchKeywords(keywords: string[]) {
|
||||||
|
const rawData = await this.getData();
|
||||||
|
const results: pageData[] = [];
|
||||||
|
|
||||||
|
const regex = new RegExp(keywords.filter((v, index, arr) => {
|
||||||
|
arr[index] = escapeRegExp(v);
|
||||||
|
return v.trim() !== '';
|
||||||
|
}).join('|'), 'gi');
|
||||||
|
|
||||||
for (const item of rawData) {
|
for (const item of rawData) {
|
||||||
|
const titleMatches: match[] = [],
|
||||||
|
contentMatches: match[] = [];
|
||||||
|
|
||||||
let result = {
|
let result = {
|
||||||
...item,
|
...item,
|
||||||
preview: '',
|
preview: '',
|
||||||
matchCount: 0
|
matchCount: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
let matched = false;
|
const contentMatchAll = item.content.matchAll(regex);
|
||||||
|
for (const match of Array.from(contentMatchAll)) {
|
||||||
for (const keyword of keywords) {
|
contentMatches.push({
|
||||||
if (keyword === '') continue;
|
start: match.index,
|
||||||
|
end: match.index + match[0].length
|
||||||
const regex = new RegExp(escapeRegExp(replaceHTMLEnt(keyword)), 'gi');
|
|
||||||
|
|
||||||
const contentMatch = regex.exec(result.content);
|
|
||||||
regex.lastIndex = 0; /// Reset regex
|
|
||||||
|
|
||||||
const titleMatch = regex.exec(result.title);
|
|
||||||
regex.lastIndex = 0; /// Reset regex
|
|
||||||
|
|
||||||
if (titleMatch) {
|
|
||||||
result.title = result.title.replace(regex, Search.marker);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (titleMatch || contentMatch) {
|
|
||||||
matched = true;
|
|
||||||
++result.matchCount;
|
|
||||||
|
|
||||||
let start = 0,
|
|
||||||
end = 100;
|
|
||||||
|
|
||||||
if (contentMatch) {
|
|
||||||
start = contentMatch.index - 20;
|
|
||||||
end = contentMatch.index + 80
|
|
||||||
|
|
||||||
if (start < 0) start = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.preview.indexOf(keyword) !== -1) {
|
|
||||||
result.preview = result.preview.replace(regex, Search.marker);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (start !== 0) result.preview += `[...] `;
|
|
||||||
result.preview += `${result.content.slice(start, end).replace(regex, Search.marker)} `;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matched) {
|
|
||||||
result.preview += '[...]';
|
|
||||||
results.push(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Result with more matches appears first */
|
|
||||||
return results.sort((a, b) => {
|
|
||||||
return b.matchCount - a.matchCount;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static marker(match) {
|
const titleMatchAll = item.title.matchAll(regex);
|
||||||
return '<mark>' + match + '</mark>';
|
for (const match of Array.from(titleMatchAll)) {
|
||||||
|
titleMatches.push({
|
||||||
|
start: match.index,
|
||||||
|
end: match.index + match[0].length
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (titleMatches.length > 0) result.title = Search.processMatches(result.title, titleMatches, false);
|
||||||
|
if (contentMatches.length > 0) {
|
||||||
|
result.preview = Search.processMatches(result.content, contentMatches);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/// If there are no matches in the content, use the first 140 characters as preview
|
||||||
|
result.preview = replaceHTMLEnt(result.content.substring(0, 140));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.matchCount = titleMatches.length + contentMatches.length;
|
||||||
|
if (result.matchCount > 0) results.push(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result with more matches appears first
|
||||||
|
return results.sort((a, b) => {
|
||||||
|
return b.matchCount - a.matchCount;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async doSearch(keywords: string[]) {
|
private async doSearch(keywords: string[]) {
|
||||||
@ -150,6 +207,11 @@ class Search {
|
|||||||
/// Not fetched yet
|
/// Not fetched yet
|
||||||
const jsonURL = this.form.dataset.json;
|
const jsonURL = this.form.dataset.json;
|
||||||
this.data = await fetch(jsonURL).then(res => res.json());
|
this.data = await fetch(jsonURL).then(res => res.json());
|
||||||
|
const parser = new DOMParser();
|
||||||
|
|
||||||
|
for (const item of this.data) {
|
||||||
|
item.content = parser.parseFromString(item.content, 'text/html').body.innerText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.data;
|
return this.data;
|
||||||
@ -160,7 +222,7 @@ class Search {
|
|||||||
|
|
||||||
const eventHandler = (e) => {
|
const eventHandler = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const keywords = this.input.value;
|
const keywords = this.input.value.trim();
|
||||||
|
|
||||||
Search.updateQueryString(keywords, true);
|
Search.updateQueryString(keywords, true);
|
||||||
|
|
||||||
@ -225,7 +287,7 @@ class Search {
|
|||||||
<a href={item.permalink}>
|
<a href={item.permalink}>
|
||||||
<div class="article-details">
|
<div class="article-details">
|
||||||
<h2 class="article-title" dangerouslySetInnerHTML={{ __html: this.displayTitle(item) }}></h2>
|
<h2 class="article-title" dangerouslySetInnerHTML={{ __html: this.displayTitle(item) }}></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">
|
||||||
|
34
assets/ts/smoothAnchors.ts
Normal file
34
assets/ts/smoothAnchors.ts
Normal 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 };
|
14
config.yaml
14
config.yaml
@ -19,6 +19,7 @@ params:
|
|||||||
lastUpdated: Jan 02, 2006 15:04 MST
|
lastUpdated: Jan 02, 2006 15:04 MST
|
||||||
|
|
||||||
sidebar:
|
sidebar:
|
||||||
|
compact: false
|
||||||
emoji:
|
emoji:
|
||||||
subtitle:
|
subtitle:
|
||||||
avatar:
|
avatar:
|
||||||
@ -95,6 +96,7 @@ params:
|
|||||||
darkTheme:
|
darkTheme:
|
||||||
reactionsEnabled: 1
|
reactionsEnabled: 1
|
||||||
emitMetadata: 0
|
emitMetadata: 0
|
||||||
|
lang:
|
||||||
|
|
||||||
gitalk:
|
gitalk:
|
||||||
owner:
|
owner:
|
||||||
@ -108,16 +110,8 @@ params:
|
|||||||
id:
|
id:
|
||||||
|
|
||||||
widgets:
|
widgets:
|
||||||
enabled:
|
homepage: []
|
||||||
- search
|
page: []
|
||||||
- archives
|
|
||||||
- tag-cloud
|
|
||||||
|
|
||||||
archives:
|
|
||||||
limit: 5
|
|
||||||
|
|
||||||
tagCloud:
|
|
||||||
limit: 10
|
|
||||||
|
|
||||||
opengraph:
|
opengraph:
|
||||||
twitter:
|
twitter:
|
||||||
|
@ -36,3 +36,11 @@ KaTeX:
|
|||||||
integrity: sha384-vZTG03m+2yp6N6BNi5iM4rW4oIwk5DfcNdFfxkk9ZWpDriOkXX8voJBFrAO7MpVl
|
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
|
||||||
|
@ -4,6 +4,21 @@ theme: hugo-theme-stack
|
|||||||
paginate: 5
|
paginate: 5
|
||||||
title: Example Site
|
title: Example Site
|
||||||
|
|
||||||
|
languages:
|
||||||
|
en:
|
||||||
|
languageName: English
|
||||||
|
title: Example Site
|
||||||
|
weight: 1
|
||||||
|
zh-cn:
|
||||||
|
languageName: 中文
|
||||||
|
title: 演示站点
|
||||||
|
weight: 2
|
||||||
|
ar:
|
||||||
|
languageName: عربي
|
||||||
|
languagedirection: rtl
|
||||||
|
title: موقع تجريبي
|
||||||
|
weight: 3
|
||||||
|
|
||||||
# Change it to your Disqus shortname before using
|
# Change it to your Disqus shortname before using
|
||||||
disqusShortname: hugo-theme-stack
|
disqusShortname: hugo-theme-stack
|
||||||
|
|
||||||
@ -11,7 +26,7 @@ disqusShortname: hugo-theme-stack
|
|||||||
googleAnalytics:
|
googleAnalytics:
|
||||||
|
|
||||||
# Theme i18n support
|
# Theme i18n support
|
||||||
# Available values: en, fr, id, ja, ko, pt-br, zh-cn, zh-tw, es, de, nl, it, th, el, uk
|
# Available values: ar, ca, de, el, en, es, fr, id, it, ja, ko, nl, pt-br, th, uk, zh-cn, zh-hk, zh-tw
|
||||||
DefaultContentLanguage: en
|
DefaultContentLanguage: en
|
||||||
|
|
||||||
# Set hasCJKLanguage to true if DefaultContentLanguage is in [zh-cn ja ko]
|
# Set hasCJKLanguage to true if DefaultContentLanguage is in [zh-cn ja ko]
|
||||||
@ -104,6 +119,12 @@ params:
|
|||||||
path:
|
path:
|
||||||
lang:
|
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:
|
giscus:
|
||||||
repo:
|
repo:
|
||||||
repoID:
|
repoID:
|
||||||
@ -125,18 +146,20 @@ params:
|
|||||||
cusdis:
|
cusdis:
|
||||||
host:
|
host:
|
||||||
id:
|
id:
|
||||||
|
|
||||||
widgets:
|
widgets:
|
||||||
enabled:
|
homepage:
|
||||||
- search
|
- type: search
|
||||||
- archives
|
- type: archives
|
||||||
- tag-cloud
|
params:
|
||||||
|
|
||||||
archives:
|
|
||||||
limit: 5
|
limit: 5
|
||||||
|
- type: categories
|
||||||
tagCloud:
|
params:
|
||||||
limit: 10
|
limit: 10
|
||||||
|
- type: tag-cloud
|
||||||
|
params:
|
||||||
|
limit: 10
|
||||||
|
page:
|
||||||
|
- type: toc
|
||||||
|
|
||||||
opengraph:
|
opengraph:
|
||||||
twitter:
|
twitter:
|
||||||
@ -169,15 +192,7 @@ params:
|
|||||||
### See https://docs.stack.jimmycai.com/configuration/custom-menu.html
|
### 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
|
### To remove about, archive and search page menu item, remove `menu` field from their FrontMatter
|
||||||
menu:
|
menu:
|
||||||
main:
|
main: []
|
||||||
- identifier: home
|
|
||||||
name: Home
|
|
||||||
url: /
|
|
||||||
weight: -100
|
|
||||||
params:
|
|
||||||
### For demonstration purpose, the home link will be open in a new tab
|
|
||||||
newTab: true
|
|
||||||
icon: home
|
|
||||||
|
|
||||||
social:
|
social:
|
||||||
- identifier: github
|
- identifier: github
|
||||||
@ -214,3 +229,9 @@ markup:
|
|||||||
startLevel: 2
|
startLevel: 2
|
||||||
highlight:
|
highlight:
|
||||||
noClasses: false
|
noClasses: false
|
||||||
|
codeFences: true
|
||||||
|
guessSyntax: true
|
||||||
|
lineNoStart: 1
|
||||||
|
lineNos: true
|
||||||
|
lineNumbersInTable: true
|
||||||
|
tabWidth: 4
|
||||||
|
8
exampleSite/content/_index.md
Normal file
8
exampleSite/content/_index.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
menu:
|
||||||
|
main:
|
||||||
|
name: Home
|
||||||
|
weight: -100
|
||||||
|
params:
|
||||||
|
icon: home
|
||||||
|
---
|
8
exampleSite/content/_index.zh-cn.md
Normal file
8
exampleSite/content/_index.zh-cn.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
menu:
|
||||||
|
main:
|
||||||
|
name: 主页
|
||||||
|
weight: -100
|
||||||
|
params:
|
||||||
|
icon: home
|
||||||
|
---
|
10
exampleSite/content/page/about/index.zh-cn.md
Normal file
10
exampleSite/content/page/about/index.zh-cn.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: 关于
|
||||||
|
menu:
|
||||||
|
main:
|
||||||
|
weight: -90
|
||||||
|
params:
|
||||||
|
icon: user
|
||||||
|
---
|
||||||
|
|
||||||
|
This is a test page for i18n support.
|
@ -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
|
||||||
@ -158,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
|
||||||
|
|
||||||
|
[](https://google.com)
|
32
exampleSite/content/post/placeholder-text/index.ar.md
Normal file
32
exampleSite/content/post/placeholder-text/index.ar.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
+++
|
||||||
|
author = "Hugo Authors"
|
||||||
|
title = "مثال نص"
|
||||||
|
date = "2019-03-09"
|
||||||
|
description = "هذا النص هو مثال لنص يمكن أن يستبدل في نفس المساحة"
|
||||||
|
categories = [
|
||||||
|
"تجربة",
|
||||||
|
"تجربة مع فراغات"
|
||||||
|
]
|
||||||
|
tags = [
|
||||||
|
"ماركداون",
|
||||||
|
"نص",
|
||||||
|
"وسم مع فراغات"
|
||||||
|
]
|
||||||
|
image = "matt-le-SJSpo9hQf7s-unsplash.jpg"
|
||||||
|
+++
|
||||||
|
## فقرة 1
|
||||||
|
|
||||||
|
هذا النص هو مثال لنص يمكن أن يستبدل في نفس المساحة، لقد تم توليد هذا النص من [مولد النص العربى](https://colorslab.com/textgator/)، حيث يمكنك أن تولد مثل هذا النص أو العديد من النصوص الأخرى إضافة إلى زيادة عدد الحروف التى يولدها التطبيق.
|
||||||
|
إذا كنت تحتاج إلى عدد أكبر من الفقرات يتيح لك مولد النص العربى زيادة عدد الفقرات كما تريد، النص لن يبدو مقسما ولا يحوي أخطاء لغوية، مولد النص العربى مفيد لمصممي المواقع على وجه الخصوص، حيث يحتاج العميل فى كثير من الأحيان أن يطلع على صورة حقيقية لتصميم الموقع.
|
||||||
|
ومن هنا وجب على المصمم أن يضع نصوصا مؤقتة على التصميم ليظهر للعميل الشكل كاملاً،دور مولد النص العربى أن يوفر على المصمم عناء البحث عن نص بديل لا علاقة له بالموضوع الذى يتحدث عنه التصميم فيظهر بشكل لا يليق.
|
||||||
|
هذا النص يمكن أن يتم تركيبه على أي تصميم دون مشكلة فلن يبدو وكأنه نص منسوخ، غير منظم، غير منسق، أو حتى غير مفهوم. لأنه مازال نصاً بديلاً ومؤقتاً.
|
||||||
|
|
||||||
|
## فقرة 2
|
||||||
|
|
||||||
|
هذا النص هو مثال لنص يمكن أن يستبدل في نفس المساحة، لقد تم توليد هذا النص من [مولد النص العربى](https://colorslab.com/textgator/)، حيث يمكنك أن تولد مثل هذا النص أو العديد من النصوص الأخرى إضافة إلى زيادة عدد الحروف التى يولدها التطبيق.
|
||||||
|
إذا كنت تحتاج إلى عدد أكبر من الفقرات يتيح لك مولد النص العربى زيادة عدد الفقرات كما تريد، النص لن يبدو مقسما ولا يحوي أخطاء لغوية، مولد النص العربى مفيد لمصممي المواقع على وجه الخصوص، حيث يحتاج العميل فى كثير من الأحيان أن يطلع على صورة حقيقية لتصميم الموقع.
|
||||||
|
ومن هنا وجب على المصمم أن يضع نصوصا مؤقتة على التصميم ليظهر للعميل الشكل كاملاً،دور مولد النص العربى أن يوفر على المصمم عناء البحث عن نص بديل لا علاقة له بالموضوع الذى يتحدث عنه التصميم فيظهر بشكل لا يليق.
|
||||||
|
هذا النص يمكن أن يتم تركيبه على أي تصميم دون مشكلة فلن يبدو وكأنه نص منسوخ، غير منظم، غير منسق، أو حتى غير مفهوم. لأنه مازال نصاً بديلاً ومؤقتاً.
|
||||||
|
|
||||||
|
## تجربة RTL
|
||||||
|
كلمة 1 Text كلمة 2
|
@ -36,3 +36,7 @@ Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-manageme
|
|||||||
## bilibilibi Shortcode
|
## bilibilibi Shortcode
|
||||||
|
|
||||||
{{< bilibili av498363026 >}}
|
{{< bilibili av498363026 >}}
|
||||||
|
|
||||||
|
## Gist Shortcode
|
||||||
|
|
||||||
|
{{< gist spf13 7896402 >}}
|
3
go.mod
Normal file
3
go.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module github.com/CaiJimmy/hugo-theme-stack/v3
|
||||||
|
|
||||||
|
go 1.17
|
70
i18n/ar.yaml
Normal file
70
i18n/ar.yaml
Normal 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: "تُقرأ خلال دقيقة"
|
||||||
|
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 }}"
|
73
i18n/ca.yaml
Normal file
73
i18n/ca.yaml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
toggleMenu:
|
||||||
|
other: Toggle Menu
|
||||||
|
|
||||||
|
darkMode:
|
||||||
|
other: Mode fosc
|
||||||
|
|
||||||
|
list:
|
||||||
|
page:
|
||||||
|
one: "{{ .Count }} pàgina"
|
||||||
|
other: "{{ .Count }} pàgines"
|
||||||
|
|
||||||
|
section:
|
||||||
|
other: Secció
|
||||||
|
|
||||||
|
subsection:
|
||||||
|
one: Subsecció
|
||||||
|
other: Subseccions
|
||||||
|
|
||||||
|
article:
|
||||||
|
back:
|
||||||
|
other: Tornar
|
||||||
|
|
||||||
|
tableOfContents:
|
||||||
|
other: Taula de contingut
|
||||||
|
|
||||||
|
relatedContents:
|
||||||
|
other: Continguts relacionats
|
||||||
|
|
||||||
|
lastUpdatedOn:
|
||||||
|
other: Última vegada actualitzat
|
||||||
|
|
||||||
|
readingTime:
|
||||||
|
one: "{{ .Count }} minut a llegir"
|
||||||
|
other: "{{ .Count }} minuts a llegir"
|
||||||
|
|
||||||
|
notFound:
|
||||||
|
title:
|
||||||
|
other: No Trobat
|
||||||
|
|
||||||
|
subtitle:
|
||||||
|
other: Aquesta pàgina no existeix
|
||||||
|
|
||||||
|
widget:
|
||||||
|
archives:
|
||||||
|
title:
|
||||||
|
other: Arxiu
|
||||||
|
|
||||||
|
more:
|
||||||
|
other: Més
|
||||||
|
|
||||||
|
tagCloud:
|
||||||
|
title:
|
||||||
|
other: Etiquetes
|
||||||
|
categoriesCloud:
|
||||||
|
title:
|
||||||
|
other: Categories
|
||||||
|
|
||||||
|
search:
|
||||||
|
title:
|
||||||
|
other: Cerca
|
||||||
|
|
||||||
|
placeholder:
|
||||||
|
other: Tecleja alguna cosa...
|
||||||
|
|
||||||
|
resultTitle:
|
||||||
|
other: "#PAGES_COUNT pàgines en (#TIME_SECONDS segons)"
|
||||||
|
|
||||||
|
footer:
|
||||||
|
builtWith:
|
||||||
|
other: Creat amb {{ .Generator }}
|
||||||
|
|
||||||
|
designedBy:
|
||||||
|
other: Tema {{ .Theme }} dissenyat per {{ .DesignedBy }}
|
@ -51,6 +51,9 @@ widget:
|
|||||||
tagCloud:
|
tagCloud:
|
||||||
title:
|
title:
|
||||||
other: Tags
|
other: Tags
|
||||||
|
categoriesCloud:
|
||||||
|
title:
|
||||||
|
other: Categories
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title:
|
title:
|
||||||
|
@ -51,6 +51,9 @@ widget:
|
|||||||
tagCloud:
|
tagCloud:
|
||||||
title:
|
title:
|
||||||
other: Etiquetas
|
other: Etiquetas
|
||||||
|
categoriesCloud:
|
||||||
|
title:
|
||||||
|
other: Categorías
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title:
|
title:
|
||||||
|
@ -51,6 +51,10 @@ widget:
|
|||||||
title:
|
title:
|
||||||
other: Tag
|
other: Tag
|
||||||
|
|
||||||
|
categoriesCloud:
|
||||||
|
title:
|
||||||
|
other: Kategori
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title:
|
title:
|
||||||
other: Cari
|
other: Cari
|
||||||
|
11
i18n/ja.yaml
11
i18n/ja.yaml
@ -38,6 +38,10 @@ widget:
|
|||||||
title:
|
title:
|
||||||
other: タグ
|
other: タグ
|
||||||
|
|
||||||
|
categoriesCloud:
|
||||||
|
title:
|
||||||
|
other: カテゴリ
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title:
|
title:
|
||||||
other: 検索
|
other: 検索
|
||||||
@ -47,3 +51,10 @@ search:
|
|||||||
|
|
||||||
resultTitle:
|
resultTitle:
|
||||||
other: "#PAGES_COUNT 件 (#TIME_SECONDS 秒)"
|
other: "#PAGES_COUNT 件 (#TIME_SECONDS 秒)"
|
||||||
|
|
||||||
|
footer:
|
||||||
|
builtWith:
|
||||||
|
other: Built with {{ .Generator }}
|
||||||
|
|
||||||
|
designedBy:
|
||||||
|
other: テーマ {{ .Theme }} は {{ .DesignedBy }} によって設計されています。
|
||||||
|
@ -51,6 +51,10 @@ widget:
|
|||||||
title:
|
title:
|
||||||
other: Tagi
|
other: Tagi
|
||||||
|
|
||||||
|
categoriesCloud:
|
||||||
|
title:
|
||||||
|
other: Kategorie
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title:
|
title:
|
||||||
other: Szukaj
|
other: Szukaj
|
||||||
|
@ -38,6 +38,10 @@ widget:
|
|||||||
title:
|
title:
|
||||||
other: 标签云
|
other: 标签云
|
||||||
|
|
||||||
|
categoriesCloud:
|
||||||
|
title:
|
||||||
|
other: 分类
|
||||||
|
|
||||||
search:
|
search:
|
||||||
title:
|
title:
|
||||||
other: 搜索
|
other: 搜索
|
||||||
@ -47,3 +51,10 @@ search:
|
|||||||
|
|
||||||
resultTitle:
|
resultTitle:
|
||||||
other: "#PAGES_COUNT 个结果 (用时 #TIME_SECONDS 秒)"
|
other: "#PAGES_COUNT 个结果 (用时 #TIME_SECONDS 秒)"
|
||||||
|
|
||||||
|
footer:
|
||||||
|
builtWith:
|
||||||
|
other: Built with {{ .Generator }}
|
||||||
|
|
||||||
|
designedBy:
|
||||||
|
other: 主题 {{ .Theme }} 由 {{ .DesignedBy }} 设计
|
73
i18n/zh-hk.yaml
Normal file
73
i18n/zh-hk.yaml
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
toggleMenu:
|
||||||
|
other: 切換選單
|
||||||
|
|
||||||
|
darkMode:
|
||||||
|
other: 深色模式
|
||||||
|
|
||||||
|
list:
|
||||||
|
page:
|
||||||
|
one: "第 {{ .Count }} 頁"
|
||||||
|
other: "第 {{ .Count }} 頁"
|
||||||
|
|
||||||
|
section:
|
||||||
|
other: Section
|
||||||
|
|
||||||
|
subsection:
|
||||||
|
one: Subsection
|
||||||
|
other: Subsections
|
||||||
|
|
||||||
|
article:
|
||||||
|
back:
|
||||||
|
other: 返回
|
||||||
|
|
||||||
|
tableOfContents:
|
||||||
|
other: 目錄
|
||||||
|
|
||||||
|
relatedContents:
|
||||||
|
other: 相關內容
|
||||||
|
|
||||||
|
lastUpdatedOn:
|
||||||
|
other: 上次改過於
|
||||||
|
|
||||||
|
readingTime:
|
||||||
|
one: "需要 {{ .Count }} 分鐘閱讀"
|
||||||
|
other: "需要 {{ .Count }} 分鐘閱讀"
|
||||||
|
|
||||||
|
notFound:
|
||||||
|
title:
|
||||||
|
other: Not Found
|
||||||
|
|
||||||
|
subtitle:
|
||||||
|
other: 頁面不存在
|
||||||
|
|
||||||
|
widget:
|
||||||
|
archives:
|
||||||
|
title:
|
||||||
|
other: Archives
|
||||||
|
|
||||||
|
more:
|
||||||
|
other: 更多
|
||||||
|
|
||||||
|
tagCloud:
|
||||||
|
title:
|
||||||
|
other: Tags
|
||||||
|
categoriesCloud:
|
||||||
|
title:
|
||||||
|
other: Categories
|
||||||
|
|
||||||
|
search:
|
||||||
|
title:
|
||||||
|
other: 搜尋
|
||||||
|
|
||||||
|
placeholder:
|
||||||
|
other: Type 關鍵字...
|
||||||
|
|
||||||
|
resultTitle:
|
||||||
|
other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
|
||||||
|
|
||||||
|
footer:
|
||||||
|
builtWith:
|
||||||
|
other: Built with {{ .Generator }}
|
||||||
|
|
||||||
|
designedBy:
|
||||||
|
other: 主題 {{ .Theme }} 由 {{ .DesignedBy }} 設計
|
@ -25,22 +25,17 @@
|
|||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
<figure
|
|
||||||
{{ if $galleryImage }}
|
|
||||||
class="gallery-image"
|
|
||||||
style="
|
|
||||||
flex-grow: {{ div (mul $image.Width 100) $image.Height }};
|
|
||||||
flex-basis: {{ div (mul $image.Width 240) $image.Height }}px"
|
|
||||||
{{ end }}>
|
|
||||||
<a href="{{ $Permalink }}" {{ if $galleryImage }}data-size="{{ $image.Width }}x{{ $image.Height }}"{{ end }}>
|
|
||||||
<img src="{{ $Permalink }}"
|
<img src="{{ $Permalink }}"
|
||||||
{{ with $Width }}width="{{ . }}"{{ end }}
|
{{ with $Width }}width="{{ . }}"{{ end }}
|
||||||
{{ with $Height }}height="{{ . }}"{{ end }}
|
{{ with $Height }}height="{{ . }}"{{ end }}
|
||||||
{{ with $Srcset }}srcset="{{ . }}"{{ end }}
|
{{ with $Srcset }}srcset="{{ . }}"{{ end }}
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
{{ with $alt }}alt="{{ . }}"{{ end }}>
|
|
||||||
</a>
|
|
||||||
{{ with $alt }}
|
{{ with $alt }}
|
||||||
<figcaption>{{ . | markdownify }}</figcaption>
|
alt="{{ . }}"
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</figure>
|
{{ 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 }}
|
||||||
|
>
|
@ -1,5 +1,6 @@
|
|||||||
{{ define "body-class" }}template-archives{{ end }}
|
{{ define "body-class" }}template-archives{{ end }}
|
||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
|
<header>
|
||||||
{{- $taxonomy := $.Site.GetPage "taxonomyTerm" "categories" -}}
|
{{- $taxonomy := $.Site.GetPage "taxonomyTerm" "categories" -}}
|
||||||
{{- $terms := $taxonomy.Pages -}}
|
{{- $terms := $taxonomy.Pages -}}
|
||||||
{{ if $terms }}
|
{{ if $terms }}
|
||||||
@ -12,6 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
</header>
|
||||||
|
|
||||||
{{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
|
{{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
|
||||||
{{ $notHidden := where .Site.RegularPages "Params.hidden" "!=" true }}
|
{{ $notHidden := where .Site.RegularPages "Params.hidden" "!=" true }}
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ .Site.LanguageCode }}">
|
<html lang="{{ .Site.LanguageCode }}" dir="{{ default `ltr` .Language.LanguageDirection }}">
|
||||||
<head>
|
<head>
|
||||||
{{- partial "head/head.html" . -}}
|
{{- partial "head/head.html" . -}}
|
||||||
{{- block "head" . -}}{{ end }}
|
{{- block "head" . -}}{{ end }}
|
||||||
</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 {{ block `container-class` . }}on-phone--column {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }}{{ end }}">
|
|
||||||
|
{{/* The container is wider when there's any activated widget */}}
|
||||||
|
{{- $hasWidget := false -}}
|
||||||
|
{{- range .Site.Params.widgets -}}
|
||||||
|
{{- if gt (len .) 0 -}}
|
||||||
|
{{- $hasWidget = true -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
<div class="container main-container flex on-phone--column {{ if $hasWidget }}extended{{ else }}compact{{ end }}">
|
||||||
{{- block "left-sidebar" . -}}
|
{{- block "left-sidebar" . -}}
|
||||||
{{ partial "sidebar/left.html" . }}
|
{{ partial "sidebar/left.html" . }}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
|
<header>
|
||||||
<h3 class="section-title">
|
<h3 class="section-title">
|
||||||
{{ if eq .Parent (.GetPage "/") }}
|
{{ if eq .Parent (.GetPage "/") }}
|
||||||
{{ T "list.section" }}
|
{{ T "list.section" }}
|
||||||
@ -41,6 +42,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
{{- $subsections := .Sections -}}
|
{{- $subsections := .Sections -}}
|
||||||
{{- $pages := .Pages | complement $subsections -}}
|
{{- $pages := .Pages | complement $subsections -}}
|
||||||
@ -53,6 +55,7 @@
|
|||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
{{- with $subsections -}}
|
{{- with $subsections -}}
|
||||||
|
<aside>
|
||||||
<h2 class="section-title">{{ T "list.subsection" (len $subsections) }}</h2>
|
<h2 class="section-title">{{ T "list.subsection" (len $subsections) }}</h2>
|
||||||
<div class="subsection-list">
|
<div class="subsection-list">
|
||||||
<div class="article-list--tile">
|
<div class="article-list--tile">
|
||||||
@ -61,6 +64,7 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</aside>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
{{/* List only pages that are not a subsection */}}
|
{{/* List only pages that are not a subsection */}}
|
||||||
@ -77,5 +81,5 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ define "right-sidebar" }}
|
{{ define "right-sidebar" }}
|
||||||
{{ partialCached "sidebar/right.html" . }}
|
{{ partial "sidebar/right.html" (dict "Context" . "Scope" "homepage") }}
|
||||||
{{ end }}
|
{{ end }}
|
@ -1,9 +1,15 @@
|
|||||||
{{- $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections -}}
|
{{- $pctx := . -}}
|
||||||
{{- $notHidden := where .Site.RegularPages "Params.hidden" "!=" true -}}
|
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
|
||||||
{{- $filtered := ($pages | intersect $notHidden) -}}
|
{{- $pages := slice -}}
|
||||||
|
{{- if or $.IsHome $.IsSection -}}
|
||||||
|
{{- $pages = $pctx.RegularPages -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{- $pages = $pctx.Pages -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- $pages := where $pages "Params.hidden" "!=" true -}}
|
||||||
{{- $limit := .Site.Config.Services.RSS.Limit -}}
|
{{- $limit := .Site.Config.Services.RSS.Limit -}}
|
||||||
{{- if ge $limit 1 -}}
|
{{- if ge $limit 1 -}}
|
||||||
{{- $filtered = $filtered | first $limit -}}
|
{{- $pages = $pages | first $limit -}}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
|
||||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
@ -20,7 +26,7 @@
|
|||||||
{{- with .OutputFormats.Get "RSS" -}}
|
{{- with .OutputFormats.Get "RSS" -}}
|
||||||
{{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
|
{{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
{{ range $filtered }}
|
{{ range $pages }}
|
||||||
{{- $content := safeHTML (.Summary | html) -}}
|
{{- $content := safeHTML (.Summary | html) -}}
|
||||||
{{- if .Site.Params.rssFullContent -}}
|
{{- if .Site.Params.rssFullContent -}}
|
||||||
{{- $content = safeHTML (.Content | html) -}}
|
{{- $content = safeHTML (.Content | html) -}}
|
@ -1,15 +1,26 @@
|
|||||||
{{ define "body-class" }}
|
{{ define "body-class" }}
|
||||||
{{ $TOCEnabled := default (default false .Site.Params.article.toc) .Params.toc }}
|
article-page
|
||||||
{{- .Scratch.Set "hasTOC" (and (ge (len .TableOfContents) 100) $TOCEnabled) -}}
|
{{/*
|
||||||
article-page {{ if (.Scratch.Get "hasTOC") }}has-toc{{ end }}
|
Enable the right sidebar if
|
||||||
{{ end }}
|
- Widget different from 'TOC' is enabled
|
||||||
|
- TOC is enabled and not empty
|
||||||
|
*/}}
|
||||||
|
{{- $HasWidgetNotTOC := false -}}
|
||||||
|
{{- $TOCWidgetEnabled := false -}}
|
||||||
|
{{- range .Site.Params.widgets.page -}}
|
||||||
|
{{- if ne .type "toc" -}}
|
||||||
|
{{ $HasWidgetNotTOC = true -}}
|
||||||
|
{{- else -}}
|
||||||
|
{{ $TOCWidgetEnabled = true -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
|
||||||
{{ define "container-class" }}
|
{{- $TOCManuallyDisabled := eq .Params.toc false -}}
|
||||||
{{ if (.Scratch.Get "hasTOC") }}
|
{{- $TOCEnabled := and (not $TOCManuallyDisabled) $TOCWidgetEnabled -}}
|
||||||
extended
|
{{- $hasTOC := ge (len .TableOfContents) 100 -}}
|
||||||
{{ else }}
|
{{- .Scratch.Set "TOCEnabled" (and $TOCEnabled $hasTOC) -}}
|
||||||
on-phone--column {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }}
|
|
||||||
{{ end }}
|
{{- .Scratch.Set "hasWidget" (or $HasWidgetNotTOC (and $TOCEnabled $hasTOC)) -}}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
@ -30,29 +41,6 @@
|
|||||||
{{ 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" }}
|
{{ define "right-sidebar" }}
|
||||||
{{ if (.Scratch.Get "hasTOC") }}
|
{{ if .Scratch.Get "hasWidget" }}{{ partial "sidebar/right.html" (dict "Context" . "Scope" "page") }}{{ end}}
|
||||||
<aside class="sidebar right-sidebar sticky">
|
|
||||||
<section class="widget archives">
|
|
||||||
<h2 class="widget-title section-title">{{ T "article.tableOfContents" }}</h2>
|
|
||||||
|
|
||||||
<div class="widget--toc">
|
|
||||||
{{ .TableOfContents }}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</aside>
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -15,5 +15,5 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ define "right-sidebar" }}
|
{{ define "right-sidebar" }}
|
||||||
{{ partial "sidebar/right.html" . }}
|
{{ partial "sidebar/right.html" (dict "Context" . "Scope" "homepage") }}
|
||||||
{{ end }}
|
{{ end }}
|
@ -16,8 +16,10 @@
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div class="search-result">
|
||||||
<h3 class="search-result--title section-title"></h3>
|
<h3 class="search-result--title section-title"></h3>
|
||||||
<div class="search-result--list article-list--compact"></div>
|
<div class="search-result--list article-list--compact"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.searchResultTitleTemplate = "{{ T `search.resultTitle` }}"
|
window.searchResultTitleTemplate = "{{ T `search.resultTitle` }}"
|
||||||
|
@ -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>
|
||||||
|
@ -47,4 +47,15 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</footer>
|
</footer>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{ if .IsTranslated }}
|
||||||
|
<footer class="article-translations">
|
||||||
|
{{ partial "helper/icon" "language" }}
|
||||||
|
<div>
|
||||||
|
{{ range .Translations }}
|
||||||
|
<a href="{{ .Permalink }}" class="link">{{ .Language.LanguageName }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
@ -1,6 +1,6 @@
|
|||||||
<aside class="related-contents--wrapper">
|
|
||||||
{{ $related := (where (.Site.RegularPages.Related .) "Params.hidden" "!=" true) | first 5 }}
|
{{ $related := (where (.Site.RegularPages.Related .) "Params.hidden" "!=" true) | first 5 }}
|
||||||
{{ with $related }}
|
{{ with $related }}
|
||||||
|
<aside class="related-contents--wrapper">
|
||||||
<h2 class="section-title">{{ T "article.relatedContents" }}</h2>
|
<h2 class="section-title">{{ T "article.relatedContents" }}</h2>
|
||||||
<div class="related-contents">
|
<div class="related-contents">
|
||||||
<div class="flex article-list--tile">
|
<div class="flex article-list--tile">
|
||||||
@ -9,5 +9,5 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
|
||||||
</aside>
|
</aside>
|
||||||
|
{{ end }}
|
29
layouts/partials/comments/provider/cactus.html
Normal file
29
layouts/partials/comments/provider/cactus.html
Normal 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 -}}
|
@ -9,41 +9,41 @@
|
|||||||
data-reactions-enabled="{{- default 1 .reactionsEnabled -}}"
|
data-reactions-enabled="{{- default 1 .reactionsEnabled -}}"
|
||||||
data-emit-metadata="{{- default 0 .emitMetadata -}}"
|
data-emit-metadata="{{- default 0 .emitMetadata -}}"
|
||||||
data-theme="{{- default `light` .lightTheme -}}"
|
data-theme="{{- default `light` .lightTheme -}}"
|
||||||
|
data-lang="{{- default `en` .lang -}}"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
async
|
async
|
||||||
></script>
|
></script>
|
||||||
<script>
|
<script>
|
||||||
function setGiscusTheme(theme) {
|
function setGiscusTheme(theme) {
|
||||||
let giscus = document.querySelector('iframe.giscus-frame');
|
let giscus = document.querySelector("iframe.giscus-frame");
|
||||||
if (giscus) {
|
if (giscus) {
|
||||||
giscus.contentWindow.postMessage(
|
giscus.contentWindow.postMessage(
|
||||||
{
|
{
|
||||||
giscus: {
|
giscus: {
|
||||||
setConfig: {
|
setConfig: {
|
||||||
theme: theme
|
theme: theme,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"https://giscus.app"
|
"https://giscus.app"
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
addEventListener('message', (e) => {
|
addEventListener("message", (e) => {
|
||||||
if (event.origin !== 'https://giscus.app') return;
|
if (event.origin !== "https://giscus.app") return;
|
||||||
handler()
|
handler();
|
||||||
});
|
});
|
||||||
window.addEventListener('onColorSchemeChange', handler);
|
window.addEventListener("onColorSchemeChange", handler);
|
||||||
|
|
||||||
function handler() {
|
function handler() {
|
||||||
if (document.documentElement.dataset.scheme === "light") {
|
if (document.documentElement.dataset.scheme === "light") {
|
||||||
setGiscusTheme('{{- default "light" .lightTheme -}}');
|
setGiscusTheme('{{- default "light" .lightTheme -}}');
|
||||||
} else {
|
} else {
|
||||||
setGiscusTheme('{{- default "dark_dimmed" .darkTheme -}}');
|
setGiscusTheme('{{- default "dark_dimmed" .darkTheme -}}');
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
}());
|
})();
|
||||||
</script>
|
</script>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<script src="//cdn.jsdelivr.net/npm/twikoo@1.4.3/dist/twikoo.all.min.js"></script>
|
<script src="//cdn.jsdelivr.net/npm/twikoo@1.4.18/dist/twikoo.all.min.js"></script>
|
||||||
<div id="tcomment"></div>
|
<div id="tcomment"></div>
|
||||||
<style>
|
<style>
|
||||||
.twikoo {
|
.twikoo {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div id="vssue"></div>
|
<div id="vssue"></div>
|
||||||
|
|
||||||
<script src="https://unpkg.com/vue/dist/vue.runtime.min.js"></script>
|
<script src="https://unpkg.com/vue@2/dist/vue.runtime.min.js"></script>
|
||||||
<script src="https://unpkg.com/vssue/dist/vssue.{{ .platform }}.min.js"></script>
|
<script src="https://unpkg.com/vssue/dist/vssue.{{ .platform }}.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -4,3 +4,9 @@
|
|||||||
{{- $script := resources.Get "ts/main.ts" | js.Build $opts -}}
|
{{- $script := resources.Get "ts/main.ts" | js.Build $opts -}}
|
||||||
|
|
||||||
<script type="text/javascript" src="{{ $script.RelPermalink }}" defer></script>
|
<script type="text/javascript" src="{{ $script.RelPermalink }}" defer></script>
|
||||||
|
|
||||||
|
{{- with resources.Get "ts/custom.ts" -}}
|
||||||
|
{{/* Place your custom script in HUGO_SITE_FOLDER/assets/ts/custom.ts */}}
|
||||||
|
{{- $customScript := . | js.Build $opts -}}
|
||||||
|
<script type="text/javascript" src="{{ $customScript.RelPermalink }}" defer></script>
|
||||||
|
{{- end -}}
|
@ -1,4 +1,4 @@
|
|||||||
{{- $ThemeVersion := "3.6.0" -}}
|
{{- $ThemeVersion := "3.11.0" -}}
|
||||||
<footer class="site-footer">
|
<footer class="site-footer">
|
||||||
<section class="copyright">
|
<section class="copyright">
|
||||||
©
|
©
|
||||||
|
@ -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" -}}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
<aside class="sidebar left-sidebar sticky">
|
<aside class="sidebar left-sidebar sticky {{ if .Site.Params.sidebar.compact }}compact{{ end }}">
|
||||||
<button class="hamburger hamburger--spin" type="button" id="toggle-menu" aria-label="{{ T `toggleMenu` }}">
|
<button class="hamburger hamburger--spin" type="button" id="toggle-menu" aria-label="{{ T `toggleMenu` }}">
|
||||||
<span class="hamburger-box">
|
<span class="hamburger-box">
|
||||||
<span class="hamburger-inner"></span>
|
<span class="hamburger-inner"></span>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<header class="site-info">
|
<header>
|
||||||
{{ with .Site.Params.sidebar.avatar }}
|
{{ with .Site.Params.sidebar.avatar }}
|
||||||
{{ if (default true .enabled) }}
|
{{ if (default true .enabled) }}
|
||||||
<figure class="site-avatar">
|
<figure class="site-avatar">
|
||||||
|
<a href="{{ .Site.BaseURL | relLangURL }}">
|
||||||
{{ if not .local }}
|
{{ if not .local }}
|
||||||
<img src="{{ .src }}" width="300" height="300" class="site-logo" loading="lazy" alt="Avatar">
|
<img src="{{ .src }}" width="300" height="300" class="site-logo" loading="lazy" alt="Avatar">
|
||||||
{{ else }}
|
{{ else }}
|
||||||
@ -22,7 +23,7 @@
|
|||||||
{{ errorf "Failed loading avatar from %q" . }}
|
{{ errorf "Failed loading avatar from %q" . }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
</a>
|
||||||
{{ with $.Site.Params.sidebar.emoji }}
|
{{ with $.Site.Params.sidebar.emoji }}
|
||||||
<span class="emoji">{{ . }}</span>
|
<span class="emoji">{{ . }}</span>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@ -30,8 +31,11 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
<div class="site-meta">
|
||||||
<h1 class="site-name"><a href="{{ .Site.BaseURL | relLangURL }}">{{ .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>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
{{- with .Site.Menus.social -}}
|
{{- with .Site.Menus.social -}}
|
||||||
<ol class="social-menu">
|
<ol class="social-menu">
|
||||||
@ -51,7 +55,6 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</ol>
|
</ol>
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
</header>
|
|
||||||
|
|
||||||
<ol class="menu" id="main-menu">
|
<ol class="menu" id="main-menu">
|
||||||
{{ $currentPage := . }}
|
{{ $currentPage := . }}
|
||||||
@ -72,6 +75,19 @@
|
|||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
<div class="menu-bottom-section">
|
||||||
|
{{- $currentLanguageCode := .Language.Lang -}}
|
||||||
|
{{ with .Site.Home.AllTranslations }}
|
||||||
|
<li id="i18n-switch">
|
||||||
|
{{ partial "helper/icon" "language" }}
|
||||||
|
<select name="language" onchange="window.location.href = this.selectedOptions[0].value">
|
||||||
|
{{ range . }}
|
||||||
|
<option value="{{ .Permalink }}" {{ if eq .Language.Lang $currentLanguageCode }}selected{{ end }}>{{ .Language.LanguageName }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{ if (default false .Site.Params.colorScheme.toggle) }}
|
{{ if (default false .Site.Params.colorScheme.toggle) }}
|
||||||
<li id="dark-mode-toggle">
|
<li id="dark-mode-toggle">
|
||||||
{{ partial "helper/icon" "toggle-left" }}
|
{{ partial "helper/icon" "toggle-left" }}
|
||||||
@ -79,5 +95,6 @@
|
|||||||
<span>{{ T "darkMode" }}</span>
|
<span>{{ T "darkMode" }}</span>
|
||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
</div>
|
||||||
</ol>
|
</ol>
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
{{ if .Site.Params.widgets.enabled }}
|
{{- $scope := default "homepage" .Scope -}}
|
||||||
{{ $context := . }}
|
{{- $context := .Context -}}
|
||||||
|
{{- with (index .Context.Site.Params.widgets $scope) -}}
|
||||||
<aside class="sidebar right-sidebar sticky">
|
<aside class="sidebar right-sidebar sticky">
|
||||||
{{ range $widget := .Site.Params.widgets.enabled }}
|
{{ range $widget := . }}
|
||||||
{{ partial (printf "widget/%s" $widget) $context }}
|
{{ if templates.Exists (printf "partials/widget/%s.html" .type) }}
|
||||||
|
{{ partial (printf "widget/%s" .type) (dict "Context" $context "Params" .params) }}
|
||||||
|
{{ else }}
|
||||||
|
{{ warnf "Widget %s not found" .type }}
|
||||||
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</aside>
|
</aside>
|
||||||
{{ end }}
|
{{ end }}
|
@ -1,20 +1,22 @@
|
|||||||
{{- $query := first 1 (where .Site.Pages "Layout" "==" "archives") -}}
|
{{- $query := first 1 (where .Context.Site.Pages "Layout" "==" "archives") -}}
|
||||||
|
{{- $context := .Context -}}
|
||||||
|
{{- $limit := default 5 .Params.limit -}}
|
||||||
{{- if $query -}}
|
{{- if $query -}}
|
||||||
{{- $archivesPage := index $query 0 -}}
|
{{- $archivesPage := index $query 0 -}}
|
||||||
<section class="widget archives">
|
<section class="widget archives">
|
||||||
<h2 class="widget-title section-title">{{ T "widget.archives.title" }}</h2>
|
<h2 class="widget-title section-title">{{ T "widget.archives.title" }}</h2>
|
||||||
|
|
||||||
{{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
|
{{ $pages := where $context.Site.RegularPages "Type" "in" $context.Site.Params.mainSections }}
|
||||||
{{ $notHidden := where .Site.RegularPages "Params.hidden" "!=" true }}
|
{{ $notHidden := where $context.Site.RegularPages "Params.hidden" "!=" true }}
|
||||||
{{ $filtered := ($pages | intersect $notHidden) }}
|
{{ $filtered := ($pages | intersect $notHidden) }}
|
||||||
{{ $archives := $filtered.GroupByDate "2006" }}
|
{{ $archives := $filtered.GroupByDate "2006" }}
|
||||||
|
|
||||||
<div class="widget-archive--list">
|
<div class="widget-archive--list">
|
||||||
{{ range $index, $item := first (add .Site.Params.widgets.archives.limit 1) ($archives) }}
|
{{ range $index, $item := first (add $limit 1) ($archives) }}
|
||||||
{{- $id := lower (replace $item.Key " " "-") -}}
|
{{- $id := lower (replace $item.Key " " "-") -}}
|
||||||
<div class="archives-year">
|
<div class="archives-year">
|
||||||
<a href="{{ $archivesPage.RelPermalink }}#{{ $id }}">
|
<a href="{{ $archivesPage.RelPermalink }}#{{ $id }}">
|
||||||
{{ if eq $index $.Site.Params.widgets.archives.limit }}
|
{{ if eq $index $limit }}
|
||||||
<span class="year">{{ T "widget.archives.more" }}</span>
|
<span class="year">{{ T "widget.archives.more" }}</span>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<span class="year">{{ .Key }}</span>
|
<span class="year">{{ .Key }}</span>
|
||||||
|
16
layouts/partials/widget/categories.html
Normal file
16
layouts/partials/widget/categories.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{{- $context := .Context -}}
|
||||||
|
{{- $limit := default 10 .Params.limit -}}
|
||||||
|
<section class="widget tagCloud">
|
||||||
|
<div class="widget-icon">
|
||||||
|
{{ partial "helper/icon" "categories" }}
|
||||||
|
</div>
|
||||||
|
<h2 class="widget-title section-title">{{ T "widget.categoriesCloud.title" }}</h2>
|
||||||
|
|
||||||
|
<div class="tagCloud-tags">
|
||||||
|
{{ range first $limit $context.Site.Taxonomies.categories.ByCount }}
|
||||||
|
<a href="{{ .Page.RelPermalink }}" class="font_size_{{ .Count }}">
|
||||||
|
{{ .Page.Title }}
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</section>
|
@ -1,4 +1,4 @@
|
|||||||
{{- $query := first 1 (where .Site.Pages "Layout" "==" "search") -}}
|
{{- $query := first 1 (where .Context.Site.Pages "Layout" "==" "search") -}}
|
||||||
{{- if $query -}}
|
{{- if $query -}}
|
||||||
{{- $searchPage := index $query 0 -}}
|
{{- $searchPage := index $query 0 -}}
|
||||||
<form action="{{ $searchPage.RelPermalink }}" class="search-form widget" {{ with .OutputFormats.Get "json" -}}data-json="{{ .Permalink }}" {{- end }}>
|
<form action="{{ $searchPage.RelPermalink }}" class="search-form widget" {{ with .OutputFormats.Get "json" -}}data-json="{{ .Permalink }}" {{- end }}>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{{- $context := .Context -}}
|
||||||
|
{{- $limit := default 10 .Params.limit -}}
|
||||||
<section class="widget tagCloud">
|
<section class="widget tagCloud">
|
||||||
<div class="widget-icon">
|
<div class="widget-icon">
|
||||||
{{ partial "helper/icon" "tag" }}
|
{{ partial "helper/icon" "tag" }}
|
||||||
@ -5,7 +7,7 @@
|
|||||||
<h2 class="widget-title section-title">{{ T "widget.tagCloud.title" }}</h2>
|
<h2 class="widget-title section-title">{{ T "widget.tagCloud.title" }}</h2>
|
||||||
|
|
||||||
<div class="tagCloud-tags">
|
<div class="tagCloud-tags">
|
||||||
{{ range first .Site.Params.widgets.tagCloud.limit .Site.Taxonomies.tags.ByCount }}
|
{{ range first $limit $context.Site.Taxonomies.tags.ByCount }}
|
||||||
<a href="{{ .Page.RelPermalink }}" class="font_size_{{ .Count }}">
|
<a href="{{ .Page.RelPermalink }}" class="font_size_{{ .Count }}">
|
||||||
{{ .Page.Title }}
|
{{ .Page.Title }}
|
||||||
</a>
|
</a>
|
||||||
|
12
layouts/partials/widget/toc.html
Normal file
12
layouts/partials/widget/toc.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{{ if (.Context.Scratch.Get "TOCEnabled") }}
|
||||||
|
<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">
|
||||||
|
{{ .Context.TableOfContents }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
Loading…
Reference in New Issue
Block a user