diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..b601136 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,31 @@ +# Update the NODE_VERSION arg in docker-compose.yml to pick a Node version: 10, 12, 14 +ARG NODE_VERSION=14 +FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${NODE_VERSION} + +# VARIANT can be either 'hugo' for the standard version or 'hugo_extended' for the extended version. +ARG VARIANT=hugo +# VERSION can be either 'latest' or a specific version number +ARG VERSION=latest + +# Download Hugo +RUN apt-get update && apt-get install -y ca-certificates openssl git curl && \ + rm -rf /var/lib/apt/lists/* && \ + case ${VERSION} in \ + latest) \ + export VERSION=$(curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') ;;\ + esac && \ + echo ${VERSION} && \ + wget -O ${VERSION}.tar.gz https://github.com/gohugoio/hugo/releases/download/v${VERSION}/${VARIANT}_${VERSION}_Linux-64bit.tar.gz && \ + tar xf ${VERSION}.tar.gz && \ + mv hugo /usr/bin/hugo + +# Hugo dev server port +EXPOSE 1313 + +# [Optional] Uncomment this section to install additional OS packages you may want. +# +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment if you want to install more global node packages +# RUN sudo -u node npm install -g diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..4bf8a19 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,45 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.202.3/containers/hugo +{ + "name": "Hugo (Community)", + "build": { + "dockerfile": "Dockerfile", + "args": { + // Update VARIANT to pick hugo variant. + // Example variants: hugo, hugo_extended + // Rebuild the container if it already exists to update. + "VARIANT": "hugo_extended", + // Update VERSION to pick a specific hugo version. + // Example versions: latest, 0.73.0, 0,71.1 + // Rebuild the container if it already exists to update. + "VERSION": "latest", + // Update NODE_VERSION to pick the Node.js version: 12, 14 + "NODE_VERSION": "14", + } + }, + + // Set *default* container specific settings.json values on container create. + "settings": { + "html.format.templating": true, + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "bungcip.better-toml", + "davidanson.vscode-markdownlint" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [ + 1313 + ], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "uname -a", + + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "node", + "features": { + "golang": "latest" + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..0a986bb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,32 +7,39 @@ assignees: '' --- -**Describe the bug** + + +## Describe the bug A clear and concise description of what the bug is. -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** +## Expected behavior A clear and concise description of what you expected to happen. -**Screenshots** -If applicable, add screenshots to help explain your problem. +## To Reproduce +Indicate the steps to reproduce this bug, if applicable. + +## Screenshots +Add screenshots to help explain your problem. + +## Environment + +- **Hugo version** [e.g: 0.80.0]: +- **Hugo extended?**: Yes / No + +## Additional context +Add any other context about the problem here. + +If it's a UI issue, fill the following information: -**Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] +## Content of config.yaml +```yaml +### Paste the content of the config file here +``` -**Additional context** -Add any other context about the problem here. +## Link to the demo site and/or source repository diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ff142d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +public +resources +assets/jsconfig.json +.hugo_build.lock \ No newline at end of file diff --git a/README.md b/README.md index c16389d..0e6caa2 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,13 @@ ## Demo -[Example Site](https://theme-stack.jimmycai.com/) +[Example Site](https://demo.stack.jimmycai.com/) [![Netlify Status](https://api.netlify.com/api/v1/badges/a2d2807a-a905-4bcb-97da-8da8d847da3d/deploy-status)](https://app.netlify.com/sites/hugo-theme-stack/deploys) -## Documentation & more information +## Documentation -[Documentation](https://docs.stack.jimmycai.com/) | [中文文档](https://docs.stack.jimmycai.com/v/zh-cn/) +[Documentation](https://docs.stack.jimmycai.com/) | [中文文档](https://docs.stack.jimmycai.com/zh/) ## Introduction @@ -27,21 +27,20 @@ Stack is a simple card-style Hugo theme designed for bloggers, some of its featu - No CSS framework, keep it simple and minimal - Properly cropped thumbnails - Subsection support +- Table of contents ## Requirements -It's necessary to use **Hugo ≥ 0.78.0**. - -Use Hugo Extended version (recommended) if you want to: - -* Use the latest feature/fix from `master` branch -* Edit SCSS files - -**Compiled CSS are updated once per release.** +It's necessary to use **Hugo Extended ≥ 0.87.0**. ## Installation -Clone / Download this repository to `theme` folder, and edit your site config following `exampleSite/config.toml`. +* Route 1: Clone / Download this repository to `theme` folder +* Route 2: Turn your site into a hugo module and add this theme as a module dependency + + Edit your site config following `exampleSite/config.yaml`. + +*Note: Remove `config.toml` if there is one in the site folder.* Check [documentation](https://docs.stack.jimmycai.com/) for more details. @@ -63,13 +62,22 @@ Your support is greatly appreciated :) ## Thanks to - - [Vibrant-Colors/node-vibrant](https://github.com/Vibrant-Colors/node-vibrant) - - [Normalize.css](https://necolas.github.io/normalize.css/) - - [Tabler icons](https://tablericons.com/) - - [Pure CSS implementation of Google Photos / 500px image layout](https://github.com/xieranmaya/blog/issues/6) - - [jonsuh/hamburgers](https://github.com/jonsuh/hamburgers) - - [PhotoSwipe](https://photoswipe.com/) - - [artchen/hexo-theme-element](https://github.com/artchen/hexo-theme-element) - - [MunifTanjim/minimo](https://github.com/MunifTanjim/minimo) - - [lepture/yue.css](https://github.com/lepture/yue.css) - - Markdown gallery syntax from [Typlog](https://typlog.com/) +| Project | Description | Licence | +| ------- | ----------- | ------- | +| [PhotoSwipe](https://photoswipe.com/) | For the lightbox effect | [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) | To extract the color from images | [MIT](https://github.com/Vibrant-Colors/node-vibrant/blob/master/LICENSE.md) +| [Tabler icons](https://github.com/tabler/tabler-icons) | Default menu icons | [MIT](https://github.com/tabler/tabler-icons/blob/master/LICENSE) | +| [jonsuh/hamburgers](https://github.com/jonsuh/hamburgers) | Hamburger icon of menu | [MIT](https://github.com/jonsuh/hamburgers/blob/master/LICENSE) | +| [lepture/yue.css](https://github.com/lepture/yue.css) | Part of it is used for styling article content | MIT | +| [Typlog](https://typlog.com/) | Where the markdown gallery syntax is borrowed from | The author gave me the permission | +| [Pure CSS implementation of Google Photos / 500px image layout](https://github.com/xieranmaya/blog/issues/6) | Used for image gallery | - | + +### References + +Some references that I took while building this theme: + +| Project | Licence| +| ------- | ------| +| [artchen/hexo-theme-element](https://github.com/artchen/hexo-theme-element) | [MIT](https://github.com/artchen/hexo-theme-element/blob/master/LICENSE) | +| [MunifTanjim/minimo](https://github.com/MunifTanjim/minimo) | [MIT](https://github.com/MunifTanjim/minimo/blob/master/LICENSE) | diff --git a/assets/icons/brand-github.svg b/assets/icons/brand-github.svg new file mode 100644 index 0000000..1fe7e0b --- /dev/null +++ b/assets/icons/brand-github.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/brand-twitter.svg b/assets/icons/brand-twitter.svg new file mode 100644 index 0000000..17ab1b1 --- /dev/null +++ b/assets/icons/brand-twitter.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/icons/date.svg b/assets/icons/date.svg new file mode 100644 index 0000000..ed92a90 --- /dev/null +++ b/assets/icons/date.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/assets/jsconfig.json b/assets/jsconfig.json index 9321136..040177a 100644 --- a/assets/jsconfig.json +++ b/assets/jsconfig.json @@ -6,5 +6,7 @@ "*" ] }, + "lib": ["es2020", "dom"], + "jsx": "preserve" } } \ No newline at end of file diff --git a/assets/scss/breakpoints.scss b/assets/scss/breakpoints.scss index e97b3e5..e9e9de7 100644 --- a/assets/scss/breakpoints.scss +++ b/assets/scss/breakpoints.scss @@ -1,4 +1,17 @@ -$on-phone: 812px; -$on-tablet: 1024px; -$on-desktop: 1519px; -$on-desktop-large: 1920px; \ No newline at end of file +$breakpoints: ( + sm: 640px, + md: 768px, + lg: 1024px, + xl: 1280px, + 2xl: 1536px, +); + +@mixin respond($breakpoint) { + @if not map-has-key($breakpoints, $breakpoint) { + @warn "'#{$breakpoint}' is not a valid breakpoint"; + } @else { + @media (min-width: map-get($breakpoints, $breakpoint)) { + @content; + } + } +} diff --git a/assets/scss/grid.scss b/assets/scss/grid.scss index f36ed34..a0eddca 100644 --- a/assets/scss/grid.scss +++ b/assets/scss/grid.scss @@ -2,67 +2,52 @@ margin-left: auto; margin-right: auto; + .left-sidebar { + max-width: var(--left-sidebar-max-width); + } + + .right-sidebar { + max-width: var(--right-sidebar-max-width); + + /// Display right sidebar when min-width: lg + @include respond(lg) { + display: block; + } + } + &.extended { - @media (min-width: $on-phone) { - max-width: 800px; - - .left-sidebar { - width: 25%; - } + @include respond(md) { + max-width: 1024px; + --left-sidebar-max-width: 25%; + --right-sidebar-max-width: 30%; } - @media (min-width: $on-tablet) { - max-width: 972px; - - .right-sidebar { - width: 25%; - } + @include respond(lg) { + max-width: 1280px; + --left-sidebar-max-width: 20%; + --right-sidebar-max-width: 30%; } - @media (min-width: $on-desktop) { - max-width: 1200px; - - .left-sidebar { - width: 20%; - } - - .right-sidebar { - width: 25%; - } - } - - @media (min-width: $on-desktop-large) { + @include respond(xl) { max-width: 1536px; - - .left-sidebar { - width: 15%; - } + --left-sidebar-max-width: 15%; + --right-sidebar-max-width: 25%; } } &.compact { - @media (min-width: $on-phone) { - max-width: 800px; - - .left-sidebar { - width: 25%; - } + @include respond(md) { + --left-sidebar-max-width: 25%; + max-width: 768px; } - @media (min-width: $on-tablet) { - max-width: 972px; + @include respond(lg) { + max-width: 1024px; + --left-sidebar-max-width: 20%; } - @media (min-width: $on-desktop) { - max-width: 1050px; - - .left-sidebar { - width: 20%; - } - } - - @media (min-width: $on-desktop-large) { - max-width: 1300px; + @include respond(xl) { + max-width: 1280px; } } } @@ -76,8 +61,9 @@ } &.on-phone--column { - @media (max-width: $on-phone) { - flex-direction: column; + flex-direction: column; + @include respond(md) { + flex-direction: unset; } } @@ -106,7 +92,6 @@ main.main { min-width: 0; - padding: 0 15px; max-width: 100%; flex-grow: 1; padding-top: var(--main-top-padding); @@ -114,4 +99,11 @@ main.main { .main-container { min-height: 100vh; -} \ No newline at end of file + align-items: flex-start; + padding: 0 15px; + column-gap: var(--section-separation); + + @include respond(md) { + padding: 0 20px; + } +} diff --git a/assets/scss/partials/article.scss b/assets/scss/partials/article.scss index 2c42ec7..459c22f 100644 --- a/assets/scss/partials/article.scss +++ b/assets/scss/partials/article.scss @@ -24,14 +24,14 @@ .article-image { img { width: 100%; - height: 200px; + height: 150px; object-fit: cover; - @media (max-width: $on-tablet) { - height: 150px; + @include respond(md) { + height: 200px; } - @media (min-width: $on-desktop-large) { + @include respond(xl) { height: 250px; } } @@ -62,7 +62,7 @@ color: var(--card-text-color-main); font-size: 2.2rem; - @media (min-width: $on-desktop-large) { + @include respond(xl) { font-size: 2.4rem; } @@ -86,7 +86,7 @@ line-height: 1.5; font-size: 1.75rem; - @media (min-width: $on-desktop-large) { + @include respond(xl) { font-size: 2rem; } } @@ -95,7 +95,9 @@ display: flex; align-items: center; color: var(--card-text-color-tertiary); + gap: 15px; margin-top: 10px; + flex-wrap: wrap; svg { vertical-align: middle; @@ -108,6 +110,11 @@ time { font-size: 1.4rem; } + + & > div { + display: inline-flex; + align-items: center; + } } .article-category, @@ -135,10 +142,10 @@ border-radius: var(--card-border-radius); box-shadow: var(--shadow-l1); background-color: var(--card-background); - --image-size: 60px; + --image-size: 50px; - @media (max-width: $on-tablet) { - --image-size: 50px; + @include respond(md) { + --image-size: 60px; } & + .pagination { @@ -165,10 +172,10 @@ .article-title { margin: 0; - font-size: 1.8rem; + font-size: 1.6rem; - @media (max-width: $on-tablet) { - font-size: 1.6rem; + @include respond(md) { + font-size: 1.8rem; } } @@ -242,20 +249,20 @@ flex-direction: column; justify-content: flex-end; z-index: 2; - padding: 20px; + padding: 15px; - @media (max-width: $on-phone) { - padding: 15px; + @include respond(sm) { + padding: 20px; } } .article-title { - font-size: 2.2rem; + font-size: 2rem; font-weight: 500; color: var(--card-text-color-main); - @media (max-width: $on-phone) { - font-size: 2rem; + @include respond(sm) { + font-size: 2.2rem; } } } diff --git a/assets/scss/partials/base.scss b/assets/scss/partials/base.scss index 42e460e..e7aefe6 100644 --- a/assets/scss/partials/base.scss +++ b/assets/scss/partials/base.scss @@ -15,3 +15,24 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } + +/* scrollbar styles for Firefox */ +* { + scrollbar-width: auto; + scrollbar-color: var(--scrollbar-thumb) transparent; +} +/**/ + +/* scrollbar styles for Chromium */ +::-webkit-scrollbar { + height: auto; +} + +::-webkit-scrollbar-thumb { + background-color: var(--scrollbar-thumb); +} + +::-webkit-scrollbar-track { + background-color: transparent; +} +/**/ diff --git a/assets/scss/partials/comments/disqusjs.scss b/assets/scss/partials/comments/disqusjs.scss new file mode 100644 index 0000000..eb270e3 --- /dev/null +++ b/assets/scss/partials/comments/disqusjs.scss @@ -0,0 +1,394 @@ +.disqus-container { + background-color: var(--card-background); + border-radius: var(--card-border-radius); + box-shadow: var(--shadow-l1); + padding: var(--card-padding); +} + +#dsqjs * { + margin: 0; + padding: 0 +} + +#dsqjs a { + text-decoration: none; + color: #076dd0 +} + +#dsqjs .dsqjs-hide { + display: none!important +} + +#dsqjs .dsqjs-disabled { + cursor: not-allowed; + opacity: .5 +} + +#dsqjs #dsqjs-msg { + text-align: center; + margin-top: 4px; + margin-bottom: 4px; + font-size: 14px +} + +#dsqjs #dsqjs-msg .dsqjs-msg-btn { + cursor: pointer +} + +#dsqjs .dsqjs-bullet { + line-height: 1.4; + margin: 0 2px +} + +#dsqjs .dsqjs-bullet:after { + color: #c2c6cc; + content: "·"; + font-weight: 700 +} + +#dsqjs .dsqjs-clearfix:after,#dsqjs .dsqjs-clearfix:before { + display: table; + content: ""; + line-height: 0; + clear: both +} + +#dsqjs .dsqjs-nav { + position: relative; + margin: 0 0 20px; + border-bottom: 2px solid #e7e9ee +} + +#dsqjs ol,#dsqjs ul { + list-style: none; + list-style-type: none +} + +#dsqjs .dsqjs-no-comment { + text-align: center; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; + overflow: hidden; + color: #2a2e2e; + margin-bottom: 6px +} + +#dsqjs .dsqjs-nav-tab { + float: left; + text-transform: capitalize; + font-size: 15px; + padding: 12px 8px; + color: #656c7a; + display: block; + margin: 0 15px 0 0; + font-weight: 700; + line-height: 1; + position: relative; + transition: all .2s ease-in-out +} + +#dsqjs .dsqjs-nav-tab:last-child { + margin: 0 +} + +#dsqjs .dsqjs-tab-active { + color: #2a2e2e +} + +#dsqjs .dsqjs-tab-active>span:after { + content: " "; + display: block; + height: 2px; + background-color: #076dd0!important; + position: absolute; + bottom: -5px; + left: 0; + right: 0 +} + +#dsqjs .dsqjs-post-list .dsqjs-post-item { + position: relative; + margin-bottom: 16px +} + +#dsqjs .dsqjs-post-list .dsqjs-post-avatar { + float: left; + margin-right: 10px; + position: relative; + background: #dbdfe4; + padding: 0; + display: block; + border-radius: 4px +} + +#dsqjs .dsqjs-post-list .dsqjs-post-avatar img { + width: 44px; + height: 44px; + display: block; + border-radius: 4px +} + +#dsqjs .dsqjs-post-list .dsqjs-post-header { + line-height: 1; + font-size: 14px; + margin-bottom: 3px +} + +#dsqjs .dsqjs-post-list .dsqjs-post-header .dsqjs-post-author { + color: #656c7a; + font-weight: 700 +} + +#dsqjs .dsqjs-post-list .dsqjs-post-header .dsqjs-admin-badge { + color: #fff; + background: #687a86; + padding: 1px 3px; + margin-left: 4px; + font-size: 12px; + line-height: 1; + font-weight: 700; + border-radius: 3px; + display: inline-block; + position: relative; + top: -1px; + left: 1px +} + +#dsqjs .dsqjs-post-list .dsqjs-post-header .dsqjs-meta { + display: inline-block; + font-size: 12px; + color: #656c7a +} + +#dsqjs .dsqjs-post-body { + font-size: 15px; + line-height: 1.5; + word-wrap: break-word; + overflow: hidden; + color: #2a2e2e +} + +#dsqjs .dsqjs-post-body code { + padding: .2em .4em; + margin: 0; + font-size: 85%; + background: #f5f5f5; + color: inherit; + border-radius: 3px +} + +#dsqjs .dsqjs-post-body pre { + padding: .5em; + overflow: auto; + font-size: 85%; + line-height: 1.45; + border-radius: 3px; + background: #f5f5f5; + margin: .5em 0 +} + +#dsqjs .dsqjs-post-body blockquote { + padding: 0 .8em; + margin: .5em 0; + color: #6a737d; + border-left: .25em solid #dfe2e5 +} + +#dsqjs .dsqjs-post-body p:last-child { + margin: 0 +} + +#dsqjs .dsqjs-post-list.dsqjs-children>li { + margin-left: 30px +} + +#dsqjs .dsqjs-post-list.dsqjs-children .dsqjs-post-avatar img { + width: 38px; + height: 38px +} + +#dsqjs .dsqjs-load-more { + font-size: 14px; + font-weight: 400; + display: block; + text-align: center; + padding: 11px 14px; + margin: 0 0 24px; + background: #687a86; + color: #fff; + cursor: pointer +} + +#dsqjs .dsqjs-load-more:hover { + opacity: .8 +} + +#dsqjs footer { + text-align: right; + line-height: 1.5; + padding-top: 10px; + padding-right: 10px; + border-top: 2px solid #e7e9ee; + margin-top: 12px; + font-weight: 700; + font-size: 16px; + color: #555 +} + +#dsqjs .dsqjs-disqus-logo { + background-image: url(https://c.disquscdn.com/next/embed/assets/img/sprite.654110a9206fd22f08cca0798e34a65e.png); + background-repeat: no-repeat; + display: inline-block; + background-size: 86px 40.5px; + height: 16.5px; + width: 86px; +} + +#dsqjs .dsqjs-order { + display: flex; + float: right; + align-items: center; + margin-top: 10px; + margin-bottom: 12px +} + +#dsqjs .dsqjs-order-radio { + display: none +} + +#dsqjs .dsqjs-order-radio:checked+.dsqjs-order-label { + color: #fff; + background-color: #888 +} + +#dsqjs .dsqjs-order-label { + display: block; + height: 20px; + line-height: 20px; + margin-right: 10px; + font-size: 12px; + border-radius: 2px; + padding: 0 5px; + background-color: #dcdcdc; + cursor: pointer +} + +#dsqjs p.dsqjs-has-more { + margin-bottom: 24px; + margin-left: 48px; + font-size: 13px; + line-height: 15px +} + +#dsqjs p.dsqjs-has-more a.dsqjs-has-more-btn { + color: #656c7a; + text-decoration: underline; + cursor: pointer +} + +@media (min-width: 768px) { + #dsqjs .dsqjs-post-list.dsqjs-children>li { + margin-left:48px + } + + #dsqjs .dsqjs-post-list .dsqjs-post-avatar { + margin-right: 12px + } + + #dsqjs .dsqjs-post-list .dsqjs-post-item { + margin-bottom: 20px + } +} + +@media (min-width: 1024px) { + #dsqjs .dsqjs-post-list.dsqjs-children>li { + margin-left:60px + } +} + +:root[data-scheme="light"] { + #dsqjs .dsqjs-disqus-logo { + background-position: 0 -7px; + } +} + +:root[data-scheme="dark"] { + #dsqjs { + --t-s: rgba(255,255,255,0.9); + --alt: #3e4b5e; + --link-hover: #47a2e0; + --hover-bg: #3e4b5e; + --tag: #3e4b5e; + --border: #435266; + --pre: #3c495b; + --c-bg: #2f3947; + --code: #c3c7cb; + --kbd: #4e5f77; + --hl: #abb2bf; + --hlc: #808895; + --hlk: #c678dd; + --hln: #e06c75; + --hll: #56b6c2; + --hls: #98c379; + --hlt: #e6c07b; + --hlv: #d19a66; + --bg: #181c27; + --main: #252d38; + --t: rgba(255,255,255,0.86); + --t-l: rgba(255,255,255,0.66); + --logo: #fff; + --link: #38a3fd; + --title: rgba(255,255,255,0.92); + --fab: #364151; + --shadow: none; + } + + #disqus_thread { + color: var(--body-text-color) + } + + #dsqjs #dsqjs-msg { + color: var(--t) + } + + #dsqjs a { + color:var(--link) + } + + #dsqjs a:focus,#dsqjs a:hover { + color: var(--link-hover) + } + + #dsqjs .dsqjs-disqus-logo { + background-position: 0 -24px; + } + + #dsqjs .dsqjs-nav,#dsqjs footer { + border-color: var(--hlc) + } + + #dsqjs .dsqjs-load-more,#dsqjs .dsqjs-load-more:hover,#dsqjs .dsqjs-nav-tab,#dsqjs .dsqjs-no-comment,#dsqjs .dsqjs-post-content { + color: var(--t) + } + + #dsqjs .dsqjs-order-label { + background-color: var(--hlc) + } + + #dsqjs .dsqjs-order-radio:checked+.dsqjs-order-label { + background-color: var(--kbd) + } + + #dsqjs .dsqjs-tab-active>span:after { + background-color: #2e9fff + } + + #dsqjs .dsqjs-footer,#dsqjs .dsqjs-meta { + color: var(--t-l) + } + + #dsqjs .dsqjs-post-body blockquote { + border-color: var(--border) + } +} diff --git a/assets/scss/partials/highlight/light.scss b/assets/scss/partials/highlight/light.scss index 9ddcff9..1798218 100644 --- a/assets/scss/partials/highlight/light.scss +++ b/assets/scss/partials/highlight/light.scss @@ -10,7 +10,8 @@ } /* Other */ -.chroma .x {} +.chroma .x { +} /* Error */ .chroma .err { @@ -40,367 +41,369 @@ .chroma .hl { display: block; width: 100%; - background-color: #ffffcc + background-color: #ffffcc; } /* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; - color: #7f7f7f + color: #7f7f7f; } /* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; - color: #7f7f7f + color: #7f7f7f; } /* Keyword */ .chroma .k { - color: #00a8c8 + color: #00a8c8; } /* KeywordConstant */ .chroma .kc { - color: #00a8c8 + color: #00a8c8; } /* KeywordDeclaration */ .chroma .kd { - color: #00a8c8 + color: #00a8c8; } /* KeywordNamespace */ .chroma .kn { - color: #f92672 + color: #f92672; } /* KeywordPseudo */ .chroma .kp { - color: #00a8c8 + color: #00a8c8; } /* KeywordReserved */ .chroma .kr { - color: #00a8c8 + color: #00a8c8; } /* KeywordType */ .chroma .kt { - color: #00a8c8 + color: #00a8c8; } /* Name */ .chroma .n { - color: #111111 + color: #111111; } /* NameAttribute */ .chroma .na { - color: #75af00 + color: #75af00; } /* NameBuiltin */ .chroma .nb { - color: #111111 + color: #111111; } /* NameBuiltinPseudo */ .chroma .bp { - color: #111111 + color: #111111; } /* NameClass */ .chroma .nc { - color: #75af00 + color: #75af00; } /* NameConstant */ .chroma .no { - color: #00a8c8 + color: #00a8c8; } /* NameDecorator */ .chroma .nd { - color: #75af00 + color: #75af00; } /* NameEntity */ .chroma .ni { - color: #111111 + color: #111111; } /* NameException */ .chroma .ne { - color: #75af00 + color: #75af00; } /* NameFunction */ .chroma .nf { - color: #75af00 + color: #75af00; } /* NameFunctionMagic */ .chroma .fm { - color: #111111 + color: #111111; } /* NameLabel */ .chroma .nl { - color: #111111 + color: #111111; } /* NameNamespace */ .chroma .nn { - color: #111111 + color: #111111; } /* NameOther */ .chroma .nx { - color: #75af00 + color: #75af00; } /* NameProperty */ .chroma .py { - color: #111111 + color: #111111; } /* NameTag */ .chroma .nt { - color: #f92672 + color: #f92672; } /* NameVariable */ .chroma .nv { - color: #111111 + color: #111111; } /* NameVariableClass */ .chroma .vc { - color: #111111 + color: #111111; } /* NameVariableGlobal */ .chroma .vg { - color: #111111 + color: #111111; } /* NameVariableInstance */ .chroma .vi { - color: #111111 + color: #111111; } /* NameVariableMagic */ .chroma .vm { - color: #111111 + color: #111111; } /* Literal */ .chroma .l { - color: #ae81ff + color: #ae81ff; } /* LiteralDate */ .chroma .ld { - color: #d88200 + color: #d88200; } /* LiteralString */ .chroma .s { - color: #d88200 + color: #d88200; } /* LiteralStringAffix */ .chroma .sa { - color: #d88200 + color: #d88200; } /* LiteralStringBacktick */ .chroma .sb { - color: #d88200 + color: #d88200; } /* LiteralStringChar */ .chroma .sc { - color: #d88200 + color: #d88200; } /* LiteralStringDelimiter */ .chroma .dl { - color: #d88200 + color: #d88200; } /* LiteralStringDoc */ .chroma .sd { - color: #d88200 + color: #d88200; } /* LiteralStringDouble */ .chroma .s2 { - color: #d88200 + color: #d88200; } /* LiteralStringEscape */ .chroma .se { - color: #8045ff + color: #8045ff; } /* LiteralStringHeredoc */ .chroma .sh { - color: #d88200 + color: #d88200; } /* LiteralStringInterpol */ .chroma .si { - color: #d88200 + color: #d88200; } /* LiteralStringOther */ .chroma .sx { - color: #d88200 + color: #d88200; } /* LiteralStringRegex */ .chroma .sr { - color: #d88200 + color: #d88200; } /* LiteralStringSingle */ .chroma .s1 { - color: #d88200 + color: #d88200; } /* LiteralStringSymbol */ .chroma .ss { - color: #d88200 + color: #d88200; } /* LiteralNumber */ .chroma .m { - color: #ae81ff + color: #ae81ff; } /* LiteralNumberBin */ .chroma .mb { - color: #ae81ff + color: #ae81ff; } /* LiteralNumberFloat */ .chroma .mf { - color: #ae81ff + color: #ae81ff; } /* LiteralNumberHex */ .chroma .mh { - color: #ae81ff + color: #ae81ff; } /* LiteralNumberInteger */ .chroma .mi { - color: #ae81ff + color: #ae81ff; } /* LiteralNumberIntegerLong */ .chroma .il { - color: #ae81ff + color: #ae81ff; } /* LiteralNumberOct */ .chroma .mo { - color: #ae81ff + color: #ae81ff; } /* Operator */ .chroma .o { - color: #f92672 + color: #f92672; } /* OperatorWord */ .chroma .ow { - color: #f92672 + color: #f92672; } /* Punctuation */ .chroma .p { - color: #111111 + color: #111111; } /* Comment */ .chroma .c { - color: #75715e + color: #75715e; } /* CommentHashbang */ .chroma .ch { - color: #75715e + color: #75715e; } /* CommentMultiline */ .chroma .cm { - color: #75715e + color: #75715e; } /* CommentSingle */ .chroma .c1 { - color: #75715e + color: #75715e; } /* CommentSpecial */ .chroma .cs { - color: #75715e + color: #75715e; } /* CommentPreproc */ .chroma .cp { - color: #75715e + color: #75715e; } /* CommentPreprocFile */ .chroma .cpf { - color: #75715e + color: #75715e; } /* Generic */ -.chroma .g {} - +.chroma .g { +} /* GenericDeleted */ -.chroma .gd {} - +.chroma .gd { + color: #f92672; +} /* GenericEmph */ .chroma .ge { - font-style: italic + font-style: italic; } - /* GenericError */ -.chroma .gr {} - +.chroma .gr { +} /* GenericHeading */ -.chroma .gh {} - +.chroma .gh { +} /* GenericInserted */ -.chroma .gi {} - +.chroma .gi { + color: #7ca727; +} /* GenericOutput */ -.chroma .go {} - +.chroma .go { +} /* GenericPrompt */ -.chroma .gp {} - +.chroma .gp { +} /* GenericStrong */ .chroma .gs { - font-weight: bold + font-weight: bold; } - /* GenericSubheading */ -.chroma .gu {} - +.chroma .gu { + color: #75715e; +} /* GenericTraceback */ -.chroma .gt {} - +.chroma .gt { +} /* GenericUnderline */ -.chroma .gl {} - +.chroma .gl { +} /* TextWhitespace */ -.chroma .w {} \ No newline at end of file +.chroma .w { +} diff --git a/assets/scss/partials/layout/article.scss b/assets/scss/partials/layout/article.scss index 3248163..af5f7bb 100644 --- a/assets/scss/partials/layout/article.scss +++ b/assets/scss/partials/layout/article.scss @@ -1,43 +1,9 @@ -.keep-sidebar { - @media (min-width: $on-phone) and (max-width: $on-tablet) { - --main-top-padding: 50px; - } -} - .article-page { - &.with-toolbar { - @media (max-width: $on-tablet) { - --main-top-padding: 0; - } - } + &.hide-sidebar-sm .left-sidebar { + display: none; - &:not(.keep-sidebar) .left-sidebar { - @media (max-width: $on-tablet) { - display: none; - } - } - - .article-sidebar { - position: sticky; - top: 50px; - flex-shrink: 0; - - @media (max-width: $on-tablet) { - display: none; - } - - @media (min-width: $on-tablet) { - padding-left: 15px; - margin-left: 1%; - } - - @media (min-width: $on-tablet) { - width: 25%; - margin-right: 1%; - } - - @media (min-width: $on-desktop) { - width: 30%; + @include respond(md) { + display: inherit; } } @@ -104,6 +70,13 @@ flex-wrap: wrap; text-transform: unset; } + + .article-copyright, + .article-lastmod { + a { + color: var(--body-text-color); + } + } } } } @@ -111,12 +84,9 @@ #article-toolbar { display: flex; align-items: center; + margin: 20px 0; - @media (max-width: $on-tablet) { - margin: 20px 0; - } - - @media (min-width: $on-tablet) { + @include respond(md) { display: none; } @@ -147,6 +117,157 @@ span { font-weight: 500; + white-space: nowrap; + } + } +} + +.article-page.has-toc { + .left-sidebar { + display: none; + } + + .right-sidebar { + width: 100%; + padding: 0; + display: none; + + @include respond(xl) { + display: block; + top: var(--main-top-padding); + } + } + + #article-toolbar { + display: block; + + @include respond(md) { + padding: 0; + } + + @include respond(xl) { + margin-top: 0; + position: sticky; + top: var(--main-top-padding); + flex-shrink: 1; + + a { + background: transparent; + box-shadow: none; + border: 1px solid var(--body-text-color); + width: 100%; + margin-right: 0; + + svg { + flex-shrink: 0; + } + } + } + } + + .main-container { + align-items: start; + flex-direction: column; + + @include respond(xl) { + flex-direction: row; + } + } + + .main { + padding-top: 0; + + @include respond(xl) { + padding-top: var(--main-top-padding); + } + } +} + +.widget--toc { + background-color: var(--card-background); + border-radius: var(--card-border-radius); + box-shadow: var(--shadow-l1); + display: flex; + flex-direction: column; + color: var(--card-text-color-main); + overflow: hidden; + + ::-webkit-scrollbar-thumb { + background-color: var(--card-separator-color); + } + + #TableOfContents { + overflow-x: auto; + max-height: 75vh; + + ol, + ul { + margin: 0; + padding: 0; + } + + ol { + list-style-type: none; + counter-reset: item; + + li a::before { + counter-increment: item; + content: counters(item, ".") ". "; + font-weight: bold; + margin-right: 5px; + } + } + + & > ul { + padding: 0 1em; + } + + li { + margin: 15px 0 15px 20px; + padding: 5px; + + & > ol, + & > ul { + margin-top: 10px; + padding-left: 10px; + margin-bottom: -5px; + + & > li:last-child { + margin-bottom: 0; + } + } + } + li.active-class > a { + border-left: var(--heading-border-size) solid var(--accent-color); + font-weight: bold; + } + + ul li.active-class > a { + display: block; + } + + @function repeat($str, $n) { + $result: ""; + @for $_ from 0 to $n { + $result: $result + $str; + } + @return $result; + } + + // Support up to 6 levels of indentation for lists in ToCs + @for $i from 0 to 5 { + & > ul #{repeat("> li > ul", $i)} > li.active-class > a { + $n: 25 + $i * 35; + margin-left: calc(-#{$n}px - 1em); + padding-left: calc(#{$n}px + 1em - var(--heading-border-size)); + } + + & > ol #{repeat("> li > ol", $i)} > li.active-class > a { + $n: 9 + $i * 35; + margin-left: calc(-#{$n}px - 1em); + padding-left: calc(#{$n}px + 1em - var(--heading-border-size)); + display: block; + } } } } @@ -187,11 +308,11 @@ .article-content { font-family: var(--article-font-family); font-size: var(--article-font-size); + padding: 0 var(--card-padding); line-height: var(--article-line-height); & > p { margin: 1.5em 0; - padding: 0 var(--card-padding); } h1, @@ -200,12 +321,12 @@ h4, h5, h6 { - padding: 0 calc(var(--card-padding) - var(--heading-border-size)); + margin-left: calc((var(--card-padding)) * -1); + padding-left: calc(var(--card-padding) - var(--heading-border-size)); border-left: var(--heading-border-size) solid var(--accent-color); } figure { - margin: 0 auto; text-align: center; figcaption { @@ -216,17 +337,12 @@ blockquote { position: relative; - margin: 10px 0; + margin: 1.5em 0; border-left: var(--blockquote-border-size) solid var(--card-separator-color); padding: 15px calc(var(--card-padding) - var(--blockquote-border-size)); background-color: var(--blockquote-background-color); } - & > ul, - & > ol { - margin: 1em var(--card-padding); - } - hr { width: 100px; margin: 40px auto; @@ -249,7 +365,7 @@ display: flex; flex-direction: row; justify-content: center; - margin: 20px 0; + margin: 1.5em 0; figure { margin: 0; @@ -278,10 +394,49 @@ } } + .highlight { + background-color: var(--pre-background-color); + padding: var(--card-padding); + position: relative; + + &:hover { + .copyCodeButton { + opacity: 1; + } + } + + pre { + margin: initial; + padding: 0; + margin: 0; + width: auto; + } + } + + .copyCodeButton { + position: absolute; + top: calc(var(--card-padding)); + right: calc(var(--card-padding)); + background: var(--card-background); + border: none; + box-shadow: var(--shadow-l2); + border-radius: var(--tag-border-radius); + padding: 8px 16px; + color: var(--card-text-color-main); + cursor: pointer; + font-size: 14px; + opacity: 0; + transition: opacity 0.3s ease; + } + + .table-wrapper { + padding: 0 var(--card-padding); + overflow-x: auto; + display: block; + } + table { - margin: 0 var(--card-padding); width: 100%; - max-width: calc(100% - var(--card-padding) * 2); border-collapse: collapse; border-spacing: 0; margin-bottom: 1.5em; @@ -306,4 +461,36 @@ .twitter-tweet { color: var(--card-text-color-main); } + + .video-wrapper { + position: relative; + width: 100%; + height: 0; + padding-bottom: 56.25%; + overflow: hidden; + + & > iframe, + & > video { + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + border: 0; + } + } + + /// Negative margins + blockquote, + figure, + .highlight, + pre, + .gallery, + .video-wrapper, + .table-wrapper, + .s_video_simple { + margin-left: calc((var(--card-padding)) * -1); + margin-right: calc((var(--card-padding)) * -1); + width: calc(100% + var(--card-padding) * 2); + } } diff --git a/assets/scss/partials/menu.scss b/assets/scss/partials/menu.scss index 024b3a6..1d612d1 100644 --- a/assets/scss/partials/menu.scss +++ b/assets/scss/partials/menu.scss @@ -106,9 +106,10 @@ z-index: 2; cursor: pointer; - @media (min-width: $on-phone + 1) { + @include respond(md) { display: none; } + outline: none; &.is-active { @@ -126,67 +127,59 @@ list-style: none; display: flex; flex-direction: column; - margin-top: var(--sidebar-element-separation); - margin-bottom: 0; overflow-y: auto; flex-grow: 1; - font-size: 1.5rem; + font-size: 1.4rem; - @media (min-width: $on-desktop-large) { - margin-top: 30px; + background-color: var(--card-background); + padding: 15px 0; + box-shadow: var(--shadow-l1); + display: none; + + margin: 0 -15px; + + &.show { + display: block; } - @media (max-width: $on-phone) { - background-color: var(--card-background); - margin-top: 0; - padding: 15px 0; - box-shadow: var(--shadow-l1); - display: none; - - &.show { - display: block; - } - } - - @media (min-width: $on-phone + 1) { + @include respond(md) { align-items: flex-end; + display: flex; + background-color: transparent; + padding: 0; + box-shadow: none; + margin: 0; + margin-top: var(--sidebar-element-separation); + } + + @include respond(xl) { + margin-top: 30px; } li { position: relative; vertical-align: middle; - padding: 10px 0; + padding: 10px 30px; &:not(:last-of-type) { margin-bottom: 15px; - @media (min-width: $on-desktop-large) { + @include respond(xl) { margin-bottom: 20px; } } - @media (max-width: $on-phone) { - padding: 10px 30px; - } - - @media (min-width: $on-phone + 1) and (max-width: ($on-desktop - 1)) { + @include respond(md) { + width: 100%; padding: 10px 0; } - @media (min-width: $on-phone + 1) { - width: 100%; - } - svg { - width: 25px; - height: 25px; stroke-width: 1.33; margin-right: 40px; - @media (max-width: $on-desktop-large) { - width: 20px; - height: 20px; - } + width: 20px; + height: 20px; } a { @@ -194,10 +187,6 @@ display: inline-flex; align-items: center; color: var(--body-text-color); - - @media (max-width: $on-desktop-large) { - font-size: 1.4rem; - } } span { @@ -212,3 +201,18 @@ } } } + +.social-menu { + list-style: none; + padding: 0%; + display: flex; + flex-direction: row; + gap: 10px; + + svg { + width: 24px; + height: 24px; + stroke: var(--body-text-color); + stroke-width: 1.33; + } +} diff --git a/assets/scss/partials/sidebar.scss b/assets/scss/partials/sidebar.scss index b6722a9..462729d 100644 --- a/assets/scss/partials/sidebar.scss +++ b/assets/scss/partials/sidebar.scss @@ -1,7 +1,6 @@ .sidebar { - padding: 0 15px; &.sticky { - @media (min-width: ($on-phone + 1)) { + @include respond(md) { position: sticky; } } @@ -11,45 +10,41 @@ display: flex; flex-direction: column; flex-shrink: 0; + align-self: stretch; - --sidebar-avatar-size: 150px; - --sidebar-element-separation: 25px; + width: 100%; + padding: 30px 0 15px 0; + max-width: none; - @media (max-width: $on-desktop-large) { - --sidebar-avatar-size: 120px; - --sidebar-element-separation: 20px; + --sidebar-avatar-size: 120px; + --sidebar-element-separation: 20px; + + @include respond(md) { + width: auto; + padding-top: var(--main-top-padding); + padding-bottom: var(--main-top-padding); + max-height: 100vh; } - @media (max-width: $on-phone) { - width: 100%; - padding: 30px 0; - max-width: none; + @include respond(2xl) { + --sidebar-avatar-size: 140px; + --sidebar-element-separation: 25px; } &.sticky { top: 0; } - - @media (min-width: $on-phone + 1) { - margin-right: 1%; - padding: var(--main-top-padding) 15px; - max-height: 100vh; - } } .right-sidebar { flex-shrink: 0; + display: none; &.sticky { top: 0; } - @media (max-width: $on-desktop - 1) { - display: none; - } - - @media (min-width: $on-tablet) { - margin-left: 1%; + @include respond(lg) { padding-top: var(--main-top-padding); } } @@ -58,8 +53,10 @@ z-index: 1; transition: box-shadow 0.5s ease; - @media (max-width: $on-phone) { - padding: 15px 30px; + padding: 15px; + + @include respond(md) { + padding: 0; } .site-avatar { @@ -79,9 +76,9 @@ .emoji { position: absolute; - width: 50px; - height: 50px; - line-height: 50px; + width: 40px; + height: 40px; + line-height: 40px; border-radius: 100%; bottom: 0; right: 0; @@ -90,20 +87,20 @@ background-color: var(--card-background); box-shadow: var(--shadow-l2); - @media (max-width: $on-desktop-large) { - width: 40px; - height: 40px; - line-height: 40px; + @include respond(2xl) { + width: 50px; + height: 50px; + line-height: 50px; } } } .site-name { color: var(--accent-color); - font-size: 2.4rem; margin: 0; + font-size: 1.8rem; - @media (max-width: $on-desktop-large) { + @include respond(2xl) { font-size: 2rem; } } @@ -112,10 +109,10 @@ color: var(--body-text-color); font-weight: normal; margin: 10px 0; - font-size: 1.8rem; + font-size: 1.6rem; - @media (max-width: $on-desktop-large) { - font-size: 1.6rem; + @include respond(2xl) { + font-size: 1.8rem; } } } diff --git a/assets/scss/variables.scss b/assets/scss/variables.scss index cba32e5..f7d45b4 100644 --- a/assets/scss/variables.scss +++ b/assets/scss/variables.scss @@ -1,27 +1,15 @@ $defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6; $defaultTagColors: #fff, #fff, #fff, #fff, #fff; -[data-scheme="light"] { - --pre-text-color: #272822; - --pre-background-color: #fafafa; - @import "partials/highlight/light.scss"; -} - -[data-scheme="dark"] { - --pre-text-color: #f8f8f2; - --pre-background-color: #272822; - @import "partials/highlight/dark.scss"; -} - /* * Global style */ :root { - @media (min-width: $on-phone + 1) { + @include respond(md) { --main-top-padding: 35px; } - @media (min-width: $on-desktop-large) { + @include respond(xl) { --main-top-padding: 50px; } @@ -36,12 +24,17 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff; --section-separation: 40px; - [data-scheme="dark"] { + --scrollbar-thumb: hsl(0, 0%, 85%); + --scrollbar-track: var(--body-background); + + &[data-scheme="dark"] { --body-background: #303030; --accent-color: #ecf0f1; --accent-color-darker: #bdc3c7; --accent-color-text: #000; --body-text-color: rgba(255, 255, 255, 0.7); + --scrollbar-thumb: hsl(0, 0%, 40%); + --scrollbar-track: var(--body-background); } } @@ -70,20 +63,23 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff; --card-border-radius: 10px; - --card-padding: 30px; - @media (max-width: $on-desktop-large) { + --card-padding: 20px; + + @include respond(md) { --card-padding: 25px; } - @media (max-width: $on-tablet) { - --card-padding: 20px; + + @include respond(xl) { + --card-padding: 30px; } - --small-card-padding: 25px; - @media (max-width: $on-tablet) { - --small-card-padding: 25px 20px; + --small-card-padding: 25px 20px; + + @include respond(md) { + --small-card-padding: 25px; } - [data-scheme="dark"] { + &[data-scheme="dark"] { --card-background: #424242; --card-background-selected: rgba(255, 255, 255, 0.16); --card-text-color-main: rgba(255, 255, 255, 0.9); @@ -98,10 +94,12 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff; */ :root { --article-font-family: var(--base-font-family); - --article-font-size: 1.7rem; - @media (max-width: $on-tablet) { - --article-font-size: 1.6rem; + --article-font-size: 1.6rem; + + @include respond(md) { + --article-font-size: 1.7rem; } + --article-line-height: 1.85; } @@ -127,7 +125,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff; --table-border-color: #dadada; --tr-even-background-color: #efefee; - [data-scheme="dark"] { + &[data-scheme="dark"] { --code-background-color: #272822; --code-text-color: rgba(255, 255, 255, 0.9); @@ -149,3 +147,15 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff; --shadow-l4: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 0px 1px rgba(0, 0, 0, 0.04); } + +[data-scheme="light"] { + --pre-text-color: #272822; + --pre-background-color: #fafafa; + @import "partials/highlight/light.scss"; +} + +[data-scheme="dark"] { + --pre-text-color: #f8f8f2; + --pre-background-color: #272822; + @import "partials/highlight/dark.scss"; +} diff --git a/assets/ts/colorScheme.ts b/assets/ts/colorScheme.ts index 7c4ff77..8cb8a20 100644 --- a/assets/ts/colorScheme.ts +++ b/assets/ts/colorScheme.ts @@ -9,7 +9,7 @@ class StackColorScheme { this.bindMatchMedia(); this.currentScheme = this.getSavedScheme(); - this.dispatchEvent(document.body.dataset.scheme as colorScheme); + this.dispatchEvent(document.documentElement.dataset.scheme as colorScheme); if (toggleEl) this.bindClick(toggleEl); @@ -22,7 +22,7 @@ class StackColorScheme { localStorage.setItem(this.localStorageKey, this.currentScheme); } - private bindClick(toggleEl) { + private bindClick(toggleEl: HTMLElement) { toggleEl.addEventListener('click', (e) => { if (this.isDark()) { /// Disable dark mode @@ -56,13 +56,13 @@ class StackColorScheme { private setBodyClass() { if (this.isDark()) { - document.body.dataset.scheme = 'dark'; + document.documentElement.dataset.scheme = 'dark'; } else { - document.body.dataset.scheme = 'light'; + document.documentElement.dataset.scheme = 'light'; } - this.dispatchEvent(document.body.dataset.scheme as colorScheme); + this.dispatchEvent(document.documentElement.dataset.scheme as colorScheme); } private getSavedScheme(): colorScheme { @@ -85,4 +85,4 @@ class StackColorScheme { } } -export default StackColorScheme; \ No newline at end of file +export default StackColorScheme; diff --git a/assets/ts/gallery.ts b/assets/ts/gallery.ts index 8bce5a9..218658d 100644 --- a/assets/ts/gallery.ts +++ b/assets/ts/gallery.ts @@ -34,7 +34,7 @@ class StackGallery { private loadItems(container: HTMLElement) { this.items = []; - const figures = container.querySelectorAll('figure'); + const figures = container.querySelectorAll('figure.gallery-image'); for (const el of figures) { const figcaption = el.querySelector('figcaption'), @@ -57,7 +57,61 @@ class StackGallery { } public static createGallery(container: HTMLElement) { - const figuresEl = container.querySelectorAll('figure'); + /// The process of wrapping image with figure tag is done using JavaScript instead of only Hugo markdown render hook + /// because it can not detect whether image is being wrapped by a link or not + /// and it lead to a invalid HTML construction (
) + + const images = container.querySelectorAll('img'); + for (const img of Array.from(images)) { + /// Images are wrapped with figure tag if the paragraph has only images without texts + /// This is done to allow inline images within paragraphs + const paragraph = img.closest('p'); + + if (!paragraph || !container.contains(paragraph)) continue; + + if (paragraph.textContent.trim() == '') { + /// Once we insert figcaption, this check no longer works + /// So we add a class to paragraph to mark it + paragraph.classList.add('no-text'); + } + + let isNewLineImage = paragraph.classList.contains('no-text'); + if (!isNewLineImage) continue; + + const hasLink = img.parentElement.tagName == 'A'; + + let el: HTMLElement = img; + /// Wrap image with figure tag, with flex-grow and flex-basis values extracted from img's data attributes + const figure = document.createElement('figure'); + figure.style.setProperty('flex-grow', img.getAttribute('data-flex-grow') || '1'); + figure.style.setProperty('flex-basis', img.getAttribute('data-flex-basis') || '0'); + if (hasLink) { + /// Wrap 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 tag if image was not wrapped by tag + if (!hasLink) { + figure.className = 'gallery-image'; + + const a = document.createElement('a'); + a.href = img.src; + a.setAttribute('target', '_blank'); + img.parentNode.insertBefore(a, img); + a.appendChild(img); + } + } + + const figuresEl = container.querySelectorAll('figure.gallery-image'); let currentGallery = []; diff --git a/assets/ts/main.ts b/assets/ts/main.ts index 2fcc152..f3160ae 100644 --- a/assets/ts/main.ts +++ b/assets/ts/main.ts @@ -10,6 +10,8 @@ import { getColor } from 'ts/color'; import menu from 'ts/menu'; import createElement from 'ts/createElement'; import StackColorScheme from 'ts/colorScheme'; +import { setupScrollspy } from 'ts/scrollspy'; +import { setupSmoothAnchors } from "ts/smoothAnchors"; let Stack = { init: () => { @@ -21,6 +23,8 @@ let Stack = { const articleContent = document.querySelector('.article-content') as HTMLElement; if (articleContent) { new StackGallery(articleContent); + setupSmoothAnchors(); + setupScrollspy(); } /** @@ -54,6 +58,39 @@ let Stack = { 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')); } } diff --git a/assets/ts/scrollspy.ts b/assets/ts/scrollspy.ts new file mode 100644 index 0000000..8a14085 --- /dev/null +++ b/assets/ts/scrollspy.ts @@ -0,0 +1,131 @@ +// Implements a scroll spy system for the ToC, displaying the current section with an indicator and scrolling to it when needed. + +// Inspired from https://gomakethings.com/debouncing-your-javascript-events/ +function debounced(func: Function) { + let timeout; + return () => { + if (timeout) { + window.cancelAnimationFrame(timeout); + } + + timeout = window.requestAnimationFrame(() => func()); + } +} + +const headersQuery = ".article-content h1[id], .article-content h2[id], .article-content h3[id], .article-content h4[id], .article-content h5[id], .article-content h6[id]"; +const tocQuery = "#TableOfContents"; +const navigationQuery = "#TableOfContents li"; +const activeClass = "active-class"; + +function scrollToTocElement(tocElement: HTMLElement, scrollableNavigation: HTMLElement) { + let textHeight = tocElement.querySelector("a").offsetHeight; + let scrollTop = tocElement.offsetTop - scrollableNavigation.offsetHeight / 2 + textHeight / 2 - scrollableNavigation.offsetTop; + if (scrollTop < 0) { + scrollTop = 0; + } + scrollableNavigation.scrollTo({ top: scrollTop, behavior: "smooth" }); +} + +type IdToElementMap = { [key: string]: HTMLElement }; + +function buildIdToNavigationElementMap(navigation: NodeListOf): IdToElementMap { + const sectionLinkRef: IdToElementMap = {}; + navigation.forEach((navigationElement: HTMLElement) => { + const link = navigationElement.querySelector("a"); + const href = link.getAttribute("href"); + if (href.startsWith("#")) { + sectionLinkRef[href.slice(1)] = navigationElement; + } + }); + + return sectionLinkRef; +} + +function computeOffsets(headers: NodeListOf) { + let sectionsOffsets = []; + headers.forEach((header: HTMLElement) => { sectionsOffsets.push({ id: header.id, offset: header.offsetTop }) }); + sectionsOffsets.sort((a, b) => a.offset - b.offset); + return sectionsOffsets; +} + +function setupScrollspy() { + let headers = document.querySelectorAll(headersQuery); + if (!headers) { + console.warn("No header matched query", headers); + return; + } + + let scrollableNavigation = document.querySelector(tocQuery) as HTMLElement | undefined; + if (!scrollableNavigation) { + console.warn("No toc matched query", tocQuery); + return; + } + + let navigation = document.querySelectorAll(navigationQuery); + if (!navigation) { + console.warn("No navigation matched query", navigationQuery); + return; + } + + let sectionsOffsets = computeOffsets(headers); + + // We need to avoid scrolling when the user is actively interacting with the ToC. Otherwise, if the user clicks on a link in the ToC, + // we would scroll their view, which is not optimal usability-wise. + let tocHovered: boolean = false; + scrollableNavigation.addEventListener("mouseenter", debounced(() => tocHovered = true)); + scrollableNavigation.addEventListener("mouseleave", debounced(() => tocHovered = false)); + + let activeSectionLink: Element; + + let idToNavigationElement: IdToElementMap = buildIdToNavigationElementMap(navigation); + + function scrollHandler() { + let scrollPosition = document.documentElement.scrollTop || document.body.scrollTop; + + let newActiveSection: HTMLElement | undefined; + + // Find the section that is currently active. + // It is possible for no section to be active, so newActiveSection may be undefined. + sectionsOffsets.forEach((section) => { + if (scrollPosition >= section.offset - 20) { + newActiveSection = document.getElementById(section.id); + } + }); + + // Find the link for the active section. Once again, there are a few edge cases: + // - No active section = no link => undefined + // - No active section but the link does not exist in toc (e.g. because it is outside of the applicable ToC levels) => undefined + let newActiveSectionLink: HTMLElement | undefined + if (newActiveSection) { + newActiveSectionLink = idToNavigationElement[newActiveSection.id]; + } + + if (newActiveSection && !newActiveSectionLink) { + // The active section does not have a link in the ToC, so we can't scroll to it. + console.debug("No link found for section", newActiveSection); + } else if (newActiveSectionLink !== activeSectionLink) { + if (activeSectionLink) + activeSectionLink.classList.remove(activeClass); + if (newActiveSectionLink) { + newActiveSectionLink.classList.add(activeClass); + if (!tocHovered) { + // Scroll so that newActiveSectionLink is in the middle of scrollableNavigation, except when it's from a manual click (hence the tocHovered check) + scrollToTocElement(newActiveSectionLink, scrollableNavigation); + } + } + activeSectionLink = newActiveSectionLink; + } + } + + window.addEventListener("scroll", debounced(scrollHandler)); + + // Resizing may cause the offset values to change: recompute them. + function resizeHandler() { + sectionsOffsets = computeOffsets(headers); + scrollHandler(); + } + + window.addEventListener("resize", debounced(resizeHandler)); +} + +export { setupScrollspy }; \ No newline at end of file diff --git a/assets/ts/search.tsx b/assets/ts/search.tsx index 8e4eb6f..68db7b3 100644 --- a/assets/ts/search.tsx +++ b/assets/ts/search.tsx @@ -8,6 +8,11 @@ interface pageData { matchCount: number } +interface match { + start: number, + end: number +} + /** * Escape HTML tags as HTML entities * Edited from: @@ -53,79 +58,131 @@ class Search { this.bindSearchForm(); } - private async searchKeywords(keywords: string[]) { - const rawData = await this.getData(); - let results: pageData[] = []; - - /// Sort keywords by their length - keywords.sort((a, b) => { - return b.length - a.length + /** + * 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(`${replaceHTMLEnt(str.substring(item.start, end))}`); + 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 } - let matched = false; - - for (const keyword of keywords) { - if (keyword === '') continue; - - const regex = new RegExp(escapeRegExp(replaceHTMLEnt(keyword)), 'gi'); - - const contentMatch = regex.exec(result.content); - regex.lastIndex = 0; /// Reset regex - - const titleMatch = regex.exec(result.title); - regex.lastIndex = 0; /// Reset regex - - if (titleMatch) { - result.title = result.title.replace(regex, Search.marker); - } - - if (titleMatch || contentMatch) { - matched = true; - ++result.matchCount; - - let start = 0, - end = 100; - - if (contentMatch) { - start = contentMatch.index - 20; - end = contentMatch.index + 80 - - if (start < 0) start = 0; - } - - if (result.preview.indexOf(keyword) !== -1) { - result.preview = result.preview.replace(regex, Search.marker); - } - else { - if (start !== 0) result.preview += `[...] `; - result.preview += `${result.content.slice(start, end).replace(regex, Search.marker)} `; - } - } + const contentMatchAll = item.content.matchAll(regex); + for (const match of Array.from(contentMatchAll)) { + contentMatches.push({ + start: match.index, + end: match.index + match[0].length + }); } - if (matched) { - result.preview += '[...]'; - results.push(result); + 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 */ + /// Result with more matches appears first return results.sort((a, b) => { return b.matchCount - a.matchCount; }); } - public static marker(match) { - return '' + match + ''; - } - private async doSearch(keywords: string[]) { const startTime = performance.now(); @@ -150,6 +207,11 @@ class Search { /// 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; @@ -160,7 +222,7 @@ class Search { const eventHandler = (e) => { e.preventDefault(); - const keywords = this.input.value; + const keywords = this.input.value.trim(); Search.updateQueryString(keywords, true); @@ -225,7 +287,7 @@ class Search {

- +
{item.image &&
diff --git a/assets/ts/smoothAnchors.ts b/assets/ts/smoothAnchors.ts new file mode 100644 index 0000000..0718bf5 --- /dev/null +++ b/assets/ts/smoothAnchors.ts @@ -0,0 +1,34 @@ +// Implements smooth scrolling when clicking on an anchor link. +// This is required instead of using modern CSS because Chromium does not currently support scrolling +// one element with scrollTo while another element is scrolled because of a click on a link. This would +// thus not work with the ToC scrollspy and e.g. footnotes. + +// Here are additional links about this issue: +// - https://stackoverflow.com/questions/49318497/google-chrome-simultaneously-smooth-scrollintoview-with-more-elements-doesn +// - https://stackoverflow.com/questions/57214373/scrollintoview-using-smooth-function-on-multiple-elements-in-chrome +// - https://bugs.chromium.org/p/chromium/issues/detail?id=833617 +// - https://bugs.chromium.org/p/chromium/issues/detail?id=1043933 +// - https://bugs.chromium.org/p/chromium/issues/detail?id=1121151 + +const anchorLinksQuery = "a[href]"; + +function setupSmoothAnchors() { + document.querySelectorAll(anchorLinksQuery).forEach(aElement => { + let href = aElement.getAttribute("href"); + if (!href.startsWith("#")) { + return; + } + aElement.addEventListener("click", clickEvent => { + clickEvent.preventDefault(); + + let targetId = aElement.getAttribute("href").substring(1); + // The replace done on ':' is here for footnotes, as this character would otherwise interfere when used as a CSS selector. + let target = document.querySelector(`#${targetId.replace(":", "\\:")}`) as HTMLElement; + + window.history.pushState({}, "", aElement.getAttribute("href")); + scrollTo({ top: target.offsetTop, behavior: "smooth" }); + }); + }); +} + +export { setupSmoothAnchors }; \ No newline at end of file diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..0e5283c --- /dev/null +++ b/config.yaml @@ -0,0 +1,147 @@ +module: + hugoVersion: + extended: true + min: "0.87.0" + +params: + mainSections: + - post + featuredImageField: image + rssFullContent: true + favicon: + + footer: + since: + customText: + + dateFormat: + published: Jan 02, 2006 + lastUpdated: Jan 02, 2006 15:04 MST + + sidebar: + emoji: + subtitle: + avatar: + enabled: true + local: true + src: img/avatar.png + + article: + math: false + toc: true + readingTime: true + license: + enabled: false + default: Licensed under CC BY-NC-SA 4.0 + + comments: + enabled: false + provider: disqus + + disqusjs: + shortname: + apiUrl: + apiKey: + admin: + adminLabel: + + utterances: + repo: + issueTerm: pathname + label: + + remark42: + host: + site: + locale: + + vssue: + platform: + owner: + repo: + clientId: + clientSecret: + autoCreateIssue: false + + # Waline client configuration see: https://waline.js.org/en/reference/client.html + waline: + serverURL: + lang: + visitor: + avatar: + emoji: + - https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo + requiredMeta: + - name + - email + - url + placeholder: + locale: + admin: Admin + + twikoo: + envId: + region: + path: + lang: + + giscus: + repo: + repoID: + category: + categoryID: + mapping: + lightTheme: + darkTheme: + reactionsEnabled: 1 + emitMetadata: 0 + + gitalk: + owner: + admin: + repo: + clientID: + clientSecret: + + cusdis: + host: + id: + + widgets: + enabled: + - search + - archives + - tag-cloud + + archives: + limit: 5 + + tagCloud: + limit: 10 + + opengraph: + twitter: + # Your Twitter username + site: + + # Available values: summary, summary_large_image + card: summary_large_image + + defaultImage: + opengraph: + enabled: false + local: false + src: + + colorScheme: + # Display toggle + toggle: true + + # Available values: auto, light, dark + default: auto + + imageProcessing: + cover: + enabled: true + content: + enabled: true diff --git a/data/external.yaml b/data/external.yaml index 2721e76..fd2b665 100644 --- a/data/external.yaml +++ b/data/external.yaml @@ -23,16 +23,24 @@ PhotoSwipe: type: style KaTeX: - - src: https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css - integrity: sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X + - src: https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.css + integrity: sha384-RZU/ijkSsFbcmivfdRBQDtwuwVqK7GMOw6IMvKyeWL2K5UAlyp6WonmB8m7Jd0Hn type: style - - src: https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.js - integrity: sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4 + - src: https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/katex.min.js + integrity: sha384-pK1WpvzWVBQiP0/GjnvRxV4mOb0oxFuyRxJlk6vVw146n3egcN5C925NCP7a7BY8 type: script defer: true - - src: https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/contrib/auto-render.min.js - integrity: sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa + - src: https://cdn.jsdelivr.net/npm/katex@0.13.13/dist/contrib/auto-render.min.js + integrity: sha384-vZTG03m+2yp6N6BNi5iM4rW4oIwk5DfcNdFfxkk9ZWpDriOkXX8voJBFrAO7MpVl type: script defer: true + +Cactus: + - src: https://latest.cactus.chat/cactus.js + integrity: + type: script + - src: https://latest.cactus.chat/style.css + integrity: + type: style diff --git a/debug.sh b/debug.sh new file mode 100755 index 0000000..4d1312e --- /dev/null +++ b/debug.sh @@ -0,0 +1 @@ +cd exampleSite && hugo server --gc --themesDir=../.. \ No newline at end of file diff --git a/exampleSite/.gitignore b/exampleSite/.gitignore index ca4d540..6db5459 100644 --- a/exampleSite/.gitignore +++ b/exampleSite/.gitignore @@ -22,6 +22,9 @@ _testmain.go *.exe *.test -/public -/themes +public +themes +resources + +assets/jsconfig.json .DS_Store diff --git a/exampleSite/config.yaml b/exampleSite/config.yaml index b55925f..d449fc9 100644 --- a/exampleSite/config.yaml +++ b/exampleSite/config.yaml @@ -11,9 +11,13 @@ disqusShortname: hugo-theme-stack googleAnalytics: # Theme i18n support -# Available values: en, fr, id, ja, ko, pt-br, zh-cn +# Available values: en, fr, id, ja, ko, pt-br, zh-cn, zh-tw, es, de, nl, it, th, el, uk DefaultContentLanguage: en +# Set hasCJKLanguage to true if DefaultContentLanguage is in [zh-cn ja ko] +# This will make .Summary and .WordCount behave correctly for CJK languages. +hasCJKLanguage: false + permalinks: post: /p/:slug/ page: /:slug/ @@ -37,11 +41,14 @@ params: 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 @@ -50,6 +57,13 @@ params: enabled: true provider: disqus + disqusjs: + shortname: + apiUrl: + apiKey: + admin: + adminLabel: + utterances: repo: issueTerm: pathname @@ -60,6 +74,63 @@ params: site: locale: + vssue: + platform: + owner: + repo: + clientId: + clientSecret: + autoCreateIssue: false + + # Waline client configuration see: https://waline.js.org/en/reference/client.html + waline: + serverURL: + lang: + visitor: + avatar: + emoji: + - https://cdn.jsdelivr.net/gh/walinejs/emojis/weibo + requiredMeta: + - name + - email + - url + placeholder: + locale: + admin: Admin + + twikoo: + envId: + region: + path: + lang: + + # 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: enabled: - search @@ -99,31 +170,32 @@ params: content: enabled: true +### Custom menu +### See https://docs.stack.jimmycai.com/configuration/custom-menu.html +### To remove about, archive and search page menu item, remove `menu` field from their FrontMatter menu: main: - identifier: home name: Home url: / weight: -100 - pre: home + params: + ### For demonstration purpose, the home link will be open in a new tab + newTab: true + icon: home - - identifier: about - name: About - url: about - weight: -90 - pre: user + social: + - identifier: github + name: GitHub + url: https://github.com/CaiJimmy/hugo-theme-stack + params: + icon: brand-github - - identifier: archives - name: Archives - url: archives - weight: -70 - pre: archives - - - identifier: search - name: Search - url: search - weight: -60 - pre: search + - identifier: twitter + name: Twitter + url: https://twitter.com + params: + icon: brand-twitter related: includeNewer: true @@ -137,5 +209,19 @@ related: weight: 200 markup: + goldmark: + renderer: + ## Set to true if you have HTML content inside Markdown + unsafe: false + tableOfContents: + endLevel: 4 + ordered: true + startLevel: 2 highlight: noClasses: false + codeFences: true + guessSyntax: true + lineNoStart: 1 + lineNos: true + lineNumbersInTable: true + tabWidth: 4 diff --git a/exampleSite/content/categories/Test/_index.md b/exampleSite/content/categories/Test/_index.md index 0b3cfa9..de42112 100644 --- a/exampleSite/content/categories/Test/_index.md +++ b/exampleSite/content/categories/Test/_index.md @@ -1,9 +1,9 @@ --- title: "Test" -description: "This is a example category" +description: "This is an example category" slug: "test" image: "hutomo-abrianto-l2jk-uxb1BY-unsplash.jpg" style: background: "#2a9d8f" color: "#fff" ---- \ No newline at end of file +--- diff --git a/exampleSite/content/page/about.md b/exampleSite/content/page/about/index.md similarity index 81% rename from exampleSite/content/page/about.md rename to exampleSite/content/page/about/index.md index 8a8789f..494cb46 100644 --- a/exampleSite/content/page/about.md +++ b/exampleSite/content/page/about/index.md @@ -1,12 +1,19 @@ -+++ -title = "About" -description = "Hugo, the world's fastest framework for building websites" -date = "2019-02-28" -aliases = ["about-us", "about-hugo", "contact"] -author = "Hugo Authors" -license = "CC BY-NC-ND" -lastmod = "2020-10-09" -+++ +--- +title: About +description: Hugo, the world's fastest framework for building websites +date: '2019-02-28' +aliases: + - about-us + - about-hugo + - contact +license: CC BY-NC-ND +lastmod: '2020-10-09' +menu: + main: + weight: -90 + params: + icon: user +--- Written in Go, Hugo is an open source static site generator available under the [Apache Licence 2.0.](https://github.com/gohugoio/hugo/blob/master/LICENSE) Hugo supports TOML, YAML and JSON data file types, Markdown and HTML content files and uses shortcodes to add rich content. Other notable features are taxonomies, multilingual mode, image processing, custom output formats, HTML/CSS/JS minification and support for Sass SCSS workflows. diff --git a/exampleSite/content/page/archives.md b/exampleSite/content/page/archives.md deleted file mode 100644 index e25595b..0000000 --- a/exampleSite/content/page/archives.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: "Archives" -date: 2019-05-28 -layout: "archives" -slug: "archives" ---- diff --git a/exampleSite/content/page/archives/index.md b/exampleSite/content/page/archives/index.md new file mode 100644 index 0000000..2862deb --- /dev/null +++ b/exampleSite/content/page/archives/index.md @@ -0,0 +1,11 @@ +--- +title: "Archives" +date: 2019-05-28 +layout: "archives" +slug: "archives" +menu: + main: + weight: -70 + params: + icon: archives +--- diff --git a/exampleSite/content/page/links/index.md b/exampleSite/content/page/links/index.md new file mode 100644 index 0000000..9e29bf3 --- /dev/null +++ b/exampleSite/content/page/links/index.md @@ -0,0 +1,37 @@ +--- +title: Links +links: + - title: GitHub + description: GitHub is the world's largest software development platform. + website: https://github.com + image: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png + - title: TypeScript + description: TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. + website: https://www.typescriptlang.org + image: ts-logo-128.jpg +menu: + main: + weight: -50 + params: + icon: link + +comments: false +--- + +To use this feature, add `links` section to frontmatter. + +This page's frontmatter: + +```yaml +links: + - title: GitHub + description: GitHub is the world's largest software development platform. + website: https://github.com + image: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png + - title: TypeScript + description: TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. + website: https://www.typescriptlang.org + image: ts-logo-128.jpg +``` + +`image` field accepts both local and external images. \ No newline at end of file diff --git a/exampleSite/content/page/links/ts-logo-128.jpg b/exampleSite/content/page/links/ts-logo-128.jpg new file mode 100644 index 0000000..85e3323 Binary files /dev/null and b/exampleSite/content/page/links/ts-logo-128.jpg differ diff --git a/exampleSite/content/page/search.md b/exampleSite/content/page/search/index.md similarity index 52% rename from exampleSite/content/page/search.md rename to exampleSite/content/page/search/index.md index 0363546..b2a5943 100644 --- a/exampleSite/content/page/search.md +++ b/exampleSite/content/page/search/index.md @@ -5,4 +5,9 @@ layout: "search" outputs: - html - json +menu: + main: + weight: -60 + params: + icon: search --- \ No newline at end of file diff --git a/exampleSite/content/post/markdown-syntax/index.md b/exampleSite/content/post/markdown-syntax/index.md index 8c53bc2..0254cca 100644 --- a/exampleSite/content/post/markdown-syntax/index.md +++ b/exampleSite/content/post/markdown-syntax/index.md @@ -69,6 +69,10 @@ Tables aren't part of the core Markdown spec, but Hugo supports supports them ou | -------- | -------- | ------ | | *italics* | **bold** | `code` | +| A | B | C | D | E | F | +|----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------|------------------------------------------------------------|----------------------------------------------------------------------| +| Lorem ipsum dolor sit amet, consectetur adipiscing elit. | Phasellus ultricies, sapien non euismod aliquam, dui ligula tincidunt odio, at accumsan nulla sapien eget ex. | Proin eleifend dictum ipsum, non euismod ipsum pulvinar et. Vivamus sollicitudin, quam in pulvinar aliquam, metus elit pretium purus | Proin sit amet velit nec enim imperdiet vehicula. | Ut bibendum vestibulum quam, eu egestas turpis gravida nec | Sed scelerisque nec turpis vel viverra. Vivamus vitae pretium sapien | + ## Code Blocks #### Code block with backticks @@ -113,6 +117,16 @@ Tables aren't part of the core Markdown spec, but Hugo supports supports them ou {{< /highlight >}} +#### Diff code block + +```diff +[dependencies.bevy] +git = "https://github.com/bevyengine/bevy" +rev = "11f52b8c72fc3a568e8bb4a4cd1f3eb025ac2e13" +- features = ["dynamic"] ++ features = ["jpeg", "dynamic"] +``` + ## List Types #### Ordered List @@ -148,3 +162,7 @@ Xn + Yn = Zn Press CTRL+ALT+Delete to end the session. Most salamanders are nocturnal, and hunt for insects, worms, and other small creatures. + +## Hyperlinked image + +[![Google](https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png)](https://google.com) \ No newline at end of file diff --git a/exampleSite/content/post/rich-content/index.md b/exampleSite/content/post/rich-content/index.md index 407c539..f2b45db 100644 --- a/exampleSite/content/post/rich-content/index.md +++ b/exampleSite/content/post/rich-content/index.md @@ -32,3 +32,11 @@ Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-manageme ## Vimeo Simple Shortcode {{< vimeo_simple 48912912 >}} + +## bilibilibi Shortcode + +{{< bilibili av498363026 >}} + +## Gist Shortcode + +{{< gist spf13 7896402 >}} \ No newline at end of file diff --git a/exampleSite/plugins/netlify-plugin-hugo-cache-resources/README.md b/exampleSite/plugins/netlify-plugin-hugo-cache-resources/README.md deleted file mode 100644 index a7c1ef2..0000000 --- a/exampleSite/plugins/netlify-plugin-hugo-cache-resources/README.md +++ /dev/null @@ -1,27 +0,0 @@ -> Original Repo: https://github.com/cdeleeuwe/netlify-plugin-hugo-cache-resources -> -# Netlify Build Plugin: Persist Hugo resources Between Builds - -Persist [Hugo](https://gohugo.io/) resources folder between Netlify builds for huge build speed improvements! ⚡️ - -This plugin caches the `resources` folder after build. If you are processing many images, this would improve build duration significantly. - -Note: Restoring cache only comes from the production branch. So once cache is saved on the production branch, the next preview build would use the cache. For example, when pushing to the same preview build, the latest preview build will not get the cache from the previous preview build, but will get it from master. - -## Usage - -To install, add the following lines to your `netlify.toml` file: - -```toml -[build] - publish = "public" - -[[plugins]] - package = "netlify-plugin-hugo-cache-resources" - - [plugins.inputs] - # If it should show more verbose logs (optional, default = true) - debug = true -``` - -Note: The `[[plugins]]` line is required for each plugin, even if you have other plugins in your `netlify.toml` file already. \ No newline at end of file diff --git a/exampleSite/plugins/netlify-plugin-hugo-cache-resources/index.js b/exampleSite/plugins/netlify-plugin-hugo-cache-resources/index.js deleted file mode 100644 index fd8ffb6..0000000 --- a/exampleSite/plugins/netlify-plugin-hugo-cache-resources/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const getResourcesDir = () => { - return 'exampleSite/resources'; -} - -const printList = (items) => { - console.log('---'); - items.forEach((item, index) => { - console.log(`${index + 1}. ${item}`); - }); -} - -module.exports = { - - async onPreBuild({ utils, inputs }) { - const path = getResourcesDir(); - const success = await utils.cache.restore(path); - - if (success) { - const cachedFiles = await utils.cache.list(path); - console.log(`Restored cached resources folder. Total files: ${cachedFiles.length}`); - if (inputs.debug) printList(cachedFiles); - } else { - console.log(`No cache found for resources folder`); - } - }, - - async onPostBuild({ utils, inputs }) { - const path = getResourcesDir(); - const success = await utils.cache.save(path); - - if (success) { - const cachedFiles = await utils.cache.list(path); - console.log(`Saved resources folder to cache. Total files: ${cachedFiles.length}`); - if (inputs.debug) printList(cachedFiles); - } else { - console.log(`No resources folder cached`); - } - } -}; \ No newline at end of file diff --git a/exampleSite/plugins/netlify-plugin-hugo-cache-resources/manifest.yml b/exampleSite/plugins/netlify-plugin-hugo-cache-resources/manifest.yml deleted file mode 100644 index d6a208f..0000000 --- a/exampleSite/plugins/netlify-plugin-hugo-cache-resources/manifest.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: netlify-plugin-hugo-cache -inputs: - - name: debug - description: Show more verbose logs - default: true \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..776617e --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/CaiJimmy/hugo-theme-stack/v3 + +go 1.12 diff --git a/i18n/de.yaml b/i18n/de.yaml new file mode 100644 index 0000000..00a8e11 --- /dev/null +++ b/i18n/de.yaml @@ -0,0 +1,70 @@ +toggleMenu: + other: Menü umschalten + +darkMode: + other: Dunkler Modus + +list: + page: + one: "{{ .Count }} Seite" + other: "{{ .Count }} Seiten" + + section: + other: Abschnitt + + subsection: + one: Unterabschnitt + other: Unterabschnitte + +article: + back: + other: Zurück + + tableOfContents: + other: Inhaltsverzeichnis + + relatedContents: + other: Verwandte Inhalte + + lastUpdatedOn: + other: Zuletzt aktualisiert am + + readingTime: + one: "{{ .Count }} Minute Lesezeit" + other: "{{ .Count }} Minuten Lesezeit" + +notFound: + title: + other: Seite nicht gefunden + + subtitle: + other: Diese Seite existiert nicht + +widget: + archives: + title: + other: Archiv + + more: + other: Weitere + + tagCloud: + title: + other: Schlagwörter + +search: + title: + other: Suche + + placeholder: + other: Etwas tippen... + + resultTitle: + other: "#PAGES_COUNT Seiten (#TIME_SECONDS Sekunden)" + +footer: + builtWith: + other: Erstellt mit {{ .Generator }} + + designedBy: + other: Theme {{ .Theme }} gestaltet von {{ .DesignedBy }} diff --git a/i18n/el.yaml b/i18n/el.yaml new file mode 100644 index 0000000..38d867f --- /dev/null +++ b/i18n/el.yaml @@ -0,0 +1,70 @@ +toggleMenu: + other: Εναλλαγή Μενού + +darkMode: + other: Σκοτεινό θέμα + +list: + page: + one: "{{ .Count }} σελιδα" + other: "{{ .Count }} σελιδες" + + section: + other: Ενότητα + + subsection: + one: Υποενότητα + other: Υποενότητες + +article: + back: + other: Πισω + + tableOfContents: + other: Πινακας περιεχομενων + + relatedContents: + other: Σχετικο περιεχομενο + + lastUpdatedOn: + other: Τελευταια τροποποιηση στις + + readingTime: + one: "{{ .Count }} λεπτό ανάγνωσης" + ### Seems that there's no need to add 's' even if it's plural in English + other: "{{ .Count }} λεπτά ανάγνωσης" + +notFound: + title: + other: Δε βρέθηκε + subtitle: + other: Η σελίδα δε βρέθηκε. + +widget: + archives: + title: + other: Αρχειο + + more: + other: Περισσότερα + + tagCloud: + title: + other: Tags + +search: + title: + other: Αναζήτηση + + placeholder: + other: Πληκτρολογήστε κάτι... + + resultTitle: + other: "#PAGES_COUNT σελιδες (#TIME_SECONDS δευτερολεπτα)" + +footer: + builtWith: + other: Δημιουργήθηκε με τη χρήση {{ .Generator }} + + designedBy: + other: Το θέμα {{ .Theme }} σχεδιάστηκε από το {{ .DesignedBy }} diff --git a/i18n/en.yaml b/i18n/en.yaml index 99d34ce..0197d89 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -17,23 +17,37 @@ list: other: Subsections article: + back: + other: Back + + tableOfContents: + other: Table of contents + relatedContents: other: Related contents + lastUpdatedOn: other: Last updated on + readingTime: + one: "{{ .Count }} minute read" + other: "{{ .Count }} minute read" + notFound: title: other: Not Found + subtitle: - other: This page does not exist. + other: This page does not exist widget: archives: title: other: Archives + more: other: More + tagCloud: title: other: Tags @@ -41,13 +55,16 @@ widget: search: title: other: Search + placeholder: other: Type something... + resultTitle: other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)" footer: builtWith: other: Built with {{ .Generator }} + designedBy: other: Theme {{ .Theme }} designed by {{ .DesignedBy }} diff --git a/i18n/es.yaml b/i18n/es.yaml new file mode 100644 index 0000000..9e1d6e1 --- /dev/null +++ b/i18n/es.yaml @@ -0,0 +1,70 @@ +toggleMenu: + other: Toggle Menu + +darkMode: + other: Modo oscuro + +list: + page: + one: "{{ .Count }} página" + other: "{{ .Count }} páginas" + + section: + other: Sección + + subsection: + one: Subsección + other: Subsecciones + +article: + back: + other: Volver + + tableOfContents: + other: Tabla de contenido + + relatedContents: + other: Contenidos relacionados + + lastUpdatedOn: + other: Última vez actualizado + + readingTime: + one: "{{ .Count }} minuto a leer" + other: "{{ .Count }} minutos a leer" + +notFound: + title: + other: No Encontrado + + subtitle: + other: Esta página no existe + +widget: + archives: + title: + other: Archivo + + more: + other: Más + + tagCloud: + title: + other: Etiquetas + +search: + title: + other: Búsqueda + + placeholder: + other: Teclea algo... + + resultTitle: + other: "#PAGES_COUNT páginas en (#TIME_SECONDS segundos)" + +footer: + builtWith: + other: Creado con {{ .Generator }} + + designedBy: + other: Tema {{ .Theme }} diseñado por {{ .DesignedBy }} diff --git a/i18n/fr.yaml b/i18n/fr.yaml index 59cc111..ac2e51a 100644 --- a/i18n/fr.yaml +++ b/i18n/fr.yaml @@ -1,12 +1,38 @@ toggleMenu: other: Afficher le menu +darkMode: + other: Mode sombre + +list: + page: + one: "{{ .Count }} page" + other: "{{ .Count }} pages" + + section: + other: Section + + subsection: + one: Sous-section + other: Sous-sections + article: + back: + other: Retour + + tableOfContents: + other: Table des matières + relatedContents: other: Contenus liés + lastUpdatedOn: other: Dernière mise à jour le + readingTime: + one: "{{ .Count }} minute de lecture" + other: "{{ .Count }} minutes de lecture" + notFound: title: other: Page non trouvée @@ -17,8 +43,10 @@ widget: archives: title: other: Archives + more: other: Autres + tagCloud: title: other: Mots clés @@ -26,13 +54,16 @@ widget: search: title: other: Rechercher + placeholder: other: Cherchez un article, une publication, etc. + resultTitle: other: "#PAGES_COUNT pages (#TIME_SECONDS secondes)" footer: builtWith: other: Généré avec {{ .Generator }} + designedBy: other: Thème {{ .Theme }} conçu par {{ .DesignedBy }} diff --git a/i18n/id.yaml b/i18n/id.yaml index 31cea39..2af893a 100644 --- a/i18n/id.yaml +++ b/i18n/id.yaml @@ -1,24 +1,52 @@ toggleMenu: other: Tampilkan Menu +darkMode: + other: Mode Gelap + +list: + page: + one: "{{ .Count }} halaman" + other: "{{ .Count }} halaman" + + section: + other: Bagian + + subsection: + one: Subbagian + other: Subbagian + article: + back: + other: Kembali + + tableOfContents: + other: Daftar Isi + relatedContents: other: Konten terkait + lastUpdatedOn: other: Terakhir diperbarui pada + readingTime: + one: "Waktu Membaca: {{ .Count }} menit" + other: "Waktu Membaca: {{ .Count }} menit" + notFound: title: - other: Not Found + other: Tidak ditemukan subtitle: - other: Halaman ini tidak ada. + other: Halaman yang Anda akses tidak ditemukan. widget: archives: title: other: Arsip + more: other: Lebih + tagCloud: title: other: Tag @@ -26,13 +54,16 @@ widget: search: title: other: Cari + placeholder: other: Ketik sesuatu... + resultTitle: other: "#PAGES_COUNT halaman (#TIME_SECONDS detik)" footer: builtWith: other: Dibangun dengan {{ .Generator }} + designedBy: other: Tema {{ .Theme }} dirancang oleh {{ .DesignedBy }} diff --git a/i18n/it.yaml b/i18n/it.yaml new file mode 100644 index 0000000..6c4114c --- /dev/null +++ b/i18n/it.yaml @@ -0,0 +1,69 @@ +toggleMenu: + other: Toggle Menu + +darkMode: + other: Dark Mode + +list: + page: + one: "{{ .Count }} pagina" + other: "{{ .Count }} pagine" + + section: + other: Sezione + + subsection: + one: Sottosezione + other: Sottosezioni + +article: + back: + other: Indietro + + tableOfContents: + other: Indice + + relatedContents: + other: Contenuti correlati + + lastUpdatedOn: + other: Aggiornato il + + readingTime: + one: "{{ .Count }} min per leggere" + other: "{{ .Count }} min per leggere" + +notFound: + title: + other: Non trovato + subtitle: + other: Questa pagina non esiste. + +widget: + archives: + title: + other: Archivi + + more: + other: Di più + + tagCloud: + title: + other: Tags + +search: + title: + other: Cerca + + placeholder: + other: Scrivi qualcosa... + + resultTitle: + other: "#PAGES_COUNT pagine (#TIME_SECONDS secondi)" + +footer: + builtWith: + other: Realizzato con {{ .Generator }} + + designedBy: + other: Tema {{ .Theme }} realizzato da {{ .DesignedBy }} diff --git a/i18n/ja.yaml b/i18n/ja.yaml index e9744c2..3f9978d 100644 --- a/i18n/ja.yaml +++ b/i18n/ja.yaml @@ -5,11 +5,21 @@ darkMode: other: ダークモード article: + back: + other: 前のページ + + tableOfContents: + other: 目次 + relatedContents: other: 関連するコンテンツ + lastUpdatedOn: other: 最終更新 + readingTime: + other: "読了時間: {{ .Count }}分" + notFound: title: other: 404 Not Found @@ -20,16 +30,20 @@ widget: archives: title: other: アーカイブ + more: other: さらに見る + tagCloud: title: other: タグ search: title: - other: サーチ + other: 検索 + placeholder: other: 入力... + resultTitle: other: "#PAGES_COUNT 件 (#TIME_SECONDS 秒)" diff --git a/i18n/ko.yaml b/i18n/ko.yaml index d9916cd..b2202e6 100644 --- a/i18n/ko.yaml +++ b/i18n/ko.yaml @@ -1,15 +1,42 @@ toggleMenu: other: 메뉴 여닫기 +darkMode: + other: 다크 모드 + +list: + page: + one: "{{ .Count }} 페이지" + other: "{{ .Count }} 페이지" + + section: + other: 섹션 + + subsection: + one: 서브섹션 + other: 서브섹션 + article: + back: + other: 뒤로가기 + + tableOfContents: + other: 목차 + relatedContents: other: 관련 글 + lastUpdatedOn: other: "마지막 수정: " + + readingTime: + one: "{{ .Count }} 분 정도" + other: "{{ .Count }} 분 정도" notFound: title: other: 찾을 수 없음 + subtitle: other: 페이지를 찾을 수 없습니다. @@ -19,6 +46,7 @@ widget: other: 보관함 more: other: 더보기 + tagCloud: title: other: 태그 @@ -26,8 +54,10 @@ widget: search: title: other: 검색 + placeholder: other: 검색어를 입력하세요... + resultTitle: other: "#PAGES_COUNT 페이지 (#TIME_SECONDS 초)" diff --git a/i18n/nl.yaml b/i18n/nl.yaml new file mode 100644 index 0000000..b9ecad3 --- /dev/null +++ b/i18n/nl.yaml @@ -0,0 +1,53 @@ +toggleMenu: + other: Open Menu + +darkMode: + other: Donkere modus + +list: + page: + one: "{{ .Count }} pagina" + other: "{{ .Count }} pagina's" + + section: + other: Sectie + + subsection: + one: Subsectie + other: Subsecties + +article: + relatedContents: + other: Gerelateerde inhoud + lastUpdatedOn: + other: Laatst bijgewerkt op + +notFound: + title: + other: Niet gevonden + subtitle: + other: Deze pagina bestaat niet. + +widget: + archives: + title: + other: Archief + more: + other: Meer + tagCloud: + title: + other: Tags + +search: + title: + other: Zoeken + placeholder: + other: Typ iets + resultTitle: + other: "#PAGES_COUNT pagina's (#TIME_SECONDS seconden)" + +footer: + builtWith: + other: Gemaakt met {{ .Generator }} + designedBy: + other: Theme {{ .Theme }} ontworpen door {{ .DesignedBy }} diff --git a/i18n/pl.yaml b/i18n/pl.yaml new file mode 100644 index 0000000..f261f86 --- /dev/null +++ b/i18n/pl.yaml @@ -0,0 +1,69 @@ +toggleMenu: + other: Przełącz Menu + +darkMode: + other: Tryb ciemny + +list: + page: + one: "{{ .Count }} strona" + other: "{{ .Count }} stron" + + section: + other: Sekcja + + subsection: + one: Podsekcja + other: Podsekcje + +article: + back: + other: Wróć + + tableOfContents: + other: Spis treści + + relatedContents: + other: Powiązane artykuły + + lastUpdatedOn: + other: Ostatnio zaktualizowany + + readingTime: + one: "Przeczytasz w {{ .Count }} minutę" + other: "Przeczytasz w {{ .Count }} minut" + +notFound: + title: + other: Nie znaleziono + subtitle: + other: Ta strona nie istnieje + +widget: + archives: + title: + other: Archiwum + + more: + other: Więcej + + tagCloud: + title: + other: Tagi + +search: + title: + other: Szukaj + + placeholder: + other: Wpisz coś... + + resultTitle: + other: "#PAGES_COUNT stron (#TIME_SECONDS sekund)" + +footer: + builtWith: + other: Zbudowano z {{ .Generator }} + + designedBy: + other: Motyw {{ .Theme }} zaprojektowany przez {{ .DesignedBy }} diff --git a/i18n/pt-BR.yaml b/i18n/pt-BR.yaml index 8240755..ffdbe41 100644 --- a/i18n/pt-BR.yaml +++ b/i18n/pt-BR.yaml @@ -17,11 +17,22 @@ list: other: Subseções article: + back: + other: Voltar + + tableOfContents: + other: Índice + relatedContents: - other: Conteúdos Relacionados + other: Conteúdo relacionado + lastUpdatedOn: other: Última atualização em + readingTime: + one: "{{ .Count }} minuto de leitura" + other: "{{ .Count }} minutos de leitura" + notFound: title: other: Não Encontrado diff --git a/i18n/ru.yaml b/i18n/ru.yaml index f0c562f..65e3be6 100644 --- a/i18n/ru.yaml +++ b/i18n/ru.yaml @@ -21,10 +21,16 @@ list: other: Подразделы article: + back: + other: Назад relatedContents: other: Также рекомендуем lastUpdatedOn: other: Обновлено + tableOfContents: + other: Содержание + readingTime: + other: "Время чтения: {{ .Count }} мин." notFound: title: diff --git a/i18n/th.yaml b/i18n/th.yaml new file mode 100644 index 0000000..b2ae1cd --- /dev/null +++ b/i18n/th.yaml @@ -0,0 +1,70 @@ +toggleMenu: + other: สลับเมนู + +darkMode: + other: ธีมมืด + +list: + page: + one: "{{ .Count }} หน้า" + other: "{{ .Count }} หน้า" + + section: + other: หมวดหมู่ + + subsection: + one: หมวดหมู่ย่อย + other: หมวดหมู่ย่อยอื่นๆ + +article: + back: + other: กลับไป + + tableOfContents: + other: สารบัญ + + relatedContents: + other: เนื้อหาคล้ายคลึงกัน + + lastUpdatedOn: + other: อัปเดตล่าสุดเมื่อ + + readingTime: + one: "น่าจะใช้เวลา {{ .Count }} นาทีในการอ่าน" + other: "น่าจะใช้เวลา {{ .Count }} นาทีในการอ่าน" + +notFound: + title: + other: ไม่พบหัวข้อ + + subtitle: + other: ไม่พบหน้านี้ในระบบ + +widget: + archives: + title: + other: เนื้อหาที่เก็บถาวรแล้ว + + more: + other: อื่นๆ นอกจากนี้ + + tagCloud: + title: + other: แท็ก + +search: + title: + other: ค้นหา + + placeholder: + other: พิมพ์เพื่อค้นหา ... + + resultTitle: + other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)" + +footer: + builtWith: + other: ถูกสร้างด้วย {{ .Generator }} + + designedBy: + other: ธีม {{ .Theme }} ออกแบบโดย {{ .DesignedBy }} diff --git a/i18n/uk.yaml b/i18n/uk.yaml new file mode 100644 index 0000000..966e141 --- /dev/null +++ b/i18n/uk.yaml @@ -0,0 +1,71 @@ +toggleMenu: + other: Показати меню + +darkMode: + other: Темна тема + +list: + page: + one: "{{ .Count }} сторінка" + few: "{{ .Count }} сторінки" + other: "{{ .Count }} сторінок" + + section: + other: Секція + + subsection: + one: Підсекція + other: Підсекції + +article: + back: + other: Назад + + tableOfContents: + other: Зміст + + relatedContents: + other: Схожі матеріали + + lastUpdatedOn: + other: Востаннє оновлено + + readingTime: + one: "Час читання: {{ .Count }} хв" + other: "Час читання: {{ .Count }} хв" + +notFound: + title: + other: Не знайдено + + subtitle: + other: Ця сторінка не існує + +widget: + archives: + title: + other: Архіви + + more: + other: Більше + + tagCloud: + title: + other: Теґи + +search: + title: + other: Пошук + + placeholder: + other: Напишіть що-небудь... + + resultTitle: + other: "#PAGES_COUNT сторінок (#TIME_SECONDS секунд)" + +footer: + builtWith: + other: Створено з {{ .Generator }} + + designedBy: + other: Тема {{ .Theme }}, дизайн {{ .DesignedBy }} diff --git a/i18n/zh-CN.yaml b/i18n/zh-CN.yaml index 9f1ac94..0f08310 100644 --- a/i18n/zh-CN.yaml +++ b/i18n/zh-CN.yaml @@ -5,11 +5,21 @@ darkMode: other: 暗色模式 article: + back: + other: 返回 + + tableOfContents: + other: 目录 + relatedContents: other: 相关文章 + lastUpdatedOn: other: 最后更新于 + readingTime: + other: "阅读时长: {{ .Count }} 分钟" + notFound: title: other: 404 错误 @@ -20,8 +30,10 @@ widget: archives: title: other: 归档 + more: other: 更多 + tagCloud: title: other: 标签云 @@ -29,7 +41,9 @@ widget: search: title: other: 搜索 + placeholder: other: 输入关键词... + resultTitle: other: "#PAGES_COUNT 个结果 (用时 #TIME_SECONDS 秒)" diff --git a/i18n/zh-TW.yaml b/i18n/zh-TW.yaml new file mode 100644 index 0000000..871d937 --- /dev/null +++ b/i18n/zh-TW.yaml @@ -0,0 +1,49 @@ +toggleMenu: + other: 切換選單 + +darkMode: + other: 夜晚模式 + +article: + back: + other: 返回 + + tableOfContents: + other: 目錄 + + relatedContents: + other: 相關文章 + + lastUpdatedOn: + other: 最後更新 + + readingTime: + other: "閱讀時間: {{ .Count }} 分鐘" + +notFound: + title: + other: 404 錯誤 + subtitle: + other: 頁面不存在 + +widget: + archives: + title: + other: 紀錄 + + more: + other: 更多 + + tagCloud: + title: + other: 標籤雲 + +search: + title: + other: 搜尋 + + placeholder: + other: 輸入關鍵字... + + resultTitle: + other: "#PAGES_COUNT 個結果 (用時 #TIME_SECONDS 秒)" diff --git a/layouts/_default/_markup/render-image.html b/layouts/_default/_markup/render-image.html index e13896a..0ed5584 100644 --- a/layouts/_default/_markup/render-image.html +++ b/layouts/_default/_markup/render-image.html @@ -1,30 +1,41 @@ {{- $image := .Page.Resources.GetMatch (printf "%s" (.Destination | safeURL)) -}} -{{- if and $image (ne (path.Ext .Destination) ".svg") -}} - {{- $alt := .PlainText | safeHTML -}} -
- - {{- $Permalink := $image.RelPermalink -}} - {{- $Width := $image.Width -}} - {{- $Height := $image.Height -}} - {{- $Srcset := "" -}} +{{- $Permalink := .Destination | relURL | safeURL -}} +{{- $alt := .PlainText | safeHTML -}} +{{- $Width := 0 -}} +{{- $Height := 0 -}} +{{- $Srcset := "" -}} - {{- if (default true .Page.Site.Params.imageProcessing.content.enabled) -}} - {{- $small := $image.Resize "480x" -}} - {{- $big := $image.Resize "1024x" -}} - {{- $Srcset = printf "%s 480w, %s 1024w" $small.RelPermalink $big.RelPermalink -}} - {{- end -}} - - - - {{ with $alt }} -
{{ . | markdownify }}
- {{ end }} -
-{{- else -}} - {{ .Text }} -{{- end -}} \ No newline at end of file +{{/* SVG and external images won't work with gallery layout, because their width and height attributes are unknown */}} +{{- $galleryImage := false -}} + +{{- if $image -}} + {{- $notSVG := ne (path.Ext .Destination) ".svg" -}} + {{- $Permalink = $image.RelPermalink -}} + + {{- if $notSVG -}} + {{- $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 -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{ . }} \ No newline at end of file diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index ce0ddae..8a5ff95 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -6,8 +6,10 @@ {{- partial "head/colorScheme" . -}} -
- {{ partial "sidebar/left.html" . }} +
+ {{- block "left-sidebar" . -}} + {{ partial "sidebar/left.html" . }} + {{- end -}}
{{- block "main" . }}{{- end }}
diff --git a/layouts/_default/single.html b/layouts/_default/single.html index 4ae562e..8ce42e9 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -1,12 +1,61 @@ -{{ define "body-class" }}article-page keep-sidebar{{ end }} +{{ define "body-class" }} + {{ $TOCEnabled := default (default false .Site.Params.article.toc) .Params.toc }} + {{- .Scratch.Set "hasTOC" (and (ge (len .TableOfContents) 100) $TOCEnabled) -}} + article-page {{ if (.Scratch.Get "hasTOC") }}has-toc{{ end }} +{{ end }} + +{{ define "container-class" }} + {{ if (.Scratch.Get "hasTOC") }} + extended + {{ else }} + on-phone--column {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }} + {{ end }} +{{ end }} + {{ define "main" }} {{ partial "article/article.html" . }} - {{ if or (not (isset .Params "comments")) (eq .Params.comments "true")}} + {{ if .Params.links }} + {{ partial "article/components/links" . }} + {{ end }} + + {{ partial "article/components/related-contents" . }} + + {{ if not (eq .Params.comments false) }} {{ partial "comments/include" . }} {{ end }} {{ partialCached "footer/footer" . }} {{ partialCached "article/components/photoswipe" . }} -{{ end }} \ No newline at end of file +{{ end }} + +{{ define "left-sidebar" }} + {{ if (.Scratch.Get "hasTOC") }} +
+ + {{ (resources.Get "icons/back.svg").Content | safeHTML }} + {{ T "article.back" }} + +
+ {{ else }} + {{ partial "sidebar/left.html" . }} + {{ end }} +{{ end }} + +{{ define "right-sidebar" }} + {{ if (.Scratch.Get "hasTOC") }} + + {{ end }} +{{ end }} diff --git a/layouts/partials/article-list/compact.html b/layouts/partials/article-list/compact.html index 0f5a216..376512a 100644 --- a/layouts/partials/article-list/compact.html +++ b/layouts/partials/article-list/compact.html @@ -29,6 +29,7 @@ {{ .Title }} {{ else }} Featured image of post {{ .Title }} diff --git a/layouts/partials/article-list/tile.html b/layouts/partials/article-list/tile.html index 0fbf812..4ec5d57 100644 --- a/layouts/partials/article-list/tile.html +++ b/layouts/partials/article-list/tile.html @@ -20,8 +20,9 @@ Featured image of post {{ .context.Title }} {{ else }} diff --git a/layouts/partials/article/components/content.html b/layouts/partials/article/components/content.html index fc0f8f6..61e536c 100644 --- a/layouts/partials/article/components/content.html +++ b/layouts/partials/article/components/content.html @@ -1,3 +1,5 @@
- {{ .Content }} + + {{ $wrappedTable := printf "
${1}
" }} + {{ .Content | replaceRE "((?:.|\n)+?
)" $wrappedTable | safeHTML }}
diff --git a/layouts/partials/article/components/details.html b/layouts/partials/article/components/details.html index 5c5397b..64d6c6e 100644 --- a/layouts/partials/article/components/details.html +++ b/layouts/partials/article/components/details.html @@ -21,12 +21,25 @@ {{ end }} - {{- if not .Date.IsZero -}} + {{ if or (not .Date.IsZero) (.Site.Params.article.readingTime) }}
- {{ partial "helper/icon" "clock" }} - + {{ if not .Date.IsZero }} +
+ {{ partial "helper/icon" "date" }} + +
+ {{ end }} + + {{ if .Site.Params.article.readingTime }} +
+ {{ partial "helper/icon" "clock" }} + +
+ {{ end }}
- {{- end -}} + {{ end }}
\ No newline at end of file diff --git a/layouts/partials/article/components/footer.html b/layouts/partials/article/components/footer.html index f518d76..6853921 100644 --- a/layouts/partials/article/components/footer.html +++ b/layouts/partials/article/components/footer.html @@ -4,16 +4,16 @@ {{ if and (.Site.Params.article.license.enabled) (not (eq .Params.license false)) }}
{{ partial "helper/icon" "copyright" }} - {{ default .Site.Params.article.license.default .Params.license }} + {{ default .Site.Params.article.license.default .Params.license | markdownify }}
{{ end }} {{- if ne .Lastmod .Date -}} -
+
{{ partial "helper/icon" "clock" }} - + {{ T "article.lastUpdatedOn" }} {{ .Lastmod.Format ( or .Site.Params.dateFormat.lastUpdated "Jan 02, 2006 15:04 MST" ) }}
{{- end -}} - \ No newline at end of file + diff --git a/layouts/partials/article/components/links.html b/layouts/partials/article/components/links.html new file mode 100644 index 0000000..118dbb3 --- /dev/null +++ b/layouts/partials/article/components/links.html @@ -0,0 +1,26 @@ +
+ {{ range $i, $link := .Params.links }} + + {{ end }} +
\ No newline at end of file diff --git a/layouts/partials/comments/provider/cactus.html b/layouts/partials/comments/provider/cactus.html new file mode 100644 index 0000000..ae172d3 --- /dev/null +++ b/layouts/partials/comments/provider/cactus.html @@ -0,0 +1,29 @@ +{{- with .Site.Params.comments.cactus -}} +{{- partial "helper/external" (dict "Context" $ "Namespace" "Cactus") -}} + + + +
+ + +{{- end -}} diff --git a/layouts/partials/comments/provider/cusdis.html b/layouts/partials/comments/provider/cusdis.html new file mode 100644 index 0000000..a89746c --- /dev/null +++ b/layouts/partials/comments/provider/cusdis.html @@ -0,0 +1,21 @@ +{{- $host := default "https://cusdis.com" .Site.Params.comments.cusdis.host -}} +
+ + + diff --git a/layouts/partials/comments/provider/disqusjs.html b/layouts/partials/comments/provider/disqusjs.html new file mode 100644 index 0000000..8dbcc13 --- /dev/null +++ b/layouts/partials/comments/provider/disqusjs.html @@ -0,0 +1,61 @@ +{{- $pc := .Site.Config.Privacy.Disqus -}} +{{- $disqusjs := .Site.Params.Comments.disqusjs -}} +{{- if and (not $pc.Disable) (and $disqusjs.Shortname $disqusjs.ApiKey) -}} + +{{- $style := resources.Get "scss/partials/comments/disqusjs.scss" | resources.ToCSS | minify -}} + + +
+
+ + +
+{{- end -}} \ No newline at end of file diff --git a/layouts/partials/comments/provider/giscus.html b/layouts/partials/comments/provider/giscus.html new file mode 100644 index 0000000..f36ead9 --- /dev/null +++ b/layouts/partials/comments/provider/giscus.html @@ -0,0 +1,49 @@ +{{- with .Site.Params.comments.giscus -}} + + +{{- end -}} + diff --git a/layouts/partials/comments/provider/gitalk.html b/layouts/partials/comments/provider/gitalk.html new file mode 100644 index 0000000..95ca133 --- /dev/null +++ b/layouts/partials/comments/provider/gitalk.html @@ -0,0 +1,30 @@ +{{- with .Site.Params.comments.gitalk -}} +
+ + + + +{{ end }} diff --git a/layouts/partials/comments/provider/remark42.html b/layouts/partials/comments/provider/remark42.html index 9928825..97196c8 100644 --- a/layouts/partials/comments/provider/remark42.html +++ b/layouts/partials/comments/provider/remark42.html @@ -7,7 +7,7 @@ components: ['embed'], url: "{{ $.Permalink }}", max_shown_comments: {{ default 15 .max_shown_comments }}, - theme: document.body.dataset.scheme, + theme: document.documentElement.dataset.scheme, page_title: '{{ $.Title }}', locale: '{{ default "en" .locale }}', show_email_subscription: {{ default true .show_email_subscription }} @@ -26,4 +26,4 @@ window.REMARK42.changeTheme(e.detail); }) -{{- end -}} \ No newline at end of file +{{- end -}} diff --git a/layouts/partials/comments/provider/twikoo.html b/layouts/partials/comments/provider/twikoo.html new file mode 100644 index 0000000..30c7033 --- /dev/null +++ b/layouts/partials/comments/provider/twikoo.html @@ -0,0 +1,49 @@ + +
+ + +{{- with .Site.Params.comments.twikoo -}} + +{{- end -}} diff --git a/layouts/partials/comments/provider/utterances.html b/layouts/partials/comments/provider/utterances.html index a0c07a6..97a0a3a 100644 --- a/layouts/partials/comments/provider/utterances.html +++ b/layouts/partials/comments/provider/utterances.html @@ -31,10 +31,10 @@ addEventListener('message', event => { if (event.origin !== 'https://utteranc.es') return; - setUtterancesTheme(document.body.dataset.scheme) + setUtterancesTheme(document.documentElement.dataset.scheme) }); window.addEventListener('onColorSchemeChange', (e) => { setUtterancesTheme(e.detail) }) - \ No newline at end of file + diff --git a/layouts/partials/comments/provider/vssue.html b/layouts/partials/comments/provider/vssue.html new file mode 100644 index 0000000..0a9ea56 --- /dev/null +++ b/layouts/partials/comments/provider/vssue.html @@ -0,0 +1,27 @@ +{{- with .Site.Params.comments.vssue -}} + + +
+ + + + + +{{- end -}} diff --git a/layouts/partials/comments/provider/waline.html b/layouts/partials/comments/provider/waline.html new file mode 100644 index 0000000..3b6eee8 --- /dev/null +++ b/layouts/partials/comments/provider/waline.html @@ -0,0 +1,32 @@ + +
+ + +{{- with .Site.Params.comments.waline -}} +{{- $config := dict "el" "#waline" "dark" `html[data-scheme="dark"]` -}} +{{- $replaceKeys := dict "serverurl" "serverURL" "requiredmeta" "requiredMeta" "wordlimit" "wordLimit" "pagesize" "pageSize" "avatarcdn" "avatarCDN" "avatarforce" "avatarForce" -}} + +{{- range $key, $val := . -}} + {{- if $val -}} + {{- $replaceKey := index $replaceKeys $key -}} + {{- $k := default $key $replaceKey -}} + + {{- $config = merge $config (dict $k $val) -}} + {{- end -}} +{{- end -}} + + +{{- end -}} diff --git a/layouts/partials/footer/components/script.html b/layouts/partials/footer/components/script.html index 83522ff..4cb350c 100644 --- a/layouts/partials/footer/components/script.html +++ b/layouts/partials/footer/components/script.html @@ -3,4 +3,10 @@ {{- $opts := dict "minify" hugo.IsProduction -}} {{- $script := resources.Get "ts/main.ts" | js.Build $opts -}} - \ No newline at end of file + + +{{- with resources.Get "ts/custom.ts" -}} + {{/* Place your custom script in HUGO_SITE_FOLDER/assets/ts/custom.ts */}} + {{- $customScript := . | js.Build $opts -}} + +{{- end -}} \ No newline at end of file diff --git a/layouts/partials/footer/footer.html b/layouts/partials/footer/footer.html index 8f8340b..072465c 100644 --- a/layouts/partials/footer/footer.html +++ b/layouts/partials/footer/footer.html @@ -1,4 +1,4 @@ -{{- $ThemeVersion := "2.2.0" -}} +{{- $ThemeVersion := "3.8.0" -}}
-
\ No newline at end of file + diff --git a/layouts/partials/head/colorScheme.html b/layouts/partials/head/colorScheme.html index 560d6c0..42f4dd8 100644 --- a/layouts/partials/head/colorScheme.html +++ b/layouts/partials/head/colorScheme.html @@ -31,9 +31,9 @@ * 1. If dark mode is set already (in local storage) * 2. Auto mode & prefere color scheme is dark */ - document.body.dataset.scheme = 'dark'; + document.documentElement.dataset.scheme = 'dark'; } else { - document.body.dataset.scheme = 'light'; + document.documentElement.dataset.scheme = 'light'; } })(); - \ No newline at end of file + diff --git a/layouts/partials/head/head.html b/layouts/partials/head/head.html index fd9ef73..4d89b89 100644 --- a/layouts/partials/head/head.html +++ b/layouts/partials/head/head.html @@ -21,5 +21,5 @@ {{ end }} -{{- template "_internal/google_analytics_async.html" . -}} +{{- template "_internal/google_analytics.html" . -}} {{- partial "head/custom.html" . -}} diff --git a/layouts/partials/head/opengraph/provider/twitter.html b/layouts/partials/head/opengraph/provider/twitter.html index e74441d..03bbf22 100644 --- a/layouts/partials/head/opengraph/provider/twitter.html +++ b/layouts/partials/head/opengraph/provider/twitter.html @@ -1,5 +1,6 @@ {{- with .Site.Params.opengraph.twitter.site -}} - + + {{- end -}} {{- $title := partialCached "data/title" . .RelPermalink -}} diff --git a/layouts/partials/head/style.html b/layouts/partials/head/style.html index 431e212..ee6b017 100644 --- a/layouts/partials/head/style.html +++ b/layouts/partials/head/style.html @@ -1,3 +1,3 @@ {{ $sass := resources.Get "scss/style.scss" }} -{{ $style := $sass | resources.ToCSS | minify }} +{{ $style := $sass | resources.ToCSS | minify | resources.Fingerprint "sha256" }} \ No newline at end of file diff --git a/layouts/partials/helper/external.html b/layouts/partials/helper/external.html index e1505d4..88d9525 100644 --- a/layouts/partials/helper/external.html +++ b/layouts/partials/helper/external.html @@ -8,7 +8,7 @@ integrity="{{ . }}" {{- end -}} crossorigin="anonymous" - defer="{{ default false .defer }}" + {{ if .defer }}defer{{ end }} > {{- else if eq .type "style" -}} diff --git a/layouts/partials/helper/image.html b/layouts/partials/helper/image.html index 8791aa7..11fc3b6 100644 --- a/layouts/partials/helper/image.html +++ b/layouts/partials/helper/image.html @@ -8,7 +8,7 @@ {{ $url := urls.Parse $imageValue }} {{ if or (eq $url.Scheme "http") (eq $url.Scheme "https") }} - + {{ $result = merge $result (dict "permalink" $imageValue) }} {{ else }} {{ $pageResourceImage := .Context.Resources.GetMatch (printf "%s" ($imageValue | safeURL)) }} @@ -23,7 +23,7 @@ {{ end }} {{ else }} - {{ $result = merge $result (dict "permalink" $imageValue) }} + {{ $result = merge $result (dict "permalink" (relURL $imageValue)) }} {{ end }} {{ end }} @@ -51,11 +51,11 @@ {{ else }} - {{ $result = merge $result (dict "permalink" $defaultImageSetting.src) }} + {{ $result = merge $result (dict "permalink" (relURL $defaultImageSetting.src)) }} {{ end }} {{ end }} {{ end }} -{{ return $result }} \ No newline at end of file +{{ return $result }} diff --git a/layouts/partials/sidebar/left.html b/layouts/partials/sidebar/left.html index 14f195e..4442dfe 100644 --- a/layouts/partials/sidebar/left.html +++ b/layouts/partials/sidebar/left.html @@ -7,7 +7,9 @@
{{ with .Site.Params.sidebar.avatar }} + {{ if (default true .enabled) }}
+ {{ if not .local }} {{ else }} @@ -21,14 +23,35 @@ {{ errorf "Failed loading avatar from %q" . }} {{ end }} {{ end }} - + {{ with $.Site.Params.sidebar.emoji }} {{ . }} {{ end }}
+ {{ end }} {{ end }} -

{{ .Site.Title }}

+ +

{{ .Site.Title }}

{{ .Site.Params.sidebar.subtitle }}

+ + {{- with .Site.Menus.social -}} + + {{- end -}}