Compare commits

...

57 Commits

Author SHA1 Message Date
Jimmy Cai
4b2bfc9c2e Merge branch 'master' into canary 2024-10-19 21:38:02 +00:00
Jimmy Cai
7bef269bb3 Merge branch 'master' into canary 2024-09-09 14:00:11 +02:00
Jimmy Cai
88ebb17a9d Merge branch 'master' into canary 2024-05-02 23:36:21 +02:00
Bohdan Korablov
39af4aceff
feat(i18n): add translations for tag and category widgets for ru and uk (#997) 2024-04-02 20:46:41 +02:00
cubercsl
703964fc93
fix: rename .social-menu to .menu-social to avoid being blocked by ad blocker (#1000)
fix: rename `.social-menu` to `.menu-social` to avoid being blocked by ad blockers

 #993
2024-04-02 20:45:34 +02:00
Jimmy Cai
b62a04332d Merge branch 'master' into canary 2024-03-30 23:28:21 +01:00
Jimmy Cai
1603aecbe2 Merge branch 'master' into canary 2024-03-30 23:27:38 +01:00
Jimmy Cai
5466ffb525 Merge branch 'master' into canary 2024-03-27 17:03:10 +01:00
Jimmy Cai
5d100a7f99
feat: new archives layout (#989)
* feat(archives): add search bar to archives page

* fix: error `file is nil; wrap it in if or with` from Hugo 0.123.0

* style: rename `search.ts` to `archives.ts`

* refactor: remove `search` page layout

* doc: remove `search` from `exampleSite`

* fix: generate JSON output for archives page in exampleSite

* feat: put archives search form under categories list
2024-03-25 00:02:47 +01:00
Jimmy Cai
9df2641193 Merge branch 'master' into canary 2024-03-17 20:45:12 +01:00
Jimmy Cai
56c8560e14 Merge branch 'master' into canary 2024-03-12 14:58:08 +01:00
Jimmy Cai
ecc435ec80 chore(docs): upgrade vitepress to 1.0.0-rc.45 2024-03-10 23:57:31 +01:00
Jimmy Cai
64ecafb57f Merge branch 'master' into canary 2024-03-10 23:48:40 +01:00
Jimmy Cai
4d6b65f63c fix: PhotoSwipe not getting the real image dimensions
This is because `img.width.toString()` returns the `<img>` element dimension, not the real image dimension (stored in the `width` attribute).

closes https://github.com/CaiJimmy/hugo-theme-stack/issues/934
2024-02-05 17:43:25 +01:00
Jimmy Cai
47a57a2ad2 feat: avoid image upscaling during image processing
There's now a threshold for each scenario. Images less than x width will not be processed.
2023-10-21 18:24:52 +02:00
Jimmy Cai
b9fb60f2ba release: 4.0.0-alpha.2 2023-10-11 20:56:58 +00:00
Jimmy Cai
158a79e9fd Merge branch 'master' into canary 2023-10-11 20:56:31 +00:00
Jimmy Cai
8dc2880a9c
feat: add file types whitelist for image processing (#885)
Add `allowedTypes` and `resizableTypes` to `imageProcessing` configuration

Prior to this commit, SVG images were not processed by `render-image.html` because SVG does not have a physical dimension like JPEG. This logic was done using a conditional.

I have now realised that Hugo can be very slow when resizing `gif` images. So I created this whitelist mechanism:

- `allowedTypes`: image types with width and height attributes
- `resizableTypes`: image types that can be resized

Here's a list of media types: bmp, gif, jpeg, png, svg+xml, tiff, webp

https://gohugo.io/templates/output-formats/#media-types
2023-10-11 22:51:42 +02:00
Jimmy Cai
d941e22120 docs: add vitepress cache folder to gitignore 2023-09-18 14:29:54 +02:00
Jimmy Cai
ca29c99ad0 docs: update demo site link 2023-09-18 14:29:15 +02:00
Jimmy Cai
a4e0036ffc chore: upgrade vitepress to 1.0.0-rc.14 2023-09-18 14:27:49 +02:00
Jimmy Cai
a649ad7a4e doc: use demo.stack.jimmycai.com as demo link for now
Because Vitepress will try to load /demo as an internal link, and return 404
2023-09-11 21:30:24 +02:00
Jimmy Cai
22c7048f89 refactor: drop support for external avatar
This simplifies the code and the configuration file
2023-09-11 18:40:53 +02:00
Jimmy Cai
692804498d doc: documentation is now in the same repository as the code
Demo site will be generated in subfolder /demo
2023-09-11 18:16:29 +02:00
Jimmy Cai
9b643df3aa refactor: use hugo.yaml instead of config.yaml in exampleSite 2023-09-10 15:57:07 +02:00
Jimmy Cai
55400d7d95 refactor: simplify exampleSite configuration files and switch to YAML
Include only overridden values.
2023-09-10 15:42:43 +02:00
Jimmy Cai
5c3277573a refactor: add all available configurations to config/params.yaml
It was stored in exampleSite
2023-09-10 15:30:50 +02:00
Jimmy Cai
5e255c9bef Merge branch 'master' into canary 2023-09-03 16:18:40 +02:00
Jimmy Cai
54540a85b0 Merge branch 'master' into canary 2023-09-02 22:37:53 +02:00
Jimmy Cai
fdae6fffd5 release: 4.0.0-alpha.1 2023-08-27 18:52:33 +02:00
Jacob Zhong
4be110e540
refactor(widget): taxonomy widget (#763)
* Add link to navbar sections and refactor names

* Prevent duplicate tags and categories information

* Add style to the widget links

* refactor: add `taxonomy` widget

Replaces `categories` and `tag-cloud` widget.

* style: remove unused `font_size_{{ .Count }}` in `taxonomy` widget

---------

Co-authored-by: Jimmy Cai <hi@jimmycai.com>
2023-08-27 18:26:53 +02:00
Jimmy Cai
5063e8f0b8 Merge branch 'master' into canary 2023-08-24 21:36:55 +02:00
cubercsl
21bd04ec89
fix(codeblock): pass through the options of chroma (#715)
* fix(codeblock): pass through the options of chroma

Pass through the options of chroma highlighting processing for whom
may use some options in codeblock.

This commit fix problem with the following scenarios:
```python {linenos=true}
print("Hello World")
```

Signed-off-by: cubercsl <2014cais01@gmail.com>

* fix(codeblock): margin of no highlight codeblock

This commit fix the wrong margin value of the codeblock which
can not highlight.

The margin is now use the value of ".article-content pre"

Signed-off-by: cubercsl <2014cais01@gmail.com>

Signed-off-by: cubercsl <2014cais01@gmail.com>
2022-12-03 13:14:37 +01:00
Jimmy Cai
2035beb5a0
Merge branch 'master' into canary 2022-10-29 16:54:09 +02:00
Jimmy Cai
b0ab8e8ef0 Merge branch 'master' into canary 2022-10-29 13:51:05 +00:00
Jimmy Cai
7f94ce9847 fix(search): long text overflows under the Search icon
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/515
2022-06-13 08:36:48 +00:00
Jimmy Cai
1e5cced034 fix(menu): jitter when closing menu
It's caused by flexbox gap property, which can't animate
2022-06-12 14:53:53 +00:00
Jimmy Cai
d89def07ac feat: i18n support for codeblock copy text 2022-06-12 14:26:20 +00:00
Jimmy Cai
7a710cbb55 feat: add style to new codeblock 2022-06-12 14:23:26 +00:00
Jimmy Cai
7b2fda70a4 fix: one line codeblock style in firefox
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/564
2022-06-12 14:06:08 +00:00
Jimmy Cai
751d7d1753 refactor: remove some usages of default in template
No longer needed thanks to Hugo's configuration merge
2022-06-12 13:51:46 +00:00
Jimmy Cai
de22945155 fix: incorrect markdown heading level in example site 2022-06-12 12:40:40 +00:00
Jimmy Cai
cdf50c757c fix: extra margin in search result 2022-06-12 12:37:30 +00:00
Jimmy Cai
fcaaadda63 refactor: remove PhotoSwipe from external.yaml 2022-06-12 12:30:52 +00:00
Jimmy Cai
dea878239a chore: bump the required hugo version to 0.100.0 2022-06-12 12:24:06 +00:00
Jimmy Cai
9d4ab5afcf feat: upgrade to PhotoSwipe v5 2022-06-12 12:10:17 +00:00
Jimmy Cai
e5f3cb11d5 refactor: drop support for hidden field in front matter 2022-06-12 11:42:35 +00:00
Jimmy Cai
6e5956b905 refactor: delete Emoji support post from example site 2022-06-12 11:42:13 +00:00
Jimmy Cai
b63e6a9c62 refactor: delete color.ts 2022-06-12 11:31:41 +00:00
Jimmy Cai
de08e29b00 feat: use Hugo's code block render hook to implement code copy button
Now it can have i18n support
2022-06-12 11:31:32 +00:00
Jimmy Cai
abf0c773aa refactor: drop linear grandient background feature
remove node-vibrant from dependencies
2022-06-12 10:55:57 +00:00
Jimmy Cai
ba3dbc4163 feat: add favicon from assets folder 2022-06-12 10:50:04 +00:00
Jimmy Cai
7d838dc7fd fix: exampleSite not using correct theme 2022-06-12 10:39:27 +00:00
Jimmy Cai
2d9c27a7c6 refactor: migrate theme configuration to TOML 2022-06-12 10:27:18 +00:00
Jimmy Cai
6cff36abee chore: modify go.mod to v4 2022-06-12 10:07:00 +00:00
Jimmy Cai
bd40dda7d9 chore: prepare repository for v4.0.0-alpha 2022-06-12 09:15:09 +00:00
Jimmy Cai
b66083e728 refactor(i18n): simplify the structure of the translation file 2022-06-12 09:13:39 +00:00
116 changed files with 3442 additions and 2581 deletions

5
.gitignore vendored
View File

@ -1,4 +1,7 @@
public
resources
assets/jsconfig.json
.hugo_build.lock
.hugo_build.lock
node_modules
docs/.vitepress/dist
docs/.vitepress/cache

View File

@ -12,8 +12,7 @@ Use this template: [CaiJimmy/hugo-theme-stack-starter](https://github.com/CaiJim
## Demo
* Starter template demo: [demo.stack.jimmycai.com](https://demo.stack.jimmycai.com)
* Dev build: [dev.stack.jimmycai.com](https://dev.stack.jimmycai.com)
[stack.jimmycai.com/demo](https://stack.jimmycai.com/demo)
## Documentation

View File

@ -5,7 +5,6 @@ date: {{ .Date }}
image:
math:
license:
hidden: false
comments: true
draft: true
---

View File

@ -203,7 +203,6 @@
.article-preview {
font-size: 1.4rem;
color: var(--card-text-color-tertiary);
margin-top: 10px;
line-height: 1.5;
}
}

View File

@ -11,4 +11,4 @@ $text-color: $color;
$name-color: #a6e22e;
$literal-color: #e6db74;
@import "common.scss";
@import "common.scss";

View File

@ -316,10 +316,12 @@
line-height: 1.428571429;
word-break: break-all;
padding: var(--card-padding);
// keep Codeblocks LTR
[dir="rtl"] & {
direction: ltr;
}
code {
color: unset;
border: none;
@ -333,15 +335,11 @@
padding: var(--card-padding);
position: relative;
&:hover {
.copyCodeButton {
opacity: 1;
}
}
// keep Codeblocks LTR
[dir="rtl"] & {
direction: ltr;
}
pre {
margin: initial;
padding: 0;
@ -350,20 +348,34 @@
}
}
.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;
.codeblock {
header {
background-color: var(--card-background-selected);
padding: 5px var(--card-padding);
display: flex;
justify-content: space-between;
box-shadow: var(--shadow-l1);
span {
text-transform: uppercase;
font-weight: bold;
color: var(--card-text-color-secondary);
}
}
.codeblock-copy {
cursor: pointer;
background-color: transparent;
border: none;
padding: 8px 16px;
color: var(--card-text-color-secondary);
font-size: 14px;
font-weight: bold;
}
pre {
margin: 0;
}
}
.table-wrapper {
@ -432,7 +444,7 @@
/// Negative margins
blockquote,
figure,
.highlight,
.codeblock,
pre,
.gallery,
.video-wrapper,

View File

@ -31,6 +31,7 @@
input {
padding: 40px 20px 20px;
padding-inline-end: var(--button-size);
border-radius: var(--card-border-radius);
background-color: var(--card-background);
box-shadow: var(--shadow-l1);
@ -78,5 +79,4 @@
height: 20px;
}
}
}
}

View File

@ -11,7 +11,6 @@
flex-direction: column;
flex-shrink: 0;
align-self: stretch;
gap: var(--sidebar-element-separation);
max-width: none;
width: 100%;
position: relative;
@ -65,6 +64,11 @@
}
}
}
.menu-social,
#main-menu {
margin-top: var(--sidebar-element-separation);
}
}
.right-sidebar {

View File

@ -65,3 +65,11 @@
}
}
}
/* Color for widget titles */
.widget-title a.widget-link {
color: var(--accent-color);
&:hover {
color: var(--accent-color-darker);
}
}

165
assets/ts/archives.ts Normal file
View File

@ -0,0 +1,165 @@
interface PageData {
title: string;
content: string;
id: string;
}
class Search {
private data: PageData[];
private form: HTMLFormElement;
private input: HTMLInputElement;
private resultTitle: HTMLHeadElement;
private resultTitleTemplate: string;
constructor({ form, input, resultTitle, resultTitleTemplate }) {
this.form = form;
this.input = input;
this.resultTitle = resultTitle;
this.resultTitleTemplate = resultTitleTemplate;
this.handleQueryString();
this.bindQueryStringChange();
this.bindSearchForm();
}
private async getIndex() {
if (!this.data) {
const jsonURL = this.form.dataset.json;
this.data = await fetch(jsonURL).then(res => res.json());
}
return this.data;
}
private async searchKeywords(keywords: string[]) {
const index = await this.getIndex();
/// Return an set of ids that match the keywords
return new Set(index.filter(item => {
return keywords.every(keyword => {
return item.title.includes(keyword) || item.content.includes(keyword);
});
}).map(item => item.id));
}
private async doSearch(keywords: string[]) {
const startTime = performance.now();
const results = await this.searchKeywords(keywords);
this.clear();
/// Hide all articles except the ones that are in the results
const archiveGroups = document.querySelectorAll('.archives-group') as NodeListOf<HTMLDivElement>;
archiveGroups.forEach(group => {
const articles = Array.from(group.querySelectorAll('article'));
articles.map(article => {
article.style.display = 'none';
article.style.removeProperty('border-bottom');
});
const matchingArticles = articles.filter(article => results.has(article.id));
const hasResults = matchingArticles.length > 0;
if (!hasResults) return group.style.display = 'none';
matchingArticles.map(article => article.style.removeProperty('display'));
matchingArticles[matchingArticles.length - 1].style.borderBottom = 'none';
});
const endTime = performance.now();
this.resultTitle.innerText = this.generateResultTitle(results.size, ((endTime - startTime) / 1000).toPrecision(1));
this.resultTitle.style.display = 'block';
}
private generateResultTitle(resultLen, time) {
return this.resultTitleTemplate.replace("#PAGES_COUNT", resultLen).replace("#TIME_SECONDS", time);
}
private bindSearchForm() {
let lastSearch = '';
const eventHandler = (e) => {
e.preventDefault();
const keywords = this.input.value.trim();
Search.updateQueryString(keywords, true);
if (keywords === '') {
lastSearch = '';
return this.clear();
}
if (lastSearch === keywords) return;
lastSearch = keywords;
this.doSearch(keywords.split(' '));
}
this.input.addEventListener('input', eventHandler);
this.input.addEventListener('compositionend', eventHandler);
}
private clear() {
this.resultTitle.style.display = 'none';
document.querySelectorAll('.archives-group, .archives-group article').forEach(el => el.removeAttribute('style'));
}
private bindQueryStringChange() {
window.addEventListener('popstate', (e) => {
this.handleQueryString()
})
}
private handleQueryString() {
const pageURL = new URL(window.location.toString());
const keywords = pageURL.searchParams.get('keyword');
this.input.value = keywords;
if (keywords) {
this.doSearch(keywords.split(' '));
}
else {
this.clear()
}
}
private static updateQueryString(keywords: string, replaceState = false) {
const pageURL = new URL(window.location.toString());
if (keywords === '') {
pageURL.searchParams.delete('keyword')
}
else {
pageURL.searchParams.set('keyword', keywords);
}
if (replaceState) {
window.history.replaceState('', '', pageURL.toString());
}
else {
window.history.pushState('', '', pageURL.toString());
}
}
}
declare global {
interface Window {
searchResultTitleTemplate: string;
}
}
window.addEventListener('load', () => {
setTimeout(function () {
const searchForm = document.getElementById('search-form') as HTMLFormElement,
searchInput = searchForm.querySelector('input') as HTMLInputElement,
searchResultTitle = document.querySelector('.search-result--title') as HTMLHeadingElement;
new Search({
form: searchForm,
input: searchInput,
resultTitle: searchResultTitle,
resultTitleTemplate: window.searchResultTitleTemplate
});
}, 0);
})
export default Search;

28
assets/ts/codeblock.ts Normal file
View File

@ -0,0 +1,28 @@
/**
* Copy button for code blocks
*/
export default () => {
const copyButtons = document.querySelectorAll('.codeblock-copy');
copyButtons.forEach(button => {
const codeblockID = button.getAttribute('data-id'),
copyText = button.textContent,
copiedText = button.getAttribute('data-copied-text');
if (!codeblockID) return;
button.addEventListener('click', (e) => {
e.preventDefault();
const codeblock = document.getElementById(codeblockID) as HTMLElement;
if (!codeblockID) return;
navigator.clipboard.writeText(codeblock.textContent)
.then(() => {
button.textContent = copiedText;
setTimeout(() => {
button.textContent = copyText;
}, 1000);
})
.catch(err => {
alert(err)
console.log('Something went wrong', err);
});
}, false);
});
}

View File

@ -1,63 +0,0 @@
interface colorScheme {
hash: string, /// Regenerate color scheme when the image hash is changed
DarkMuted: {
hex: string,
rgb: Number[],
bodyTextColor: string
},
Vibrant: {
hex: string,
rgb: Number[],
bodyTextColor: string
}
}
let colorsCache: { [key: string]: colorScheme } = {};
if (localStorage.hasOwnProperty('StackColorsCache')) {
try {
colorsCache = JSON.parse(localStorage.getItem('StackColorsCache'));
}
catch (e) {
colorsCache = {};
}
}
async function getColor(key: string, hash: string, imageURL: string) {
if (!key) {
/**
* If no key is provided, do not cache the result
*/
return await Vibrant.from(imageURL).getPalette();
}
if (!colorsCache.hasOwnProperty(key) || colorsCache[key].hash !== hash) {
/**
* If key is provided, but not found in cache, or the hash mismatches => Regenerate color scheme
*/
const palette = await Vibrant.from(imageURL).getPalette();
colorsCache[key] = {
hash: hash,
Vibrant: {
hex: palette.Vibrant.hex,
rgb: palette.Vibrant.rgb,
bodyTextColor: palette.Vibrant.bodyTextColor
},
DarkMuted: {
hex: palette.DarkMuted.hex,
rgb: palette.DarkMuted.rgb,
bodyTextColor: palette.DarkMuted.bodyTextColor
}
}
/* Save the result in localStorage */
localStorage.setItem('StackColorsCache', JSON.stringify(colorsCache));
}
return colorsCache[key];
}
export {
getColor
}

View File

@ -1,186 +1,92 @@
declare global {
interface Window {
PhotoSwipe: any;
PhotoSwipeUI_Default: any
const wrap = (figures: HTMLElement[]) => {
const galleryContainer = document.createElement('div');
galleryContainer.className = 'gallery';
const parentNode = figures[0].parentNode,
first = figures[0];
parentNode.insertBefore(galleryContainer, first)
for (const figure of figures) {
galleryContainer.appendChild(figure);
}
}
interface PhotoSwipeItem {
w: number;
h: number;
src: string;
msrc: string;
title?: string;
el: HTMLElement;
}
export default (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') as NodeListOf<HTMLImageElement>;
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');
class StackGallery {
private galleryUID: number;
private items: PhotoSwipeItem[] = [];
if (!paragraph || !container.contains(paragraph)) continue;
constructor(container: HTMLElement, galleryUID = 1) {
if (window.PhotoSwipe == undefined || window.PhotoSwipeUI_Default == undefined) {
console.error("PhotoSwipe lib not loaded.");
return;
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');
}
this.galleryUID = galleryUID;
let isNewLineImage = paragraph.classList.contains('no-text');
if (!isNewLineImage) continue;
StackGallery.createGallery(container);
this.loadItems(container);
this.bindClick();
}
const hasLink = img.parentElement.tagName == 'A';
private loadItems(container: HTMLElement) {
this.items = [];
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);
const figures = container.querySelectorAll('figure.gallery-image');
/// Add figcaption if it exists
if (img.hasAttribute('alt')) {
const figcaption = document.createElement('figcaption');
figcaption.innerText = img.getAttribute('alt');
figure.appendChild(figcaption);
}
for (const el of figures) {
const figcaption = el.querySelector('figcaption'),
img = el.querySelector('img');
/// Wrap img tag with <a> tag if image was not wrapped by <a> tag
if (!hasLink) {
figure.className = 'gallery-image';
let aux: PhotoSwipeItem = {
w: parseInt(img.getAttribute('width')),
h: parseInt(img.getAttribute('height')),
src: img.src,
msrc: img.getAttribute('data-thumb') || img.src,
el: el
}
if (figcaption) {
aux.title = figcaption.innerHTML;
}
this.items.push(aux);
const a = document.createElement('a');
a.href = img.src;
a.setAttribute('target', '_blank');
a.setAttribute('data-pswp-width', img.getAttribute('width'));
a.setAttribute('data-pswp-height', img.getAttribute('height'));
img.parentNode.insertBefore(a, img);
a.appendChild(img);
}
}
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') as NodeListOf<HTMLElement>;
let currentGallery = [];
for (const figure of Array.from(figuresEl)) {
if (!currentGallery.length) {
/// First iteration
currentGallery = [figure];
}
const figuresEl = container.querySelectorAll('figure.gallery-image');
let currentGallery = [];
for (const figure of figuresEl) {
if (!currentGallery.length) {
/// First iteration
currentGallery = [figure];
}
else if (figure.previousElementSibling === currentGallery[currentGallery.length - 1]) {
/// Adjacent figures
currentGallery.push(figure);
}
else if (currentGallery.length) {
/// End gallery
StackGallery.wrap(currentGallery);
currentGallery = [figure];
}
else if (figure.previousElementSibling === currentGallery[currentGallery.length - 1]) {
/// Adjacent figures
currentGallery.push(figure);
}
if (currentGallery.length > 0) {
StackGallery.wrap(currentGallery);
else if (currentGallery.length) {
/// End gallery
wrap(currentGallery);
currentGallery = [figure];
}
}
/**
* Wrap adjacent figure tags with div.gallery
* @param figures
*/
public static wrap(figures: HTMLElement[]) {
const galleryContainer = document.createElement('div');
galleryContainer.className = 'gallery';
const parentNode = figures[0].parentNode,
first = figures[0];
parentNode.insertBefore(galleryContainer, first)
for (const figure of figures) {
galleryContainer.appendChild(figure);
}
if (currentGallery.length > 0) {
wrap(currentGallery);
}
public open(index: number) {
const pswp = document.querySelector('.pswp') as HTMLDivElement;
const ps = new window.PhotoSwipe(pswp, window.PhotoSwipeUI_Default, this.items, {
index: index,
galleryUID: this.galleryUID,
getThumbBoundsFn: (index) => {
const thumbnail = this.items[index].el.getElementsByTagName('img')[0],
pageYScroll = window.pageYOffset || document.documentElement.scrollTop,
rect = thumbnail.getBoundingClientRect();
return { x: rect.left, y: rect.top + pageYScroll, w: rect.width };
}
});
ps.init();
}
private bindClick() {
for (const [index, item] of this.items.entries()) {
const a = item.el.querySelector('a');
a.addEventListener('click', (e) => {
e.preventDefault();
this.open(index);
})
}
}
}
export default StackGallery;
};

View File

@ -5,8 +5,7 @@
* @website: https://jimmycai.com
* @link: https://github.com/CaiJimmy/hugo-theme-stack
*/
import StackGallery from "ts/gallery";
import { getColor } from 'ts/color';
import StackCodeBlock from "ts/codeblock";
import menu from 'ts/menu';
import createElement from 'ts/createElement';
import StackColorScheme from 'ts/colorScheme';
@ -22,76 +21,12 @@ let Stack = {
const articleContent = document.querySelector('.article-content') as HTMLElement;
if (articleContent) {
new StackGallery(articleContent);
setupSmoothAnchors();
setupScrollspy();
}
/**
* Add linear gradient background to tile style article
*/
const articleTile = document.querySelector('.article-list--tile');
if (articleTile) {
let observer = new IntersectionObserver(async (entries, observer) => {
entries.forEach(entry => {
if (!entry.isIntersecting) return;
observer.unobserve(entry.target);
const articles = entry.target.querySelectorAll('article.has-image');
articles.forEach(async articles => {
const image = articles.querySelector('img'),
imageURL = image.src,
key = image.getAttribute('data-key'),
hash = image.getAttribute('data-hash'),
articleDetails: HTMLDivElement = articles.querySelector('.article-details');
const colors = await getColor(key, hash, imageURL);
articleDetails.style.background = `
linear-gradient(0deg,
rgba(${colors.DarkMuted.rgb[0]}, ${colors.DarkMuted.rgb[1]}, ${colors.DarkMuted.rgb[2]}, 0.5) 0%,
rgba(${colors.Vibrant.rgb[0]}, ${colors.Vibrant.rgb[1]}, ${colors.Vibrant.rgb[2]}, 0.75) 100%)`;
})
})
});
observer.observe(articleTile)
}
/**
* Add copy button to code block
*/
const highlights = document.querySelectorAll('.article-content div.highlight');
const copyText = `Copy`,
copiedText = `Copied!`;
highlights.forEach(highlight => {
const copyButton = document.createElement('button');
copyButton.innerHTML = copyText;
copyButton.classList.add('copyCodeButton');
highlight.appendChild(copyButton);
const codeBlock = highlight.querySelector('code[data-lang]');
if (!codeBlock) return;
copyButton.addEventListener('click', () => {
navigator.clipboard.writeText(codeBlock.textContent)
.then(() => {
copyButton.textContent = copiedText;
setTimeout(() => {
copyButton.textContent = copyText;
}, 1000);
})
.catch(err => {
alert(err)
console.log('Something went wrong', err);
});
});
});
new StackColorScheme(document.getElementById('dark-mode-toggle'));
StackCodeBlock();
}
}

View File

@ -1,326 +0,0 @@
interface pageData {
title: string,
date: string,
permalink: string,
content: string,
image?: string,
preview: string,
matchCount: number
}
interface match {
start: number,
end: number
}
/**
* Escape HTML tags as HTML entities
* Edited from:
* @link https://stackoverflow.com/a/5499821
*/
const tagsToReplace = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'…': '&hellip;'
};
function replaceTag(tag) {
return tagsToReplace[tag] || tag;
}
function replaceHTMLEnt(str) {
return str.replace(/[&<>"]/g, replaceTag);
}
function escapeRegExp(string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
}
class Search {
private data: pageData[];
private form: HTMLFormElement;
private input: HTMLInputElement;
private list: HTMLDivElement;
private resultTitle: HTMLHeadElement;
private resultTitleTemplate: string;
constructor({ form, input, list, resultTitle, resultTitleTemplate }) {
this.form = form;
this.input = input;
this.list = list;
this.resultTitle = resultTitle;
this.resultTitleTemplate = resultTitleTemplate;
this.handleQueryString();
this.bindQueryStringChange();
this.bindSearchForm();
}
/**
* Processes search matches
* @param str original text
* @param matches array of matches
* @param ellipsis whether to add ellipsis to the end of each match
* @param charLimit max length of preview string
* @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) {
const titleMatches: match[] = [],
contentMatches: match[] = [];
let result = {
...item,
preview: '',
matchCount: 0
}
const contentMatchAll = item.content.matchAll(regex);
for (const match of Array.from(contentMatchAll)) {
contentMatches.push({
start: match.index,
end: match.index + match[0].length
});
}
const titleMatchAll = item.title.matchAll(regex);
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[]) {
const startTime = performance.now();
const results = await this.searchKeywords(keywords);
this.clear();
for (const item of results) {
this.list.append(Search.render(item));
}
const endTime = performance.now();
this.resultTitle.innerText = this.generateResultTitle(results.length, ((endTime - startTime) / 1000).toPrecision(1));
}
private generateResultTitle(resultLen, time) {
return this.resultTitleTemplate.replace("#PAGES_COUNT", resultLen).replace("#TIME_SECONDS", time);
}
public async getData() {
if (!this.data) {
/// Not fetched yet
const jsonURL = this.form.dataset.json;
this.data = await fetch(jsonURL).then(res => res.json());
const parser = new DOMParser();
for (const item of this.data) {
item.content = parser.parseFromString(item.content, 'text/html').body.innerText;
}
}
return this.data;
}
private bindSearchForm() {
let lastSearch = '';
const eventHandler = (e) => {
e.preventDefault();
const keywords = this.input.value.trim();
Search.updateQueryString(keywords, true);
if (keywords === '') {
lastSearch = '';
return this.clear();
}
if (lastSearch === keywords) return;
lastSearch = keywords;
this.doSearch(keywords.split(' '));
}
this.input.addEventListener('input', eventHandler);
this.input.addEventListener('compositionend', eventHandler);
}
private clear() {
this.list.innerHTML = '';
this.resultTitle.innerText = '';
}
private bindQueryStringChange() {
window.addEventListener('popstate', (e) => {
this.handleQueryString()
})
}
private handleQueryString() {
const pageURL = new URL(window.location.toString());
const keywords = pageURL.searchParams.get('keyword');
this.input.value = keywords;
if (keywords) {
this.doSearch(keywords.split(' '));
}
else {
this.clear()
}
}
private static updateQueryString(keywords: string, replaceState = false) {
const pageURL = new URL(window.location.toString());
if (keywords === '') {
pageURL.searchParams.delete('keyword')
}
else {
pageURL.searchParams.set('keyword', keywords);
}
if (replaceState) {
window.history.replaceState('', '', pageURL.toString());
}
else {
window.history.pushState('', '', pageURL.toString());
}
}
public static render(item: pageData) {
return <article>
<a href={item.permalink}>
<div class="article-details">
<h2 class="article-title" dangerouslySetInnerHTML={{ __html: item.title }}></h2>
<section class="article-preview" dangerouslySetInnerHTML={{ __html: item.preview }}></section>
</div>
{item.image &&
<div class="article-image">
<img src={item.image} loading="lazy" />
</div>
}
</a>
</article>;
}
}
declare global {
interface Window {
searchResultTitleTemplate: string;
}
}
window.addEventListener('load', () => {
setTimeout(function () {
const searchForm = document.querySelector('.search-form') as HTMLFormElement,
searchInput = searchForm.querySelector('input') as HTMLInputElement,
searchResultList = document.querySelector('.search-result--list') as HTMLDivElement,
searchResultTitle = document.querySelector('.search-result--title') as HTMLHeadingElement;
new Search({
form: searchForm,
input: searchInput,
list: searchResultList,
resultTitle: searchResultTitle,
resultTitleTemplate: window.searchResultTitleTemplate
});
}, 0);
})
export default Search;

View File

@ -1,149 +0,0 @@
module:
hugoVersion:
extended: true
min: "0.87.0"
params:
mainSections:
- post
featuredImageField: image
rssFullContent: true
favicon:
footer:
since:
customText:
dateFormat:
published: Jan 02, 2006
lastUpdated: Jan 02, 2006 15:04 MST
sidebar:
compact: false
emoji:
subtitle:
avatar:
enabled: true
local: true
src: img/avatar.png
article:
headingAnchor: false
math: false
toc: true
readingTime: true
license:
enabled: false
default: Licensed under CC BY-NC-SA 4.0
comments:
enabled: false
provider: disqus
disqusjs:
shortname:
apiUrl:
apiKey:
admin:
adminLabel:
utterances:
repo:
issueTerm: pathname
label:
beaudar:
repo:
issueTerm: pathname
label:
theme:
remark42:
host:
site:
locale:
vssue:
platform:
owner:
repo:
clientId:
clientSecret:
autoCreateIssue: false
# Waline client configuration see: https://waline.js.org/en/reference/client/props.html
waline:
serverURL:
lang:
visitor:
avatar:
emoji:
- https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo
requiredMeta:
- nick
- mail
locale:
admin: Admin
placeholder:
twikoo:
envId:
region:
path:
lang:
giscus:
repo:
repoID:
category:
categoryID:
mapping:
strict:
lightTheme:
darkTheme:
reactionsEnabled: 1
emitMetadata: 0
inputPosition:
lang:
gitalk:
owner:
admin:
repo:
clientID:
clientSecret:
cusdis:
host:
id:
widgets:
homepage: []
page: []
opengraph:
twitter:
# Your Twitter username
site:
# Available values: summary, summary_large_image
card: summary_large_image
defaultImage:
opengraph:
enabled: false
local: false
src:
colorScheme:
# Display toggle
toggle: true
# Available values: auto, light, dark
default: auto
imageProcessing:
cover:
enabled: true
content:
enabled: true

View File

@ -0,0 +1,3 @@
hugoVersion:
extended: true
min: 0.100.0

180
config/_default/params.yaml Normal file
View File

@ -0,0 +1,180 @@
# Theme's default configuration, can be overridden by user configuration
# Pages in the following sections will appear on the homepage and in RSS
mainSections:
- post
# Front matter field for cover image
featuredImageField: image
# Output full content to RSS feed
rssFullContent: true
# Path to favicon, relative to assets folder
favicon:
footer:
# Year when site was first published
since:
# Custom text in footer, supports HTML
customText:
dateFormat:
# Date format for published date
published: "Jan 02, 2006"
# Date format for last updated date
lastUpdated: "Jan 02, 2006 15:04 MST"
sidebar:
emoji:
subtitle:
compact: false
avatar: img/avatar.png
article:
headingAnchor: false
# Enable LaTeX math syntax for all pages by default
math: false
readingTime: true
license:
enabled: false
default: Licensed under CC BY-NC-SA 4.0
comments:
enabled: false
# Available providers: cactus, cusdis, disqus, disqusjs, giscus, gitalk, remark42, twikoo, utterances, vssue, waline
provider: disqus
# Note: for Disqus, set disqusShortname at top level
disqusjs:
shortname:
apiUrl:
apiKey:
admin:
adminLabel:
utterances:
repo:
issueTerm: pathname
label:
beaudar:
repo:
issueTerm: pathname
label:
theme:
remark42:
host:
site:
locale:
vssue:
platform:
owner:
repo:
clientId:
clientSecret:
autoCreateIssue: false
# Waline client configuration see: https://waline.js.org/en/reference/client.html
waline:
serverURL:
lang:
pageview:
emoji:
- https://unpkg.com/@waline/emojis@1.0.1/weibo
requiredMeta:
- name
- email
- url
locale:
admin: Admin
placeholder:
twikoo:
envId:
region:
path:
lang:
# See https://cactus.chat/docs/reference/web-client/#configuration for description of the various options
cactus:
defaultHomeserverUrl: "https://matrix.cactus.chat:8448"
serverName: "cactus.chat"
siteName: "" # You must insert a unique identifier here matching the one you registered (See https://cactus.chat/docs/getting-started/quick-start/#register-your-site)
giscus:
repo:
repoID:
category:
categoryID:
mapping:
lightTheme:
darkTheme:
reactionsEnabled: 1
emitMetadata: 0
gitalk:
owner:
admin:
repo:
clientID:
clientSecret:
cusdis:
host:
id:
widgets:
homepage: []
page: []
opengraph:
twitter:
# Your Twitter username
site:
# Available values: summary, summary_large_image
card: summary_large_image
defaultImage:
opengraph:
enabled: false
local: false
src:
colorScheme:
# Display toggle
toggle: true
# Available values: auto, light, dark
default: auto
imageProcessing:
allowedTypes:
- jpeg
- png
- gif
- webp
resizableTypes:
- jpeg
- png
- webp
cover:
enabled: true
small:
width: 800
threshold: 1000 # Only process images above this width
big:
width: 1600
threshold: 2000
content:
enabled: true
small:
width: 480
threshold: 600
big:
width: 1024
threshold: 1200

View File

@ -1,25 +1,3 @@
Vibrant:
- src: https://cdn.jsdelivr.net/npm/node-vibrant@3.1.6/dist/vibrant.min.js
integrity: sha256-awcR2jno4kI5X0zL8ex0vi2z+KMkF24hUW8WePSA9HM=
type: script
PhotoSwipe:
- src: https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js
integrity: sha256-ePwmChbbvXbsO02lbM3HoHbSHTHFAeChekF1xKJdleo=
type: script
defer: true
- src: https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js
integrity: sha256-UKkzOn/w1mBxRmLLGrSeyB4e1xbrp4xylgAWb3M42pU=
type: script
defer: true
- src: https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css
type: style
- src: https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css
type: style
KaTeX:
- src: https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css
integrity: sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV

View File

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

160
docs/.vitepress/config.ts Normal file
View File

@ -0,0 +1,160 @@
import { defineConfig } from 'vitepress'
export default defineConfig({
lang: 'en-US',
title: 'Stack',
description: 'Card-style Hugo theme designed for bloggers',
lastUpdated: true,
outDir: '../public',
head: [
['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }],
['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }],
['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }],
['link', { rel: 'manifest', href: '/site.webmanifest' }],
['link', { rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#5bbad5' }],
['meta', { name: 'msapplication-TileColor', content: '#00aba9' }],
['meta', { name: 'theme-color', content: '#ffffff' }],
['script', { defer: "true", "data-domain": "stack.jimmycai.com", src: 'https://stat.jimmycai.com/js/include.js' }],
],
markdown: {
lineNumbers: true,
theme: 'one-dark-pro'
},
cleanUrls: true,
themeConfig: {
logo: '/logo.png',
footer: {
message: "Documentation released under the MIT License, logo designed by Jimmy Cai, all rights reserved.",
copyright: "Copyright © 2020 - Present Jimmy Cai",
},
sidebar: {
'/guide/': sidebarGuide(),
'/config/': sidebarGuide(),
'/writing/': sidebarGuide(),
},
nav: [
{ text: 'Guide', link: '/guide/' },
{ text: 'Config', link: '/config/' },
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/CaiJimmy/hugo-theme-stack' }
],
editLink: {
pattern: 'https://github.com/CaiJimmy/hugo-theme-stack/edit/master/docs/:path',
text: 'Edit this page on GitHub'
},
outline: [2, 3],
carbonAds: {
code: 'CEAIE27W',
placement: 'stackjimmycaicom'
},
algolia: {
appId: '6OC1XCG4R5',
apiKey: '7779946cc768ec3699123e60a91d0ddc',
indexName: 'stack-jimmycai',
}
},
});
function sidebarGuide() {
return [
{
text: 'Introduction',
collapsible: true,
items: [
{ text: 'About Stack', link: '/guide/' },
{ text: 'Getting Started', link: '/guide/getting-started' },
{ text: 'Modify Theme', link: '/guide/modify-theme' }
]
},
{
text: 'Writing',
collapsible: true,
items: [
{ text: 'Markdown', link: '/writing/markdown' },
{
text: 'Frontmatter Configs', link: '/writing/frontmatter'
},
{ text: 'Shortcodes', link: '/writing/shortcodes' },
]
},
{
text: 'Config',
collapsible: true,
items: [
{
text: 'Introduction',
link: '/config/'
},
{
text: 'Site Configs',
link: '/config/site'
},
{
text: 'i18n Configs',
link: '/config/i18n'
},
{
text: 'Custom Menu',
link: '/config/menu'
},
{
text: 'Custom Header / Footer',
link: '/config/header-footer'
},
{
text: 'Date Format',
link: '/config/date-format'
},
{
text: 'Sidebar',
link: '/config/sidebar'
},
{
text: 'Footer',
link: '/config/footer'
},
{
text: 'Article',
link: '/config/article'
},
{
text: 'Comments',
link: '/config/comments'
},
{
text: 'Widgets',
link: '/config/widgets'
},
{
text: 'Open Graph',
link: '/config/open-graph'
},
{
text: 'Default Image',
link: '/config/default-image'
},
{
text: 'Color Scheme',
link: '/config/color-scheme'
},
{
text: 'Image Processing',
link: '/config/image-processing'
}
]
},
]
}

View File

@ -0,0 +1,6 @@
:root {
--vp-c-brand-1: var(--vp-c-green-1);
--vp-c-brand-2: var(--vp-c-green-2);
--vp-c-brand-3: var(--vp-c-green-3);
--vp-c-brand-soft: var(--vp-c-green-soft);
}

View File

@ -0,0 +1,15 @@
import DefaultTheme from 'vitepress/theme'
import './custom.css'
export default {
...DefaultTheme,
enhanceApp({ router }) {
const oldOnAfterRouteChanged = router.onAfterRouteChanged;
router.onAfterRouteChanged = () => {
oldOnAfterRouteChanged && oldOnAfterRouteChanged();
if (typeof _carbonads !== 'undefined')
_carbonads.refresh();
}
}
}

49
docs/config/article.md Normal file
View File

@ -0,0 +1,49 @@
# Article
Configuration for the article page.
Fields under `[Params.Article]`.
## math
- Type: `bool`
Enable math support by [KaTeX](https://katex.org/). Can be overridden by front matter field `toc`.
## toc
- Type: `bool`
Enable by default table of contents. Can be overridden by front matter field `toc`.
::: warning
You will still need to add [`toc` widget](widgets.md#toc) to the sidebar to display the table of contents.
:::
## readingTime
- Type: `bool`
- Default: `true`
Display an estimated reading time for the article.
## license
- Type: `map[string]:(bool|string)`
Configurations related with license.
### license.enabled
- Type: `bool`
- Default: `false`
Display license information under the article.
### license.default
- Type: `string`
- Default: `Licensed under CC BY-NC-SA 4.0`
Default license text displayed under the article. Can be overridden by front matter field `license`.

View File

@ -0,0 +1,22 @@
# Color Scheme
Light and dark color schemes are available in this theme.
## toggle
- Type: `bool`
- Default: `true`
Display the color scheme toggle button.
If it's set to `false`, the color scheme will be determined by the `default` option.
## default
- Type: `string (light|dark|auto)`
- Default: `auto`
The default color scheme, used when the `toggle` option is set to `false` or when the user visits the site for the first time.
When set to `auto`, the color scheme will be determined by the user's system preference (`prefers-color-scheme` media query).

56
docs/config/comments.md Normal file
View File

@ -0,0 +1,56 @@
# Comments
Comment system is a very important part of a blog. It allows readers to express their opinions and thoughts about the post. It also allows the author to interact with the readers.
Stack currently supports the following comment systems:
- [Cactus](https://cactus.chat/)
- [Cusdis](https://cusdis.com/)
- [Disqus](https://disqus.com/)
- [DisqusJS](https://github.com/SukkaW/DisqusJS)
- [Giscus](https://giscus.app/)
- [Gitalk](https://github.com/gitalk/gitalk)
- [Remark42](https://remark42.com/)
- [Twikoo](https://twikoo.js.org/)
- [utterances](https://utteranc.es/)
- [Vssue](https://vssue.js.org/)
- [Waline](https://waline.js.org/)
Each comment system has its own configuration options placed under `[Params.Comments.COMMENT_SYSTEM]` section.
For example, utterances's configuration options are placed under `[Params.Comments.utterances]` section.
::: tip
A full list of supported configuration options can be found in [here](https://github.com/CaiJimmy/hugo-theme-stack/blob/master/config.yaml#L38)
For more information about the meaning of each configuration option, please refer to the documentation of the comment system.
:::
::: warning
In case of Disqus, the only configuration option is `disqusShortname`, which is not available at `[Params.Comments.disqus]` section. Instead, it is placed at root section of configuration file.
:::
## enabled
- Type: `bool`
- Default: `false`
Enable / disable comment system.
## provider
- Type: `string`
Comment system provider. Possible values are:
- `cactus`
- `cusdis`
- `disqus`
- `disqusjs`
- `giscus`
- `gitalk`
- `remark42`
- `twikoo`
- `utterances`
- `vssue`
- `waline`

View File

@ -0,0 +1,19 @@
# Date format
Fields under `[Params.DateFormat]`.
Date format setting. Notice that Go's date format is slightly different than other programming language, take a look at official documentation: [dateFormat](https://gohugo.io/functions/dateformat/)
## published
- Type: `string`
- Default: `Jan 02, 2006`
Page's publish date format.
## lastUpdated
- Type: `string`
- Default: `Jan 02, 2006 15:04 MST`
Page's last updated date format

View File

@ -0,0 +1,29 @@
# Default Image
The default image is the image that will be used on a page if no featured image is set. This is useful for Open Graph and Twitter cards.
## opengraph
The default image for Open Graph and Twitter.
### opengraph.enabled
- Type: `bool`
- Default: `false`
Enable the default image for Open Graph and Twitter.
### opengraph.src
- Type: `string`
Path to the image file.
### opengraph.local
- Type: `bool`
- Default: `false`
If `true`, the image is a local file, and must be placed under `assets` folder. Otherwise, it is a remote URL.
For example, if `src` is set to `img/default.jpg`, the image file must be placed under `assets/img/default.jpg`.

17
docs/config/footer.md Normal file
View File

@ -0,0 +1,17 @@
# Footer
The footer is the last section of the page. It is usually used to display the copyright information.
Fields under `[Params.Footer]`.
## since
- Type: `int`
The year when the site is created.
## customText
- Type: `string`
Custom text displayed in the footer. HTML is supported.

View File

@ -0,0 +1,38 @@
# Custom Header / Footer
There are two empty files reserved for custom HTML in the theme, useful for adding custom scripts or stylesheets:
* `layouts/partials/head/custom.html`
* `layouts/partials/footer/custom.html`
To overwrite them:
1. Create `layout/partials/footer/custom.html` under your Hugo site folder
2. Insert custom code in that file.
## Example: Custom font family for article content
By default, this theme uses [Lato](https://fonts.google.com/specimen/Lato) for article content. This example shows how to use another font instead. For example, let's change article font family to [Merriweather](https://fonts.google.com/specimen/Merriweather).
Create `layouts/partials/head/custom.html` under your Hugo site folder, with following code:
```html
<style>
/// Overwrite CSS variable
:root {
--article-font-family: "Merriweather", var(--base-font-family);
}
</style>
<script>
(function () {
const customFont = document.createElement('link');
customFont.href = "https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap";
customFont.type = "text/css";
customFont.rel = "stylesheet";
document.head.appendChild(customFont);
}());
</script>
```

38
docs/config/i18n.md Normal file
View File

@ -0,0 +1,38 @@
# i18n
Hugo has built-in support for multilingual sites. You can find more information about it in the [official documentation](https://gohugo.io/content-management/multilingual/).
Translation files are placed in the `i18n` directory. The file name is the language code. For example, the `en.yaml` file is the translation file for English.
In order to use a language, set `DefaultContentLanguage` to the language code in the configuration file. For example, if you want to use English, set `DefaultContentLanguage` to `en`.
Currently, the theme supports the following languages:
* `ar`: Arabic
* `bn`: Bengali
* `ca`: Catalan
* `de`: German
* `el`: Greek
* `en`: English
* `es`: Spanish
* `fa`: Persian
* `fr`: French
* `hu`: Hungarian
* `id`: Indonesian
* `it`: Italian
* `ja`: Japanese
* `ko`: Korean
* `nl`: Dutch
* `pl`: Polish
* `pt-br`: Portuguese
* `ru`: Russian
* `th`: Thai
* `tr`: Turkish
* `uk`: Ukrainian
* `zh-cn`: Chinese (Simplified)
* `zh-hk`: Chinese (Traditional) (Hong Kong)
* `zh-tw`: Chinese (Traditional) (Taiwan)
::: tip
PRs for more language support are welcome 😉.
:::

View File

@ -0,0 +1,27 @@
# Image Processing
This theme uses Hugo's built-in image processing features to resize and optimize local images (included using page bundle feature). This is done automatically when you build your site.
When there are many images in your site, this can slow down the build process. You can choose to disable this feature here.
## cover
- Type: `map[string]bool`
### cover.enabled
- Type: `bool`
- Default: `true`
Enable image processing for cover (featured) images.
## content
- Type: `map[string]bool`
### content.enabled
- Type: `bool`
- Default: `true`
Enable image processing for images in content.

13
docs/config/index.md Normal file
View File

@ -0,0 +1,13 @@
# Introduction
Hugo acepts `TOML`, `YAML` and `JSON` as configuration formats. This theme currently uses `YAML` and `TOML` as configuration formats.
::: info
In the foreseeable future, this theme will migrate all its configurations to `TOML` format.
:::
If you are using `hugo-theme-stack-starter` template, you can find the configuration file under `config/_default/` folder, separated in different major sections to make it easier to find the configuration you want to modify.
A full list of available configurations can be found in `config.yaml` file located in the root directory of this theme. (If it is not there, please send an issue to let me know.)
There are plenty tools that converts `YAML` to `TOML` and vice versa. You can use them to convert the configuration file to the format you prefer.

67
docs/config/menu.md Normal file
View File

@ -0,0 +1,67 @@
# Custom Menu
There are two menus in the theme: the main menu (`menu.main`) and the social menu (`menu.social`, icon only). They can be configured in a similar way.
## First Method (Recommended)
If the menu item you'd like to add is a page, add `menu` field to its Front Matter:
```yaml
menu:
main:
name: title (optional)
weight: -90
params:
icon: icon-name
```
## Second Method
::: warning
This method is not recommended, because the theme can not detect if the current page is in the menu, and the menu item will not be highlighted.
:::
If the menu item you'd like to add is not a page, you can add it to the menu section in the config file:
Example in TOML:
```toml
[menu]
[[menu.main]]
name = "Home"
url = "/"
weight = 10
identifier = "home"
[menu.main.params]
icon = "home"
newTab = true
```
Or in YAML:
```yaml
menu:
main:
- identifier: home
name: Home
url: /
weight: -100
params:
icon: home
newTab: true
```
* `identifier`: Item ID
* `name`: Display text
* `url`: Link
* `weight`: Priority of the item, lower value means higher priority.
* `params`:
* `icon`: Specify which SVG icon should be used
* `newTab`: Open this link in new tab
If `params.icon` is set to `archive`, theme will look for `archive.svg` under `assets/icons` folder.
## Add custom icon
This theme comes with some SVG icons from [Tabler Icons](https://tablericons.com). You can find them under theme folder `assets/icons`.
To include more icons, just download them from website above, and place them under `assets/icons` folder of your Hugo site.

26
docs/config/open-graph.md Normal file
View File

@ -0,0 +1,26 @@
# Open Graph
The Open Graph protocol enables any web page to become a rich object in a social graph.
For more information, see [Open Graph protocol](http://ogp.me/).
Fields are under `[Params.opengraph]` section.
## twitter
- type: `map[string]string`
Available fields:
### site
- type: `string`
The Twitter account name of the site (without `@`).
### card
- type: `string`
[Twitter card type](https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards). Available values: `summary`, `summary_large_image`.

51
docs/config/sidebar.md Normal file
View File

@ -0,0 +1,51 @@
# Sidebar
Settings related with left-side sidebar.
Fields are under `[Params.Sidebar]` section.
## compact
- Type: `bool`
- Default: `false`
Enable compact version of sidebar.
## emoji
- Type: `string`
Emoji displayed above the avatar.
## subtitle
- Type: `string`
Subtitle displayed below the site title.
## avatar
- Type: `map[string]:(bool|string)`
Configurations related with avatar.
### avatar.enable
- Type: `bool`
- Default: `true`
Enable avatar.
### avatar.src
- Type: `string`
- Default: `img/avatar.png`
Path to avatar image.
### avatar.local
- Type: `bool`
- Default: `true`
If `true`, the avatar image should be placed at `assets/${avatar.src}`, this allows theme to automatically resize the image.

40
docs/config/site.md Normal file
View File

@ -0,0 +1,40 @@
# Site-wide settings
Fields under `[Params]`:
## description
- Type: `string`
Site description. By default, it falls back to `.Params.Sidebar.Subtitle`.
## mainSections
- Type: `[string]`
- Default: `["post"]`
Pages places under this/those sections will be shown on homepage and archive page.
For more information, take a look at Hugo's documentation on [Content Sections](https://gohugo.io/content-management/sections/).
## featuredImageField
- Type: `string`
- Default: `image`
Front Matter **field** used to get the featured image of a page.
## rssFullContent
- Type: `bool`
- Default: `true`
Output page's full content in RSS.
## favicon
- Type: `string`
Site favicon path.
For example, if you want to use the favicon in `static/favicon.ico`, set `favicon` to `/favicon.ico`.

46
docs/config/widgets.md Normal file
View File

@ -0,0 +1,46 @@
# Widgets
Widgets are placed at right sidebar of the blog. They are used to display some information such as categories, tags, etc.
You can configure which widgets to display and their order in the homepage and post page.
`widget.homepage` and `widget.page` are arrays of maps. Each map contains two keys: `type` and `params`. `type` is the name of the widget. `params` is the configuration of the widget.
## Available widgets
### archives
Display a list of years with the number of posts published in each year.
You need to create a page with `layout: archives` previously.
#### Paramters
- `limit`: Number of years to display. Default: `10`.
### search
Display a search box.
You need to create a page with `layout: search` previously.
### categories
Display a list of categories available in the blog.
#### Parameters
- `limit`: number of categories to display. Default: 10
### toc
Display a table of contents of the page.
### tag-cloud
Display a tag cloud.
#### Parameters
- `limit`: number of tags to display. Default: 10

View File

@ -0,0 +1 @@
## Configuration

View File

@ -0,0 +1,92 @@
# Getting Started
::: tip
Try this quickstart template to get started with Stack and Hugo in a few minutes:
https://github.com/CaiJimmy/hugo-theme-stack-starter
:::
## Requirements
Before you start, make sure you have installed Hugo **extended version**. For more information, see [Hugo's documentation](https://gohugo.io/getting-started/installing/).
This theme uses SCSS and TypeScript, that's why Hugo extended version is required. If you are using a non-extended Hugo installation, you will get the following error:
```
Error: Error building site: TOCSS: failed to transform "scss/style.scss" (text/x-scss): this feature is not available in your current Hugo version
```
Once you have installed Hugo, you can check the version by running the following command:
```bash
hugo version
```
Which should output something like this (the version number may be different), notice the `extended` keyword:
```
hugo v0.102.3-b76146b129d7caa52417f8e914fc5b9271bf56fc+extended windows/amd64 BuildDate=2022-09-01T10:16:19Z VendorInfo=gohugoio
```
The minimum required Hugo version can be seen in the [theme's `theme.toml` file](https://github.com/CaiJimmy/hugo-theme-stack/blob/master/theme.toml#L23)
## Installation
### Git
On the master branch, you can find the theme's latest source code. To use the latest version, you can clone the repository to `themes/hugo-theme-stack` by running the following command in the root directory of your Hugo site:
```bash
git clone https://github.com/CaiJimmy/hugo-theme-stack/ themes/hugo-theme-stack
```
If you are already using Git for your site, you can add the theme as a submodule by running the following command in the root directory of your Hugo site:
```bash
git submodule add https://github.com/CaiJimmy/hugo-theme-stack/ themes/hugo-theme-stack
```
### Hugo module
::: warning
Using this method, there won't be any file under `themes` directory. In order to modify the theme, you will have to copy the file you want to modify to the same directory under `layouts` directory.
For example, in order to modify the `themes/hugo-theme-stack/layouts/partials/header.html` file, you will have to copy it to `layouts/partials/header.html` and modify it there (copy the code from theme's repository). The same applies to `assets` and `static` directories.
:::
This theme is also available as a [Hugo module](https://gohugo.io/hugo-modules/). Run the following command in the root directory of your Hugo site:
First turn your site into a Hugo module (in case you haven't done it yet):
```sh
hugo mod init github.com/me/my-new-blog
```
Then import the theme as a dependency adding the following line to the `module` section of your site's configuration file.
```toml
# config.toml
[[module.imports]]
path = "github.com/CaiJimmy/hugo-theme-stack/v3"
```
```yaml
# config.yaml
module:
imports:
- path: github.com/CaiJimmy/hugo-theme-stack/v3
```
This makes Hugo use the latest stable `v3` version of the theme (available in release page, which probably won't coincide with the latest commit in the `master` branch).
To update the theme to the latest version, run the following command:
```sh
hugo mod get -u github.com/CaiJimmy/hugo-theme-stack/v3
hugo mod tidy
```
::: info
In the future, if a new major version of the theme is released, you will need to manually update the version number in the `path` field.
:::
### Download manually (not recommended)
You can also download the theme from the [release page](https://github.com/CaiJimmy/hugo-theme-stack/releases) and extract it to `themes/hugo-theme-stack` directory.

54
docs/guide/index.md Normal file
View File

@ -0,0 +1,54 @@
# Welcome
Stack is a simple card-style Hugo theme designed for Bloggers. Here are some of the features:
* Responsive images support
* Lazy load images
* Dark mode
* Local search
* [PhotoSwipe](https://photoswipe.com/) integration
* Archive page template
* Full native JavaScript, no jQuery or any other frameworks are used
* No CSS framework, keep it simple and minimal
* Properly cropped thumbnails
* Subsection support
* Table of contents
## Copyright
**Licensed under the GNU General Public License v3.0**
Please do not remove the "*Theme Stack designed by Jimmy*" text and link.
If you want to port this theme to another blogging platform, please let me know🙏.
## Sponsoring
If you like this theme, give it a star, and consider supporting its development:
<iframe src="https://github.com/sponsors/CaiJimmy/button" title="Sponsor CaiJimmy" height="35" width="116" style="border: 0;"></iframe>
<a href='https://ko-fi.com/C0C530AXX' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://cdn.ko-fi.com/cdn/kofi2.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
Your support is greatly appreciated :)
## Thanks to
| Project | Licence |
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| [PhotoSwipe](https://photoswipe.com/) | [MIT](https://github.com/dimsemenov/PhotoSwipe/blob/master/LICENSE) |
| [Normalize.css](https://github.com/necolas/normalize.css) | [MIT](https://github.com/necolas/normalize.css/blob/master/LICENSE.md) |
| [Node Vibrant](https://github.com/Vibrant-Colors/node-vibrant) | [MIT](https://github.com/Vibrant-Colors/node-vibrant/blob/master/LICENSE.md) |
| [Tabler icons](https://github.com/tabler/tabler-icons) | [MIT](https://github.com/tabler/tabler-icons/blob/master/LICENSE) |
| [jonsuh/hamburgers](https://github.com/jonsuh/hamburgers) | [MIT](https://github.com/jonsuh/hamburgers/blob/master/LICENSE) |
| [lepture/yue.css](https://github.com/lepture/yue.css) | MIT |
| [Typlog](https://typlog.com/) | The author gave me the permission |
| [xieranmaya/blog#6](https://github.com/xieranmaya/blog/issues/6) | - |
### References
Some references that I took while building this theme:
| Project | Licence |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| [artchen/hexo-theme-element](https://github.com/artchen/hexo-theme-element) | [MIT](https://github.com/artchen/hexo-theme-element/blob/master/LICENSE) |
| [MunifTanjim/minimo](https://github.com/MunifTanjim/minimo) | [MIT](https://github.com/MunifTanjim/minimo/blob/master/LICENSE) |

View File

@ -0,0 +1,22 @@
# Modify theme
Depending on how you installed the theme, it might be harder or easier to modify it.
## Hugo module
Using this method, there won't be any file under `themes` directory. In order to modify the theme, you will have to copy the file you want to modify to the same directory under `layouts` directory.
For example, in order to modify the `themes/hugo-theme-stack/layouts/partials/head/custom.html` file, you will have to copy it to `layouts/partials/head/custom.html` and modify it there (copy the code from theme's repository).
The same applies to `assets` and `static` directories.
## Git submodule
::: tip
The method described above for Hugo module works here too. In fact it's the recommended way for small changes.
:::
If you installed the theme through Git / Git submodule, you can modify the theme file directly and see the changes in your local site.
However, **you can not commit and push the changes directly** since you don't have the permission to push to the theme repository.
You need to **fork** the theme repository and push your changes to your forked repository (change submodule's repository url). Then, you can commit those changes to your site repository.

38
docs/index.md Normal file
View File

@ -0,0 +1,38 @@
---
layout: home
title: Stack
titleTemplate: Card-style Hugo theme designed for bloggers
hero:
name: Stack
text: Card-style theme designed for bloggers
image:
src: /logo.png
alt: Stack
actions:
- theme: brand
text: Get Started
link: /guide/getting-started
- theme: alt
text: View Demo
link: /demo/
target: _blank
- theme: alt
text: View on GitHub
link: https://github.com/CaiJimmy/hugo-theme-stack
features:
- title: No CSS and JavaScript framework
details: Keep your site lightweight and fast. All the styles are written in SCSS and the scripts are written in vanilla JavaScript.
icon: ⚡️
- title: Dark mode
details: Dark mode is supported by default. It will be automatically enabled when the system is in dark mode.
icon: 🌙
- title: Multilingual mode and RTL support
details: Support for multiple languages and right-to-left languages out of the box. No need to worry about i18n.
icon: 🌐
- title: A set of useful features
details: Table of contents, local search, code highlighting, image zooming, and more.
icon: 🧰
---

View File

@ -0,0 +1,73 @@
# Frontmatter Configs
[[toc]]
## description
* Type: `string`
* Available in: single pages and list pages
Description of the page.
## image
* Type: `string`
* Available in: single pages and list pages
Featured image of the page.
## comments
* Type: `bool`
* Available in: single pages
Show / hide comment section of the page.
## license
* Type: `string|bool`
* Available in: single pages
* Default: `.Site.Params.Article.License.Default`
License of the page. If it's set to `false`, the license section will be hidden.
## math
* Type: `bool`
* Available in: single pages
Enable / disable KaTeX rendering.
## toc
* Type: `bool`
* Available in: single pages
* Default: `.Site.Params.Article.toc`
Show / hide table of contents of the page.
::: info
TOC will be shown only if the page has at least one heading.
:::
## style
* Type: `map[string]string`
* Available in: list pages
Additional CSS styles for taxonomy term badge that appears in article page.
Currently only `background` (background of the badge) and `color` (text color) are supported.
## keywords
* Type: `[]string`
Keywords of the page. Useful for SEO.
## readingTime
* Type: `bool`
* Default: `.Site.Params.Article.ReadingTime`
Show / hide reading time of the page.

52
docs/writing/markdown.md Normal file
View File

@ -0,0 +1,52 @@
# Writing
Stack uses Hugo's **page bundles** to organize your content. A page bundle is a directory that contains a content file and any related resources. For example, a page bundle for a blog post might look like this:
```
content
└── post
└── my-first-post
├── index.md
├── image1.png
└── image2.png
```
This is the recommended way to organize your content. You can read more about page bundles in [Hugo's documentation](https://gohugo.io/content-management/page-bundles/).
::: warning
Inserting external images is supported, but **it is not recommended**.
Features like image gallery and image zooming will not work with external images. Those feature needs to know the image's dimensions, which is not possible with external images.
:::
With above organization, you can insert images in your content like this:
```markdown
--- content/post/my-first-post/index.md ---
![Image 1](image1.png)
![Image 2](image2.png)
```
## Insert image gallery
To insert an image gallery, you need to create a page bundle for the gallery. For example:
```
content
└── gallery
└── my-first-gallery
├── index.md
├── image1.png
├── image2.png
└── image3.png
```
Then, you can insert the gallery in your content like this:
```markdown
--- content/gallery/my-first-gallery/index.md ---
![Image 1](image1.png) ![Image 2](image2.png)
![Image 3](image3.png)
```
Which will render in two rows, with two images in the first row and one image in the second row.

View File

@ -0,0 +1,69 @@
# Shortcodes
Stack comes with a set of [shortcodes](https://gohugo.io/content-management/shortcodes/) that you can use in your content.
This page only includes the shortcodes that are specific to Stack. Hugo's built-in shortcodes are documented [here](https://gohugo.io/content-management/shortcodes/#use-hugos-built-in-shortcodes).
## Bilibili video
Embed a [Bilibili](https://www.bilibili.com/) video.
```markdown
{{< bilibili VIDEO_ID PART_NUMBER >}}
```
The `Video_ID` can be found in the URL of the video. For example, the video ID of `https://www.bilibili.com/video/av12345678` is `av12345678`. Both `AV` and `BV` are supported.
The `PART_NUMBER` is optional. It can be used to specify the part of the video to play. For example, the part number of `https://www.bilibili.com/video/av12345678?p=2` is `2`.
## Tencent video
Embed a [Tencent Video](https://v.qq.com/) video.
```markdown
{{< tencent VIDEO_ID >}}
```
The `Video_ID` can be found in the URL of the video. For example, the video ID of `https://v.qq.com/x/cover/hzgtnf6tbvfekfv/g0014r3khdw.html` is `g0014r3khdw`.
## YouTube video
Embed a [YouTube](https://www.youtube.com/) video.
```markdown
{{< youtube VIDEO_ID >}}
```
The `Video_ID` can be found in the URL of the video. For example, the video ID of `https://www.youtube.com/watch?v=VIDEO_ID` is `VIDEO_ID`.
## Generic video file
Embed a video file.
```markdown
{{< video VIDEO_URL >}}
{{< video src="VIDEO_URL" autoplay="true" poster="./video-poster.png" >}}
```
The `VIDEO_URL` can be a URL or a path relative to the `static` directory. For example, `src="/video/my-video.mp4"` will embed the video file `static/video/my-video.mp4` of your site folder.
The `autoplay` attribute is optional. It can be used to specify whether the video should be played automatically. The `poster` attribute is optional. It can be used to specify the poster image of the video.
## GitLab
Embed a [GitLab](https://gitlab.com/) snippets.
```markdown
{{< gitlab SNIPPET_ID >}}
```
The `SNIPPET_ID` can be found in the URL of the snippet. For example, the snippet ID of `https://gitlab.com/-/snippets/1234567` is `1234567`.
## Quote
```markdown
{{< quote author="A famous person" source="The book they wrote" url="https://en.wikipedia.org/wiki/Book">}}
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
{{< /quote >}}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,25 @@
baseurl: https://stack.jimmycai.com/demo/
languageCode: en-us
# Note: This title is overridden by the title in i18n config file
title: Hugo Theme Stack Starter
theme: hugo-theme-stack
DefaultContentLanguage: en
# Set hasCJKLanguage to true if DefaultContentLanguage is in [zh-cn ja ko]
# This will make .Summary and .WordCount behave correctly for CJK languages.
hasCJKLanguage: false
services:
disqus:
shortname: hugo-theme-stack
# GA Tracking ID
googleAnalytics:
id:
publishDir: ../public/demo
pagination:
pagerSize: 3

View File

@ -0,0 +1,22 @@
# Enable multilanguage site support
en:
languageName: English
title: Hugo Theme Stack Example Site
weight: 1
params:
sidebar:
subtitle: Example description
zh-cn:
languageName: 中文
title: Hugo 主题 Stack 演示站点
weight: 2
params:
sidebar:
subtitle: 演示说明
ar:
languageName: عربي
languagedirection: rtl
title: موقع تجريبي
weight: 3
sidebar:
subtitle: وصف تجريبي

View File

@ -0,0 +1,27 @@
goldmark:
extensions:
passthrough:
enable: true
delimiters:
block:
- - \[
- \]
- - $$
- $$
inline:
- - \(
- \)
renderer:
unsafe: false # Allow HTML in markdown
tableOfContents:
endLevel: 4
ordered: true
startLevel: 2
highlight:
noClasses: false
codeFences: true
guessSyntax: true
lineNoStart: 1
lineNos: true
lineNumbersInTable: true
tabWidth: 4

View File

@ -0,0 +1,15 @@
### Custom menu
### See https://stack.jimmycai.com/config/menu
### To remove about, archive and search page menu item, remove `menu` field from their FrontMatter
main: []
social:
- identifier: github
name: GitHub
url: https://github.com/CaiJimmy/hugo-theme-stack
params:
icon: brand-github
- identifier: twitter
name: Twitter
url: https://twitter.com
params:
icon: brand-twitter

View File

@ -0,0 +1,34 @@
# This params.yaml overrides the theme's default params.yaml.
# A full list of available params with their default values can be found in the theme's config/_default/params.yaml file.
favicon: img/favicon.png
footer:
since: 2020
sidebar:
emoji: 🍥
subtitle: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
avatar: img/logo.jpg
comments:
enabled: true
provider: disqus
widgets:
homepage:
- type: search
- type: archives
params:
limit: 5
- type: taxonomy
params:
limit: 10
type: tags
icon: tag
- type: taxonomy
params:
limit: 10
type: categories
icon: categories
page:
- type: toc

View File

@ -0,0 +1,3 @@
# Permalinks format of each content section
post: /p/:slug/
page: /:slug/

View File

@ -0,0 +1,9 @@
# Related contents configuration
includeNewer: true
threshold: 60
toLower: false
indices:
- name: tags
weight: 100
- name: categories
weight: 200

View File

@ -3,6 +3,9 @@ title: "Archives"
date: 2019-05-28
layout: "archives"
slug: "archives"
outputs:
- html
- json
menu:
main:
weight: -70

View File

@ -1,13 +0,0 @@
---
title: "Search"
slug: "search"
layout: "search"
outputs:
- html
- json
menu:
main:
weight: -60
params:
icon: search
---

View File

@ -1,50 +0,0 @@
+++
author = "Hugo Authors"
title = "Emoji Support"
date = "2019-03-05"
description = "Guide to emoji usage in Hugo"
categories = [
"Test"
]
tags = [
"emoji",
]
image = "the-creative-exchange-d2zvqp3fpro-unsplash.jpg"
+++
Emoji can be enabled in a Hugo project in a number of ways.
<!--more-->
The [`emojify`](https://gohugo.io/functions/emojify/) function can be called directly in templates or [Inline Shortcodes](https://gohugo.io/templates/shortcode-templates/#inline-shortcodes).
To enable emoji globally, set `enableEmoji` to `true` in your site's [configuration](https://gohugo.io/getting-started/configuration/) and then you can type emoji shorthand codes directly in content files; e.g.
<p><span class="nowrap"><span class="emojify">🙈</span> <code>:see_no_evil:</code></span> <span class="nowrap"><span class="emojify">🙉</span> <code>:hear_no_evil:</code></span> <span class="nowrap"><span class="emojify">🙊</span> <code>:speak_no_evil:</code></span></p>
<br>
The [Emoji cheat sheet](http://www.emoji-cheat-sheet.com/) is a useful reference for emoji shorthand codes.
***
**N.B.** The above steps enable Unicode Standard emoji characters and sequences in Hugo, however the rendering of these glyphs depends on the browser and the platform. To style the emoji you can either use a third party emoji font or a font stack; e.g.
{{< highlight html >}}
.emoji {
font-family: Apple Color Emoji, Segoe UI Emoji, NotoColorEmoji, Segoe UI Symbol, Android Emoji, EmojiSymbols;
}
{{< /highlight >}}
{{< css.inline >}}
<style>
.emojify {
font-family: Apple Color Emoji, Segoe UI Emoji, NotoColorEmoji, Segoe UI Symbol, Android Emoji, EmojiSymbols;
font-size: 2rem;
vertical-align: middle;
}
@media screen and (max-width:650px) {
.nowrap {
display: block;
margin: 25px 0;
}
}
</style>
{{< /css.inline >}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -42,12 +42,12 @@ Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sap
The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a `footer` or `cite` element, and optionally with in-line changes such as annotations and abbreviations.
#### Blockquote without attribution
### Blockquote without attribution
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
> **Note** that you can use *Markdown syntax* within a blockquote.
#### Blockquote with attribution
### Blockquote with attribution
> Don't communicate by sharing memory, share memory by communicating.<br>
> — <cite>Rob Pike[^1]</cite>
@ -63,7 +63,7 @@ Tables aren't part of the core Markdown spec, but Hugo supports supports them ou
Bob | 27
Alice | 23
#### Inline Markdown within tables
### Inline Markdown within tables
| Italics | Bold | Code |
| -------- | -------- | ------ |
@ -74,8 +74,7 @@ Tables aren't part of the core Markdown spec, but Hugo supports supports them ou
| 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 block with backticks
### Code block with backticks
```html
<!doctype html>
@ -90,7 +89,7 @@ Tables aren't part of the core Markdown spec, but Hugo supports supports them ou
</html>
```
#### Code block indented with four spaces
### Code block indented with four spaces
<!doctype html>
<html lang="en">
@ -103,21 +102,7 @@ Tables aren't part of the core Markdown spec, but Hugo supports supports them ou
</body>
</html>
#### Code block with Hugo's internal highlight shortcode
{{< highlight html >}}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example HTML5 Document</title>
</head>
<body>
<p>Test</p>
</body>
</html>
{{< /highlight >}}
#### Diff code block
### Diff code block
```diff
[dependencies.bevy]
@ -127,21 +112,27 @@ rev = "11f52b8c72fc3a568e8bb4a4cd1f3eb025ac2e13"
+ features = ["jpeg", "dynamic"]
```
### One line code block
```html
<p>A paragraph</p>
```
## List Types
#### Ordered List
### Ordered List
1. First item
2. Second item
3. Third item
#### Unordered List
### Unordered List
* List item
* Another item
* And another item
#### Nested list
### Nested list
* Fruit
* Apple

View File

@ -1,269 +0,0 @@
baseurl: https://example.com/
languageCode: en-us
theme: hugo-theme-stack
title: Example Site
copyright: Example Person
# Theme i18n support
# Available values: ar, bn, ca, de, el, en, es, fr, hu, id, it, ja, ko, nl, pt-br, th, uk, zh-cn, zh-hk, zh-tw
DefaultContentLanguage: en
# Set hasCJKLanguage to true if DefaultContentLanguage is in [zh-cn ja ko]
# This will make .Summary and .WordCount behave correctly for CJK languages.
hasCJKLanguage: false
languages:
en:
languageName: English
title: Example Site
weight: 1
params:
sidebar:
subtitle: Example description
zh-cn:
languageName: 中文
title: 演示站点
weight: 2
params:
sidebar:
subtitle: 演示说明
ar:
languageName: عربي
languagedirection: rtl
title: موقع تجريبي
weight: 3
params:
sidebar:
subtitle: وصف تجريبي
services:
# Change it to your Disqus shortname before using
disqus:
shortname: "hugo-theme-stack"
# GA Tracking ID
googleAnalytics:
id:
pagination:
pagerSize: 3
permalinks:
post: /p/:slug/
page: /:slug/
params:
mainSections:
- post
featuredImageField: image
rssFullContent: true
favicon: # e.g.: favicon placed in `static/favicon.ico` of your site folder, then set this field to `/favicon.ico` (`/` is necessary)
footer:
since: 2020
customText:
dateFormat:
published: Jan 02, 2006
lastUpdated: Jan 02, 2006 15:04 MST
sidebar:
emoji: 🍥
subtitle: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
avatar:
enabled: true
local: true
src: img/avatar.png
article:
math: false
toc: true
readingTime: true
license:
enabled: true
default: Licensed under CC BY-NC-SA 4.0
comments:
enabled: true
provider: disqus
disqusjs:
shortname:
apiUrl:
apiKey:
admin:
adminLabel:
utterances:
repo:
issueTerm: pathname
label:
beaudar:
repo:
issueTerm: pathname
label:
theme:
remark42:
host:
site:
locale:
vssue:
platform:
owner:
repo:
clientId:
clientSecret:
autoCreateIssue: false
# Waline client configuration see: https://waline.js.org/en/reference/component.html
waline:
serverURL:
lang:
pageview:
emoji:
- https://unpkg.com/@waline/emojis@1.0.1/weibo
requiredMeta:
- name
- email
- url
locale:
admin: Admin
placeholder:
twikoo:
envId:
region:
path:
lang:
# See https://cactus.chat/docs/reference/web-client/#configuration for description of the various options
cactus:
defaultHomeserverUrl: "https://matrix.cactus.chat:8448"
serverName: "cactus.chat"
siteName: "" # You must insert a unique identifier here matching the one you registered (See https://cactus.chat/docs/getting-started/quick-start/#register-your-site)
giscus:
repo:
repoID:
category:
categoryID:
mapping:
lightTheme:
darkTheme:
reactionsEnabled: 1
emitMetadata: 0
gitalk:
owner:
admin:
repo:
clientID:
clientSecret:
proxy:
cusdis:
host:
id:
widgets:
homepage:
- type: search
- type: archives
params:
limit: 5
- type: categories
params:
limit: 10
- type: tag-cloud
params:
limit: 10
page:
- type: toc
opengraph:
twitter:
# Your Twitter username
site:
# Available values: summary, summary_large_image
card: summary_large_image
defaultImage:
opengraph:
enabled: false
local: false
src:
colorScheme:
# Display toggle
toggle: true
# Available values: auto, light, dark
default: auto
imageProcessing:
cover:
enabled: true
content:
enabled: true
### Custom menu
### See https://stack.jimmycai.com/config/menu
### To remove about, archive and search page menu item, remove `menu` field from their FrontMatter
menu:
main: []
social:
- identifier: github
name: GitHub
url: https://github.com/CaiJimmy/hugo-theme-stack
params:
icon: brand-github
- identifier: twitter
name: Twitter
url: https://twitter.com
params:
icon: brand-twitter
related:
includeNewer: true
threshold: 60
toLower: false
indices:
- name: tags
weight: 100
- name: categories
weight: 200
markup:
goldmark:
extensions:
passthrough:
enable: true
delimiters:
block:
- - \[
- \]
- - $$
- $$
inline:
- - \(
- \)
renderer:
## Set to true if you have HTML content inside Markdown
unsafe: true
tableOfContents:
endLevel: 4
ordered: true
startLevel: 2
highlight:
noClasses: false
codeFences: true
guessSyntax: true
lineNoStart: 1
lineNos: true
lineNumbersInTable: true
tabWidth: 4

2
go.mod
View File

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

View File

@ -1,74 +1,39 @@
toggleMenu:
other: اخفي القائمة
toggleMenu: اخفي القائمة
darkMode:
other: الوضع الداكن
darkMode: الوضع الداكن
list:
page:
one: "{{ .Count }} صفحه"
other: "{{ .Count }} صفحات"
section:
other: قسم
section: قسم
subsection:
one: قسم فرعي
other: اقسام فرعية
article:
back:
other: خلف
tableOfContents:
other: جدول المحتويات
relatedContent:
other: محتوى مشابهه
lastUpdatedOn:
other: التعديل الاخير
back: خلف
tableOfContents: جدول المحتويات
relatedContent: محتوى مشابهه
lastUpdatedOn: التعديل الاخير
readingTime:
one: "تُقرأ خلال دقيقة"
other: "تُقرأ خلال {{ .Count }} دقائق"
notFound:
title:
other: غير موجود
subtitle:
other: تعذر العثور على الصفحة المطلوبة.
title: غير موجود
subtitle: تعذر العثور على الصفحة المطلوبة.
widget:
archives:
title:
other: الارشيفات
more:
other: اكثر
tagCloud:
title:
other: وسوم
categoriesCloud:
title:
other: التصنيفات
title: الارشيفات
more: اكثر
search:
title:
other: بحث
placeholder:
other: اكتب...
resultTitle:
other: "#PAGES_COUNT نتيجة (#TIME_SECONDS ثواني)"
title: بحث
placeholder: اكتب...
resultTitle: "#PAGES_COUNT نتيجة (#TIME_SECONDS ثواني)"
footer:
builtWith:
other: "مبني بستخدام {{ .Generator }}"
designedBy:
other: "قالب {{ .Theme }} مصمم من {{ .DesignedBy }}"
builtWith: "مبني بستخدام {{ .Generator }}"
designedBy: "قالب {{ .Theme }} مصمم من {{ .DesignedBy }}"

View File

@ -1,73 +1,48 @@
toggleMenu:
other: টগল মেনু
toggleMenu: টগল মেনু
darkMode:
other: ডার্ক মোড
darkMode: ডার্ক মোড
list:
page:
one: "{{ .Count }} পাতা"
other: "{{ .Count }} পাতা"
section:
other: অনুচ্ছেদ
section: অনুচ্ছেদ
subsection:
one: উপ-অনুচ্ছেদ
other: উপ-অনুচ্ছেদ
article:
back:
other: পেছনে
back: পেছনে
tableOfContents:
other: সূচিপত্র
tableOfContents: সূচিপত্র
relatedContent:
other: সম্পর্কিত বিষয়বস্তু
relatedContent: সম্পর্কিত বিষয়বস্তু
lastUpdatedOn:
other: সর্বশেষ আপডেট করা হয়েছে
lastUpdatedOn: সর্বশেষ আপডেট করা হয়েছে
readingTime:
one: "{{ .Count }} মিনিটে পড়া যাবে"
other: "{{ .Count }} মিনিটে পড়া যাবে"
notFound:
title:
other: পাওয়া যায়নি
title: পাওয়া যায়নি
subtitle:
other: এই পাতাটি বিদ্যমান নেই
subtitle: এই পাতাটি বিদ্যমান নেই
widget:
archives:
title:
other: আর্কাইভ
more:
other: আরও
tagCloud:
title:
other: ট্যাগ
categoriesCloud:
title:
other: বিভাগ
title: আর্কাইভ
more: আরও
search:
title:
other: অনুসন্ধান
title: অনুসন্ধান
placeholder:
other: কিছু টাইপ করুন...
placeholder: কিছু টাইপ করুন...
resultTitle:
other: "#PAGES_COUNT পাতা (#TIME_SECONDS সেকেন্ড)"
resultTitle: "#PAGES_COUNT পাতা (#TIME_SECONDS সেকেন্ড)"
footer:
builtWith:
other: "{{ .Generator }} দিয়ে নির্মিত"
designedBy:
other: "থিম {{ .Theme }} ডিজাইন করেছেন {{ .DesignedBy }}"
builtWith: "{{ .Generator }} দিয়ে নির্মিত"
designedBy: "থিম {{ .Theme }} ডিজাইন করেছেন {{ .DesignedBy }}"

View File

@ -1,73 +1,39 @@
toggleMenu:
other: Toggle Menu
toggleMenu: Toggle Menu
darkMode:
other: Mode fosc
darkMode: Mode fosc
list:
page:
one: "{{ .Count }} pàgina"
other: "{{ .Count }} pàgines"
section:
other: Secció
section: Secció
subsection:
one: Subsecció
other: Subseccions
article:
back:
other: Tornar
tableOfContents:
other: Taula de contingut
relatedContent:
other: Continguts relacionats
lastUpdatedOn:
other: Última vegada actualitzat
back: Tornar
tableOfContents: Taula de contingut
relatedContent: Continguts relacionats
lastUpdatedOn: Ú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
title: No Trobat
subtitle: Aquesta pàgina no existeix
widget:
archives:
title:
other: Arxiu
more:
other: Més
tagCloud:
title:
other: Etiquetes
categoriesCloud:
title:
other: Categories
title: Arxiu
more: Més
search:
title:
other: Cerca
placeholder:
other: Tecleja alguna cosa...
resultTitle:
other: "#PAGES_COUNT pàgines en (#TIME_SECONDS segons)"
title: Cerca
placeholder: Tecleja alguna cosa...
resultTitle: "#PAGES_COUNT pàgines en (#TIME_SECONDS segons)"
footer:
builtWith:
other: Creat amb {{ .Generator }}
designedBy:
other: Tema {{ .Theme }} dissenyat per {{ .DesignedBy }}
builtWith: Creat amb {{ .Generator }}
designedBy: Tema {{ .Theme }} dissenyat per {{ .DesignedBy }}

View File

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

View File

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

View File

@ -1,73 +1,42 @@
toggleMenu:
other: Toggle Menu
toggleMenu: Toggle Menu
darkMode:
other: Dark Mode
darkMode: Dark Mode
list:
page:
one: "{{ .Count }} page"
other: "{{ .Count }} pages"
section:
other: Section
section: Section
subsection:
one: Subsection
other: Subsections
article:
back:
other: Back
tableOfContents:
other: Table of contents
relatedContent:
other: Related content
lastUpdatedOn:
other: Last updated on
back: Back
tableOfContents: Table of contents
relatedContent: Related content
lastUpdatedOn: Last updated on
readingTime:
one: "{{ .Count }} minute read"
other: "{{ .Count }} minute read"
codeblock:
copy: Copy
copied: Copied!
notFound:
title:
other: Not Found
subtitle:
other: This page does not exist
title: Not Found
subtitle: This page does not exist
widget:
archives:
title:
other: Archives
more:
other: More
tagCloud:
title:
other: Tags
categoriesCloud:
title:
other: Categories
title: Archives
more: More
search:
title:
other: Search
placeholder:
other: Type something...
resultTitle:
other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
title: Search
placeholder: Type something...
resultTitle: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
footer:
builtWith:
other: Built with {{ .Generator }}
designedBy:
other: Theme {{ .Theme }} designed by {{ .DesignedBy }}
builtWith: Built with {{ .Generator }}
designedBy: Theme {{ .Theme }} designed by {{ .DesignedBy }}

View File

@ -1,73 +1,39 @@
toggleMenu:
other: Ocultar menú
toggleMenu: Ocultar menú
darkMode:
other: Modo oscuro
darkMode: Modo oscuro
list:
page:
one: "{{ .Count }} página"
other: "{{ .Count }} páginas"
section:
other: Sección
section: Sección
subsection:
one: Subsección
other: Subsecciones
article:
back:
other: Volver
tableOfContents:
other: Tabla de contenido
relatedContent:
other: Contenidos relacionados
lastUpdatedOn:
other: Última actualización
back: Volver
tableOfContents: Tabla de contenido
relatedContent: Contenidos relacionados
lastUpdatedOn: Última actualización
readingTime:
one: "Tiempo de lectura {{ .Count }} minuto"
other: "Tiempo de lectura {{ .Count }} minutos"
notFound:
title:
other: No Encontrado
subtitle:
other: Esta página no existe
title: No Encontrado
subtitle: Esta página no existe
widget:
archives:
title:
other: Archivo
more:
other: Más
tagCloud:
title:
other: Etiquetas
categoriesCloud:
title:
other: Categorías
title: Archivo
more: Más
search:
title:
other: Búsqueda
placeholder:
other: Escribe algo...
resultTitle:
other: "#PAGES_COUNT páginas en (#TIME_SECONDS segundos)"
title: Búsqueda
placeholder: Escribe algo...
resultTitle: "#PAGES_COUNT páginas en (#TIME_SECONDS segundos)"
footer:
builtWith:
other: Creado con {{ .Generator }}
designedBy:
other: Tema {{ .Theme }} diseñado por {{ .DesignedBy }}
builtWith: Creado con {{ .Generator }}
designedBy: Tema {{ .Theme }} diseñado por {{ .DesignedBy }}

View File

@ -1,73 +1,39 @@
toggleMenu:
other: منو
toggleMenu: منو
darkMode:
other: حالت شب
darkMode: حالت شب
list:
page:
one: "{{ .Count }} صفحه"
other: "{{ .Count }} صفحه"
section:
other: بخش
section: بخش
subsection:
one: زیربخش
other: زیربخش
article:
back:
other: قبلی
tableOfContents:
other: فهرست
relatedContent:
other: مطالب مرتبط
lastUpdatedOn:
other: آخرین بروزرسانی در
back: قبلی
tableOfContents: فهرست
relatedContent: مطالب مرتبط
lastUpdatedOn: آخرین بروزرسانی در
readingTime:
one: "مطالعه در {{ .Count }} دقیقه"
other: "مطالعه در {{ .Count }} دقیقه"
notFound:
title:
other: یافت نشد
subtitle:
other: این صحه وجود ندارد
title: یافت نشد
subtitle: این صحه وجود ندارد
widget:
archives:
title:
other: آرشیو
more:
other: بیشتر
tagCloud:
title:
other: تگ ها
categoriesCloud:
title:
other: دسته بندی
title: آرشیو
more: بیشتر
search:
title:
other: جستجو
placeholder:
other: تایپ کنید ...
resultTitle:
other: "#PAGES_COUNT صفحه (#TIME_SECONDS ثانیه)"
title: جستجو
placeholder: تایپ کنید ...
resultTitle: "#PAGES_COUNT صفحه (#TIME_SECONDS ثانیه)"
footer:
builtWith:
other: قدرت گرفته از {{ .Generator }}
designedBy:
other: قالب {{ .Theme }} ساخته شده توسط {{ .DesignedBy }}
builtWith: قدرت گرفته از {{ .Generator }}
designedBy: قالب {{ .Theme }} ساخته شده توسط {{ .DesignedBy }}

View File

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

View File

@ -1,73 +1,39 @@
toggleMenu:
other: Menü Kapcsolása
toggleMenu: Menü Kapcsolása
darkMode:
other: Sötét Mód
darkMode: Sötét Mód
list:
page:
one: "{{ .Count }} oldal"
other: "{{ .Count }} oldalak"
section:
other: Szekció
section: Szekció
subsection:
one: Alszekció
other: Alszekciók
article:
back:
other: Vissza
tableOfContents:
other: Tartalomjegyzék
relatedContent:
other: Kapcsolódó tartalom
lastUpdatedOn:
other: Utolsó frissítés időpontja
back: Vissza
tableOfContents: Tartalomjegyzék
relatedContent: Kapcsolódó tartalom
lastUpdatedOn: Utolsó frissítés időpontja
readingTime:
one: "{{ .Count }} percnyi olvasmány"
other: "{{ .Count }} percnyi olvasmány"
notFound:
title:
other: Nem található
subtitle:
other: Ez az oldal nem létezik
title: Nem található
subtitle: Ez az oldal nem létezik
widget:
archives:
title:
other: Archívum
more:
other: Több
tagCloud:
title:
other: Cimkék
categoriesCloud:
title:
other: Kategóriák
title: Archívum
more: Több
search:
title:
other: Keresés
placeholder:
other: Írj valamit...
resultTitle:
other: "#PAGES_COUNT oldal (#TIME_SECONDS másodperc alatt)"
title: Keresés
placeholder: Írj valamit...
resultTitle: "#PAGES_COUNT oldal (#TIME_SECONDS másodperc alatt)"
footer:
builtWith:
other: "{{ .Generator }} használatával készült"
designedBy:
other: A {{ .Theme }} dizájnt {{ .DesignedBy }} tervezte
builtWith: "{{ .Generator }} használatával készült"
designedBy: A {{ .Theme }} dizájnt {{ .DesignedBy }} tervezte

View File

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

View File

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

View File

@ -1,70 +1,33 @@
toggleMenu:
other: メニューを開く・閉じる
toggleMenu: メニューを開く・閉じる
darkMode:
other: ダークモード
darkMode: ダークモード
list:
page:
other: "{{ .Count }} ページ目"
section:
other: セクション
subsection:
other: サブセクション
page: "{{ .Count }} ページ目"
section: セクション
subsection: サブセクション
article:
back:
other: 前のページ
tableOfContents:
other: 目次
relatedContent:
other: 関連するコンテンツ
lastUpdatedOn:
other: 最終更新
readingTime:
other: "読了時間: {{ .Count }}分"
back: 前のページ
tableOfContents: 目次
relatedContent: 関連するコンテンツ
lastUpdatedOn: 最終更新
readingTime: "読了時間: {{ .Count }}分"
notFound:
title:
other: 404 Not Found
subtitle:
other: 指定されたページは存在しません。
title: 404 Not Found
subtitle: 指定されたページは存在しません。
widget:
archives:
title:
other: アーカイブ
more:
other: さらに見る
tagCloud:
title:
other: タグ
categoriesCloud:
title:
other: カテゴリ
title: アーカイブ
more: さらに見る
search:
title:
other: 検索
placeholder:
other: 入力...
resultTitle:
other: "#PAGES_COUNT 件 #TIME_SECONDS 秒)"
title: 検索
placeholder: 入力...
resultTitle: "#PAGES_COUNT 件 #TIME_SECONDS 秒)"
footer:
builtWith:
other: "{{ .Generator }} で構築されています。"
designedBy:
other: テーマ {{ .Theme }} は {{ .DesignedBy }} によって設計されています。
builtWith: "{{ .Generator }} で構築されています。"
designedBy: テーマ {{ .Theme }} は {{ .DesignedBy }} によって設計されています。

View File

@ -1,72 +1,39 @@
toggleMenu:
other: 메뉴 여닫기
toggleMenu: 메뉴 여닫기
darkMode:
other: 다크 모드
darkMode: 다크 모드
list:
page:
one: "{{ .Count }} 페이지"
other: "{{ .Count }} 페이지"
section:
other: 섹션
section: 섹션
subsection:
one: 서브섹션
other: 서브섹션
article:
back:
other: 뒤로가기
tableOfContents:
other: 목차
relatedContent:
other: 관련 글
lastUpdatedOn:
other: "마지막 수정: "
back: 뒤로가기
tableOfContents: 목차
relatedContent: 관련 글
lastUpdatedOn: "마지막 수정: "
readingTime:
one: "{{ .Count }} 분 정도"
other: "{{ .Count }} 분 정도"
notFound:
title:
other: 찾을 수 없음
subtitle:
other: 페이지를 찾을 수 없습니다.
title: 찾을 수 없음
subtitle: 페이지를 찾을 수 없습니다.
widget:
archives:
title:
other: 보관함
more:
other: 더보기
categoriesCloud:
title:
other: 카테고리
tagCloud:
title:
other: 태그
title: 보관함
more: 더보기
search:
title:
other: 검색
placeholder:
other: 검색어를 입력하세요...
resultTitle:
other: "#PAGES_COUNT 페이지 (#TIME_SECONDS 초)"
title: 검색
placeholder: 검색어를 입력하세요...
resultTitle: "#PAGES_COUNT 페이지 (#TIME_SECONDS 초)"
footer:
builtWith:
other: "{{ .Generator }}로 만듦"
designedBy:
other: "{{ .DesignedBy }}의 {{ .Theme }} 테마 사용 중"
builtWith: "{{ .Generator }}로 만듦"
designedBy: "{{ .DesignedBy }}의 {{ .Theme }} 테마 사용 중"

View File

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

View File

@ -1,8 +1,6 @@
toggleMenu:
other: Przełącz Menu
toggleMenu: Przełącz Menu
darkMode:
other: Tryb ciemny
darkMode: Tryb ciemny
list:
page:
@ -10,10 +8,7 @@ list:
few: "{{ .Count }} strony"
many: "{{ .Count }} stron"
other: "{{ .Count }} stron"
section:
other: Sekcja
section: Sekcja
subsection:
one: Podsekcja
few: Podsekcje
@ -21,18 +16,10 @@ list:
other: Podsekcji
article:
back:
other: Wróć
tableOfContents:
other: Spis treści
relatedContent:
other: Powiązane artykuły
lastUpdatedOn:
other: Ostatnio zaktualizowany
back: Wróć
tableOfContents: Spis treści
relatedContent: Powiązane artykuły
lastUpdatedOn: Ostatnio zaktualizowany
readingTime:
one: "Przeczytasz w {{ .Count }} minutę"
few: "Przeczytasz w {{ .Count }} minuty"
@ -40,33 +27,18 @@ article:
other: "Przeczytasz w {{ .Count }} minut"
notFound:
title:
other: Nie znaleziono
subtitle:
other: Ta strona nie istnieje
title: Nie znaleziono
subtitle: Ta strona nie istnieje
widget:
archives:
title:
other: Archiwum
more:
other: Więcej
tagCloud:
title:
other: Tagi
categoriesCloud:
title:
other: Kategorie
title: Archiwum
more: Więcej
search:
title:
other: Szukaj
title: Szukaj
placeholder:
other: Wpisz coś...
placeholder: Wpisz coś...
resultTitle:
one: "#PAGES_COUNT strona (#TIME_SECONDS sekund)"
@ -75,8 +47,5 @@ search:
other: "#PAGES_COUNT stron (#TIME_SECONDS sekund)"
footer:
builtWith:
other: Zbudowano z {{ .Generator }}
designedBy:
other: Motyw {{ .Theme }} zaprojektowany przez {{ .DesignedBy }}
builtWith: Zbudowano z {{ .Generator }}
designedBy: Motyw {{ .Theme }} zaprojektowany przez {{ .DesignedBy }}

View File

@ -1,67 +1,39 @@
toggleMenu:
other: Alternar Menu
toggleMenu: Alternar Menu
darkMode:
other: Modo Escuro
darkMode: Modo Escuro
list:
page:
one: "{{ .Count }} página"
other: "{{ .Count }} páginas"
section:
other: Seção
section: Seção
subsection:
one: Subseção
other: Subseções
article:
back:
other: Voltar
tableOfContents:
other: Índice
relatedContent:
other: Conteúdo relacionado
lastUpdatedOn:
other: Última atualização em
back: Voltar
tableOfContents: Índice
relatedContent: Conteúdo relacionado
lastUpdatedOn: Última atualização em
readingTime:
one: "{{ .Count }} minuto de leitura"
other: "{{ .Count }} minutos de leitura"
notFound:
title:
other: Não Encontrado
subtitle:
other: Esta página não existe.
title: Não Encontrado
subtitle: Esta página não existe.
widget:
archives:
title:
other: Arquivos
more:
other: Mais
tagCloud:
title:
other: Tags
categoriesCloud:
title:
other: Categorias
title: Arquivos
more: Mais
search:
title:
other: Busca
placeholder:
other: Digite algo...
resultTitle:
other: "#PAGES_COUNT páginas (#TIME_SECONDS segundos)"
title: Busca
placeholder: Digite algo...
resultTitle: "#PAGES_COUNT páginas (#TIME_SECONDS segundos)"
footer:
builtWith:
other: Criado com {{ .Generator }}
designedBy:
other: Tema {{ .Theme }} desenvolvido por {{ .DesignedBy }}
builtWith: Criado com {{ .Generator }}
designedBy: Tema {{ .Theme }} desenvolvido por {{ .DesignedBy }}

View File

@ -45,12 +45,6 @@ widget:
other: Arquivos
more:
other: Mais
tagCloud:
title:
other: Tags
categoriesCloud:
title:
other: Categorias
search:
title:

View File

@ -1,8 +1,6 @@
toggleMenu:
other: Показать/скрыть меню
toggleMenu: Показать/скрыть меню
darkMode:
other: Тёмный режим
darkMode: Тёмный режим
list:
page:
@ -10,10 +8,7 @@ list:
few: "{{ .Count }} страницы"
many: "{{ .Count }} страниц"
other: "{{ .Count }} страниц"
section:
other: Раздел
section: Раздел
subsection:
one: Подраздел
few: Подразделы
@ -21,43 +16,30 @@ list:
other: Подразделы
article:
back:
other: Назад
relatedContent:
other: Также рекомендуем
lastUpdatedOn:
other: Обновлено
tableOfContents:
other: Содержание
readingTime:
other: "Время чтения: {{ .Count }} мин."
back: Назад
relatedContent: Также рекомендуем
lastUpdatedOn: Обновлено
tableOfContents: Содержание
readingTime: "Время чтения: {{ .Count }} мин."
notFound:
title:
other: Не найдено
subtitle:
other: Запрашиваемая страница не существует
title: Не найдено
subtitle: Запрашиваемая страница не существует
widget:
archives:
title:
other: Архивы
more:
other: Ещё
title: Архивы
more: Ещё
tagCloud:
title:
other: Теги
title: Теги
categoriesCloud:
title: Категории
search:
title:
other: Поиск
placeholder:
other: Введите что-нибудь...
resultTitle:
other: "Найдено #PAGES_COUNT страниц (за #TIME_SECONDS с.)"
title: Поиск
placeholder: Введите что-нибудь...
resultTitle: "Найдено #PAGES_COUNT страниц (за #TIME_SECONDS с.)"
footer:
builtWith:
other: Создано при помощи {{ .Generator }}
designedBy:
other: Тема {{ .Theme }}, дизайн {{ .DesignedBy }}
builtWith: Создано при помощи {{ .Generator }}
designedBy: Тема {{ .Theme }}, дизайн {{ .DesignedBy }}

View File

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

View File

@ -1,53 +1,34 @@
toggleMenu:
other: Menüyü Gizle
toggleMenu: Menüyü Gizle
darkMode:
other: Koyu Mod
darkMode: Koyu Mod
list:
page:
one: "{{ .Count }} makale"
other: "{{ .Count }} makale"
section:
other: Bölüm
section: Bölüm
subsection:
one: Alt bölüm
other: Alt bölümler
article:
relatedContent:
other: Alakalı içerikler
lastUpdatedOn:
other: Son güncelleme
relatedContent: Alakalı içerikler
lastUpdatedOn: Son güncelleme
notFound:
title:
other: Bulunamadı
subtitle:
other: Aradığınız sayfa mevcut değil.
title: Bulunamadı
subtitle: Aradığınız sayfa mevcut değil.
widget:
archives:
title:
other: Arşiv
more:
other: Daha fazla
tagCloud:
title:
other: Etiketler
title: Arşiv
more: Daha fazla
search:
title:
other: Arama
placeholder:
other: Birşeyler yazın...
resultTitle:
other: "#PAGES_COUNT sayfa (#TIME_SECONDS saniye)"
title: Arama
placeholder: Birşeyler yazın...
resultTitle: "#PAGES_COUNT sayfa (#TIME_SECONDS saniye)"
footer:
builtWith:
other: "{{ .Generator }} ile oluşturuldu."
designedBy:
other: "{{ .Theme }} teması {{ .DesignedBy }} tarafından tasarlandı"
builtWith: "{{ .Generator }} ile oluşturuldu."
designedBy: "{{ .Theme }} teması {{ .DesignedBy }} tarafından tasarlandı"

View File

@ -1,71 +1,44 @@
toggleMenu:
other: Показати меню
toggleMenu: Показати меню
darkMode:
other: Темна тема
darkMode: Темна тема
list:
page:
one: "{{ .Count }} сторінка"
few: "{{ .Count }} сторінки"
other: "{{ .Count }} сторінок"
section:
other: Секція
section: Секція
subsection:
one: Підсекція
other: Підсекції
article:
back:
other: Назад
tableOfContents:
other: Зміст
relatedContent:
other: Схожі матеріали
lastUpdatedOn:
other: Востаннє оновлено
back: Назад
tableOfContents: Зміст
relatedContent: Схожі матеріали
lastUpdatedOn: Востаннє оновлено
readingTime:
one: "Час читання: {{ .Count }} хв"
other: "Час читання: {{ .Count }} хв"
notFound:
title:
other: Не знайдено
subtitle:
other: Ця сторінка не існує
title: Не знайдено
subtitle: Ця сторінка не існує
widget:
archives:
title:
other: Архіви
more:
other: Більше
title: Архіви
more: Більше
tagCloud:
title:
other: Теґи
title: Теґи
categoriesCloud:
title: Категорії
search:
title:
other: Пошук
placeholder:
other: Напишіть що-небудь...
resultTitle:
other: "#PAGES_COUNT сторінок (#TIME_SECONDS секунд)"
title: Пошук
placeholder: Напишіть що-небудь...
resultTitle: "#PAGES_COUNT сторінок (#TIME_SECONDS секунд)"
footer:
builtWith:
other: Створено з {{ .Generator }}
designedBy:
other: Тема {{ .Theme }}, дизайн {{ .DesignedBy }}
builtWith: Створено з {{ .Generator }}
designedBy: Тема {{ .Theme }}, дизайн {{ .DesignedBy }}

View File

@ -48,14 +48,6 @@ widget:
more:
other: Còn nữa
tagCloud:
title:
other: Nhãn dán
categoriesCloud:
title:
other: Chuỗi bài
search:
title:
other: Tìm kiếm

View File

@ -1,67 +1,33 @@
toggleMenu:
other: 切换菜单
toggleMenu: 切换菜单
darkMode:
other: 暗色模式
darkMode: 暗色模式
list:
page: "{{ .Count }} 个页面"
section: 章节
subsection: 子章节
article:
back:
other: 返回
tableOfContents:
other: 目录
relatedContent:
other: 相关文章
lastUpdatedOn:
other: 最后更新于
readingTime:
other: "阅读时长: {{ .Count }} 分钟"
back: 返回
tableOfContents: 目录
relatedContent: 相关文章
lastUpdatedOn: 最后更新于
readingTime: "阅读时长: {{ .Count }} 分钟"
notFound:
title:
other: 404 错误
subtitle:
other: 页面不存在
title: 404 错误
subtitle: 页面不存在
widget:
archives:
title:
other: 归档
more:
other: 更多
tagCloud:
title:
other: 标签云
categoriesCloud:
title:
other: 分类
title: 归档
more: 更多
search:
title:
other: 搜索
placeholder:
other: 输入关键词...
resultTitle:
other: "#PAGES_COUNT 个结果 (用时 #TIME_SECONDS 秒)"
title: 搜索
placeholder: 输入关键词...
resultTitle: "#PAGES_COUNT 个结果 (用时 #TIME_SECONDS 秒)"
footer:
builtWith:
other: 使用 {{ .Generator }} 构建
designedBy:
other: 主题 {{ .Theme }} 由 {{ .DesignedBy }} 设计
builtWith: 使用 {{ .Generator }} 构建
designedBy: 主题 {{ .Theme }} 由 {{ .DesignedBy }} 设计

View File

@ -1,73 +1,39 @@
toggleMenu:
other: 切換選單
toggleMenu: 切換選單
darkMode:
other: 深色模式
darkMode: 深色模式
list:
page:
one: "第 {{ .Count }} 頁"
other: "第 {{ .Count }} 頁"
section:
other: Section
section: Section
subsection:
one: Subsection
other: Subsections
article:
back:
other: 返回
tableOfContents:
other: 目錄
relatedContent:
other: 相關內容
lastUpdatedOn:
other: 上次改過於
back: 返回
tableOfContents: 目錄
relatedContent: 相關內容
lastUpdatedOn: 上次改過於
readingTime:
one: "需要 {{ .Count }} 分鐘閱讀"
other: "需要 {{ .Count }} 分鐘閱讀"
notFound:
title:
other: Not Found
subtitle:
other: 頁面不存在
title: Not Found
subtitle: 頁面不存在
widget:
archives:
title:
other: Archives
more:
other: 更多
tagCloud:
title:
other: Tags
categoriesCloud:
title:
other: Categories
title: Archives
more: 更多
search:
title:
other: 搜尋
placeholder:
other: Type 關鍵字...
resultTitle:
other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
title: 搜尋
placeholder: Type 關鍵字...
resultTitle: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
footer:
builtWith:
other: Built with {{ .Generator }}
designedBy:
other: 主題 {{ .Theme }} 由 {{ .DesignedBy }} 設計
builtWith: Built with {{ .Generator }}
designedBy: 主題 {{ .Theme }} 由 {{ .DesignedBy }} 設計

View File

@ -1,8 +1,6 @@
toggleMenu:
other: 切換選單
toggleMenu: 切換選單
darkMode:
other: 夜晚模式
darkMode: 夜晚模式
list:
page:
@ -17,57 +15,26 @@ list:
other: 小節
article:
back:
other: 返回
tableOfContents:
other: 目錄
relatedContent:
other: 相關文章
lastUpdatedOn:
other: 最後更新
readingTime:
one: "閱讀時間: {{ .Count }} 分鐘"
other: "閱讀時間: {{ .Count }} 分鐘"
back: 返回
tableOfContents: 目錄
relatedContent: 相關文章
lastUpdatedOn: 最後更新
readingTime: "閱讀時間: {{ .Count }} 分鐘"
notFound:
title:
other: 404 錯誤
subtitle:
other: 頁面不存在
title: 404 錯誤
subtitle: 頁面不存在
widget:
archives:
title:
other: 紀錄
more:
other: 更多
tagCloud:
title:
other: 標籤雲
categoriesCloud:
title:
other: 分類
title: 紀錄
more: 更多
search:
title:
other: 搜尋
placeholder:
other: 輸入關鍵字...
resultTitle:
other: "#PAGES_COUNT 個結果 (用時 #TIME_SECONDS 秒)"
title: 搜尋
placeholder: 輸入關鍵字...
resultTitle: "#PAGES_COUNT 個結果 (用時 #TIME_SECONDS 秒)"
footer:
builtWith:
other: 使用 {{ .Generator }} 建立
designedBy:
other: 主題 {{ .Theme }} 由 {{ .DesignedBy }} 設計
builtWith: 使用 {{ .Generator }} 建立
designedBy: 主題 {{ .Theme }} 由 {{ .DesignedBy }} 設計

View File

@ -0,0 +1,20 @@
{{- $class := .Attributes.class | default "" -}}
{{- $lang := .Attributes.lang | default .Type -}}
<div class="codeblock">
<header>
<span class="codeblock-lang">{{ $lang }}</span>
<button
class="codeblock-copy"
data-id="codeblock-id-{{ .Ordinal }}"
data-copied-text="{{ T `article.codeblock.copied` }}"
>
{{ T `article.codeblock.copy` }}
</button>
</header>
<code id="codeblock-id-{{ .Ordinal }}" style="display:none;">{{- .Inner -}}</code>
{{- if transform.CanHighlight $lang -}}
<div class="{{ $class }}">{{- highlight .Inner $lang .Options -}}</div>
{{- else -}}
<pre><code class="{{ $class }}">{{- .Inner -}}</code></pre>
{{- end -}}
</div>

View File

@ -3,24 +3,35 @@
{{- $alt := .PlainText | safeHTML -}}
{{- $Width := 0 -}}
{{- $Height := 0 -}}
{{- $Srcset := "" -}}
{{/* SVG and external images won't work with gallery layout, because their width and height attributes are unknown */}}
{{- $Srcset := slice -}}
{{- $imageProcessing := .Page.Site.Params.imageProcessing.content.enabled -}}
{{- $allowedTypes := .Page.Site.Params.ImageProcessing.AllowedTypes -}}
{{- $resizableTypes := .Page.Site.Params.ImageProcessing.ResizableTypes -}}
{{- $galleryImage := false -}}
{{- if $image -}}
{{- $notSVG := ne (path.Ext .Destination) ".svg" -}}
{{- $type := $image.MediaType.SubType -}}
{{- $allowed := in $allowedTypes $type -}}
{{- $resizable := in $resizableTypes $type -}}
{{- $imageProcessing := and $imageProcessing $resizable -}}
{{- $Permalink = $image.RelPermalink -}}
{{- if $notSVG -}}
{{- if $allowed -}}
{{- $Width = $image.Width -}}
{{- $Height = $image.Height -}}
{{- $galleryImage = true -}}
{{- if (default true .Page.Site.Params.imageProcessing.content.enabled) -}}
{{- $small := $image.Resize `480x` -}}
{{- $big := $image.Resize `1024x` -}}
{{- $Srcset = printf `%s 480w, %s 1024w` $small.RelPermalink $big.RelPermalink -}}
{{- if $imageProcessing -}}
{{- $keys := slice "small" "big" -}}
{{- range $keys -}}
{{- with (index $.Page.Site.Params.ImageProcessing.Content .) -}}
{{- if gt $Width .Threshold -}}
{{- $resized := $image.Resize (printf "%dx" .Width) -}}
{{- $Srcset = $Srcset | append (printf `%s %dw` $resized.RelPermalink .Width) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $Srcset = $Srcset | append (printf `%s %dw` $Permalink $Width) -}}
{{- end -}}
{{- end -}}
{{- end -}}
@ -28,7 +39,7 @@
<img src="{{ $Permalink }}"
{{ with $Width }}width="{{ . }}"{{ end }}
{{ with $Height }}height="{{ . }}"{{ end }}
{{ with $Srcset }}srcset="{{ . }}"{{ end }}
{{ with $Srcset }}srcset="{{ delimit . `, ` }}"{{ end }}
loading="lazy"
{{ with $alt }}
alt="{{ . }}"
@ -38,4 +49,4 @@
data-flex-grow="{{ div (mul $image.Width 100) $image.Height }}"
data-flex-basis="{{ div (mul $image.Width 240) $image.Height }}px"
{{ end }}
>
>

View File

@ -1,9 +1,14 @@
{{ define "body-class" }}template-archives{{ end }}
{{ define "body-class" }}template-archives template-search{{ end }}
{{ define "head" }}
{{- with .OutputFormats.Get "json" -}}
<link rel="preload" href="{{ .RelPermalink }}" as="fetch" crossorigin="anonymous">
{{- end -}}
{{ end }}
{{ define "main" }}
{{- $taxonomy := $.Site.GetPage "taxonomyTerm" "categories" -}}
{{- $terms := $taxonomy.Pages -}}
{{ if $terms }}
<header>
{{- $taxonomy := $.Site.GetPage "taxonomyTerm" "categories" -}}
{{- $terms := $taxonomy.Pages -}}
{{ if $terms }}
<h2 class="section-title">{{ $taxonomy.Title }}</h2>
<div class="subsection-list">
<div class="article-list--tile">
@ -12,14 +17,25 @@
{{ end }}
</div>
</div>
{{ end }}
</header>
{{ end }}
<form id="search-form" action="{{ .RelPermalink }}" class="search-form"{{ with .OutputFormats.Get "json" -}} data-json="{{ .RelPermalink }}"{{- end }}>
<p>
<label>{{ T "search.title" }}</label>
<input name="keyword" placeholder="{{ T `search.placeholder` }}" />
</p>
<button title="{{ T `search.title` }}">
{{ partial "helper/icon" "search" }}
</button>
</form>
<h3 class="search-result--title section-title"></h3>
{{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
{{ $notHidden := where .Site.RegularPages "Params.hidden" "!=" true }}
{{ $filtered := ($pages | intersect $notHidden) }}
{{ range $filtered.GroupByDate "2006" }}
{{ range $pages.GroupByDate "2006" }}
{{ $id := lower (replace .Key " " "-") }}
<div class="archives-group" id="{{ $id }}">
<h2 class="archives-date section-title"><a href="{{ $.RelPermalink }}#{{ $id }}">{{ .Key }}</a></h2>
@ -32,4 +48,16 @@
{{ end }}
{{ partialCached "footer/footer" . }}
<script>
window.searchResultTitleTemplate = "{{ T `search.resultTitle` }}"
</script>
{{- $opts := dict "minify" hugo.IsProduction -}}
{{- $archivesScript := resources.Get "ts/archives.ts" | js.Build $opts -}}
<script type="text/javascript" src="{{ $archivesScript.RelPermalink }}" defer></script>
{{ end }}
{{ define "right-sidebar" }}
{{ partial "sidebar/right.html" (dict "Context" . "Scope" "homepage") }}
{{ end }}

View File

@ -0,0 +1,13 @@
{{- $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections -}}
{{- $result := slice -}}
{{- range $pages -}}
{{- $data := dict
"title" .Title
"content" (.Plain)
"id" .File.UniqueID
-}}
{{- $result = $result | append $data -}}
{{- end -}}
{{ jsonify $result }}

View File

@ -11,7 +11,7 @@
<div class="section-card">
<div class="section-details">
<h3 class="section-count">{{ T "list.page" (len .Pages) }}</h3>
<h1 class="section-term">{{ .Title }}</h1>
<h1 class="section-term">{{ default (strings.FirstUpper .Type) .Title }}</h1>
{{ with .Params.description }}
<h2 class="section-description">{{ . }}</h2>
{{ end }}
@ -25,7 +25,7 @@
{{- $Width := $image.resource.Width -}}
{{- $Height := $image.resource.Height -}}
{{- if (default true .Page.Site.Params.imageProcessing.cover.enabled) -}}
{{- if .Page.Site.Params.imageProcessing.cover.enabled -}}
{{- $thumbnail := $image.resource.Fill "120x120" -}}
{{- $Permalink = $thumbnail.RelPermalink -}}
{{- $Width = $thumbnail.Width -}}

View File

@ -6,7 +6,6 @@
{{- else -}}
{{- $pages = $pctx.Pages -}}
{{- end -}}
{{- $pages := where $pages "Params.hidden" "!=" true -}}
{{- $limit := .Site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}

View File

@ -1,9 +1,6 @@
{{ define "main" }}
{{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
{{ $notHidden := where .Site.RegularPages "Params.hidden" "!=" true }}
{{ $filtered := ($pages | intersect $notHidden) }}
{{ $pag := .Paginate ($filtered) }}
{{ $pag := .Paginate ($pages) }}
<section class="article-list">
{{ range $index, $element := $pag.Pages }}
{{ partial "article-list/default" . }}

View File

@ -1,33 +0,0 @@
{{ define "body-class" }}template-search{{ end }}
{{ define "head" }}
{{- with .OutputFormats.Get "json" -}}
<link rel="preload" href="{{ .RelPermalink }}" as="fetch" crossorigin="anonymous">
{{- end -}}
{{ end }}
{{ define "main" }}
<form action="{{ .RelPermalink }}" class="search-form"{{ with .OutputFormats.Get "json" -}} data-json="{{ .RelPermalink }}"{{- end }}>
<p>
<label>{{ T "search.title" }}</label>
<input name="keyword" placeholder="{{ T `search.placeholder` }}" />
</p>
<button title="{{ T `search.title` }}">
{{ partial "helper/icon" "search" }}
</button>
</form>
<div class="search-result">
<h3 class="search-result--title section-title"></h3>
<div class="search-result--list article-list--compact"></div>
</div>
<script>
window.searchResultTitleTemplate = "{{ T `search.resultTitle` }}"
</script>
{{- $opts := dict "minify" hugo.IsProduction "JSXFactory" "createElement" -}}
{{- $searchScript := resources.Get "ts/search.tsx" | js.Build $opts -}}
<script type="text/javascript" src="{{ $searchScript.RelPermalink }}" defer></script>
{{ partialCached "footer/footer" . }}
{{ end }}

View File

@ -1,26 +0,0 @@
{{- $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections -}}
{{- $notHidden := where .Site.RegularPages "Params.hidden" "!=" true -}}
{{- $filtered := ($pages | intersect $notHidden) -}}
{{- $result := slice -}}
{{- range $filtered -}}
{{- $data := dict "title" .Title "date" .Date "permalink" .Permalink "content" (.Plain) -}}
{{- $image := partialCached "helper/image" (dict "Context" . "Type" "articleList") .RelPermalink "articleList" -}}
{{- if $image.exists -}}
{{- $imagePermalink := "" -}}
{{- if and $image.resource (default true .Page.Site.Params.imageProcessing.cover.enabled) -}}
{{- $thumbnail := $image.resource.Fill "120x120" -}}
{{- $imagePermalink = (absURL $thumbnail.Permalink) -}}
{{- else -}}
{{- $imagePermalink = $image.permalink -}}
{{- end -}}
{{- $data = merge $data (dict "image" (absURL $imagePermalink)) -}}
{{- end -}}
{{- $result = $result | append $data -}}
{{- end -}}
{{ jsonify $result }}

View File

@ -1,4 +1,4 @@
<article>
<article id="{{ with .File }}{{ .UniqueID }}{{ end }}">
<a href="{{ .RelPermalink }}">
<div class="article-details">
<h2 class="article-title">
@ -19,7 +19,7 @@
{{- $Width := $image.resource.Width -}}
{{- $Height := $image.resource.Height -}}
{{- if (default true .Page.Site.Params.imageProcessing.cover.enabled) -}}
{{- if .Page.Site.Params.imageProcessing.cover.enabled -}}
{{- $thumbnail := $image.resource.Fill "120x120" -}}
{{- $Permalink = $thumbnail.RelPermalink -}}
{{- $Width = $thumbnail.Width -}}

View File

@ -7,19 +7,23 @@
{{- $Permalink := $image.resource.RelPermalink -}}
{{- $Width := $image.resource.Width -}}
{{- $Height := $image.resource.Height -}}
{{- $Srcset := "" -}}
{{- $Srcset := slice -}}
{{- if (default true .Page.Site.Params.imageProcessing.cover.enabled) -}}
{{- $thumbnail := $image.resource.Resize "800x" -}}
{{- $thumbnailRetina := $image.resource.Resize "1600x" -}}
{{- $Srcset = printf "%s 800w, %s 1600w" $thumbnail.RelPermalink $thumbnailRetina.RelPermalink -}}
{{- $Permalink = $thumbnail.RelPermalink -}}
{{- $Width = $thumbnail.Width -}}
{{- $Height = $thumbnail.Height -}}
{{- if .Page.Site.Params.ImageProcessing.Cover.Enabled -}}
{{- $keys := slice "small" "big" -}}
{{- range $keys -}}
{{- with (index $.Page.Site.Params.ImageProcessing.Cover .) -}}
{{- if gt $Width .Threshold -}}
{{- $resized := $image.resource.Resize (printf "%dx" .Width) -}}
{{- $Srcset = $Srcset | append (printf `%s %dw` $resized.RelPermalink .Width) -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- $Srcset = $Srcset | append (printf `%s %dw` $Permalink $Width) -}}
{{- end -}}
<img src="{{ $Permalink }}"
{{ with $Srcset }}srcset="{{ . }}"{{ end }}
{{ with $Srcset }}srcset="{{ delimit . `, ` }}"{{ end }}
width="{{ $Width }}"
height="{{ $Height }}"
loading="lazy"

View File

@ -1,68 +1,20 @@
<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
{{- $opts := dict "minify" hugo.IsProduction "format" "esm" -}}
{{- $galleryScript := resources.Get "ts/gallery.ts" | js.Build $opts -}}
<!-- Background of PhotoSwipe.
It's a separate element as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@5.2.7/dist/photoswipe.css"
integrity="sha256-olf9rfn3AG8zR6lkPXkN3PZq63z8tElx7Ela6T4eklo=" crossorigin="anonymous">
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
<script type="module">
import StackGallery from '{{ $galleryScript.RelPermalink }}';
import PhotoSwipeLightbox from 'https://cdn.jsdelivr.net/npm/photoswipe@5.2.7/dist/photoswipe-lightbox.esm.min.js';
console.log(StackGallery)
StackGallery(document.querySelector('.article-content'));
<!-- Container that holds slides.
PhotoSwipe keeps only 3 of them in the DOM to save memory.
Don't modify these 3 pswp__item elements, data is added later on. -->
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title="Share"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
{{- partial "helper/external" (dict "Context" . "Namespace" "PhotoSwipe") -}}
const lightbox = new PhotoSwipeLightbox({
gallery: '.article-content',
children: '.gallery-image a',
pswpModule: () => import('https://cdn.jsdelivr.net/npm/photoswipe@5.2.7/dist/photoswipe.esm.min.js')
});
lightbox.init();
</script>

Some files were not shown because too many files have changed in this diff Show More