1
0
mirror of https://github.com/cotes2020/jekyll-theme-chirpy.git synced 2025-12-18 05:41:31 +00:00

Compare commits

..

27 Commits

Author SHA1 Message Date
Andrew
9706811c3a Merge a4812e4a1f into 65f960c31a 2024-11-21 07:02:30 -05:00
Andrew
a4812e4a1f Merge branch 'master' into bugfix-PR 2024-11-21 07:02:26 -05:00
Cotes Chung
65f960c31a perf: speed up page rendering and jekyll build process (#2034)
Some checks failed
Close stale issues and PRs / stale (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Style Lint / stylelint (push) Has been cancelled
Lint Commit Messages / commitlint (push) Has been cancelled
Build and Deploy / build (push) Has been cancelled
Build and Deploy / deploy (push) Has been cancelled
- Ensure inline scripts execute after the DOM has fully loaded.
- Use Rollup to bundle the theme-mode and Mermaid scripts, reducing the number of Jekyll include snippets.
2024-11-16 22:49:55 +08:00
Cotes Chung
d51345e297 ci: reduce unnecessary pr-filter runs (#2033)
- Checking the repository of the PR is more effective than checking the label to identify bot-initiated PRs
- This change also allows more flexible PR body definitions for developers with write access to the repository
2024-11-08 22:35:18 +08:00
Cotes Chung
2f844978aa chore: change stale label to inactive 2024-11-08 22:15:31 +08:00
Supreeth Mysore Venkatesh
42dea8ee29 build(deps): update wdm gem version for compatibility (#2028) 2024-11-04 00:45:59 +08:00
classicrocker883
81d3d055e4 fix: mitigate Dart Sass depreciation warnings 2024-11-02 05:05:02 -04:00
Alexander Fuks
86b13c917f chore: improve feed interoperability (#2024) 2024-11-01 14:39:03 +04:00
Cotes Chung
4ef3cd8efc ci: improve workflow triggers (#2017)
- Unchain commit-lint and CI
  - Even if a commit does not meet the CI path filter, it still needs to lint the commit message.
- Unchain PR filter and CI
  - The CI workflow needs to be triggered when the commits in a pull request are modified.
- Allow manual publishing
  - Sometimes `semantic-release` will error out due to commit messages referencing discussions, but this does not affect the final RubyGems/GitHub Release. In such cases, manual triggering of the publish process is needed to complete the remaining publishing steps.
2024-10-29 22:56:32 +08:00
Cotes Chung
c7f967529c ci: skip test for invalid PRs (#2013) 2024-10-26 16:58:07 +08:00
Cotes Chung
74ed06321c ci: block invalid pull requests (#2010) 2024-10-25 19:48:11 +08:00
Cotes Chung
d4f7f39ece refactor: simplify sidebar animation 2024-10-22 11:13:06 +08:00
Cotes Chung
c1bd9eb9ee refactor: reduce duplicate scss 2024-10-20 13:52:42 +08:00
Cotes Chung
6f461132c0 refactor: improve toc popup module 2024-10-19 21:15:31 +08:00
Alexander Fuks
03e302cbf6 chore: close toc-popup gracefully with Esc key (#1990) 2024-10-14 01:16:25 +08:00
Alexander Fuks
8a064a5e5a feat: show toc on mobile screens (#1964) 2024-10-11 22:32:10 +08:00
Bence Boros
740bd84c51 chore(i18n): update hungarian translation (#1976) 2024-10-01 23:19:39 +08:00
Cotes Chung
93f616b25d fix: pagination error when pinned posts exceed the page size (#1965) 2024-09-26 22:30:56 +08:00
Alexander Fuks
e6b87d2811 chore: remove h1 element from site title (#1960) 2024-09-24 08:21:34 +08:00
semantic-release-bot
73749067c5 Merge branch 'production' 2024-09-23 11:56:46 +00:00
semantic-release-bot
fd0df8320f chore(release): 7.1.1
## [7.1.1](https://github.com/cotes2020/jekyll-theme-chirpy/compare/v7.1.0...v7.1.1) (2024-09-23)

### Bug Fixes

* **i18n:** correct fr-FR translations ([#1949](https://github.com/cotes2020/jekyll-theme-chirpy/issues/1949)) ([367262e](367262e74d))
* **pwa:** site baseurl not passed to `app.js` ([#1955](https://github.com/cotes2020/jekyll-theme-chirpy/issues/1955)) ([5a63244](5a63244721))
2024-09-23 11:56:31 +00:00
Cotes Chung
fbcdf8ce85 Merge branch 'master' into production 2024-09-23 19:48:06 +08:00
Cotes Chung
fac6116af1 build(dev-deps): bump 7 dependencies versions 2024-09-23 19:41:28 +08:00
Cotes Chung
5a63244721 fix(pwa): site baseurl not passed to app.js (#1955) 2024-09-18 22:32:26 +08:00
Cotes Chung
3ab3b844d2 refactor: replace deprecated meta element
Replace `apple-mobile-web-app-capable` with `mobile-web-app-capable`
2024-09-18 21:41:08 +08:00
denis-games
367262e74d fix(i18n): correct fr-FR translations (#1949)
Co-authored-by: Denis Jean <dj@denisjean.fr>
2024-09-16 20:38:46 +08:00
semantic-release-bot
cbc93193e1 Merge branch 'production' 2024-08-27 07:29:10 +00:00
68 changed files with 1293 additions and 759 deletions

View File

@@ -2,13 +2,12 @@ name: CD
on:
push:
branches:
- production
tags-ignore:
- "**"
branches: [production]
tags-ignore: ["**"]
jobs:
release:
if: ${{ ! startsWith(github.event.head_commit.message, 'chore(release)') }}
permissions:
contents: write
issues: write

View File

@@ -1,17 +1,25 @@
name: "CI"
name: CI
on:
push:
branches:
- "master"
- "hotfix/**"
- master
- "hotfix/*"
paths-ignore:
- ".github/**"
- "!.github/workflows/ci.yml"
- ".gitignore"
- .gitignore
- "docs/**"
- "README.md"
- "LICENSE"
- README.md
- LICENSE
pull_request:
paths-ignore:
- ".github/**"
- "!.github/workflows/ci.yml"
- .gitignore
- "docs/**"
- README.md
- LICENSE
jobs:
build:

View File

@@ -1,5 +1,11 @@
name: Lint Commit Messages
on: pull_request
on:
push:
branches:
- master
- "hotfix/*"
pull_request:
jobs:
commitlint:

25
.github/workflows/pr-filter.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: PR Filter
on:
pull_request_target:
types: [opened, reopened]
jobs:
check-template:
if: github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Check PR Content
id: intercept
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const script = require('.github/workflows/scripts/pr-filter.js');
await script({ github, context, core });

View File

@@ -10,6 +10,7 @@ on:
required: true
BUILDER:
required: true
workflow_dispatch:
jobs:
launch:

36
.github/workflows/scripts/pr-filter.js vendored Normal file
View File

@@ -0,0 +1,36 @@
function hasTypes(markdown) {
return /## Type of change/.test(markdown) && /-\s\[x\]/i.test(markdown);
}
function hasDescription(markdown) {
return (
/## Description/.test(markdown) &&
!/## Description\s*\n\s*(##|\s*$)/.test(markdown)
);
}
module.exports = async ({ github, context, core }) => {
const pr = context.payload.pull_request;
const body = pr.body === null ? '' : pr.body;
const markdown = body.replace(/<!--[\s\S]*?-->/g, '');
const action = context.payload.action;
const isValid =
markdown !== '' && hasTypes(markdown) && hasDescription(markdown);
if (!isValid) {
await github.rest.pulls.update({
...context.repo,
pull_number: pr.number,
state: 'closed'
});
await github.rest.issues.createComment({
...context.repo,
issue_number: pr.number,
body: `Oops, it seems you've ${action} an invalid pull request. No worries, we'll close it for you.`
});
core.setFailed('PR content does not meet template requirements.');
}
};

View File

@@ -9,7 +9,7 @@ permissions:
pull-requests: write
env:
STALE_LABEL: stale
STALE_LABEL: inactive
EXEMPT_LABELS: "pending,planning,in progress"
MESSAGE: >
This conversation has been automatically marked as stale because it has not had recent activity.

View File

@@ -1,8 +1,10 @@
name: "Style Lint"
name: Style Lint
on:
push:
branches: ["master", "hotfix/**"]
branches:
- master
- "hotfix/*"
paths: ["_sass/**/*.scss"]
pull_request:
paths: ["_sass/**/*.scss"]

View File

@@ -11,4 +11,4 @@ platforms :mingw, :x64_mingw, :mswin, :jruby do
gem "tzinfo-data"
end
gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
gem "wdm", "~> 0.2.0", :platforms => [:mingw, :x64_mingw, :mswin]

View File

@@ -14,7 +14,7 @@ tabs:
categories: Catégories
tags: Tags
archives: Archives
about: A propos de
about: À propos
# the text displayed in the search bar & search results
search:
@@ -32,12 +32,12 @@ copyright:
license:
template: Cet article est sous licence :LICENSE_NAME par l'auteur.
name: CC BY 4.0
link: https://creativecommons.org/licenses/by/4.0/
link: https://creativecommons.org/licenses/by/4.0/deed.fr
# Displayed in the footer
brief: Certains droits réservés.
verbose: >-
Sauf mention contraire, les articles de ce site sont publiés sous licence
Sauf mention contraire, les articles de ce site sont publiés
sous la licence Creative Commons Attribution 4.0 International (CC BY 4.0) par l'auteur.
meta: Propulsé par :PLATFORM avec le thème :THEME

View File

@@ -14,24 +14,23 @@ tabs:
categories: Kategóriák
tags: Címkék
archives: Archívum
about: Rólam
about: Bemutatkozás
# the text displayed in the search bar & search results
search:
hint: keresés
cancel: Mégse
no_results: Oops! Nincs találat a keresésre.
no_results: Hop! Nincs találat a keresésre.
panel:
lastmod: Legutóbb frissítve
trending_tags: Népszerű Címkék
toc: Tartalom
links: Blog linkek
copyright:
# Shown at the bottom of the post
license:
template: A bejegyzés :LICENSE_NAME licenccel rendelkezik.
template: A bejegyzést a szerző :LICENSE_NAME licenc alatt engedélyezte.
name: CC BY 4.0
link: https://creativecommons.org/licenses/by/4.0/
@@ -42,7 +41,7 @@ copyright:
Creative Commons Attribution 4.0 International (CC BY 4.0) licenccel rendelkeznek,
hacsak másképp nincs jelezve.
meta: Készítve :PLATFORM motorral :THEME témával
meta: Készítve :THEME témával a :PLATFORM platformra.
not_found:
statement: Sajnáljuk, az URL-t rosszul helyeztük el, vagy valami nem létezőre mutat.
@@ -73,7 +72,21 @@ post:
title: Link másolása
succeed: Link sikeresen másolva!
# Date time format.
# See: <http://strftime.net/>, <https://day.js.org/docs/en/display/format>
df:
post:
strftime: "%Y. %B. %e."
dayjs: "YYYY. MMMM D."
archives:
strftime: "%B"
dayjs: "MMM"
# categories page
categories:
category_measure: kategória
post_measure: bejegyzés
category_measure:
singular: kategória
plural: kategória
post_measure:
singular: bejegyzés
plural: bejegyzés

View File

@@ -4,4 +4,3 @@
src="https://static.cloudflareinsights.com/beacon.min.js"
data-cf-beacon='{"token": "{{ site.analytics.cloudflare.id }}"}'
></script>
<!-- End Cloudflare Web Analytics -->

View File

@@ -2,6 +2,5 @@
<script
src="https://cdn.usefathom.com/script.js"
data-site="{{ site.analytics.fathom.id }}"
defer>
</script>
<!-- End Fathom Code -->
defer
></script>

View File

@@ -1,7 +1,7 @@
<!-- Global site tag (gtag.js) - Google Analytics -->
<script defer src="https://www.googletagmanager.com/gtag/js?id={{ site.analytics.google.id }}"></script>
<script>
document.addEventListener('DOMContentLoaded', function (event) {
document.addEventListener('DOMContentLoaded', () => {
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);

View File

@@ -1,14 +1,13 @@
<!-- Matomo -->
<script type="text/javascript">
var _paq = window._paq = window._paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function() {
<script>
document.addEventListener('DOMContentLoaded', () => {
var _paq = (window._paq = window._paq || []);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
var u="//{{ site.analytics.matomo.domain }}/";
_paq.push(['setTrackerUrl', u+'matomo.php']);
_paq.push(['setSiteId', {{ site.analytics.matomo.id }}]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
})();
});
</script>
<!-- End Matomo Code -->

View File

@@ -1,38 +1,25 @@
<!-- The Disqus lazy loading. -->
<div id="disqus_thread">
<p class="text-center text-muted small">Comments powered by <a href="https://disqus.com/">Disqus</a>.</p>
</div>
<script type="text/javascript">
<script>
var disqus_config = function () {
this.page.url = '{{ page.url | absolute_url }}';
this.page.identifier = '{{ page.url }}';
};
{%- comment -%} Lazy loading {%- endcomment -%}
var disqus_observer = new IntersectionObserver(
function (entries) {
if (entries[0].isIntersecting) {
(function () {
var d = document,
s = d.createElement('script');
s.src = 'https://{{ site.comments.disqus.shortname }}.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
function addDisqus() {
let disqusThread = document.createElement('div');
let paragraph = document.createElement('p');
disqus_observer.disconnect();
}
},
{ threshold: [0] }
);
disqusThread.id = 'disqus_thread';
paragraph.className = 'text-center text-muted small';
paragraph.innerHTML = 'Comments powered by <a href="https://disqus.com/">Disqus</a>.';
disqusThread.appendChild(paragraph);
disqus_observer.observe(document.getElementById('disqus_thread'));
const footer = document.querySelector('footer');
footer.insertAdjacentElement("beforebegin", disqusThread);
}
{%- comment -%} Auto switch theme {%- endcomment -%}
function reloadDisqus() {
if (event.source === window && event.data && event.data.direction === ModeToggle.ID) {
function reloadDisqus(event) {
if (event.source === window && event.data && event.data.id === Theme.ID) {
{%- comment -%} Disqus hasn't been loaded {%- endcomment -%}
if (typeof DISQUS === 'undefined') {
return;
@@ -44,7 +31,27 @@
}
}
if (document.getElementById('mode-toggle')) {
window.addEventListener('message', reloadDisqus);
addDisqus();
if (Theme.switchable) {
addEventListener('message', reloadDisqus);
}
{%- comment -%} Lazy loading {%- endcomment -%}
var disqusObserver = new IntersectionObserver(
function (entries) {
if (entries[0].isIntersecting) {
var d = document,
s = d.createElement('script');
s.src = 'https://{{ site.comments.disqus.shortname }}.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
disqusObserver.disconnect();
}
},
{ threshold: [0] }
);
disqusObserver.observe(document.getElementById('disqus_thread'));
</script>

View File

@@ -1,21 +1,8 @@
<!-- https://giscus.app/ -->
<script type="text/javascript">
<script>
(function () {
const origin = 'https://giscus.app';
const lightTheme = 'light';
const darkTheme = 'dark_dimmed';
let initTheme = lightTheme;
const html = document.documentElement;
if (
(html.hasAttribute('data-mode') &&
html.getAttribute('data-mode') === 'dark') ||
(!html.hasAttribute('data-mode') &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
initTheme = darkTheme;
}
const themeMapper = Theme.getThemeMapper('light', 'dark_dimmed');
const initTheme = themeMapper[Theme.visualState];
let lang = '{{ site.comments.giscus.lang | default: lang }}';
{%- comment -%} https://github.com/giscus/giscus/tree/main/locales {%- endcomment -%}
@@ -41,30 +28,27 @@
async: ''
};
let giscusScript = document.createElement('script');
let giscusNode = document.createElement('script');
Object.entries(giscusAttributes).forEach(([key, value]) =>
giscusScript.setAttribute(key, value)
giscusNode.setAttribute(key, value)
);
document.getElementById('tail-wrapper').appendChild(giscusScript);
const $footer = document.querySelector('footer');
$footer.insertAdjacentElement("beforebegin", giscusNode);
addEventListener('message', (event) => {
if (
event.source === window &&
event.data &&
event.data.direction === ModeToggle.ID
) {
{%- comment -%} global theme mode changed {%- endcomment -%}
const mode = event.data.message;
const theme = mode === ModeToggle.DARK_MODE ? darkTheme : lightTheme;
if (event.source === window && event.data && event.data.id === Theme.ID) {
const newTheme = themeMapper[Theme.visualState];
const message = {
setConfig: {
theme: theme
theme: newTheme
}
};
const giscus = document.getElementsByClassName('giscus-frame')[0].contentWindow;
giscus.postMessage({ giscus: message }, origin);
const giscus =
document.getElementsByClassName('giscus-frame')[0].contentWindow;
giscus.postMessage({ giscus: message }, 'https://giscus.app');
}
});
})();

View File

@@ -1,49 +1,38 @@
<!-- https://utteranc.es/ -->
<script
src="https://utteranc.es/client.js"
repo="{{ site.comments.utterances.repo }}"
issue-term="{{ site.comments.utterances.issue_term }}"
crossorigin="anonymous"
async
></script>
<script type="text/javascript">
<script>
(function () {
const origin = 'https://utteranc.es';
const lightTheme = 'github-light';
const darkTheme = 'github-dark';
let initTheme = lightTheme;
const html = document.documentElement;
const themeMapper = Theme.getThemeMapper('github-light', 'github-dark');
const initTheme = themeMapper[Theme.visualState];
if (
(html.hasAttribute('data-mode') && html.getAttribute('data-mode') === 'dark') ||
(!html.hasAttribute('data-mode') && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
initTheme = darkTheme;
}
let script = document.createElement('script');
script.src = 'https://utteranc.es/client.js';
script.setAttribute('repo', '{{ site.comments.utterances.repo }}');
script.setAttribute('issue-term', '{{ site.comments.utterances.issue_term }}');
script.setAttribute('theme', initTheme);
script.crossOrigin = 'anonymous';
script.async = true;
const $footer = document.querySelector('footer');
$footer.insertAdjacentElement('beforebegin', script);
addEventListener('message', (event) => {
let theme;
let newTheme;
{%- comment -%} credit to <https://github.com/utterance/utterances/issues/170#issuecomment-594036347> {%- endcomment -%}
if (event.origin === origin) {
{%- comment -%} page initial {%- endcomment -%}
theme = initTheme;
} else if (event.source === window && event.data && event.data.direction === ModeToggle.ID) {
{%- comment -%} global theme mode changed {%- endcomment -%}
const mode = event.data.message;
theme = mode === ModeToggle.DARK_MODE ? darkTheme : lightTheme;
} else {
return;
{%- comment -%}
Credit to <https://github.com/utterance/utterances/issues/170#issuecomment-594036347>
{%- endcomment -%}
if (event.source === window && event.data && event.data.id === Theme.ID) {
newTheme = themeMapper[Theme.visualState];
const message = {
type: 'set-theme',
theme: newTheme
};
const utterances = document.querySelector('.utterances-frame').contentWindow;
utterances.postMessage(message, origin);
}
const message = {
type: 'set-theme',
theme: theme
};
const utterances = document.getElementsByClassName('utterances-frame')[0].contentWindow;
utterances.postMessage(message, origin);
});
})();
</script>

View File

@@ -2,7 +2,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#f7f7f7">
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#1b1b1e">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta
name="viewport"
@@ -97,11 +97,32 @@
<link rel="stylesheet" href="{{ site.data.origin[type].glightbox.css | relative_url }}">
{% endif %}
<!-- JavaScript -->
<!-- Scripts -->
{% unless site.theme_mode %}
{% include mode-toggle.html %}
<script src="{{ '/assets/js/dist/theme.min.js' | relative_url }}"></script>
{% endunless %}
{% include js-selector.html lang=lang %}
{% if jekyll.environment == 'production' %}
<!-- PWA -->
{% if site.pwa.enabled %}
<script
defer
src="{{ '/app.min.js' | relative_url }}?baseurl={{ site.baseurl | default: '' }}&register={{ site.pwa.cache.enabled }}"
></script>
{% endif %}
<!-- Web Analytics -->
{% for analytics in site.analytics %}
{% capture str %}{{ analytics }}{% endcapture %}
{% assign platform = str | split: '{' | first %}
{% if site.analytics[platform].id and site.analytics[platform].id != empty %}
{% include analytics/{{ platform }}.html %}
{% endif %}
{% endfor %}
{% endif %}
{% include metadata-hook.html %}
</head>

View File

@@ -62,12 +62,12 @@
{% capture script %}/assets/js/dist/{{ js }}.min.js{% endcapture %}
<script src="{{ script | relative_url }}"></script>
<script defer src="{{ script | relative_url }}"></script>
{% if page.math %}
<!-- MathJax -->
<script src="{{ '/assets/js/data/mathjax.js' | relative_url }}"></script>
<script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=es6"></script>
<script async src="{{ '/assets/js/data/mathjax.js' | relative_url }}"></script>
<script async src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="{{ site.data.origin[type].mathjax.js | relative_url }}"></script>
{% endif %}
@@ -84,23 +84,3 @@
{% endcase %}
{% endif %}
{% endif %}
{% if page.mermaid %}
{% include mermaid.html %}
{% endif %}
{% if jekyll.environment == 'production' %}
<!-- PWA -->
{% if site.pwa.enabled %}
<script defer src="{{ 'app.min.js' | relative_url }}"></script>
{% endif %}
<!-- Web Analytics -->
{% for analytics in site.analytics %}
{% capture str %}{{ analytics }}{% endcapture %}
{% assign type = str | split: '{' | first %}
{% if site.analytics[type].id and site.analytics[type].id != empty %}
{% include analytics/{{ type }}.html %}
{% endif %}
{% endfor %}
{% endif %}

View File

@@ -1,6 +1,6 @@
{% assign urls = include.urls | split: ',' %}
{% assign combined_urls = nil %}
{% assign combined_urls = null %}
{% assign domain = 'https://cdn.jsdelivr.net/' %}
@@ -15,12 +15,12 @@
{% endif %}
{% elsif url contains '//' %}
<script src="{{ url }}"></script>
<script defer src="{{ url }}"></script>
{% else %}
<script src="{{ url | relative_url }}"></script>
<script defer src="{{ url | relative_url }}"></script>
{% endif %}
{% endfor %}
{% if combined_urls %}
<script src="{{ combined_urls }}"></script>
<script defer src="{{ combined_urls }}"></script>
{% endif %}

View File

@@ -1,62 +0,0 @@
<!-- mermaid-js loader -->
<script type="text/javascript">
function updateMermaid(event) {
if (event.source === window && event.data && event.data.direction === ModeToggle.ID) {
const mode = event.data.message;
if (typeof mermaid === 'undefined') {
return;
}
let expectedTheme = mode === ModeToggle.DARK_MODE ? 'dark' : 'default';
let config = { theme: expectedTheme };
{%- comment -%}
Re-render the SVG <https://github.com/mermaid-js/mermaid/issues/311#issuecomment-332557344>
{%- endcomment -%}
const mermaidList = document.getElementsByClassName('mermaid');
[...mermaidList].forEach((elem) => {
const svgCode = elem.previousSibling.children.item(0).innerHTML;
elem.innerHTML = svgCode;
elem.removeAttribute('data-processed');
});
mermaid.initialize(config);
mermaid.init(undefined, '.mermaid');
}
}
(function () {
let initTheme = 'default';
const html = document.documentElement;
if (
(html.hasAttribute('data-mode') && html.getAttribute('data-mode') === 'dark') ||
(!html.hasAttribute('data-mode') && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
initTheme = 'dark';
}
let mermaidConf = {
theme: initTheme {%- comment -%} <default | dark | forest | neutral> {%- endcomment -%}
};
{%- comment -%} Create mermaid tag {%- endcomment -%}
const basicList = document.getElementsByClassName('language-mermaid');
[...basicList].forEach((elem) => {
const svgCode = elem.textContent;
const backup = elem.parentElement;
backup.classList.add('d-none');
{%- comment -%} create mermaid node {%- endcomment -%}
let mermaid = document.createElement('pre');
mermaid.classList.add('mermaid');
const text = document.createTextNode(svgCode);
mermaid.appendChild(text);
backup.after(mermaid);
});
mermaid.initialize(mermaidConf);
window.addEventListener('message', updateMermaid);
})();
</script>

View File

@@ -1,116 +0,0 @@
<!-- Switch the mode between dark and light. -->
<script type="text/javascript">
class ModeToggle {
static get MODE_KEY() {
return 'mode';
}
static get MODE_ATTR() {
return 'data-mode';
}
static get DARK_MODE() {
return 'dark';
}
static get LIGHT_MODE() {
return 'light';
}
static get ID() {
return 'mode-toggle';
}
constructor() {
let self = this;
{%- comment -%} always follow the system prefers {%- endcomment -%}
this.sysDarkPrefers.addEventListener('change', () => {
if (self.hasMode) {
self.clearMode();
}
self.notify();
});
if (!this.hasMode) {
return;
}
if (this.isDarkMode) {
this.setDark();
} else {
this.setLight();
}
}
get sysDarkPrefers() {
return window.matchMedia('(prefers-color-scheme: dark)');
}
get isPreferDark() {
return this.sysDarkPrefers.matches;
}
get isDarkMode() {
return this.mode === ModeToggle.DARK_MODE;
}
get hasMode() {
return this.mode != null;
}
get mode() {
return sessionStorage.getItem(ModeToggle.MODE_KEY);
}
{%- comment -%} get the current mode on screen {%- endcomment -%}
get modeStatus() {
if (this.hasMode) {
return this.mode;
} else {
return this.isPreferDark ? ModeToggle.DARK_MODE : ModeToggle.LIGHT_MODE;
}
}
setDark() {
document.documentElement.setAttribute(ModeToggle.MODE_ATTR, ModeToggle.DARK_MODE);
sessionStorage.setItem(ModeToggle.MODE_KEY, ModeToggle.DARK_MODE);
}
setLight() {
document.documentElement.setAttribute(ModeToggle.MODE_ATTR, ModeToggle.LIGHT_MODE);
sessionStorage.setItem(ModeToggle.MODE_KEY, ModeToggle.LIGHT_MODE);
}
clearMode() {
document.documentElement.removeAttribute(ModeToggle.MODE_ATTR);
sessionStorage.removeItem(ModeToggle.MODE_KEY);
}
{%- comment -%}
Notify another plugins that the theme mode has changed
{%- endcomment -%}
notify() {
window.postMessage(
{
direction: ModeToggle.ID,
message: this.modeStatus
},
'*'
);
}
flipMode() {
if (this.hasMode) {
this.clearMode();
} else {
if (this.isPreferDark) {
this.setLight();
} else {
this.setDark();
}
}
this.notify();
}
}
const modeToggle = new ModeToggle();
</script>

View File

@@ -19,29 +19,31 @@
{% capture not_found %}<p class="mt-5">{{ site.data.locales[include.lang].search.no_results }}</p>{% endcapture %}
<script>
{%- comment -%} Note: dependent library will be loaded in `js-selector.html` {%- endcomment -%}
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('search-results'),
json: '{{ '/assets/js/data/search.json' | relative_url }}',
searchResultTemplate: '{{ result_elem | strip_newlines }}',
noResultsText: '{{ not_found }}',
templateMiddleware: function(prop, value, template) {
if (prop === 'categories') {
if (value === '') {
return `${value}`;
} else {
return `<div class="me-sm-4"><i class="far fa-folder fa-fw"></i>${value}</div>`;
{% comment %} Note: dependent library will be loaded in `js-selector.html` {% endcomment %}
document.addEventListener('DOMContentLoaded', () => {
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('search-results'),
json: '{{ '/assets/js/data/search.json' | relative_url }}',
searchResultTemplate: '{{ result_elem | strip_newlines }}',
noResultsText: '{{ not_found }}',
templateMiddleware: function(prop, value, template) {
if (prop === 'categories') {
if (value === '') {
return `${value}`;
} else {
return `<div class="me-sm-4"><i class="far fa-folder fa-fw"></i>${value}</div>`;
}
}
}
if (prop === 'tags') {
if (value === '') {
return `${value}`;
} else {
return `<div><i class="fa fa-tag fa-fw"></i>${value}</div>`;
if (prop === 'tags') {
if (value === '') {
return `${value}`;
} else {
return `<div><i class="fa fa-tag fa-fw"></i>${value}</div>`;
}
}
}
}
});
});
</script>

View File

@@ -11,9 +11,7 @@
{%- endif -%}
</a>
<h1 class="site-title">
<a href="{{ '/' | relative_url }}">{{ site.title }}</a>
</h1>
<a class="site-title d-block" href="{{ '/' | relative_url }}">{{ site.title }}</a>
<p class="site-subtitle fst-italic mb-0">{{ site.tagline }}</p>
</header>
<!-- .profile-wrapper -->

10
_includes/toc-status.html Normal file
View File

@@ -0,0 +1,10 @@
{% comment %}
Determine TOC state and return it through variable "enable_toc"
{% endcomment %}
{% assign enable_toc = false %}
{% if site.toc and page.toc %}
{% if page.content contains '<h2' or page.content contains '<h3' %}
{% assign enable_toc = true %}
{% endif %}
{% endif %}

View File

@@ -1,12 +1,7 @@
{% assign enable_toc = false %}
{% if site.toc and page.toc %}
{% if page.content contains '<h2' or page.content contains '<h3' %}
{% assign enable_toc = true %}
{% endif %}
{% endif %}
{% include toc-status.html %}
{% if enable_toc %}
<section id="toc-wrapper" class="d-none ps-0 pe-4">
<section id="toc-wrapper" class="ps-0 pe-4">
<h2 class="panel-heading ps-3 mb-2">{{- site.data.locales[include.lang].panel.toc -}}</h2>
<nav id="toc"></nav>
</section>

View File

@@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
import { categoryCollapse } from './modules/plugins';
import { categoryCollapse } from './modules/components';
basic();
initSidebar();

View File

@@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
import { initLocaleDatetime, loadImg } from './modules/plugins';
import { initLocaleDatetime, loadImg } from './modules/components';
loadImg();
initLocaleDatetime();

View File

@@ -1,5 +1,5 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
import { initLocaleDatetime } from './modules/plugins';
import { initLocaleDatetime } from './modules/components';
initSidebar();
initTopbar();

View File

@@ -3,4 +3,8 @@ export { initClipboard } from './components/clipboard';
export { loadImg } from './components/img-loading';
export { imgPopup } from './components/img-popup';
export { initLocaleDatetime } from './components/locale-datetime';
export { toc } from './components/toc';
export { initToc } from './components/toc';
export { loadMermaid } from './components/mermaid';
export { modeWatcher } from './components/mode-toggle';
export { back2top } from './components/back-to-top';
export { loadTooptip } from './components/tooltip-loader';

View File

@@ -4,7 +4,6 @@
* Dependencies: https://github.com/biati-digital/glightbox
*/
const html = document.documentElement;
const lightImages = '.popup:not(.dark)';
const darkImages = '.popup:not(.light)';
let selector = lightImages;
@@ -33,26 +32,17 @@ export function imgPopup() {
document.querySelector('.popup.dark') === null
);
if (
(html.hasAttribute('data-mode') &&
html.getAttribute('data-mode') === 'dark') ||
(!html.hasAttribute('data-mode') &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
if (Theme.visualState === Theme.DARK) {
selector = darkImages;
}
let current = GLightbox({ selector: `${selector}` });
if (hasDualImages && document.getElementById('mode-toggle')) {
if (hasDualImages && Theme.switchable) {
let reverse = null;
window.addEventListener('message', (event) => {
if (
event.source === window &&
event.data &&
event.data.direction === ModeToggle.ID
) {
if (event.source === window && event.data && event.data.id === Theme.ID) {
updateImages(current, reverse);
}
});

View File

@@ -0,0 +1,60 @@
/**
* Mermaid-js loader
*/
const MERMAID = 'mermaid';
const themeMapper = Theme.getThemeMapper('default', 'dark');
function refreshTheme(event) {
if (event.source === window && event.data && event.data.id === Theme.ID) {
// Re-render the SVG <https://github.com/mermaid-js/mermaid/issues/311#issuecomment-332557344>
const mermaidList = document.getElementsByClassName(MERMAID);
[...mermaidList].forEach((elem) => {
const svgCode = elem.previousSibling.children.item(0).innerHTML;
elem.textContent = svgCode;
elem.removeAttribute('data-processed');
});
const newTheme = themeMapper[Theme.visualState];
mermaid.initialize({ theme: newTheme });
mermaid.init(null, `.${MERMAID}`);
}
}
function setNode(elem) {
const svgCode = elem.textContent;
const backup = elem.parentElement;
backup.classList.add('d-none');
// Create mermaid node
const mermaid = document.createElement('pre');
mermaid.classList.add(MERMAID);
const text = document.createTextNode(svgCode);
mermaid.appendChild(text);
backup.after(mermaid);
}
export function loadMermaid() {
if (
typeof mermaid === 'undefined' ||
typeof mermaid.initialize !== 'function'
) {
return;
}
const initTheme = themeMapper[Theme.visualState];
let mermaidConf = {
theme: initTheme
};
const basicList = document.getElementsByClassName('language-mermaid');
[...basicList].forEach(setNode);
mermaid.initialize(mermaidConf);
if (Theme.switchable) {
window.addEventListener('message', refreshTheme);
}
}

View File

@@ -0,0 +1,15 @@
/**
* Add listener for theme mode toggle
*/
const $toggle = document.getElementById('mode-toggle');
export function modeWatcher() {
if (!$toggle) {
return;
}
$toggle.addEventListener('click', () => {
Theme.flip();
});
}

View File

@@ -1,14 +0,0 @@
/**
* Add listener for theme mode toggle
*/
const toggle = document.getElementById('mode-toggle');
export function modeWatcher() {
if (!toggle) {
return;
}
toggle.addEventListener('click', () => {
modeToggle.flipMode();
});
}

View File

@@ -1,27 +0,0 @@
/**
* Expand or close the sidebar in mobile screens.
*/
const ATTR_DISPLAY = 'sidebar-display';
class SidebarUtil {
static isExpanded = false;
static toggle() {
if (SidebarUtil.isExpanded === false) {
document.body.setAttribute(ATTR_DISPLAY, '');
} else {
document.body.removeAttribute(ATTR_DISPLAY);
}
SidebarUtil.isExpanded = !SidebarUtil.isExpanded;
}
}
export function sidebarExpand() {
document
.getElementById('sidebar-trigger')
.addEventListener('click', SidebarUtil.toggle);
document.getElementById('mask').addEventListener('click', SidebarUtil.toggle);
}

View File

@@ -1,15 +1,33 @@
export function toc() {
if (document.querySelector('main h2, main h3')) {
// see: https://github.com/tscanlin/tocbot#usage
tocbot.init({
tocSelector: '#toc',
contentSelector: '.content',
ignoreSelector: '[data-toc-skip]',
headingSelector: 'h2, h3, h4',
orderedList: false,
scrollSmooth: false
});
import { TocMobile as mobile } from './toc/toc-mobile';
import { TocDesktop as desktop } from './toc/toc-desktop';
document.getElementById('toc-wrapper').classList.remove('d-none');
const desktopMode = matchMedia('(min-width: 1200px)');
function refresh(e) {
if (e.matches) {
if (mobile.popupOpened) {
mobile.hidePopup();
}
desktop.refresh();
} else {
mobile.refresh();
}
}
function init() {
if (document.querySelector('main>article[data-toc="true"]') === null) {
return;
}
// Avoid create multiple instances of Tocbot. Ref: <https://github.com/tscanlin/tocbot/issues/203>
if (desktopMode.matches) {
desktop.init();
} else {
mobile.init();
}
desktopMode.onchange = refresh;
}
export { init as initToc };

View File

@@ -0,0 +1,22 @@
export class TocDesktop {
/* Tocbot options Ref: https://github.com/tscanlin/tocbot#usage */
static options = {
tocSelector: '#toc',
contentSelector: '.content',
ignoreSelector: '[data-toc-skip]',
headingSelector: 'h2, h3, h4',
orderedList: false,
scrollSmooth: false,
headingsOffset: 16 * 2 // 2rem
};
static refresh() {
tocbot.refresh(this.options);
}
static init() {
if (document.getElementById('toc-wrapper')) {
tocbot.init(this.options);
}
}
}

View File

@@ -0,0 +1,125 @@
/**
* TOC button, topbar and popup for mobile devices
*/
const $tocBar = document.getElementById('toc-bar');
const $soloTrigger = document.getElementById('toc-solo-trigger');
const $triggers = document.getElementsByClassName('toc-trigger');
const $popup = document.getElementById('toc-popup');
const $btnClose = document.getElementById('toc-popup-close');
const SCROLL_LOCK = 'overflow-hidden';
const CLOSING = 'closing';
export class TocMobile {
static #invisible = true;
static #barHeight = 16 * 3; // 3rem
static options = {
tocSelector: '#toc-popup-content',
contentSelector: '.content',
ignoreSelector: '[data-toc-skip]',
headingSelector: 'h2, h3, h4',
orderedList: false,
scrollSmooth: false,
collapseDepth: 4,
headingsOffset: this.#barHeight
};
static initBar() {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
$tocBar.classList.toggle('invisible', entry.isIntersecting);
});
},
{ rootMargin: `-${this.#barHeight}px 0px 0px 0px` }
);
observer.observe($soloTrigger);
this.#invisible = false;
}
static listenAnchors() {
const $anchors = document.getElementsByClassName('toc-link');
[...$anchors].forEach((anchor) => {
anchor.onclick = () => this.hidePopup();
});
}
static refresh() {
if (this.#invisible) {
this.initComponents();
}
tocbot.refresh(this.options);
this.listenAnchors();
}
static get popupOpened() {
return $popup.open;
}
static showPopup() {
this.lockScroll(true);
$popup.showModal();
const activeItem = $popup.querySelector('li.is-active-li');
activeItem.scrollIntoView({ block: 'center' });
}
static hidePopup() {
$popup.toggleAttribute(CLOSING);
$popup.addEventListener(
'animationend',
() => {
$popup.toggleAttribute(CLOSING);
$popup.close();
},
{ once: true }
);
this.lockScroll(false);
}
static lockScroll(enable) {
document.documentElement.classList.toggle(SCROLL_LOCK, enable);
document.body.classList.toggle(SCROLL_LOCK, enable);
}
static clickBackdrop(event) {
if ($popup.hasAttribute(CLOSING)) {
return;
}
const rect = event.target.getBoundingClientRect();
if (
event.clientX < rect.left ||
event.clientX > rect.right ||
event.clientY < rect.top ||
event.clientY > rect.bottom
) {
this.hidePopup();
}
}
static initComponents() {
this.initBar();
[...$triggers].forEach((trigger) => {
trigger.onclick = () => this.showPopup();
});
$popup.onclick = (e) => this.clickBackdrop(e);
$btnClose.onclick = () => this.hidePopup();
$popup.oncancel = (e) => {
e.preventDefault();
this.hidePopup();
};
}
static init() {
tocbot.init(this.options);
this.listenAnchors();
this.initComponents();
}
}

View File

@@ -1,7 +1,7 @@
import { back2top } from '../components/back-to-top';
import { loadTooptip } from '../components/tooltip-loader';
import { back2top, loadTooptip, modeWatcher } from '../components';
export function basic() {
modeWatcher();
back2top();
loadTooptip();
}

View File

@@ -1,7 +1,19 @@
import { modeWatcher } from '../components/mode-watcher';
import { sidebarExpand } from '../components/sidebar';
const ATTR_DISPLAY = 'sidebar-display';
const $sidebar = document.getElementById('sidebar');
const $trigger = document.getElementById('sidebar-trigger');
const $mask = document.getElementById('mask');
class SidebarUtil {
static #isExpanded = false;
static toggle() {
this.#isExpanded = !this.#isExpanded;
document.body.toggleAttribute(ATTR_DISPLAY, this.#isExpanded);
$sidebar.classList.toggle('z-2', this.#isExpanded);
$mask.classList.toggle('d-none', !this.#isExpanded);
}
}
export function initSidebar() {
modeWatcher();
sidebarExpand();
$trigger.onclick = $mask.onclick = () => SidebarUtil.toggle();
}

View File

@@ -0,0 +1,135 @@
/**
* Theme management class
*
* To reduce flickering during page load, this script should be loaded synchronously.
*/
class Theme {
static #modeKey = 'mode';
static #modeAttr = 'data-mode';
static #darkMedia = window.matchMedia('(prefers-color-scheme: dark)');
static switchable = !document.documentElement.hasAttribute(this.#modeAttr);
static get DARK() {
return 'dark';
}
static get LIGHT() {
return 'light';
}
/**
* @returns {string} Theme mode identifier
*/
static get ID() {
return 'theme-mode';
}
/**
* Gets the current visual state of the theme.
*
* @returns {string} The current visual state, either the mode if it exists,
* or the system dark mode state ('dark' or 'light').
*/
static get visualState() {
if (this.#hasMode) {
return this.#mode;
} else {
return this.#sysDark ? this.DARK : this.LIGHT;
}
}
static get #mode() {
return sessionStorage.getItem(this.#modeKey);
}
static get #isDarkMode() {
return this.#mode === this.DARK;
}
static get #hasMode() {
return this.#mode !== null;
}
static get #sysDark() {
return this.#darkMedia.matches;
}
/**
* Maps theme modes to provided values
* @param {string} light Value for light mode
* @param {string} dark Value for dark mode
* @returns {Object} Mapped values
*/
static getThemeMapper(light, dark) {
return {
[this.LIGHT]: light,
[this.DARK]: dark
};
}
/**
* Initializes the theme based on system preferences or stored mode
*/
static init() {
if (!this.switchable) {
return;
}
this.#darkMedia.addEventListener('change', () => {
const lastMode = this.#mode;
this.#clearMode();
if (lastMode !== this.visualState) {
this.#notify();
}
});
if (!this.#hasMode) {
return;
}
if (this.#isDarkMode) {
this.#setDark();
} else {
this.#setLight();
}
}
/**
* Flips the current theme mode
*/
static flip() {
if (this.#hasMode) {
this.#clearMode();
} else {
this.#sysDark ? this.#setLight() : this.#setDark();
}
this.#notify();
}
static #setDark() {
document.documentElement.setAttribute(this.#modeAttr, this.DARK);
sessionStorage.setItem(this.#modeKey, this.DARK);
}
static #setLight() {
document.documentElement.setAttribute(this.#modeAttr, this.LIGHT);
sessionStorage.setItem(this.#modeKey, this.LIGHT);
}
static #clearMode() {
document.documentElement.removeAttribute(this.#modeAttr);
sessionStorage.removeItem(this.#modeKey);
}
/**
* Notifies other plugins that the theme mode has changed
*/
static #notify() {
window.postMessage({ id: this.ID }, '*');
}
}
Theme.init();
export default Theme;

View File

@@ -1,9 +1,15 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
import { loadImg, imgPopup, initClipboard } from './modules/plugins';
import {
loadImg,
imgPopup,
initClipboard,
loadMermaid
} from './modules/components';
loadImg();
imgPopup();
initSidebar();
initTopbar();
initClipboard();
loadMermaid();
basic();

View File

@@ -1,17 +1,20 @@
import { basic, initSidebar, initTopbar } from './modules/layouts';
import { basic, initTopbar, initSidebar } from './modules/layouts';
import {
loadImg,
imgPopup,
initLocaleDatetime,
initClipboard,
toc
} from './modules/plugins';
initToc,
loadMermaid
} from './modules/components';
loadImg();
toc();
initToc();
imgPopup();
initSidebar();
initLocaleDatetime();
initClipboard();
initTopbar();
loadMermaid();
basic();

View File

@@ -1,15 +1,19 @@
import { pwa, baseurl } from '../../_config.yml';
import Toast from 'bootstrap/js/src/toast';
if ('serviceWorker' in navigator) {
if (pwa.enabled) {
const swUrl = `${baseurl}/sw.min.js`;
// Get Jekyll config from URL parameters
const src = new URL(document.currentScript.src);
const register = src.searchParams.get('register');
const baseUrl = src.searchParams.get('baseurl');
if (register) {
const swUrl = `${baseUrl}/sw.min.js`;
const notification = document.getElementById('notification');
const btnRefresh = notification.querySelector('.toast-body>button');
const popupWindow = Toast.getOrCreateInstance(notification);
navigator.serviceWorker.register(swUrl).then((registration) => {
// In case the user ignores the notification
// Restore the update window that was last manually closed by the user
if (registration.waiting) {
popupWindow.show();
}

View File

@@ -1,6 +1,4 @@
import { baseurl } from '../../_config.yml';
importScripts(`${baseurl}/assets/js/data/swconf.js`);
importScripts('./assets/js/data/swconf.js');
const purge = swconf.purge;
const interceptor = swconf.interceptor;

View File

@@ -68,14 +68,18 @@ layout: compress
</aside>
</div>
<div id="mask"></div>
<div id="mask" class="d-none position-fixed w-100 h-100 z-1"></div>
{% if site.pwa.enabled %}
{% include_cached notification.html lang=lang %}
{% endif %}
<!-- JavaScripts -->
{% include js-selector.html lang=lang %}
<!-- Embedded scripts -->
{% for _include in layout.script_includes %}
{% assign _include_path = _include | append: '.html' %}
{% include {{ _include_path }} %}
{% endfor %}
{% include_cached search-loader.html lang=lang %}
</body>

View File

@@ -5,38 +5,45 @@ refactor: true
{% include lang.html %}
{% assign pinned = site.posts | where: 'pin', 'true' %}
{% assign default = site.posts | where_exp: 'item', 'item.pin != true and item.hidden != true' %}
{% assign all_pinned = site.posts | where: 'pin', 'true' %}
{% assign all_normal = site.posts | where_exp: 'item', 'item.pin != true and item.hidden != true' %}
{% assign posts = '' | split: '' %}
<!-- Get pinned posts -->
<!-- Get pinned posts on current page -->
{% assign offset = paginator.page | minus: 1 | times: paginator.per_page %}
{% assign pinned_num = pinned.size | minus: offset %}
{% assign visible_start = paginator.page | minus: 1 | times: paginator.per_page %}
{% assign visible_end = visible_start | plus: paginator.per_page %}
{% if pinned_num > 0 %}
{% for i in (offset..pinned.size) limit: pinned_num %}
{% assign posts = posts | push: pinned[i] %}
{% if all_pinned.size > visible_start %}
{% if all_pinned.size > visible_end %}
{% assign pinned_size = paginator.per_page %}
{% else %}
{% assign pinned_size = all_pinned.size | minus: visible_start %}
{% endif %}
{% for i in (visible_start..all_pinned.size) limit: pinned_size %}
{% assign posts = posts | push: all_pinned[i] %}
{% endfor %}
{% else %}
{% assign pinned_num = 0 %}
{% assign pinned_size = 0 %}
{% endif %}
<!-- Get default posts -->
<!-- Get normal posts on current page -->
{% assign default_beg = offset | minus: pinned.size %}
{% assign normal_size = paginator.posts | size | minus: pinned_size %}
{% if default_beg < 0 %}
{% assign default_beg = 0 %}
{% endif %}
{% if normal_size > 0 %}
{% if pinned_size > 0 %}
{% assign normal_start = 0 %}
{% else %}
{% assign normal_start = visible_start | minus: all_pinned.size %}
{% endif %}
{% assign default_num = paginator.posts | size | minus: pinned_num %}
{% assign default_end = default_beg | plus: default_num | minus: 1 %}
{% assign normal_end = normal_start | plus: normal_size | minus: 1 %}
{% if default_num > 0 %}
{% for i in (default_beg..default_end) %}
{% assign posts = posts | push: default[i] %}
{% for i in (normal_start..normal_end) %}
{% assign posts = posts | push: all_normal[i] %}
{% endfor %}
{% endif %}

View File

@@ -6,12 +6,15 @@ panel_includes:
tail_includes:
- related-posts
- post-nav
- comments
script_includes:
- comment
---
{% include lang.html %}
<article class="px-1">
{% include toc-status.html %}
<article class="px-1" data-toc="{{ enable_toc }}">
<header>
<h1 data-toc-skip>{{ page.title }}</h1>
{% if page.description %}
@@ -95,6 +98,30 @@ tail_includes:
</div>
</header>
{% if enable_toc %}
<div id="toc-bar" class="d-flex align-items-center justify-content-between invisible">
<span class="label text-truncate">{{ page.title }}</span>
<button type="button" class="toc-trigger btn me-1">
<i class="fa-solid fa-list-ul fa-fw"></i>
</button>
</div>
<button id="toc-solo-trigger" type="button" class="toc-trigger btn btn-outline-secondary btn-sm">
<span class="label ps-2 pe-1">{{- site.data.locales[lang].panel.toc -}}</span>
<i class="fa-solid fa-angle-right fa-fw"></i>
</button>
<dialog id="toc-popup" class="p-0">
<div class="header d-flex flex-row align-items-center justify-content-between">
<div class="label text-truncate py-2 ms-4">{{- page.title -}}</div>
<button id="toc-popup-close" type="button" class="btn mx-1 my-1 opacity-75">
<i class="fas fa-close"></i>
</button>
</div>
<div id="toc-popup-content" class="px-4 py-3 pb-4"></div>
</dialog>
{% endif %}
<div class="content">
{{ content }}
</div>

View File

@@ -1,3 +1,8 @@
@use '../colors/typography-dark';
@use '../colors/typography-light';
@use 'module';
@use 'variables';
/* The common styles */
html {
@@ -6,22 +11,22 @@ html {
@media (prefers-color-scheme: light) {
&:not([data-mode]),
&[data-mode='light'] {
@include light-scheme;
@include typography-light.light-scheme;
}
&[data-mode='dark'] {
@include dark-scheme;
@include typography-dark.dark-scheme;
}
}
@media (prefers-color-scheme: dark) {
&:not([data-mode]),
&[data-mode='dark'] {
@include dark-scheme;
@include typography-dark.dark-scheme;
}
&[data-mode='light'] {
@include light-scheme;
@include typography-light.light-scheme;
}
}
}
@@ -32,17 +37,17 @@ body {
env(safe-area-inset-bottom) env(safe-area-inset-left);
color: var(--text-color);
-webkit-font-smoothing: antialiased;
font-family: $font-family-base;
font-family: variables.$font-family-base;
}
/* --- Typography --- */
@for $i from 1 through 5 {
h#{$i} {
@extend %heading;
@extend %heading !optional;
@if $i > 1 {
@extend %anchor;
@extend %anchor !optional;
}
@if $i < 5 {
@@ -70,7 +75,7 @@ body {
}
a {
@extend %link-color;
@extend %link-color !optional;
text-decoration: none;
}
@@ -104,7 +109,7 @@ blockquote {
padding: 1rem 1rem 1rem 3rem;
color: var(--prompt-text-color);
@extend %rounded;
@extend %rounded !optional;
&::before {
text-align: center;
@@ -117,10 +122,10 @@ blockquote {
}
}
@include prompt('tip', '\f0eb', $fa-style: 'regular');
@include prompt('info', '\f06a', $rotate: 180);
@include prompt('warning', '\f06a');
@include prompt('danger', '\f071');
@include module.prompt('tip', '\f0eb', $fa-style: 'regular');
@include module.prompt('info', '\f06a', $rotate: 180);
@include module.prompt('warning', '\f06a');
@include module.prompt('danger', '\f071');
}
kbd {
@@ -134,7 +139,7 @@ kbd {
padding-top: 0.1rem;
color: var(--kbd-text-color);
background-color: var(--kbd-bg-color);
border-radius: $radius-sm;
border-radius: variables.$radius-sm;
border: solid 1px var(--kbd-wrap-color);
box-shadow: inset 0 -2px 0 var(--kbd-wrap-color);
}
@@ -146,21 +151,21 @@ hr {
footer {
background-color: var(--main-bg);
height: $footer-height;
height: variables.$footer-height;
border-top: 1px solid var(--main-border-color);
@extend %text-xs;
@extend %text-xs !optional;
a {
@extend %text-highlight;
@extend %text-highlight !optional;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
}
em {
@extend %text-highlight;
@extend %text-highlight !optional;
}
p {
@@ -173,7 +178,7 @@ footer {
i {
&.far,
&.fas {
@extend %no-cursor;
@extend %no-cursor !optional;
}
}
@@ -210,7 +215,7 @@ i {
font-family: inherit;
line-height: inherit;
@include label(inherit);
@include module.label(inherit);
}
.post-tag {
@@ -231,10 +236,10 @@ i {
color: inherit;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
@extend %no-bottom-border;
@extend %no-bottom-border !optional;
}
}
@@ -247,27 +252,27 @@ i {
margin-bottom: 0.3rem;
}
@extend %sup-fn-target;
@extend %sup-fn-target !optional;
> p {
margin-left: 0.25em;
margin-top: 0;
margin-bottom: 0;
@include module.mt-mb(0);
}
}
}
.footnote {
@at-root a#{&} {
@include ml-mr(1px);
@include pl-pr(2px);
@include module.ml-mr(1px);
@include module.pl-pr(2px);
border-bottom-style: none !important;
}
}
sup {
@extend %sup-fn-target;
@extend %sup-fn-target !optional;
}
.reversefootnote {
@@ -297,7 +302,7 @@ sup {
border-bottom: solid 2px rgba(210, 215, 217, 0.75);
th {
@extend %table-cell;
@extend %table-cell !optional;
}
}
@@ -314,7 +319,7 @@ sup {
}
td {
@extend %table-cell;
@extend %table-cell !optional;
}
}
} /* tbody */
@@ -329,7 +334,7 @@ sup {
height: 100%;
overflow: hidden;
@extend %rounded;
@extend %rounded !optional;
&:not(.no-bg) {
background: var(--img-bg);
@@ -340,7 +345,7 @@ sup {
-o-object-fit: cover;
object-fit: cover;
@extend %rounded;
@extend %rounded !optional;
@at-root #post-list & {
width: 100%;
@@ -349,14 +354,14 @@ sup {
}
.post-preview {
@extend %rounded;
@extend %rounded !optional;
border: 0;
background: var(--card-bg);
box-shadow: var(--card-shadow);
&::before {
@extend %rounded;
@extend %rounded !optional;
content: '';
width: 100%;
@@ -384,7 +389,7 @@ main {
p {
> a.popup {
&:not(.normal):not(.left):not(.right) {
@include align-center;
@include module.align-center;
}
}
}
@@ -393,22 +398,22 @@ main {
#tags,
#archives {
a:not(:hover) {
@extend %no-bottom-border;
@extend %no-bottom-border !optional;
}
}
}
.post-meta {
@extend %text-sm;
@extend %text-sm !optional;
a {
&:not([class]):hover {
@extend %link-hover;
@extend %link-hover !optional;
}
}
em {
@extend %normal-font-style;
@extend %normal-font-style !optional;
}
}
@@ -419,18 +424,18 @@ main {
a {
&.popup {
@extend %no-cursor;
@extend %img-caption;
@include mt-mb(0.5rem);
@extend %no-cursor !optional;
@extend %img-caption !optional;
@include module.mt-mb(0.5rem);
cursor: zoom-in;
}
&:not(.img-link) {
@extend %link-underline;
@extend %link-underline !optional;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
}
}
@@ -497,7 +502,7 @@ main {
} /* .content */
.tag:hover {
@extend %tag-hover;
@extend %tag-hover !optional;
}
.post-tag {
@@ -566,7 +571,7 @@ main {
margin-bottom: 1rem;
aspect-ratio: 16 / 9;
@extend %rounded;
@extend %rounded !optional;
&.twitch {
aspect-ratio: 310 / 189;
@@ -582,14 +587,14 @@ main {
margin-bottom: 0;
}
@extend %img-caption;
@extend %img-caption !optional;
}
.embed-audio {
width: 100%;
display: block;
@extend %img-caption;
@extend %img-caption !optional;
}
/* --- buttons --- */
@@ -680,15 +685,14 @@ $btn-border-width: 3px;
$btn-mb: 0.5rem;
#sidebar {
@include pl-pr(0);
@include module.pl-pr(0);
position: fixed;
top: 0;
left: 0;
height: 100%;
overflow-y: auto;
width: $sidebar-width;
z-index: 99;
width: variables.$sidebar-width;
background: var(--sidebar-bg);
border-right: 1px solid var(--sidebar-border-color);
@@ -708,7 +712,7 @@ $btn-mb: 0.5rem;
}
a {
@extend %sidebar-links;
@extend %sidebar-links !optional;
}
#avatar {
@@ -729,8 +733,8 @@ $btn-mb: 0.5rem;
}
.profile-wrapper {
@include mt-mb(2.5rem);
@extend %clickable-transition;
@include module.mt-mb(2.5rem);
@extend %clickable-transition !optional;
padding-left: 2.5rem;
padding-right: 1.25rem;
@@ -738,6 +742,9 @@ $btn-mb: 0.5rem;
}
.site-title {
@extend %clickable-transition !optional;
@extend %sidebar-link-hover !optional;
font-family: inherit;
font-weight: 900;
font-size: 1.75rem;
@@ -745,13 +752,8 @@ $btn-mb: 0.5rem;
letter-spacing: 0.25px;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
a {
@extend %clickable-transition;
@extend %sidebar-link-hover;
color: var(--site-title-color);
}
width: fit-content;
color: var(--site-title-color);
}
.site-subtitle {
@@ -771,11 +773,11 @@ $btn-mb: 0.5rem;
li.nav-item {
opacity: 0.9;
width: 100%;
padding-left: 1.5rem;
padding-right: 1.5rem;
@include module.pl-pr(1.5rem);
a.nav-link {
@include pt-pb(0.6rem);
@include module.pt-pb(0.6rem);
display: flex;
align-items: center;
@@ -844,12 +846,12 @@ $btn-mb: 0.5rem;
}
a {
@extend %button;
@extend %sidebar-link-hover;
@extend %clickable-transition;
@extend %button !optional;
@extend %sidebar-link-hover !optional;
@extend %clickable-transition !optional;
&:not(:last-child) {
margin-right: $sb-btn-gap;
margin-right: variables.$sb-btn-gap;
}
}
@@ -858,14 +860,14 @@ $btn-mb: 0.5rem;
}
#mode-toggle {
@extend %button;
@extend %sidebar-links;
@extend %sidebar-link-hover;
@extend %button !optional;
@extend %sidebar-links !optional;
@extend %sidebar-link-hover !optional;
}
.icon-border {
@extend %no-cursor;
@include ml-mr(calc(($sb-btn-gap - $btn-border-width) / 2));
@extend %no-cursor !optional;
@include module.ml-mr(calc((variables.$sb-btn-gap - $btn-border-width) / 2));
background-color: var(--sidebar-btn-color);
content: '';
@@ -905,14 +907,12 @@ $btn-mb: 0.5rem;
/* --- top-bar --- */
#topbar-wrapper {
height: $topbar-height;
height: variables.$topbar-height;
background-color: var(--topbar-bg);
}
#topbar {
button i {
color: #999999;
}
@extend %btn-color !optional;
#breadcrumb {
font-size: 1rem;
@@ -920,7 +920,7 @@ $btn-mb: 0.5rem;
padding-left: 0.5rem;
a:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
span {
@@ -935,43 +935,43 @@ $btn-mb: 0.5rem;
} /* #topbar */
::-webkit-input-placeholder {
@include placeholder;
@include module.placeholder;
}
::-moz-placeholder {
@include placeholder;
@include module.placeholder;
}
:-ms-input-placeholder {
@include placeholder;
@include module.placeholder;
}
::-ms-input-placeholder {
@include placeholder;
@include module.placeholder;
}
::placeholder {
@include placeholder;
@include module.placeholder;
}
:focus::-webkit-input-placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
:focus::-moz-placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
:focus:-ms-input-placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
:focus::-ms-input-placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
:focus::placeholder {
@include placeholder-focus;
@include module.placeholder-focus;
}
search {
@@ -1000,7 +1000,7 @@ search {
display: none;
white-space: nowrap;
@extend %cursor-pointer;
@extend %cursor-pointer !optional;
}
#search-input {
@@ -1038,7 +1038,7 @@ search {
padding-right: 0.2rem;
}
@extend %link-color;
@extend %link-color !optional;
}
}
@@ -1047,15 +1047,15 @@ search {
a {
font-size: 1.4rem;
line-height: 2.5rem;
line-height: 1.5rem;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
@extend %link-color;
@extend %no-bottom-border;
@extend %heading;
@extend %link-color !optional;
@extend %no-bottom-border !optional;
@extend %heading !optional;
}
> article {
@@ -1073,8 +1073,9 @@ search {
}
> p {
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis !optional;
white-space: break-spaces;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
@@ -1090,23 +1091,11 @@ search {
color: var(--topbar-text-color);
text-align: center;
width: 70%;
overflow: hidden;
text-overflow: ellipsis;
word-break: keep-all;
white-space: nowrap;
}
#mask {
display: none;
position: fixed;
inset: 0 0 0 0;
height: 100%;
width: 100%;
z-index: 1;
@at-root [#{$sidebar-display}] & {
display: block !important;
}
}
/* --- basic wrappers --- */
@@ -1114,7 +1103,7 @@ search {
#main-wrapper {
position: relative;
@include pl-pr(0);
@include module.pl-pr(0);
> .container {
min-height: 100vh;
@@ -1124,7 +1113,7 @@ search {
#topbar-wrapper.row,
#main-wrapper > .container > .row,
#search-result-wrapper > .row {
@include ml-mr(0);
@include module.ml-mr(0);
}
#tail-wrapper {
@@ -1142,12 +1131,12 @@ search {
cursor: pointer;
position: fixed;
right: 1rem;
bottom: calc($footer-height-large - $back2top-size / 2);
bottom: calc(variables.$footer-height-large - variables.$back2top-size / 2);
background: var(--button-bg);
color: var(--btn-backtotop-color);
padding: 0;
width: $back2top-size;
height: $back2top-size;
width: variables.$back2top-size;
height: variables.$back2top-size;
border-radius: 50%;
border: 1px solid var(--btn-backtotop-border-color);
transition: opacity 0.5s ease-in-out, transform 0.2s ease-out;
@@ -1158,7 +1147,7 @@ search {
}
i {
line-height: $back2top-size;
line-height: variables.$back2top-size;
position: relative;
bottom: 2px;
}
@@ -1232,7 +1221,7 @@ search {
main {
.content {
> blockquote[class^='prompt-'] {
@include ml-mr(-1rem);
@include module.ml-mr(-1rem);
border-radius: 0;
max-width: none;
@@ -1252,12 +1241,12 @@ search {
}
#topbar {
@extend %full-width;
@extend %full-width !optional;
}
#main-wrapper > .container {
@extend %full-width;
@include pl-pr(0);
@extend %full-width !optional;
@include module.pl-pr(0);
}
}
@@ -1276,7 +1265,7 @@ search {
footer {
@include slide;
height: $footer-height-large;
height: variables.$footer-height-large;
padding: 1.5rem 0;
}
@@ -1286,7 +1275,7 @@ search {
}
#main-wrapper {
transform: translateX($sidebar-width);
transform: translateX(variables.$sidebar-width);
}
#back-to-top {
@@ -1297,8 +1286,8 @@ search {
#sidebar {
@include slide;
transform: translateX(-$sidebar-width); /* hide */
-webkit-transform: translateX(-$sidebar-width);
transform: translateX(-(variables.$sidebar-width)); /* hide */
-webkit-transform: translateX(-(variables.$sidebar-width));
}
#main-wrapper {
@@ -1361,7 +1350,7 @@ search {
}
#main-wrapper {
margin-left: $sidebar-width;
margin-left: variables.$sidebar-width;
}
#sidebar {
@@ -1375,11 +1364,11 @@ search {
}
search {
max-width: $search-max-width;
max-width: variables.$search-max-width;
}
#search-result-wrapper {
max-width: $main-content-max-width;
max-width: variables.$main-content-max-width;
justify-content: start !important;
}
@@ -1396,7 +1385,7 @@ search {
/* button 'back-to-Top' position */
#back-to-top {
right: 5%;
bottom: calc($footer-height - $back2top-size / 2);
bottom: calc(variables.$footer-height - variables.$back2top-size / 2);
}
#topbar-title {
@@ -1473,7 +1462,7 @@ search {
@media all and (min-width: 1400px) {
#back-to-top {
right: calc((100vw - $sidebar-width - 1140px) / 2 + 3rem);
right: calc((100vw - variables.$sidebar-width - 1140px) / 2 + 3rem);
}
}
@@ -1481,23 +1470,23 @@ search {
$icon-gap: 1rem;
#main-wrapper {
margin-left: $sidebar-width-large;
margin-left: variables.$sidebar-width-large;
}
#topbar-wrapper {
left: $sidebar-width-large;
left: variables.$sidebar-width-large;
}
search {
margin-right: calc(
$main-content-max-width / 4 - $search-max-width - 0.75rem
variables.$main-content-max-width / 4 - variables.$search-max-width - 0.75rem
);
}
#main-wrapper > .container {
max-width: $main-content-max-width;
padding-left: 1.75rem !important;
padding-right: 1.75rem !important;
max-width: variables.$main-content-max-width;
@include module.pl-pr(1.75rem, true);
}
main.col-12,
@@ -1507,12 +1496,12 @@ search {
#back-to-top {
right: calc(
(100vw - $sidebar-width-large - $main-content-max-width) / 2 + 2rem
(100vw - variables.$sidebar-width-large - variables.$main-content-max-width) / 2 + 2rem
);
}
#sidebar {
width: $sidebar-width-large;
width: variables.$sidebar-width-large;
.profile-wrapper {
margin-top: 3.5rem;
@@ -1522,7 +1511,7 @@ search {
ul {
li.nav-item {
@include pl-pr(2.75rem);
@include module.pl-pr(2.75rem);
}
}
@@ -1531,11 +1520,11 @@ search {
margin-bottom: 1.75rem;
a:not(:last-child) {
margin-right: $sb-btn-gap-lg;
margin-right: variables.$sb-btn-gap-lg;
}
.icon-border {
@include ml-mr(calc(($sb-btn-gap-lg - $btn-border-width) / 2));
@include module.ml-mr(calc((variables.$sb-btn-gap-lg - $btn-border-width) / 2));
}
}
}

View File

@@ -1,3 +1,5 @@
@use 'variables';
/*
* Mainly scss modules, only imported to `assets/css/main.scss`
*/
@@ -7,7 +9,8 @@
%heading {
color: var(--heading-color);
font-weight: 400;
font-family: $font-family-heading;
font-family: variables.$font-family-heading;
scroll-margin-top: 3.5rem;
}
%anchor {
@@ -81,7 +84,7 @@
}
%rounded {
border-radius: $radius-lg;
border-radius: variables.$radius-lg;
}
%img-caption {
@@ -111,6 +114,16 @@
-webkit-box-orient: vertical;
}
@mixin text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
%text-ellipsis {
@include text-ellipsis;
}
%text-highlight {
color: var(--text-muted-highlight-color);
font-weight: 600;
@@ -134,6 +147,12 @@
}
}
%btn-color {
button i {
color: #999999;
}
}
/* ---------- scss mixin --------- */
@mixin mt-mb($value) {
@@ -151,9 +170,14 @@
padding-bottom: $val;
}
@mixin pl-pr($val) {
padding-left: $val;
padding-right: $val;
@mixin pl-pr($val, $important: false) {
@if $important {
padding-left: $val !important;
padding-right: $val !important;
} @else {
padding-left: $val;
padding-right: $val;
}
}
@mixin placeholder {

View File

@@ -2,29 +2,31 @@
* The syntax highlight.
*/
@import 'colors/syntax-light';
@import 'colors/syntax-dark';
@use '../colors/syntax-light';
@use '../colors/syntax-dark';
@use 'module';
@use 'variables';
html {
@media (prefers-color-scheme: light) {
&:not([data-mode]),
&[data-mode='light'] {
@include light-syntax;
@include syntax-light.light-syntax;
}
&[data-mode='dark'] {
@include dark-syntax;
@include syntax-dark.dark-syntax;
}
}
@media (prefers-color-scheme: dark) {
&:not([data-mode]),
&[data-mode='dark'] {
@include dark-syntax;
@include syntax-dark.dark-syntax;
}
&[data-mode='light'] {
@include light-syntax;
@include syntax-light.light-syntax;
}
}
}
@@ -47,19 +49,19 @@ html {
}
.highlight {
@extend %rounded;
@extend %code-snippet-bg;
@extend %rounded !optional;
@extend %code-snippet-bg !optional;
overflow: auto;
padding-bottom: 0.75rem;
@at-root figure#{&} {
@extend %code-snippet-bg;
@extend %code-snippet-bg !optional;
}
pre {
margin-bottom: 0;
font-size: $code-font-size;
font-size: variables.$code-font-size;
line-height: 1.4rem;
word-wrap: normal; /* Fixed Safari overflow-x */
}
@@ -101,10 +103,10 @@ code {
color: var(--code-color);
&.highlighter-rouge {
font-size: $code-font-size;
font-size: variables.$code-font-size;
padding: 3px 5px;
word-break: break-word;
border-radius: $radius-sm;
border-radius: variables.$radius-sm;
background-color: var(--inline-code-bg);
}
@@ -130,7 +132,7 @@ code {
}
td.rouge-code {
@extend %code-snippet-padding;
@extend %code-snippet-padding !optional;
/*
Prevent some browser extends from
@@ -144,13 +146,13 @@ td.rouge-code {
}
div[class^='language-'] {
@extend %rounded;
@extend %code-snippet-bg;
@extend %rounded !optional;
@extend %code-snippet-bg !optional;
box-shadow: var(--language-border-color) 0 0 0 1px;
.content > & {
@include ml-mr(-1rem);
@include module.ml-mr(-1rem);
border-radius: 0;
}
@@ -179,23 +181,23 @@ div {
}
.code-header {
@extend %no-cursor;
@extend %no-cursor !optional;
display: flex;
justify-content: space-between;
align-items: center;
height: $code-header-height;
height: variables.$code-header-height;
margin-left: 0.75rem;
margin-right: 0.25rem;
/* the label block */
span {
line-height: $code-header-height;
line-height: variables.$code-header-height;
/* label icon */
i {
font-size: 1rem;
width: $code-icon-width;
width: variables.$code-icon-width;
color: var(--code-header-icon-color);
&.small {
@@ -219,12 +221,12 @@ div {
/* clipboard */
button {
@extend %cursor-pointer;
@extend %rounded;
@extend %cursor-pointer !optional;
@extend %rounded !optional;
border: 1px solid transparent;
height: $code-header-height;
width: $code-header-height;
height: variables.$code-header-height;
width: variables.$code-header-height;
padding: 0;
background-color: inherit;
@@ -259,13 +261,13 @@ div {
@media all and (min-width: 576px) {
div[class^='language-'] {
.content > & {
@include ml-mr(0);
@include module.ml-mr(0);
border-radius: $radius-lg;
border-radius: variables.$radius-lg;
}
.code-header {
@include ml-mr(0);
@include module.ml-mr(0);
$dot-margin: 1rem;
@@ -273,19 +275,19 @@ div {
content: '';
display: inline-block;
margin-left: $dot-margin;
width: $code-dot-size;
height: $code-dot-size;
width: variables.$code-dot-size;
height: variables.$code-dot-size;
border-radius: 50%;
background-color: var(--code-header-muted-color);
box-shadow: ($code-dot-size + $code-dot-gap) 0 0
box-shadow: (variables.$code-dot-size + variables.$code-dot-gap) 0 0
var(--code-header-muted-color),
($code-dot-size + $code-dot-gap) * 2 0 0
(variables.$code-dot-size + variables.$code-dot-gap) * 2 0 0
var(--code-header-muted-color);
}
span {
// center the text of label
margin-left: calc(($dot-margin + $code-dot-size) / 2 * -1);
margin-left: calc(($dot-margin + variables.$code-dot-size) / 2 * -1);
}
}
}

View File

@@ -22,7 +22,6 @@
--btn-border-color: #2e2f31;
--btn-backtotop-color: var(--text-color);
--btn-backtotop-border-color: #212122;
--btn-box-shadow: var(--main-bg);
--card-header-bg: #292929;
--checkbox-color: rgb(118, 120, 121);
--checkbox-checked-color: var(--link-color);
@@ -60,6 +59,7 @@
/* Posts */
--toc-highlight: rgb(116, 178, 243);
--toc-popup-border-color: #373737;
--tag-hover: rgb(43, 56, 62);
--tb-odd-bg: #252526; /* odd rows of the posts' table */
--tb-even-bg: rgb(31, 31, 34); /* even rows of the posts' table */

View File

@@ -22,7 +22,6 @@
--btn-border-color: #e9ecef;
--btn-backtotop-color: #686868;
--btn-backtotop-border-color: #f1f1f1;
--btn-box-shadow: #eaeaea;
--checkbox-color: #c5c5c5;
--checkbox-checked-color: #07a8f7;
--img-bg: radial-gradient(
@@ -63,6 +62,7 @@
/* Posts */
--toc-highlight: #0550ae;
--toc-popup-border-color: lightgray;
--btn-share-color: gray;
--btn-share-hover-color: #0d6efd;
--card-bg: white;

View File

@@ -23,7 +23,7 @@
margin-left: -$timeline-width;
&::before {
@extend %timeline;
@extend %timeline !optional;
height: 72px;
left: 79px;
@@ -31,7 +31,7 @@
}
&:first-child::before {
@extend %timeline;
@extend %timeline !optional;
height: 32px;
top: 24px;
@@ -58,9 +58,8 @@
li {
font-size: 1.1rem;
line-height: 3rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis !optional;
&:nth-child(odd) {
background-color: var(--main-bg, #ffffff);
@@ -75,7 +74,7 @@
}
&::before {
@extend %timeline;
@extend %timeline !optional;
top: 0;
left: 77px;

View File

@@ -1,3 +1,5 @@
@use '../addon/variables';
/*
Style for Tab Categories
*/
@@ -12,11 +14,11 @@
&.card,
.list-group {
@extend %rounded;
@extend %rounded !optional;
}
.card-header {
$radius: calc($radius-lg - 1px);
$radius: calc(variables.$radius-lg - 1px);
padding: 0.75rem;
border-radius: $radius;
@@ -29,7 +31,7 @@
}
i {
@extend %category-icon-color;
@extend %category-icon-color !optional;
font-size: 86%; /* fontawesome icons */
}

View File

@@ -1,3 +1,5 @@
@use '../addon/module';
/*
Style for page Category and Tag
*/
@@ -28,7 +30,7 @@
/* post's title */
> a {
@extend %no-bottom-border;
@extend %no-bottom-border !optional;
font-size: 1.1rem;
}
@@ -48,7 +50,7 @@
#page-tag,
#access-lastmod {
a:hover {
@extend %link-hover;
@extend %link-hover !optional;
margin-bottom: -1px; /* Avoid jumping */
}
@@ -63,9 +65,7 @@
}
> a {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@include module.text-ellipsis;
}
}
}

View File

@@ -1,3 +1,5 @@
@use '../addon/variables';
/*
Style for Homepage
*/
@@ -20,14 +22,14 @@
background: none;
%img-radius {
border-radius: $radius-lg $radius-lg 0 0;
border-radius: variables.$radius-lg variables.$radius-lg 0 0;
}
.preview-img {
@extend %img-radius;
@extend %img-radius !optional;
img {
@extend %img-radius;
@extend %img-radius !optional;
}
}
@@ -36,7 +38,7 @@
padding: 1rem;
.card-title {
@extend %text-clip;
@extend %text-clip !optional;
color: var(--heading-color) !important;
font-size: 1.25rem;
@@ -47,10 +49,10 @@
}
.card-text.content {
@extend %muted;
@extend %muted !optional;
p {
@extend %text-clip;
@extend %text-clip !optional;
line-height: 1.5;
margin: 0;
@@ -58,7 +60,7 @@
}
.post-meta {
@extend %muted;
@extend %muted !optional;
i {
&:not(:first-child) {
@@ -67,16 +69,15 @@
}
em {
@extend %normal-font-style;
@extend %normal-font-style !optional;
color: inherit;
}
> div:first-child {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis !optional;
}
}
}
@@ -131,7 +132,7 @@
/* Tablet */
@media all and (min-width: 768px) {
%img-radius {
border-radius: 0 $radius-lg $radius-lg 0;
border-radius: 0 variables.$radius-lg variables.$radius-lg 0;
}
#post-list {

View File

@@ -1,6 +1,9 @@
/*
Post-specific style
*/
@use '../addon/module';
@use '../addon/variables';
/**
* Post-specific styles
*/
%btn-post-nav {
width: 50%;
@@ -16,7 +19,7 @@
header {
.post-desc {
@extend %heading;
@extend %heading !optional;
font-size: 1.125rem;
line-height: 1.6;
@@ -29,7 +32,7 @@ header {
em,
time {
@extend %text-highlight;
@extend %text-highlight !optional;
}
em {
@@ -45,7 +48,7 @@ header {
}
.post-tail-wrapper {
@extend %text-sm;
@extend %text-sm !optional;
margin-top: 6rem;
border-bottom: 1px double var(--main-border-color);
@@ -54,20 +57,20 @@ header {
line-height: 1.2rem;
> a {
@extend %text-highlight;
@extend %text-highlight !optional;
&:hover {
@extend %link-hover;
@extend %link-hover !optional;
}
}
span:last-child {
@extend %text-sm;
@extend %text-sm !optional;
}
} /* .license-wrapper */
.post-meta a:not(:hover) {
@extend %link-underline;
@extend %link-underline !optional;
}
.share-wrapper {
@@ -87,17 +90,17 @@ header {
i {
color: var(--btn-share-color);
@extend %icon-size;
@extend %icon-size !optional;
}
> * {
@extend %icon-size;
@extend %icon-size !optional;
margin-left: 0.5rem;
&:hover {
i {
@extend %btn-share-hovor;
@extend %btn-share-hover !optional;
}
}
}
@@ -107,7 +110,7 @@ header {
border: none;
line-height: inherit;
@extend %cursor-pointer;
@extend %cursor-pointer !optional;
}
} /* .share-icons */
} /* .share-wrapper */
@@ -131,16 +134,16 @@ header {
.post-tag {
&:hover {
@extend %link-hover;
@extend %tag-hover;
@extend %no-bottom-border;
@extend %link-hover !optional;
@extend %tag-hover !optional;
@extend %no-bottom-border !optional;
}
}
}
.post-navigation {
.btn {
@extend %btn-post-nav;
@extend %btn-post-nav !optional;
&:not(:hover) {
color: var(--link-color);
@@ -153,7 +156,7 @@ header {
}
&.disabled {
@extend %btn-post-nav;
@extend %btn-post-nav !optional;
pointer-events: auto;
cursor: not-allowed;
@@ -173,12 +176,12 @@ header {
}
&:first-child {
border-radius: $radius-lg 0 0 $radius-lg;
border-radius: variables.$radius-lg 0 0 variables.$radius-lg;
left: 0.5px;
}
&:last-child {
border-radius: 0 $radius-lg $radius-lg 0;
border-radius: 0 variables.$radius-lg variables.$radius-lg 0;
right: 0.5px;
}
}
@@ -228,6 +231,7 @@ header {
}
}
/* TOC panel */
#toc-wrapper {
border-left: 1px solid rgba(158, 158, 158, 0.17);
position: -webkit-sticky;
@@ -257,9 +261,8 @@ header {
.toc-link {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@extend %text-ellipsis !optional;
&:hover {
color: var(--toc-highlight);
@@ -290,25 +293,229 @@ header {
}
}
/* --- TOC button, bar and popup in mobile/tablet --- */
#toc-bar {
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 1;
margin: 0 -1rem;
height: variables.$topbar-height;
background: var(--main-bg);
border-bottom: 1px solid var(--main-border-color);
transition: all 0.2s ease-in-out;
@extend %btn-color !optional;
.label {
@extend %heading !optional;
margin-left: 0.25rem;
padding: 0 0.75rem;
color: inherit;
}
&.invisible {
top: -(variables.$topbar-height);
transition: none;
}
}
#toc-solo-trigger {
color: var(--text-muted-color);
border-color: var(--btn-border-color);
border-radius: variables.$radius-lg;
.label {
font-size: 1rem;
font-family: variables.$font-family-heading;
}
&:hover {
box-shadow: none;
background: none;
}
}
@mixin slide-in {
from {
opacity: 0.7;
transform: translateY(-(variables.$topbar-height));
}
to {
opacity: 1;
transform: translateY(0);
}
}
@mixin slide-out {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-(variables.$topbar-height));
opacity: 0;
}
}
@-webkit-keyframes slide-in {
@include slide-in;
}
@keyframes slide-in {
@include slide-in;
}
@-webkit-keyframes slide-out {
@include slide-out;
}
@keyframes slide-out {
@include slide-out;
}
#toc-popup {
$slide-in: slide-in 0.3s ease-out;
$slide-out: slide-out 0.3s ease-out;
$curtain-height: 2rem;
$backdrop: blur(5px);
border-color: var(--toc-popup-border-color);
border-width: 1px;
border-radius: variables.$radius-lg;
color: var(--text-color);
background: var(--card-bg);
margin-top: variables.$topbar-height;
min-width: 20rem;
font-size: 1.05rem;
@media all and (min-width: 576px) {
max-width: 32rem;
}
&[open] {
-webkit-animation: $slide-in;
animation: $slide-in;
}
&[closing] {
-webkit-animation: $slide-out;
animation: $slide-out;
}
@media all and (min-width: 850px) {
left: variables.$sidebar-width;
}
.header {
@extend %btn-color !optional;
position: -webkit-sticky;
position: sticky;
top: 0;
background-color: inherit;
border-bottom: 1px solid var(--main-border-color);
.label {
font-family: variables.$font-family-heading;
}
}
button {
> i {
font-size: 1.25rem;
vertical-align: middle;
}
&:focus-visible {
box-shadow: none;
}
}
ul {
list-style-type: none;
padding-left: 0;
li {
ul,
& + li {
margin-top: 0.25rem;
}
a {
display: flex;
line-height: 1.5;
padding: 0.375rem 0;
padding-right: 1.125rem;
&.toc-link::before {
display: none;
}
}
}
}
@for $i from 2 through 4 {
.node-name--H#{$i} {
padding-left: 1.125rem * ($i - 1);
}
}
.is-active-link {
color: var(--toc-highlight) !important;
font-weight: 600;
}
&::-webkit-backdrop {
-webkit-backdrop-filter: $backdrop;
backdrop-filter: $backdrop;
}
&::backdrop {
-webkit-backdrop-filter: $backdrop;
backdrop-filter: $backdrop;
}
&::after {
display: flex;
content: '';
position: relative;
background: linear-gradient(transparent, var(--card-bg) 70%);
height: $curtain-height;
}
#toc-popup-content {
overflow: auto;
max-height: calc(100vh - 4 * variables.$topbar-height);
font-family: variables.$font-family-heading;
margin-bottom: -$curtain-height;
}
}
/* --- Related Posts --- */
#related-posts {
> h3 {
@include label(1.1rem, 600);
@include module.label(1.1rem, 600);
}
time {
@extend %normal-font-style;
@extend %text-xs;
@extend %normal-font-style !optional;
@extend %text-xs !optional;
color: var(--text-muted-color);
}
p {
@extend %text-ellipsis !optional;
font-size: 0.9rem;
margin-bottom: 0.5rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: break-spaces;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
@@ -316,7 +523,7 @@ header {
.card {
h4 {
@extend %text-clip;
@extend %text-clip !optional;
}
}
}
@@ -328,14 +535,15 @@ header {
.utterances {
max-width: 100%;
min-height: 269px;
}
%btn-share-hovor {
%btn-share-hover {
color: var(--btn-share-hover-color) !important;
}
.share-label {
@include label(inherit, 400, inherit);
@include module.label(inherit, 400, inherit);
&::after {
content: ':';
@@ -362,9 +570,20 @@ header {
/* Hide SideBar and TOC */
@media all and (max-width: 849px) {
.post-navigation {
padding-left: 0;
padding-right: 0;
margin-left: -0.5rem;
margin-right: -0.5rem;
@include module.pl-pr(0);
@include module.ml-mr(-0.5rem);
}
}
@media all and (min-width: 1200px) {
h2,
h3,
h4 {
scroll-margin-top: 2rem;
}
#toc-bar,
#toc-solo-trigger {
display: none !important;
}
}

View File

@@ -1,2 +1,2 @@
@import 'dist/bootstrap';
@import 'main';
@use 'dist/bootstrap';
@use 'main';

View File

@@ -1,13 +1,13 @@
@import 'colors/typography-light';
@import 'colors/typography-dark';
@import 'addon/variables';
@import 'variables-hook';
@import 'addon/module';
@import 'addon/syntax';
@import 'addon/commons';
@import 'layout/home';
@import 'layout/post';
@import 'layout/tags';
@import 'layout/archives';
@import 'layout/categories';
@import 'layout/category-tag';
@use 'colors/typography-light';
@use 'colors/typography-dark';
@use 'addon/variables';
@use 'variables-hook';
@use 'addon/module';
@use 'addon/syntax';
@use 'addon/commons';
@use 'layout/home';
@use 'layout/post';
@use 'layout/tags';
@use 'layout/archives';
@use 'layout/categories';
@use 'layout/category-tag';

View File

@@ -1,7 +1,7 @@
---
---
@import 'main
@use 'main
{%- if jekyll.environment == 'production' -%}
.bundle
{%- endif -%}

View File

@@ -34,7 +34,7 @@ permalink: /feed.xml
<updated>{{ post.date | date_to_xmlschema }}</updated>
{% endif %}
<id>{{ post_absolute_url }}</id>
<content src="{{ post_absolute_url }}" />
<content type="text/html" src="{{ post_absolute_url }}" />
<author>
<name>{{ post.author | default: site.social.name }}</name>
</author>

View File

@@ -1,5 +1,12 @@
# Changelog
## [7.1.1](https://github.com/cotes2020/jekyll-theme-chirpy/compare/v7.1.0...v7.1.1) (2024-09-23)
### Bug Fixes
* **i18n:** correct fr-FR translations ([#1949](https://github.com/cotes2020/jekyll-theme-chirpy/issues/1949)) ([367262e](https://github.com/cotes2020/jekyll-theme-chirpy/commit/367262e74d1005bddf1328bb2b3a2b9e152c0086))
* **pwa:** site baseurl not passed to `app.js` ([#1955](https://github.com/cotes2020/jekyll-theme-chirpy/issues/1955)) ([5a63244](https://github.com/cotes2020/jekyll-theme-chirpy/commit/5a63244721d21b1ad3a0ae83420723a2f0379e8b))
## [7.1.0](https://github.com/cotes2020/jekyll-theme-chirpy/compare/v7.0.1...v7.1.0) (2024-08-27)
### Features

View File

@@ -2,7 +2,7 @@
Gem::Specification.new do |spec|
spec.name = "jekyll-theme-chirpy"
spec.version = "7.1.0"
spec.version = "7.1.1"
spec.authors = ["Cotes Chung"]
spec.email = ["cotes.chung@gmail.com"]

View File

@@ -1,6 +1,6 @@
{
"name": "jekyll-theme-chirpy",
"version": "7.1.0",
"version": "7.1.1",
"description": "A minimal, responsive, and feature-rich Jekyll theme for technical writing.",
"repository": {
"type": "git",
@@ -30,23 +30,23 @@
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-transform-class-properties": "^7.25.4",
"@babel/plugin-transform-private-methods": "^7.25.7",
"@babel/preset-env": "^7.25.4",
"@commitlint/cli": "^19.4.0",
"@commitlint/config-conventional": "^19.2.2",
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@rollup/plugin-babel": "^6.0.4",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-yaml": "^4.1.2",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"concurrently": "^8.2.2",
"concurrently": "^9.0.1",
"conventional-changelog-conventionalcommits": "^8.0.0",
"husky": "^9.1.5",
"husky": "^9.1.6",
"purgecss": "^6.0.0",
"rollup": "^4.21.0",
"semantic-release": "^24.1.0",
"stylelint": "^16.8.2",
"rollup": "^4.21.3",
"semantic-release": "^24.1.1",
"stylelint": "^16.9.0",
"stylelint-config-standard-scss": "^13.1.0"
},
"prettier": {

View File

@@ -1,7 +1,6 @@
import babel from '@rollup/plugin-babel';
import terser from '@rollup/plugin-terser';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import yaml from '@rollup/plugin-yaml';
import fs from 'fs';
import pkg from './package.json';
@@ -35,27 +34,34 @@ function insertFrontmatter() {
};
}
function build(filename, { src = SRC_DEFAULT, jekyll = false } = {}) {
function build(
filename,
{ src = SRC_DEFAULT, jekyll = false, outputName = null } = {}
) {
const input = `${src}/${filename}.js`;
return {
input: `${src}/${filename}.js`,
input,
output: {
file: `${DIST}/${filename}.min.js`,
format: 'iife',
name: 'Chirpy',
...(outputName !== null && { name: outputName }),
banner,
sourcemap: !isProd
sourcemap: !isProd && !jekyll
},
watch: {
include: `${src}/**`
include: input
},
plugins: [
babel({
babelHelpers: 'bundled',
presets: ['@babel/env'],
plugins: ['@babel/plugin-transform-class-properties']
plugins: [
'@babel/plugin-transform-class-properties',
'@babel/plugin-transform-private-methods'
]
}),
nodeResolve(),
yaml(),
isProd && terser(),
jekyll && insertFrontmatter()
]
@@ -71,6 +77,7 @@ export default [
build('page'),
build('post'),
build('misc'),
build('theme', { src: `${SRC_DEFAULT}/modules`, outputName: 'Theme' }),
build('app', { src: SRC_PWA, jekyll: true }),
build('sw', { src: SRC_PWA, jekyll: true })
];